From f74e2ee58023ea1f82b43dd9f8c60b0b362a327f Mon Sep 17 00:00:00 2001 From: zerobio <100849341+zerobio@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:48:27 +0800 Subject: [PATCH] =?UTF-8?q?"Sort=20By"=20=E6=B7=BB=E5=8A=A0=20"soft=20clip?= =?UTF-8?q?ped=20reads"=20=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Sort By" 添加 "soft clipped reads" 选项 --- igv.js | 62198 +++++++++++++++++++++++++++++++++++++++++++++++++++ igv.min.js | 48 + 2 files changed, 62246 insertions(+) create mode 100644 igv.js create mode 100644 igv.min.js diff --git a/igv.js b/igv.js new file mode 100644 index 0000000..2649187 --- /dev/null +++ b/igv.js @@ -0,0 +1,62198 @@ +(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$1 = window.document; + + var getProto = Object.getPrototypeOf; + + var slice = arr.slice; + + var concat = arr.concat; + + var push = arr.push; + + var indexOf = 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 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$1; + + 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$1 = "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$1, + + 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, + 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$1 + 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.call(ret, arr); + } + } + + return ret; + }, + + inArray: function (elem, arr, i) { + return arr == null ? -1 : indexOf.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.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 = "" + + ""; + + // 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 = "" + + ""; + + // 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: ) 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.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 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$1, + 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$1.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$1); + + + 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.call(jQuery(elem), this[0]); + } + + // Locate the position of the desired element + return indexOf.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$1, [jQuery]); + } + }); + + jQuery.ready.then = readyList.then; + + // The ready event handler and self cleanup method + function completed() { + document$1.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$1.readyState === "complete" || + (document$1.readyState !== "loading" && !document$1.documentElement.doScroll)) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout(jQuery.ready); + + } else { + + // Use the handy event callback + document$1.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, ""], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [1, "", "
"], + col: [2, "", "
"], + tr: [2, "", "
"], + td: [3, "", "
"], + + _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$1.createDocumentFragment(), + div = fragment.appendChild(document$1.createElement("div")), + input = document$1.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 = ""; + support.noCloneChecked = !!div.cloneNode(true).lastChild.defaultValue; + })(); + var documentElement = document$1.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$1.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 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 = /\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.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>"); + }, + + 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.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$1.createElement("div"), + div = document$1.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$1.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$1.createElement("input"), + select = document$1.createElement("select"), + opt = select.appendChild(document$1.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$1.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$1], + type = hasOwn.call(event, "type") ? event.type : event, + namespaces = hasOwn.call(event, "namespace") ? event.namespace.split(".") : []; + + cur = lastElement = tmp = elem = elem || document$1; + + // 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$1)) { + 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$1.implementation.createHTMLDocument("").body; + body.innerHTML = "
"; + 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$1.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$1.location.href; + context.head.appendChild(base); + } else { + context = document$1; + } + } + + 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(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 hideAll(selector) { + document.querySelectorAll(selector).forEach(elem => { hide(elem); }); + } + + function empty(elem) { + while(elem.firstChild){ + elem.removeChild(elem.firstChild); + } + } + + 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} + } + } + + 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(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 = (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 }); + + } + + var domUtils = /*#__PURE__*/Object.freeze({ + __proto__: null, + applyStyle: applyStyle, + create: create, + div: div, + empty: empty, + guid: guid$2, + hide: hide, + hideAll: hideAll, + offset: offset, + pageCoordinates: pageCoordinates, + relativeDOMBBox: relativeDOMBBox, + show: show, + translateMouseCoordinates: translateMouseCoordinates + }); + + function createCheckbox$1(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"], + }; + + var icons$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + createCheckbox: createCheckbox$1, + createIcon: createIcon + }); + + 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(); + }); + } + + var uiUtils = /*#__PURE__*/Object.freeze({ + __proto__: null, + attachDialogCloseHandlerWithParent: attachDialogCloseHandlerWithParent + }); + + /** + * 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, constraint) { + + handle.addEventListener('mousedown', dragStart.bind(target)); + + function dragStart(event) { + + event.stopPropagation(); + event.preventDefault(); + + const dragFunction = drag.bind(this); + const dragEndFunction = dragEnd.bind(this); + const computedStyle = getComputedStyle(this); + + dragData = + { + constraint, + dragFunction, + dragEndFunction, + screenX: event.screenX, + screenY: event.screenY, + top: parseInt(computedStyle.top.replace("px", "")), + left: parseInt(computedStyle.left.replace("px", "")) + }; + + document.addEventListener('mousemove', dragFunction); + document.addEventListener('mouseup', dragEndFunction); + document.addEventListener('mouseleave', dragEndFunction); + document.addEventListener('mouseexit', dragEndFunction); + } + } + + function drag(event) { + + if (!dragData) { + console.error("No drag data!"); + return; + } + event.stopPropagation(); + event.preventDefault(); + const dx = event.screenX - dragData.screenX; + const dy = event.screenY - dragData.screenY; + + const left = dragData.left + dx; + const top = dragData.constraint ? Math.max(dragData.constraint.minY, dragData.top + dy) : dragData.top + dy; + + this.style.left = `${ left }px`; + this.style.top = `${ top }px`; + } + + function dragEnd(event) { + + if (!dragData) { + console.error("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 } + ); + } + } + } + + 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); + this.clampLocation(e.clientX, e.clientY); + + } + + clampLocation(clientX, clientY) { + + const { width:w, height:h } = this.container.getBoundingClientRect(); + const wh = window.innerHeight; + const ww = window.innerWidth; + + const y = Math.min(wh - h, clientY); + const x = Math.min(ww - w, clientX); + 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" + }; + + 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$1("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; + } + + class GenericColorPicker extends GenericContainer { + + constructor({parent, width}) { + 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); + + for (let hexColorString of hexColorStrings) { + const swatch = div({class: 'igv-ui-color-swatch'}); + this.container.appendChild(swatch); + this.decorateSwatch(swatch, hexColorString); + } + + if (defaultColors) { + for (let hexColorString of defaultColors) { + const swatch = div({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); + }); + + } + + } + + function embedCSS$2() { + const style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.setAttribute('title', 'igv-ui.css'); + style.innerHTML = `.igv-ui-popover { + cursor: default; + position: absolute; + z-index: 2048; + border-color: #7F7F7F; + border-radius: 4px; + border-style: solid; + border-width: 1px; + font-family: "Open Sans", sans-serif; + font-size: small; + background-color: white; +} +.igv-ui-popover > div:first-child { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-between; + align-items: center; + width: 100%; + height: 24px; + cursor: move; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-color: #7F7F7F; + border-bottom-style: solid; + border-bottom-width: thin; + background-color: #eee; +} +.igv-ui-popover > div:first-child > div:first-child { + margin-left: 4px; +} +.igv-ui-popover > div:first-child > div:last-child { + margin-right: 4px; + height: 12px; + width: 12px; + color: #7F7F7F; +} +.igv-ui-popover > div:first-child > div:last-child:hover { + cursor: pointer; + color: #444; +} +.igv-ui-popover > div:last-child { + overflow-y: auto; + overflow-x: hidden; + max-height: 400px; + max-width: 800px; + background-color: white; +} +.igv-ui-popover > div:last-child > div { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + margin-left: 4px; + margin-right: 4px; + min-width: 220px; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.igv-ui-popover > div:last-child > div > span { + font-weight: bolder; +} +.igv-ui-popover > div:last-child hr { + width: 100%; +} + +.igv-ui-alert-dialog-container { + box-sizing: content-box; + position: absolute; + z-index: 2048; + top: 50%; + left: 50%; + width: 400px; + height: 200px; + border-color: #7F7F7F; + border-radius: 4px; + border-style: solid; + border-width: thin; + outline: none; + font-family: "Open Sans", sans-serif; + font-size: 15px; + font-weight: 400; + background-color: white; + display: flex; + flex-flow: column; + flex-wrap: nowrap; + justify-content: space-between; + align-items: center; +} +.igv-ui-alert-dialog-container > div:first-child { + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: flex-start; + align-items: center; + width: 100%; + height: 24px; + cursor: move; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-color: #7F7F7F; + border-bottom-style: solid; + border-bottom-width: thin; + background-color: #eee; +} +.igv-ui-alert-dialog-container > div:first-child div:first-child { + padding-left: 8px; +} +.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + color: #373737; + width: 100%; + height: calc(100% - 24px - 64px); + overflow-y: scroll; +} +.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy { + margin: 16px; + width: auto; + height: auto; + overflow-wrap: break-word; + word-break: break-word; + background-color: white; + border: unset; +} +.igv-ui-alert-dialog-container > div:last-child { + width: 100%; + margin-bottom: 10px; + background-color: white; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: center; + align-items: center; +} +.igv-ui-alert-dialog-container > div:last-child div { + margin: unset; + width: 40px; + height: 30px; + line-height: 30px; + text-align: center; + color: white; + font-family: "Open Sans", sans-serif; + font-size: small; + font-weight: 400; + border-color: #2B81AF; + border-style: solid; + border-width: thin; + border-radius: 4px; + background-color: #2B81AF; +} +.igv-ui-alert-dialog-container > div:last-child div:hover { + cursor: pointer; + border-color: #25597f; + background-color: #25597f; +} + +.igv-ui-color-swatch { + position: relative; + box-sizing: content-box; + display: flex; + flex-flow: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; + width: 32px; + height: 32px; + border-style: solid; + border-width: 2px; + border-color: white; + border-radius: 4px; +} + +.igv-ui-color-swatch:hover { + border-color: dimgray; +} + +.igv-ui-colorpicker-menu-close-button { + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: flex-end; + align-items: center; + width: 100%; + height: 32px; + margin-top: 4px; + margin-bottom: 4px; + padding-right: 8px; +} +.igv-ui-colorpicker-menu-close-button i.fa { + display: block; + margin-left: 4px; + margin-right: 4px; + color: #5f5f5f; +} +.igv-ui-colorpicker-menu-close-button i.fa:hover, +.igv-ui-colorpicker-menu-close-button i.fa:focus, +.igv-ui-colorpicker-menu-close-button i.fa:active { + cursor: pointer; + color: #0f0f0f; +} + +.igv-ui-generic-dialog-container { + box-sizing: content-box; + position: fixed; + top: 0; + left: 0; + width: 300px; + height: 200px; + border-color: #7F7F7F; + border-radius: 4px; + border-style: solid; + border-width: thin; + font-family: "Open Sans", sans-serif; + font-size: medium; + font-weight: 400; + z-index: 2048; + background-color: white; + display: flex; + flex-flow: column; + flex-wrap: nowrap; + justify-content: flex-start; + align-items: center; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header { + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: flex-end; + align-items: center; + width: 100%; + height: 24px; + cursor: move; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-color: #7F7F7F; + border-bottom-style: solid; + border-bottom-width: thin; + background-color: #eee; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div { + margin-right: 4px; + margin-bottom: 2px; + height: 12px; + width: 12px; + color: #7F7F7F; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover { + cursor: pointer; + color: #444; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner { + color: #373737; + width: 95%; + height: 24px; + line-height: 24px; + text-align: left; + margin-top: 8px; + padding-left: 8px; + overflow-wrap: break-word; + background-color: white; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input { + margin-top: 8px; + width: 95%; + height: 24px; + color: #373737; + line-height: 24px; + padding-left: 8px; + background-color: white; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: flex-start; + align-items: center; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input div { + width: 30%; + height: 100%; + font-size: 16px; + text-align: right; + padding-right: 8px; + background-color: white; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input { + display: block; + height: 100%; + width: 100%; + padding-left: 4px; + font-family: "Open Sans", sans-serif; + font-weight: 400; + color: #373737; + text-align: left; + outline: none; + border-style: solid; + border-width: thin; + border-color: #7F7F7F; + background-color: white; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input { + width: 50%; + font-size: 16px; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input { + margin-top: 8px; + width: calc(100% - 16px); + height: 24px; + color: #373737; + line-height: 24px; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: space-around; + align-items: center; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input { + display: block; + height: 100%; + width: 100%; + padding-left: 4px; + font-family: "Open Sans", sans-serif; + font-weight: 400; + color: #373737; + text-align: left; + outline: none; + border-style: solid; + border-width: thin; + border-color: #7F7F7F; + background-color: white; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input { + font-size: 16px; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel { + width: 100%; + height: 28px; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: space-around; + align-items: center; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div { + margin-top: 32px; + color: white; + font-family: "Open Sans", sans-serif; + font-size: 14px; + font-weight: 400; + width: 75px; + height: 28px; + line-height: 28px; + text-align: center; + border-color: transparent; + border-style: solid; + border-width: thin; + border-radius: 2px; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child { + margin-left: 32px; + margin-right: 0; + background-color: #5ea4e0; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child { + margin-left: 0; + margin-right: 32px; + background-color: #c4c4c4; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover { + cursor: pointer; + background-color: #3b5c7f; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover { + cursor: pointer; + background-color: #7f7f7f; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok { + width: 100%; + height: 36px; + margin-top: 32px; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: space-around; + align-items: center; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div { + width: 98px; + height: 36px; + line-height: 36px; + text-align: center; + color: white; + font-family: "Open Sans", sans-serif; + font-size: medium; + font-weight: 400; + border-color: white; + border-style: solid; + border-width: thin; + border-radius: 4px; + background-color: #2B81AF; +} +.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover { + cursor: pointer; + background-color: #25597f; +} + +.igv-ui-generic-container { + box-sizing: content-box; + position: absolute; + z-index: 2048; + background-color: white; + cursor: pointer; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + align-items: center; +} +.igv-ui-generic-container > div:first-child { + cursor: move; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: flex-end; + align-items: center; + height: 24px; + width: 100%; + background-color: #dddddd; +} +.igv-ui-generic-container > div:first-child > div { + display: block; + color: #5f5f5f; + cursor: pointer; + width: 14px; + height: 14px; + margin-right: 8px; + margin-bottom: 4px; +} + +.igv-ui-dialog { + z-index: 2048; + position: fixed; + width: fit-content; + height: fit-content; + display: flex; + flex-flow: column; + flex-wrap: nowrap; + justify-content: flex-start; + background-color: white; + border-color: #7F7F7F; + border-radius: 4px; + border-style: solid; + border-width: thin; + font-family: "Open Sans", sans-serif; + font-size: medium; + font-weight: 400; +} +.igv-ui-dialog .igv-ui-dialog-header { + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: flex-end; + align-items: center; + width: 100%; + height: 24px; + cursor: move; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-color: #7F7F7F; + border-bottom-style: solid; + border-bottom-width: thin; + background-color: #eee; +} +.igv-ui-dialog .igv-ui-dialog-header div { + margin-right: 4px; + margin-bottom: 2px; + height: 12px; + width: 12px; + color: #7F7F7F; +} +.igv-ui-dialog .igv-ui-dialog-header div:hover { + cursor: pointer; + color: #444; +} +.igv-ui-dialog .igv-ui-dialog-one-liner { + width: 95%; + height: 24px; + line-height: 24px; + text-align: left; + margin: 8px; + overflow-wrap: break-word; + background-color: white; + font-weight: bold; +} +.igv-ui-dialog .igv-ui-dialog-ok-cancel { + width: 100%; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: space-around; + align-items: center; +} +.igv-ui-dialog .igv-ui-dialog-ok-cancel div { + margin: 16px; + margin-top: 32px; + color: white; + font-family: "Open Sans", sans-serif; + font-size: 14px; + font-weight: 400; + width: 75px; + height: 28px; + line-height: 28px; + text-align: center; + border-color: transparent; + border-style: solid; + border-width: thin; + border-radius: 2px; +} +.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child { + background-color: #5ea4e0; +} +.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child { + background-color: #c4c4c4; +} +.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover { + cursor: pointer; + background-color: #3b5c7f; +} +.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover { + cursor: pointer; + background-color: #7f7f7f; +} +.igv-ui-dialog .igv-ui-dialog-ok { + width: 100%; + height: 36px; + margin-top: 32px; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + justify-content: space-around; + align-items: center; +} +.igv-ui-dialog .igv-ui-dialog-ok div { + width: 98px; + height: 36px; + line-height: 36px; + text-align: center; + color: white; + font-family: "Open Sans", sans-serif; + font-size: medium; + font-weight: 400; + border-color: white; + border-style: solid; + border-width: thin; + border-radius: 4px; + background-color: #2B81AF; +} +.igv-ui-dialog .igv-ui-dialog-ok div:hover { + cursor: pointer; + background-color: #25597f; +} + +.igv-ui-panel, .igv-ui-panel-row, .igv-ui-panel-column { + z-index: 2048; + background-color: white; + font-family: "Open Sans", sans-serif; + font-size: medium; + font-weight: 400; + display: flex; + justify-content: flex-start; + align-items: flex-start; +} + +.igv-ui-panel-column { + display: flex; + flex-direction: column; +} + +.igv-ui-panel-row { + display: flex; + flex-direction: row; +} + +.igv-ui-textbox { + background-color: white; + font-family: "Open Sans", sans-serif; + font-size: medium; + font-weight: 400; + display: flex; + justify-content: flex-start; + align-items: flex-start; +} + +.igv-ui-table { + background-color: white; +} + +.igv-ui-table thead { + position: sticky; + top: 0; +} + +.igv-ui-table th { + text-align: left; +} + +.igv-ui-table td { + padding-right: 20px; +} + +.igv-ui-table tr:hover { + background-color: lightblue; +} + +/*# sourceMappingURL=igv-ui.css.map */ +`; + document.head.append(style); + } + + if (typeof document !== 'undefined') { + + if (!stylesheetExists("igv-ui.css")) { + embedCSS$2(); + } + + function stylesheetExists(stylesheetName) { + for (let ss of document.styleSheets) { + ss = ss.href ? ss.href.replace(/^.*[\\\/]/, '') : ''; + if (ss === stylesheetName) { + return true + } + } + return false + } + } + + function createCheckbox(name, initialState) { + + const container = domUtils.div({class: 'igv-menu-popup-check-container'}); + + const div = domUtils.div(); + container.appendChild(div); + + const svg = icons$1.createIcon('check', (true === initialState ? '#444' : 'transparent')); + div.appendChild(svg); + + const label = domUtils.div(); + 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('
'); + menuItems.push(colorPickerMenuItem({trackView, label: "Set track color", option: "color"})); + menuItems.push(unsetColorMenuItem({trackView, label: "Unset track color"})); + if(trackView.track.config.type === 'wig' || trackView.track.config.type === 'annotation') { + menuItems.push(colorPickerMenuItem({trackView, label: "Set alt color", option: "altColor"})); + menuItems.push(unsetAltColorMenuItem({trackView, label: "Unset alt color"})); + } + } + + if (trackView.track.menuItemList) { + menuItems = menuItems.concat(trackView.track.menuItemList()); + } + + if (hasVizWindow || vizWindowTypes.has(trackView.track.type)) { + menuItems.push('
'); + menuItems.push(visibilityWindowMenuItem(trackView)); + } + + if (trackView.track.removable !== false) { + menuItems.push('
'); + menuItems.push(trackRemovalMenuItem(trackView)); + } + + return menuItems + }, + + numericDataMenuItems: function (trackView) { + + const menuItems = []; + + menuItems.push('
'); + + // Data range + const object = $$1('
'); + 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("Log scale", trackView.track.logScale)), + click: () => { + trackView.track.logScale = !trackView.track.logScale; + trackView.repaintViews(); + } + } + ); + } + + menuItems.push({ + object: $$1(createCheckbox("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('
'); + $e.text(item.name); + } else if (item.object) { + $e = item.object; + } else if (typeof item.label === 'string') { + $e = $$1('
'); + $e.html(item.label); + } else if (typeof item === 'string') { + + if (item.startsWith("<")) { + $e = $$1(item); + } else { + $e = $$1("
" + item + "
"); + } + } + + 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('
'); + 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('
'); + object.text('Set visibility window'); + return {object, click} + + } + + function trackRemovalMenuItem(trackView) { + + const object = $$1('
'); + object.text('Remove track'); + + return {object, click: () => trackView.browser.removeTrack(trackView.track)} + + } + + function colorPickerMenuItem({trackView, label, option}) { + + const $e = $$1('
'); + $e.text(label); + + return { + object: $e, + click: () => trackView.presentColorPicker(option) + } + } + + function unsetColorMenuItem({trackView, label}) { + + const $e = $$1('
'); + $e.text(label); + + return { + object: $e, + click: () => { + trackView.track.color = undefined; + trackView.repaintViews(); + } + } + } + + function unsetAltColorMenuItem({trackView, label}) { + + const $e = $$1('
'); + $e.text(label); + + return { + object: $e, + click: () => { + trackView.track.altColor = 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('
'); + 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('
'); + 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 + } + + class DataRangeDialog { + + constructor(browser, $parent, alert) { + + this.browser = browser; + + // dialog container + this.$container = $$1("
", {class: 'igv-generic-dialog-container'}); + $parent.append(this.$container); + this.$container.offset({left: 0, top: 0}); + + // dialog header + const $header = $$1("
", {class: 'igv-generic-dialog-header'}); + this.$container.append($header); + uiUtils.attachDialogCloseHandlerWithParent($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("
", {class: 'igv-generic-dialog-label-input'}); + this.$container.append(this.$minimum); + const $mindiv = $$1('
'); + $mindiv.text('Minimum'); + this.$minimum.append($mindiv); + this.$minimum_input = $$1(""); + this.$minimum.append(this.$minimum_input); + + + // maximum + this.$maximum = $$1("
", {class: 'igv-generic-dialog-label-input'}); + this.$container.append(this.$maximum); + const $maxdiv = $$1('
'); + $maxdiv.text('Maximum'); + this.$maximum.append($maxdiv); + this.$maximum_input = $$1(""); + this.$maximum.append(this.$maximum_input); + + // ok | cancel + const $buttons = $$1("
", {class: 'igv-generic-dialog-ok-cancel'}); + this.$container.append($buttons); + + // ok + this.$ok = $$1("
"); + $buttons.append(this.$ok); + this.$ok.text('OK'); + + // cancel + this.$cancel = $$1("
"); + $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(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)) { + this.browser.alert.present(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(); + } + } + + /** + * Covers string literals and String objects + * @param x + * @returns {boolean} + */ + function isString$2(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 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; + } + + /** + * Return the filename from the path. Example + * https://foo.com/bar.bed?param=2 => bar.bed + * @param urlOrFile + */ + + function getFilename$2(urlOrFile) { + + if (urlOrFile.name !== undefined) { + return urlOrFile.name + } else if (isString$2(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')) + } + + 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); + } + + 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; + } + + /*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */ + // (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$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$1 = 3; + const MAX_MATCH$1$1 = 258; + /* The minimum and maximum match lengths */ + + // From deflate.h + /* =========================================================================== + * Internal compression state. + */ + + const LENGTH_CODES$1$1 = 29; + /* number of length codes, not counting the special END_BLOCK code */ + + const LITERALS$1$1 = 256; + /* number of literal bytes 0..255 */ + + const L_CODES$1$1 = LITERALS$1$1 + 1 + LENGTH_CODES$1$1; + /* number of Literal or Length codes, including the END_BLOCK code */ + + const D_CODES$1$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 + 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$1 = 512; /* see definition of array dist_code below */ + + // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 + const static_ltree$1 = new Array((L_CODES$1$1 + 2) * 2); + zero$1$1(static_ltree$1); + /* 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$1 = new Array(D_CODES$1$1 * 2); + zero$1$1(static_dtree$1); + /* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + + const _dist_code$1 = new Array(DIST_CODE_LEN$1); + zero$1$1(_dist_code$1); + /* 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$1 = new Array(MAX_MATCH$1$1 - MIN_MATCH$1$1 + 1); + zero$1$1(_length_code$1); + /* length code for each normalized match length (0 == MIN_MATCH) */ + + const base_length$1 = new Array(LENGTH_CODES$1$1); + zero$1$1(base_length$1); + /* First normalized length for each code (0 = MIN_MATCH) */ + + const base_dist$1 = new Array(D_CODES$1$1); + zero$1$1(base_dist$1); + /* 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$1[dist] : _dist_code$1[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; } + + // Tracev((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) { + // Tracev((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++) { + code = (code + bl_count[bits - 1]) << 1; + next_code[bits] = code; + } + /* 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< { + + 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 - 1; code++) { + base_length$1[code] = length; + for (n = 0; n < (1 << extra_lbits[code]); n++) { + _length_code$1[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$1[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + base_dist$1[code] = dist; + for (n = 0; n < (1 << extra_dbits[code]); n++) { + _dist_code$1[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$1; code++) { + base_dist$1[code] = dist << 7; + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { + _dist_code$1[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$1[n * 2 + 1]/*.Len*/ = 8; + n++; + bl_count[8]++; + } + while (n <= 255) { + static_ltree$1[n * 2 + 1]/*.Len*/ = 9; + n++; + bl_count[9]++; + } + while (n <= 279) { + static_ltree$1[n * 2 + 1]/*.Len*/ = 7; + n++; + bl_count[7]++; + } + while (n <= 287) { + static_ltree$1[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$1, L_CODES$1$1 + 1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES$1$1; n++) { + static_dtree$1[n * 2 + 1]/*.Len*/ = 5; + static_dtree$1[n * 2]/*.Code*/ = bi_reverse(n, 5); + } + + // Now data ready and we can init static trees + static_l_desc = new StaticTreeDesc(static_ltree$1, extra_lbits, LITERALS$1$1 + 1, L_CODES$1$1, MAX_BITS$1); + static_d_desc = new StaticTreeDesc(static_dtree$1, extra_dbits, 0, D_CODES$1$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$1; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } + for (n = 0; n < D_CODES$1$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.sym_next = 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; + }; + + /* =========================================================================== + * 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 sx = 0; /* running index in sym_buf */ + let code; /* the code to send */ + let extra; /* number of extra bits to send */ + + if (s.sym_next !== 0) { + do { + dist = s.pending_buf[s.sym_buf + sx++] & 0xff; + dist += (s.pending_buf[s.sym_buf + sx++] & 0xff) << 8; + lc = s.pending_buf[s.sym_buf + sx++]; + 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$1[lc]; + send_code(s, code + LITERALS$1$1 + 1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra !== 0) { + lc -= base_length$1[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$1[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and sym_buf is ok: */ + //Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); + + } while (sx < s.sym_next); + } + + 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 + * "block list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "allow 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) => { + /* block_mask is the bit mask of block-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + let block_mask = 0xf3ffc07f; + let n; + + /* Check for non-textual ("block-listed") bytes. */ + for (n = 0; n <= 31; n++, block_mask >>>= 1) { + if ((block_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) { + return Z_BINARY; + } + } + + /* Check for textual ("allow-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$1; n++) { + if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { + return Z_TEXT; + } + } + + /* There are no "block-listed" or "allow-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 */ + bi_windup(s); /* align on byte boundary */ + put_short(s, stored_len); + put_short(s, ~stored_len); + if (stored_len) { + s.pending_buf.set(s.window.subarray(buf, buf + stored_len), s.pending); + } + s.pending += stored_len; + }; + + + /* =========================================================================== + * 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$1); + bi_flush(s); + }; + + + /* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and write out the encoded block. + */ + 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->sym_next / 3)); + + 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$1, static_dtree$1); + + } 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) */ + + s.pending_buf[s.sym_buf + s.sym_next++] = dist; + s.pending_buf[s.sym_buf + s.sym_next++] = dist >> 8; + s.pending_buf[s.sym_buf + s.sym_next++] = lc; + 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$1[lc] + LITERALS$1$1 + 1) * 2]/*.Freq*/++; + s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; + } + + return (s.sym_next === s.sym_end); + }; + + 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$1 = (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$1 = adler32$1; + + // 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$1 = () => { + 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$1 = new Uint32Array(makeTable$1()); + + + const crc32$1 = (crc, buf, len, pos) => { + const t = crcTable$1; + 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$1 = crc32$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. + + var messages$1 = { + 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$1 = { + + /* 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$1; + + /*============================================================================*/ + + + 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; /* zlib header -> BUSY_STATE */ + //#ifdef GZIP + const GZIP_STATE = 57; /* gzip header -> BUSY_STATE | EXTRA_STATE */ + //#endif + const EXTRA_STATE = 69; /* gzip extra block -> NAME_STATE */ + const NAME_STATE = 73; /* gzip file name -> COMMENT_STATE */ + const COMMENT_STATE = 91; /* gzip comment -> HCRC_STATE */ + const HCRC_STATE = 103; /* gzip header CRC -> BUSY_STATE */ + const BUSY_STATE = 113; /* deflate -> FINISH_STATE */ + const FINISH_STATE = 666; /* stream complete */ + + 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$1[errorCode]; + return errorCode; + }; + + const rank = (f) => { + return ((f) * 2) - ((f) > 4 ? 9 : 0); + }; + + const zero = (buf) => { + let len = buf.length; while (--len >= 0) { buf[len] = 0; } + }; + + /* =========================================================================== + * Slide the hash table when sliding the window down (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. + */ + const slide_hash = (s) => { + let n, m; + let p; + let wsize = s.w_size; + + n = s.hash_size; + p = n; + do { + m = s.head[--p]; + s.head[p] = (m >= wsize ? m - wsize : 0); + } while (--n); + n = wsize; + //#ifndef FASTEST + p = n; + do { + m = s.prev[--p]; + s.prev[p] = (m >= wsize ? m - wsize : 0); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); + //#endif + }; + + /* 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, except for + * some deflate_stored() output, goes through this function so some + * applications may wish to modify it to avoid allocating a large + * strm->next_out 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$1(strm.adler, buf, len, start); + } + + else if (strm.state.wrap === 2) { + strm.adler = crc32_1$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 n, 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 - more), 0); + s.match_start -= _w_size; + s.strstart -= _w_size; + /* we now have strstart >= MAX_DIST */ + s.block_start -= _w_size; + if (s.insert > s.strstart) { + s.insert = s.strstart; + } + slide_hash(s); + 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. + * + * In case deflateParams() is used to later switch to a non-zero compression + * level, s->matches (otherwise unused when storing) keeps track of the number + * of hash table slides to perform. If s->matches is 1, then one hash table + * slide will be done when switching. If s->matches is 2, the maximum value + * allowed here, then the hash table will be cleared, since two or more slides + * is the same as a clear. + * + * deflate_stored() is written to minimize the number of times an input byte is + * copied. It is most efficient with large input and output buffers, which + * maximizes the opportunites to have a single copy from next_in to next_out. + */ + const deflate_stored = (s, flush) => { + + /* Smallest worthy block size when not flushing or finishing. By default + * this is 32K. This can be as small as 507 bytes for memLevel == 1. For + * large input and output buffers, the stored block size will be larger. + */ + let min_block = s.pending_buf_size - 5 > s.w_size ? s.w_size : s.pending_buf_size - 5; + + /* Copy as many min_block or larger stored blocks directly to next_out as + * possible. If flushing, copy the remaining available input to next_out as + * stored blocks, if there is enough space. + */ + let len, left, have, last = 0; + let used = s.strm.avail_in; + do { + /* Set len to the maximum size block that we can copy directly with the + * available input data and output space. Set left to how much of that + * would be copied from what's left in the window. + */ + len = 65535/* MAX_STORED */; /* maximum deflate stored block length */ + have = (s.bi_valid + 42) >> 3; /* number of header bytes */ + if (s.strm.avail_out < have) { /* need room for header */ + break; + } + /* maximum stored block length that will fit in avail_out: */ + have = s.strm.avail_out - have; + left = s.strstart - s.block_start; /* bytes left in window */ + if (len > left + s.strm.avail_in) { + len = left + s.strm.avail_in; /* limit len to the input */ + } + if (len > have) { + len = have; /* limit len to the output */ + } + + /* If the stored block would be less than min_block in length, or if + * unable to copy all of the available input when flushing, then try + * copying to the window and the pending buffer instead. Also don't + * write an empty block when flushing -- deflate() does that. + */ + if (len < min_block && ((len === 0 && flush !== Z_FINISH$3) || + flush === Z_NO_FLUSH$2 || + len !== left + s.strm.avail_in)) { + break; + } + + /* Make a dummy stored block in pending to get the header bytes, + * including any pending bits. This also updates the debugging counts. + */ + last = flush === Z_FINISH$3 && len === left + s.strm.avail_in ? 1 : 0; + _tr_stored_block(s, 0, 0, last); + + /* Replace the lengths in the dummy stored block with len. */ + s.pending_buf[s.pending - 4] = len; + s.pending_buf[s.pending - 3] = len >> 8; + s.pending_buf[s.pending - 2] = ~len; + s.pending_buf[s.pending - 1] = ~len >> 8; + + /* Write the stored block header bytes. */ + flush_pending(s.strm); + + //#ifdef ZLIB_DEBUG + // /* Update debugging counts for the data about to be copied. */ + // s->compressed_len += len << 3; + // s->bits_sent += len << 3; + //#endif + + /* Copy uncompressed bytes from the window to next_out. */ + if (left) { + if (left > len) { + left = len; + } + //zmemcpy(s->strm->next_out, s->window + s->block_start, left); + s.strm.output.set(s.window.subarray(s.block_start, s.block_start + left), s.strm.next_out); + s.strm.next_out += left; + s.strm.avail_out -= left; + s.strm.total_out += left; + s.block_start += left; + len -= left; + } + + /* Copy uncompressed bytes directly from next_in to next_out, updating + * the check value. + */ + if (len) { + read_buf(s.strm, s.strm.output, s.strm.next_out, len); + s.strm.next_out += len; + s.strm.avail_out -= len; + s.strm.total_out += len; + } + } while (last === 0); + + /* Update the sliding window with the last s->w_size bytes of the copied + * data, or append all of the copied data to the existing window if less + * than s->w_size bytes were copied. Also update the number of bytes to + * insert in the hash tables, in the event that deflateParams() switches to + * a non-zero compression level. + */ + used -= s.strm.avail_in; /* number of input bytes directly copied */ + if (used) { + /* If any input was used, then no unused input remains in the window, + * therefore s->block_start == s->strstart. + */ + if (used >= s.w_size) { /* supplant the previous history */ + s.matches = 2; /* clear hash */ + //zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); + s.window.set(s.strm.input.subarray(s.strm.next_in - s.w_size, s.strm.next_in), 0); + s.strstart = s.w_size; + s.insert = s.strstart; + } + else { + if (s.window_size - s.strstart <= used) { + /* Slide the window down. */ + s.strstart -= s.w_size; + //zmemcpy(s->window, s->window + s->w_size, s->strstart); + s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0); + if (s.matches < 2) { + s.matches++; /* add a pending slide_hash() */ + } + if (s.insert > s.strstart) { + s.insert = s.strstart; + } + } + //zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); + s.window.set(s.strm.input.subarray(s.strm.next_in - used, s.strm.next_in), s.strstart); + s.strstart += used; + s.insert += used > s.w_size - s.insert ? s.w_size - s.insert : used; + } + s.block_start = s.strstart; + } + if (s.high_water < s.strstart) { + s.high_water = s.strstart; + } + + /* If the last block was written to next_out, then done. */ + if (last) { + return BS_FINISH_DONE; + } + + /* If flushing and all input has been consumed, then done. */ + if (flush !== Z_NO_FLUSH$2 && flush !== Z_FINISH$3 && + s.strm.avail_in === 0 && s.strstart === s.block_start) { + return BS_BLOCK_DONE; + } + + /* Fill the window with any remaining input. */ + have = s.window_size - s.strstart; + if (s.strm.avail_in > have && s.block_start >= s.w_size) { + /* Slide the window down. */ + s.block_start -= s.w_size; + s.strstart -= s.w_size; + //zmemcpy(s->window, s->window + s->w_size, s->strstart); + s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0); + if (s.matches < 2) { + s.matches++; /* add a pending slide_hash() */ + } + have += s.w_size; /* more space now */ + if (s.insert > s.strstart) { + s.insert = s.strstart; + } + } + if (have > s.strm.avail_in) { + have = s.strm.avail_in; + } + if (have) { + read_buf(s.strm, s.window, s.strstart, have); + s.strstart += have; + s.insert += have > s.w_size - s.insert ? s.w_size - s.insert : have; + } + if (s.high_water < s.strstart) { + s.high_water = s.strstart; + } + + /* There was not enough avail_out to write a complete worthy or flushed + * stored block to next_out. Write a stored block to pending instead, if we + * have enough input for a worthy block, or if flushing and there is enough + * room for the remaining input as a stored block in the pending buffer. + */ + have = (s.bi_valid + 42) >> 3; /* number of header bytes */ + /* maximum stored block length that will fit in pending: */ + have = s.pending_buf_size - have > 65535/* MAX_STORED */ ? 65535/* MAX_STORED */ : s.pending_buf_size - have; + min_block = have > s.w_size ? s.w_size : have; + left = s.strstart - s.block_start; + if (left >= min_block || + ((left || flush === Z_FINISH$3) && flush !== Z_NO_FLUSH$2 && + s.strm.avail_in === 0 && left <= have)) { + len = left > have ? have : left; + last = flush === Z_FINISH$3 && s.strm.avail_in === 0 && + len === left ? 1 : 0; + _tr_stored_block(s, s.block_start, len, last); + s.block_start += len; + flush_pending(s.strm); + } + + /* We've done all we can with the available input and output. */ + return last ? BS_FINISH_STARTED : 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.sym_next) { + /*** 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.sym_next) { + /*** 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.sym_next) { + /*** 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.sym_next) { + /*** 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.sym_buf = 0; /* buffer for distances and literals/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.sym_next = 0; /* running index in sym_buf */ + this.sym_end = 0; /* symbol table full when sym_next reaches this */ + + 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. + */ + } + + + /* ========================================================================= + * Check for a valid deflate stream state. Return 0 if ok, 1 if not. + */ + const deflateStateCheck = (strm) => { + + if (!strm) { + return 1; + } + const s = strm.state; + if (!s || s.strm !== strm || (s.status !== INIT_STATE && + //#ifdef GZIP + s.status !== GZIP_STATE && + //#endif + s.status !== EXTRA_STATE && + s.status !== NAME_STATE && + s.status !== COMMENT_STATE && + s.status !== HCRC_STATE && + s.status !== BUSY_STATE && + s.status !== FINISH_STATE)) { + return 1; + } + return 0; + }; + + + const deflateResetKeep = (strm) => { + + if (deflateStateCheck(strm)) { + 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 = + //#ifdef GZIP + s.wrap === 2 ? GZIP_STATE : + //#endif + 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 = -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 (deflateStateCheck(strm) || 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 || (windowBits === 8 && wrap !== 1)) { + 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.status = INIT_STATE; /* to pass state test in deflateReset() */ + + 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 */ + + /* We overlay pending_buf and sym_buf. This works since the average size + * for length/distance pairs over any compressed block is assured to be 31 + * bits or less. + * + * Analysis: The longest fixed codes are a length code of 8 bits plus 5 + * extra bits, for lengths 131 to 257. The longest fixed distance codes are + * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest + * possible fixed-codes length/distance pair is then 31 bits total. + * + * sym_buf starts one-fourth of the way into pending_buf. So there are + * three bytes in sym_buf for every four bytes in pending_buf. Each symbol + * in sym_buf is three bytes -- two for the distance and one for the + * literal/length. As each symbol is consumed, the pointer to the next + * sym_buf value to read moves forward three bytes. From that symbol, up to + * 31 bits are written to pending_buf. The closest the written pending_buf + * bits gets to the next sym_buf symbol to read is just before the last + * code is written. At that time, 31*(n-2) bits have been written, just + * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at + * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1 + * symbols are written.) The closest the writing gets to what is unread is + * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and + * can range from 128 to 32768. + * + * Therefore, at a minimum, there are 142 bits of space between what is + * written and what is read in the overlain buffers, so the symbols cannot + * be overwritten by the compressed data. That space is actually 139 bits, + * due to the three-bit fixed-code block header. + * + * That covers the case where either Z_FIXED is specified, forcing fixed + * codes, or when the use of fixed codes is chosen, because that choice + * results in a smaller compressed block than dynamic codes. That latter + * condition then assures that the above analysis also covers all dynamic + * blocks. A dynamic-code block will only be chosen to be emitted if it has + * fewer bits than a fixed-code block would for the same set of symbols. + * Therefore its average symbol length is assured to be less than 31. So + * the compressed data for a dynamic block also cannot overwrite the + * symbols from which it is being constructed. + */ + + s.pending_buf_size = s.lit_bufsize * 4; + s.pending_buf = new Uint8Array(s.pending_buf_size); + + // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) + //s->sym_buf = s->pending_buf + s->lit_bufsize; + s.sym_buf = s.lit_bufsize; + + //s->sym_end = (s->lit_bufsize - 1) * 3; + s.sym_end = (s.lit_bufsize - 1) * 3; + /* We avoid equality with lit_bufsize*3 because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ + + 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) => { + + if (deflateStateCheck(strm) || 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.avail_in !== 0 && !strm.input) || + (s.status === FINISH_STATE && flush !== Z_FINISH$3)) { + return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2); + } + + const old_flush = s.last_flush; + s.last_flush = flush; + + /* 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); + } + + /* Write the header */ + if (s.status === INIT_STATE && s.wrap === 0) { + s.status = BUSY_STATE; + } + if (s.status === INIT_STATE) { + /* zlib 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); + + 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); + s.status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s.pending !== 0) { + s.last_flush = -1; + return Z_OK$3; + } + } + //#ifdef GZIP + if (s.status === GZIP_STATE) { + /* 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; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s.pending !== 0) { + s.last_flush = -1; + return Z_OK$3; + } + } + 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$1(strm.adler, s.pending_buf, s.pending, 0); + } + s.gzindex = 0; + s.status = EXTRA_STATE; + } + } + if (s.status === EXTRA_STATE) { + if (s.gzhead.extra/* != Z_NULL*/) { + let beg = s.pending; /* start of bytes to update crc */ + let left = (s.gzhead.extra.length & 0xffff) - s.gzindex; + while (s.pending + left > s.pending_buf_size) { + let copy = s.pending_buf_size - s.pending; + // zmemcpy(s.pending_buf + s.pending, + // s.gzhead.extra + s.gzindex, copy); + s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + copy), s.pending); + s.pending = s.pending_buf_size; + //--- HCRC_UPDATE(beg) ---// + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1$1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + //---// + s.gzindex += copy; + flush_pending(strm); + if (s.pending !== 0) { + s.last_flush = -1; + return Z_OK$3; + } + beg = 0; + left -= copy; + } + // JS specific: s.gzhead.extra may be TypedArray or Array for backward compatibility + // TypedArray.slice and TypedArray.from don't exist in IE10-IE11 + let gzhead_extra = new Uint8Array(s.gzhead.extra); + // zmemcpy(s->pending_buf + s->pending, + // s->gzhead->extra + s->gzindex, left); + s.pending_buf.set(gzhead_extra.subarray(s.gzindex, s.gzindex + left), s.pending); + s.pending += left; + //--- HCRC_UPDATE(beg) ---// + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1$1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + //---// + s.gzindex = 0; + } + s.status = NAME_STATE; + } + if (s.status === NAME_STATE) { + if (s.gzhead.name/* != Z_NULL*/) { + let beg = s.pending; /* start of bytes to update crc */ + let val; + do { + if (s.pending === s.pending_buf_size) { + //--- HCRC_UPDATE(beg) ---// + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1$1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + //---// + flush_pending(strm); + if (s.pending !== 0) { + s.last_flush = -1; + return Z_OK$3; + } + beg = 0; + } + // 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); + //--- HCRC_UPDATE(beg) ---// + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1$1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + //---// + s.gzindex = 0; + } + s.status = COMMENT_STATE; + } + if (s.status === COMMENT_STATE) { + if (s.gzhead.comment/* != Z_NULL*/) { + let beg = s.pending; /* start of bytes to update crc */ + let val; + do { + if (s.pending === s.pending_buf_size) { + //--- HCRC_UPDATE(beg) ---// + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1$1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + //---// + flush_pending(strm); + if (s.pending !== 0) { + s.last_flush = -1; + return Z_OK$3; + } + beg = 0; + } + // 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); + //--- HCRC_UPDATE(beg) ---// + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1$1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + //---// + } + 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 !== 0) { + s.last_flush = -1; + return Z_OK$3; + } + } + 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; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s.pending !== 0) { + s.last_flush = -1; + return Z_OK$3; + } + } + //#endif + + /* 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.level === 0 ? deflate_stored(s, flush) : + 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; + } + } + } + + 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 (deflateStateCheck(strm)) { + return Z_STREAM_ERROR$2; + } + + const status = strm.state.status; + + 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 (deflateStateCheck(strm)) { + 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$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.deflateGetDictionary = deflateGetDictionary; + 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$1 = (obj, key) => { + return Object.prototype.hasOwnProperty.call(obj, key); + }; + + var assign$1 = 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$1(source, p)) { + obj[p] = source[p]; + } + } + } + + return obj; + }; + + + // Join array of chunks to single array. + var flattenChunks$1 = (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$1 = { + assign: assign$1, + flattenChunks: flattenChunks$1 + }; + + // 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$1 = true; + + try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK$1 = 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$1 = new Uint8Array(256); + for (let q = 0; q < 256; q++) { + _utf8len$1[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1); + } + _utf8len$1[254] = _utf8len$1[254] = 1; // Invalid sequence start + + + // convert string to array (typed, when possible) + var string2buf$1 = (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$1 = (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$1) { + 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$1 = (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$1[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$1(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$1 = (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$1[buf[pos]] > max) ? pos : max; + }; + + var strings$1 = { + string2buf: string2buf$1, + buf2string: buf2string$1, + utf8border: utf8border$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. + + function ZStream$1() { + /* 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$1 = ZStream$1; + + const toString$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$1; + + /* ===========================================================================*/ + + + /** + * 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$1.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$1(); + 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$1[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$1.string2buf(opt.dictionary); + } else if (toString$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$1[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$1.string2buf(data); + } else if (toString$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$1.flattenChunks(this.chunks); + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; + }; + + + /** + * deflate(data[, options]) -> Uint8Array + * - data (Uint8Array|ArrayBuffer|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$1[deflator.err]; } + + return deflator.result; + } + + + /** + * deflateRaw(data[, options]) -> Uint8Array + * - data (Uint8Array|ArrayBuffer|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|ArrayBuffer|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$1; + + 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$1 = 16209; /* got a data error -- remain here until reset */ + const TYPE$1$1 = 16191; /* 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$1 = 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$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$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$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$1; + break top; + } + else { + strm.msg = 'invalid literal/length code'; + state.mode = BAD$1$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$1 = 15; + const ENOUGH_LENS$1$1 = 852; + const ENOUGH_DISTS$1$1 = 592; + //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + + const CODES$1$1 = 0; + const LENS$1$1 = 1; + const DISTS$1$1 = 2; + + const lbase$1 = 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$1 = 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$1 = 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$1 = 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$1 = (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 shoextra; /* extra bits table to use */ + let match; /* use base and extra for symbol >= match */ + const count = new Uint16Array(MAXBITS$1 + 1); //[MAXBITS+1]; /* number of codes of each length */ + const offs = new Uint16Array(MAXBITS$1 + 1); //[MAXBITS+1]; /* offsets in table for each length */ + let extra = null; + + 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$1; 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$1; 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$1; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) { + return -1; + } /* over-subscribed */ + } + if (left > 0 && (type === CODES$1$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$1; 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$1) { + base = extra = work; /* dummy value--not used */ + match = 20; + + } else if (type === LENS$1$1) { + base = lbase$1; + extra = lext$1; + match = 257; + + } else { /* DISTS */ + base = dbase$1; + extra = dext$1; + match = 0; + } + + /* 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$1 && used > ENOUGH_LENS$1$1) || + (type === DISTS$1$1 && used > ENOUGH_DISTS$1$1)) { + return 1; + } + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here_bits = len - drop; + if (work[sym] + 1 < match) { + here_op = 0; + here_val = work[sym]; + } + else if (work[sym] >= match) { + here_op = extra[work[sym] - match]; + here_val = base[work[sym] - match]; + } + 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$1 && used > ENOUGH_LENS$1$1) || + (type === DISTS$1$1 && used > ENOUGH_DISTS$1$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$1 = inflate_table$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. + + + + + + + const CODES$2 = 0; + const LENS$2 = 1; + const DISTS$2 = 2; + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + const { + Z_FINISH: Z_FINISH$1$1, Z_BLOCK: Z_BLOCK$2, Z_TREES: Z_TREES$1, + Z_OK: Z_OK$1$1, Z_STREAM_END: Z_STREAM_END$1$1, Z_NEED_DICT: Z_NEED_DICT$1$1, Z_STREAM_ERROR: Z_STREAM_ERROR$1$1, Z_DATA_ERROR: Z_DATA_ERROR$1$1, Z_MEM_ERROR: Z_MEM_ERROR$1$1, Z_BUF_ERROR: Z_BUF_ERROR$2, + Z_DEFLATED: Z_DEFLATED$3 + } = constants$2$1; + + + /* STATES ====================================================================*/ + /* ===========================================================================*/ + + + const HEAD$1 = 16180; /* i: waiting for magic header */ + const FLAGS$1 = 16181; /* i: waiting for method and flags (gzip) */ + const TIME$1 = 16182; /* i: waiting for modification time (gzip) */ + const OS$1 = 16183; /* i: waiting for extra flags and operating system (gzip) */ + const EXLEN$1 = 16184; /* i: waiting for extra length (gzip) */ + const EXTRA$1 = 16185; /* i: waiting for extra bytes (gzip) */ + const NAME$1 = 16186; /* i: waiting for end of file name (gzip) */ + const COMMENT$1 = 16187; /* i: waiting for end of comment (gzip) */ + const HCRC$1 = 16188; /* i: waiting for header crc (gzip) */ + const DICTID$1 = 16189; /* i: waiting for dictionary check value */ + const DICT$1 = 16190; /* waiting for inflateSetDictionary() call */ + const TYPE$2 = 16191; /* i: waiting for type bits, including last-flag bit */ + const TYPEDO$1 = 16192; /* i: same, but skip check to exit inflate on new block */ + const STORED$1 = 16193; /* i: waiting for stored size (length and complement) */ + const COPY_$1 = 16194; /* i/o: same as COPY below, but only first time in */ + const COPY$1 = 16195; /* i/o: waiting for input or output to copy stored block */ + const TABLE$1 = 16196; /* i: waiting for dynamic block table lengths */ + const LENLENS$1 = 16197; /* i: waiting for code length code lengths */ + const CODELENS$1 = 16198; /* i: waiting for length/lit and distance code lengths */ + const LEN_$1 = 16199; /* i: same as LEN below, but only first time in */ + const LEN$1 = 16200; /* i: waiting for length/lit/eob code */ + const LENEXT$1 = 16201; /* i: waiting for length extra bits */ + const DIST$1 = 16202; /* i: waiting for distance code */ + const DISTEXT$1 = 16203; /* i: waiting for distance extra bits */ + const MATCH$1 = 16204; /* o: waiting for output space to copy string */ + const LIT$1 = 16205; /* o: waiting for output space to write literal */ + const CHECK$1 = 16206; /* i: waiting for 32-bit check value */ + const LENGTH$1 = 16207; /* i: waiting for 32-bit length (gzip) */ + const DONE$1 = 16208; /* finished check, done -- remain here until reset */ + const BAD$2 = 16209; /* got a data error -- remain here until reset */ + const MEM$1 = 16210; /* got an inflate() memory error -- remain here until reset */ + const SYNC$1 = 16211; /* looking for synchronization bytes to restart inflate() */ + + /* ===========================================================================*/ + + + + const ENOUGH_LENS$2 = 852; + const ENOUGH_DISTS$2 = 592; + //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + + const MAX_WBITS$2 = 15; + /* 32K LZ77 window */ + const DEF_WBITS$1 = MAX_WBITS$2; + + + const zswap32$1 = (q) => { + + return (((q >>> 24) & 0xff) + + ((q >>> 8) & 0xff00) + + ((q & 0xff00) << 8) + + ((q & 0xff) << 24)); + }; + + + function InflateState$1() { + this.strm = null; /* pointer back to this zlib stream */ + 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, + bit 2 true to validate check value */ + this.havedict = false; /* true if dictionary provided */ + this.flags = 0; /* gzip header method and flags (0 if zlib), or + -1 if raw or no header yet */ + 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 inflateStateCheck = (strm) => { + + if (!strm) { + return 1; + } + const state = strm.state; + if (!state || state.strm !== strm || + state.mode < HEAD$1 || state.mode > SYNC$1) { + return 1; + } + return 0; + }; + + + const inflateResetKeep$1 = (strm) => { + + if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1$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$1; + state.last = 0; + state.havedict = 0; + state.flags = -1; + 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$2); + state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS$2); + + state.sane = 1; + state.back = -1; + //Tracev((stderr, "inflate: reset\n")); + return Z_OK$1$1; + }; + + + const inflateReset$1 = (strm) => { + + if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1$1; } + const state = strm.state; + state.wsize = 0; + state.whave = 0; + state.wnext = 0; + return inflateResetKeep$1(strm); + + }; + + + const inflateReset2$1 = (strm, windowBits) => { + let wrap; + + /* get the state */ + if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1$1; } + const state = strm.state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 5; + 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$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$1(strm); + }; + + + const inflateInit2$1 = (strm, windowBits) => { + + if (!strm) { return Z_STREAM_ERROR$1$1; } + //strm.msg = Z_NULL; /* in case we return an error */ + + const state = new InflateState$1(); + + //if (state === Z_NULL) return Z_MEM_ERROR; + //Tracev((stderr, "inflate: allocated\n")); + strm.state = state; + state.strm = strm; + state.window = null/*Z_NULL*/; + state.mode = HEAD$1; /* to pass state test in inflateReset2() */ + const ret = inflateReset2$1(strm, windowBits); + if (ret !== Z_OK$1$1) { + strm.state = null/*Z_NULL*/; + } + return ret; + }; + + + const inflateInit$1 = (strm) => { + + return inflateInit2$1(strm, DEF_WBITS$1); + }; + + + /* + 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$1 = true; + + let lenfix$1, distfix$1; // We have no pointers in JS, so keep tables separate + + + const fixedtables$1 = (state) => { + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin$1) { + lenfix$1 = new Int32Array(512); + distfix$1 = 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$1(LENS$2, state.lens, 0, 288, lenfix$1, 0, state.work, { bits: 9 }); + + /* distance table */ + sym = 0; + while (sym < 32) { state.lens[sym++] = 5; } + + inftrees$1(DISTS$2, state.lens, 0, 32, distfix$1, 0, state.work, { bits: 5 }); + + /* do this just once */ + virgin$1 = false; + } + + state.lencode = lenfix$1; + state.lenbits = 9; + state.distcode = distfix$1; + 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$1 = (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$1 = (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 (inflateStateCheck(strm) || !strm.output || + (!strm.input && strm.avail_in !== 0)) { + return Z_STREAM_ERROR$1$1; + } + + state = strm.state; + if (state.mode === TYPE$2) { state.mode = TYPEDO$1; } /* 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$1; + + inf_leave: // goto emulation + for (;;) { + switch (state.mode) { + case HEAD$1: + if (state.wrap === 0) { + state.mode = TYPEDO$1; + 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 */ + if (state.wbits === 0) { + state.wbits = 15; + } + 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$1(state.check, hbuf, 2, 0); + //===// + + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = FLAGS$1; + break; + } + 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$2; + break; + } + if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED$3) { + strm.msg = 'unknown compression method'; + state.mode = BAD$2; + break; + } + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + len = (hold & 0x0f)/*BITS(4)*/ + 8; + if (state.wbits === 0) { + state.wbits = len; + } + if (len > 15 || len > state.wbits) { + strm.msg = 'invalid window size'; + state.mode = BAD$2; + 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; + + state.flags = 0; /* indicate zlib header */ + //Tracev((stderr, "inflate: zlib header ok\n")); + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = hold & 0x200 ? DICTID$1 : TYPE$2; + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + break; + case FLAGS$1: + //=== 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$3) { + strm.msg = 'unknown compression method'; + state.mode = BAD$2; + break; + } + if (state.flags & 0xe000) { + strm.msg = 'unknown header flags set'; + state.mode = BAD$2; + break; + } + if (state.head) { + state.head.text = ((hold >> 8) & 1); + } + if ((state.flags & 0x0200) && (state.wrap & 4)) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1$1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = TIME$1; + /* falls through */ + case TIME$1: + //=== 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) && (state.wrap & 4)) { + //=== 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$1(state.check, hbuf, 4, 0); + //=== + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = OS$1; + /* falls through */ + case OS$1: + //=== 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) && (state.wrap & 4)) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1$1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = EXLEN$1; + /* falls through */ + case EXLEN$1: + 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) && (state.wrap & 4)) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1$1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + else if (state.head) { + state.head.extra = null/*Z_NULL*/; + } + state.mode = EXTRA$1; + /* falls through */ + case EXTRA$1: + 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.wrap & 4)) { + state.check = crc32_1$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.wrap & 4)) { + state.check = crc32_1$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$1; + /* falls through */ + case COMMENT$1: + 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.wrap & 4)) { + state.check = crc32_1$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$1; + /* falls through */ + case HCRC$1: + if (state.flags & 0x0200) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((state.wrap & 4) && hold !== (state.check & 0xffff)) { + strm.msg = 'header crc mismatch'; + state.mode = BAD$2; + 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$2; + break; + case DICTID$1: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + strm.adler = state.check = zswap32$1(hold); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = DICT$1; + /* falls through */ + case DICT$1: + 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$1; + } + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = TYPE$2; + /* falls through */ + case TYPE$2: + if (flush === Z_BLOCK$2 || flush === Z_TREES$1) { break inf_leave; } + /* falls through */ + case TYPEDO$1: + if (state.last) { + //--- BYTEBITS() ---// + hold >>>= bits & 7; + bits -= bits & 7; + //---// + state.mode = CHECK$1; + 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$1; + break; + case 1: /* fixed block */ + fixedtables$1(state); + //Tracev((stderr, "inflate: fixed codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = LEN_$1; /* decode codes */ + if (flush === Z_TREES$1) { + //--- 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$1; + break; + case 3: + strm.msg = 'invalid block type'; + state.mode = BAD$2; + } + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break; + case STORED$1: + //--- 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$2; + break; + } + state.length = hold & 0xffff; + //Tracev((stderr, "inflate: stored length %u\n", + // state.length)); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = COPY_$1; + if (flush === Z_TREES$1) { break inf_leave; } + /* falls through */ + case COPY_$1: + state.mode = COPY$1; + /* falls through */ + case COPY$1: + 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$2; + break; + case TABLE$1: + //=== 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$2; + break; + } + //#endif + //Tracev((stderr, "inflate: table sizes ok\n")); + state.have = 0; + state.mode = LENLENS$1; + /* falls through */ + case LENLENS$1: + 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$1(CODES$2, 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$2; + break; + } + //Tracev((stderr, "inflate: code lengths ok\n")); + state.have = 0; + state.mode = CODELENS$1; + /* falls through */ + case CODELENS$1: + 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$2; + 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$2; + break; + } + while (copy--) { + state.lens[state.have++] = len; + } + } + } + + /* handle error breaks in while */ + if (state.mode === BAD$2) { 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$2; + 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$1(LENS$2, 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$2; + break; + } + + state.distbits = 6; + //state.distcode.copy(state.codes); + // Switch to use dynamic table + state.distcode = state.distdyn; + opts = { bits: state.distbits }; + ret = inftrees$1(DISTS$2, 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$2; + break; + } + //Tracev((stderr, 'inflate: codes ok\n')); + state.mode = LEN_$1; + if (flush === Z_TREES$1) { break inf_leave; } + /* falls through */ + case LEN_$1: + state.mode = LEN$1; + /* falls through */ + case LEN$1: + 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$1(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$2) { + 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$1; + break; + } + if (here_op & 32) { + //Tracevv((stderr, "inflate: end of block\n")); + state.back = -1; + state.mode = TYPE$2; + break; + } + if (here_op & 64) { + strm.msg = 'invalid literal/length code'; + state.mode = BAD$2; + break; + } + state.extra = here_op & 15; + state.mode = LENEXT$1; + /* falls through */ + case LENEXT$1: + 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$1; + /* falls through */ + case DIST$1: + 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$2; + break; + } + state.offset = here_val; + state.extra = (here_op) & 15; + state.mode = DISTEXT$1; + /* falls through */ + case DISTEXT$1: + 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$2; + break; + } + //#endif + //Tracevv((stderr, "inflate: distance %u\n", state.offset)); + state.mode = MATCH$1; + /* falls through */ + case MATCH$1: + 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$2; + 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$1; } + break; + case LIT$1: + if (left === 0) { break inf_leave; } + output[put++] = state.length; + left--; + state.mode = LEN$1; + break; + case CHECK$1: + 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 ((state.wrap & 4) && _out) { + strm.adler = state.check = + /*UPDATE_CHECK(state.check, put - _out, _out);*/ + (state.flags ? crc32_1$1(state.check, output, _out, put - _out) : adler32_1$1(state.check, output, _out, put - _out)); + + } + _out = left; + // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too + if ((state.wrap & 4) && (state.flags ? hold : zswap32$1(hold)) !== state.check) { + strm.msg = 'incorrect data check'; + state.mode = BAD$2; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: check matches trailer\n")); + } + state.mode = LENGTH$1; + /* falls through */ + case LENGTH$1: + if (state.wrap && state.flags) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((state.wrap & 4) && hold !== (state.total & 0xffffffff)) { + strm.msg = 'incorrect length check'; + state.mode = BAD$2; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: length matches trailer\n")); + } + state.mode = DONE$1; + /* falls through */ + case DONE$1: + ret = Z_STREAM_END$1$1; + break inf_leave; + case BAD$2: + ret = Z_DATA_ERROR$1$1; + break inf_leave; + case MEM$1: + return Z_MEM_ERROR$1$1; + case SYNC$1: + /* falls through */ + default: + return Z_STREAM_ERROR$1$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$2 && + (state.mode < CHECK$1 || flush !== Z_FINISH$1$1))) { + if (updatewindow$1(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 & 4) && _out) { + strm.adler = state.check = /*UPDATE_CHECK(state.check, strm.next_out - _out, _out);*/ + (state.flags ? crc32_1$1(state.check, output, _out, strm.next_out - _out) : adler32_1$1(state.check, output, _out, strm.next_out - _out)); + } + strm.data_type = state.bits + (state.last ? 64 : 0) + + (state.mode === TYPE$2 ? 128 : 0) + + (state.mode === LEN_$1 || state.mode === COPY_$1 ? 256 : 0); + if (((_in === 0 && _out === 0) || flush === Z_FINISH$1$1) && ret === Z_OK$1$1) { + ret = Z_BUF_ERROR$2; + } + return ret; + }; + + + const inflateEnd$1 = (strm) => { + + if (inflateStateCheck(strm)) { + return Z_STREAM_ERROR$1$1; + } + + let state = strm.state; + if (state.window) { + state.window = null; + } + strm.state = null; + return Z_OK$1$1; + }; + + + const inflateGetHeader$1 = (strm, head) => { + + /* check state */ + if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1$1; } + const state = strm.state; + if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR$1$1; } + + /* save header structure */ + state.head = head; + head.done = false; + return Z_OK$1$1; + }; + + + const inflateSetDictionary$1 = (strm, dictionary) => { + const dictLength = dictionary.length; + + let state; + let dictid; + let ret; + + /* check state */ + if (inflateStateCheck(strm)) { return Z_STREAM_ERROR$1$1; } + state = strm.state; + + if (state.wrap !== 0 && state.mode !== DICT$1) { + return Z_STREAM_ERROR$1$1; + } + + /* check for correct dictionary identifier */ + if (state.mode === DICT$1) { + dictid = 1; /* adler32(0, null, 0)*/ + /* dictid = adler32(dictid, dictionary, dictLength); */ + dictid = adler32_1$1(dictid, dictionary, dictLength, 0); + if (dictid !== state.check) { + return Z_DATA_ERROR$1$1; + } + } + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow$1(strm, dictionary, dictLength, dictLength); + if (ret) { + state.mode = MEM$1; + return Z_MEM_ERROR$1$1; + } + state.havedict = 1; + // Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK$1$1; + }; + + + var inflateReset_1$1 = inflateReset$1; + var inflateReset2_1$1 = inflateReset2$1; + var inflateResetKeep_1$1 = inflateResetKeep$1; + var inflateInit_1$1 = inflateInit$1; + var inflateInit2_1$1 = inflateInit2$1; + var inflate_2$1$1 = inflate$2$1; + var inflateEnd_1$1 = inflateEnd$1; + var inflateGetHeader_1$1 = inflateGetHeader$1; + var inflateSetDictionary_1$1 = inflateSetDictionary$1; + var inflateInfo$1 = 'pako inflate (from Nodeca project)'; + + /* Not implemented + module.exports.inflateCodesUsed = inflateCodesUsed; + 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; + module.exports.inflateValidate = inflateValidate; + */ + + var inflate_1$2$1 = { + inflateReset: inflateReset_1$1, + inflateReset2: inflateReset2_1$1, + inflateResetKeep: inflateResetKeep_1$1, + inflateInit: inflateInit_1$1, + inflateInit2: inflateInit2_1$1, + inflate: inflate_2$1$1, + inflateEnd: inflateEnd_1$1, + inflateGetHeader: inflateGetHeader_1$1, + inflateSetDictionary: inflateSetDictionary_1$1, + inflateInfo: inflateInfo$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. + + function GZheader$1() { + /* 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$1 = GZheader$1; + + const toString$2 = Object.prototype.toString; + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + const { + Z_NO_FLUSH: Z_NO_FLUSH$3, Z_FINISH: Z_FINISH$4, + Z_OK: Z_OK$4, Z_STREAM_END: Z_STREAM_END$4, Z_NEED_DICT: Z_NEED_DICT$2, Z_STREAM_ERROR: Z_STREAM_ERROR$3, Z_DATA_ERROR: Z_DATA_ERROR$3, Z_MEM_ERROR: Z_MEM_ERROR$2 + } = constants$2$1; + + /* ===========================================================================*/ + + + /** + * 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$1(options) { + this.options = common$1.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$1(); + this.strm.avail_out = 0; + + let status = inflate_1$2$1.inflateInit2( + this.strm, + opt.windowBits + ); + + if (status !== Z_OK$4) { + throw new Error(messages$1[status]); + } + + this.header = new gzheader$1(); + + inflate_1$2$1.inflateGetHeader(this.strm, this.header); + + // Setup dictionary + if (opt.dictionary) { + // Convert data if needed + if (typeof opt.dictionary === 'string') { + opt.dictionary = strings$1.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$1.inflateSetDictionary(this.strm, opt.dictionary); + if (status !== Z_OK$4) { + throw new Error(messages$1[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$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$4 : Z_NO_FLUSH$3; + + // 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$1.inflate(strm, _flush_mode); + + if (status === Z_NEED_DICT$2 && dictionary) { + status = inflate_1$2$1.inflateSetDictionary(strm, dictionary); + + if (status === Z_OK$4) { + status = inflate_1$2$1.inflate(strm, _flush_mode); + } else if (status === Z_DATA_ERROR$3) { + // Replace code with more verbose + status = Z_NEED_DICT$2; + } + } + + // Skip snyc markers if more data follows and not raw mode + while (strm.avail_in > 0 && + status === Z_STREAM_END$4 && + strm.state.wrap > 0 && + data[strm.next_in] !== 0) + { + inflate_1$2$1.inflateReset(strm); + status = inflate_1$2$1.inflate(strm, _flush_mode); + } + + switch (status) { + case Z_STREAM_ERROR$3: + case Z_DATA_ERROR$3: + case Z_NEED_DICT$2: + case Z_MEM_ERROR$2: + 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$4) { + + if (this.options.to === 'string') { + + let next_out_utf8 = strings$1.utf8border(strm.output, strm.next_out); + + let tail = strm.next_out - next_out_utf8; + let utf8str = strings$1.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$4 && last_avail_out === 0) continue; + + // Finalize if end of stream reached. + if (status === Z_STREAM_END$4) { + status = inflate_1$2$1.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$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$1.prototype.onEnd = function (status) { + // On success - join + if (status === Z_OK$4) { + if (this.options.to === 'string') { + this.result = this.chunks.join(''); + } else { + this.result = common$1.flattenChunks(this.chunks); + } + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; + }; + + + /** + * inflate(data[, options]) -> Uint8Array|String + * - data (Uint8Array|ArrayBuffer): 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$1(input, options) { + const inflator = new Inflate$1$1(options); + + inflator.push(input); + + // That will never happens, if you don't cheat with options :) + if (inflator.err) throw inflator.msg || messages$1[inflator.err]; + + return inflator.result; + } + + + /** + * inflateRaw(data[, options]) -> Uint8Array|String + * - data (Uint8Array|ArrayBuffer): 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$1(input, options) { + options = options || {}; + options.raw = true; + return inflate$1$1(input, options); + } + + + /** + * ungzip(data[, options]) -> Uint8Array|String + * - data (Uint8Array|ArrayBuffer): 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$1 = Inflate$1$1; + var inflate_2$2 = inflate$1$1; + var inflateRaw_1$1$1 = inflateRaw$1$1; + var ungzip$1$1 = inflate$1$1; + var constants$3 = constants$2$1; + + var inflate_1$1$1 = { + Inflate: Inflate_1$1$1, + inflate: inflate_2$2, + inflateRaw: inflateRaw_1$1$1, + ungzip: ungzip$1$1, + constants: constants$3 + }; + + const { Deflate, deflate, deflateRaw, gzip } = deflate_1$1; + + const { Inflate: Inflate$2, inflate: inflate$3, inflateRaw: inflateRaw$2, ungzip: ungzip$2 } = inflate_1$1$1; + var deflateRaw_1 = deflateRaw; + var inflate_1$3 = inflate$3; + var inflateRaw_1 = inflateRaw$2; + var ungzip_1$1 = ungzip$2; + + 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_blocks(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 ungzip_1$1(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 = inflateRaw_1(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$1(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'); + + 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 deflateRaw_1(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 = inflateRaw_1(compressedBytes); + + let str = ''; + for (let b of bytes) { + str += String.fromCharCode(b); + } + return str; + } + + + /** + * @param dataURI + * @returns {Array|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 = ungzip_1$1(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]; + } + }; + + // 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"; + + class Oauth { + + constructor() { + this.oauthTokens = {}; + } + + + setToken(token, host) { + host = host || DEFAULT_HOST; + this.oauthTokens[host] = token; + } + + getToken(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(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; + } + } + } + + // 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, '\\$&') + } + + 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}`) + } + + } + + /** + * 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$1.has(letter)) { + result += encodings$1.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$1 = new Map(); + encodings$1.set("!", "%21"); + encodings$1.set("#", "%23"); + encodings$1.set("$", "%24"); + encodings$1.set("%", "%25"); + encodings$1.set("&", "%26"); + encodings$1.set("'", "%27"); + encodings$1.set("(", "%28"); + encodings$1.set(")", "%29"); + encodings$1.set("*", "%2A"); + encodings$1.set("+", "%2B"); + encodings$1.set(",", "%2C"); + encodings$1.set("/", "%2F"); + encodings$1.set(":", "%3A"); + encodings$1.set(";", "%3B"); + encodings$1.set("=", "%3D"); + encodings$1.set("?", "%3F"); + encodings$1.set("@", "%40"); + encodings$1.set("[", "%5B"); + encodings$1.set("]", "%5D"); + encodings$1.set(" ", "%20"); + + // Convenience functions for the gapi oAuth library. + + async function init$1(config) { + + if (!(google.accounts.oauth2.initTokenClient)) { + throw new Error("Google accounts token client not loaded (https://accounts.google.com/gsi/client)") + } + + if (isInitialized()) { + throw new Error("Google client is already initialized") + } + + // Note: callback is added when accessToken is requested + const codeClientConfig = { + client_id: config.client_id, + scope: config.scope || 'https://www.googleapis.com/auth/userinfo.profile', + state: config.state || 'igv', + error: (err) => { + throw new Error(err.type) + }, + hint: config.hint, // Optional + hosted_domain: config.hosted_domain // Optional + }; + + const tokenClient = google.accounts.oauth2.initTokenClient(codeClientConfig); + + // Attach an object to keep igv state + google.igv = { + tokenClient: tokenClient, + apiKey: config.apiKey + }; + } + + function isInitialized() { + return window.google && window.google.igv + } + + /** + * 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() { + return (isInitialized() && google.igv.tokenResponse && Date.now() < google.igv.tokenExpiresAt) ? + google.igv.tokenResponse.access_token : + undefined + } + + + /** + * Return a promise for an access token for the given scope. If the user hasn't authorized the scope request it + * + * @param scope + * @returns {Promise} + */ + async function getAccessToken(scope) { + + if (!isInitialized()) { + throw Error("Google oAuth has not been initialized") + } + + if (google.igv.tokenResponse && + Date.now() < google.igv.tokenExpiresAt && + google.accounts.oauth2.hasGrantedAllScopes(google.igv.tokenResponse, scope)) { + return google.igv.tokenResponse.access_token + } else { + const tokenClient = google.igv.tokenClient; + return new Promise((resolve, reject) => { + try { + // Settle this promise in the response callback for requestAccessToken() + tokenClient.callback = (tokenResponse) => { + if (tokenResponse.error !== undefined) { + reject(tokenResponse); + } + google.igv.tokenResponse = tokenResponse; + google.igv.tokenExpiresAt = Date.now() + tokenResponse.expires_in * 1000; + resolve(tokenResponse.access_token); + }; + tokenClient.requestAccessToken({scope}); + } catch (err) { + console.log(err); + } + }) + } + } + + // gapi.auth2.getAuthInstance().isSignedIn.listen(status => { + // const user = gapi.auth2.getAuthInstance().currentUser.get() + // queryGoogleAuthenticationStatus(user, status) + // }) + + function getScopeForURL(url) { + if (isGoogleDriveURL(url)) { + return "https://www.googleapis.com/auth/drive.readonly" + } 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 google.igv.apiKey + } + + /** + * Return information about a specific google drive URL + * + * @param googleDriveURL + * @returns {Promise} + */ + 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) { + let scope = "https://www.googleapis.com/auth/drive.readonly"; + const access_token = await getAccessToken(scope); + 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; + } + + + function getDriveDownloadURL(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); + + + } + + // 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. + */ + + class IGVXhr { + + constructor() { + this.apiKey = undefined; + this.googleThrottle = new Throttle({ + requestsPerSecond: 8 + }); + this.RANGE_WARNING_GIVEN = false; + this.oauth = new Oauth(); + } + + setApiKey(key) { + this.apiKey = key; + } + + async loadArrayBuffer(url, options) { + options = options || {}; + if (!options.responseType) { + options.responseType = "arraybuffer"; + } + if (isFile(url)) { + return this._loadFileSlice(url, options) + } else { + return this.load(url, options) + } + } + + async loadJson(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 + } + } + + async loadString(path, options) { + options = options || {}; + if (path instanceof File) { + return this._loadStringFromFile(path, options) + } else { + return this._loadStringFromUrl(path, options) + } + } + + async 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 this._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 = getDriveDownloadURL(url); + } + if (isGoogleDriveURL(url) || url.startsWith("https://www.dropbox.com")) { + return this.googleThrottle.add(async () => { + return this._loadURL(url, options) + }) + } else { + return this._loadURL(url, options) + } + } + } else { + throw Error(`url must be either a 'File', 'string', 'function', or 'Promise'. Actual type: ${urlType}`) + } + } + + async _loadURL(url, options) { + + const self = this; + + url = mapUrl$1(url); + + options = options || {}; + + let oauthToken = options.oauthToken || this.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 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) { + let rangeEnd = ""; + if (range.size) { + rangeEnd = 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 + if (xhr.status === 0 || (xhr.status >= 200 && xhr.status <= 300)) { + if ("HEAD" === options.method) { + // Support fetching specific headers. Attempting to fetch all headers can be problematic with CORS + const headers = options.requestedHeaders || ['content-length']; + const headerMap = {}; + for (let h of headers) { + headerMap[h] = xhr.getResponseHeader(h); + } + resolve(headerMap); + } else { + // Assume "GET" or "POST" + 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 && !self.RANGE_WARNING_GIVEN) { + alert(`Warning: Range header ignored for URL: ${url}. This can have severe performance impacts.`); + } + resolve(xhr.response.slice(range.start, range.start + range.size)); + } else { + resolve(xhr.response); + } + } + } else if (xhr.status === 416) { + handleError(Error(`416 Unsatisfiable Range`)); + } 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 { + handleError(xhr.status); + } + } + }; + + + xhr.onerror = function (event) { + if (isGoogleURL(url) && !options.retries) { + tryGoogleAuth(); + } else { + 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) { + if (isGoogleURL(url) && !options.retries) { + tryGoogleAuth(); + } else { + handleError(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 self.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 _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 _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 _loadStringFromUrl(url, options) { + + options = options || {}; + options.responseType = "arraybuffer"; + const data = await this.load(url, options); + return arrayBufferToString(data) + } + + /** + * Explicity set an oAuth token for use with given host. If host is undefined token is used for google api access* + * @param token + * @param host + */ + setOauthToken(token, host) { + this.oauth.setToken(token, host); + } + + /** + * Return an oauth token for the URL if we have one. This method does not force sign-in, and the token may + * or may not be valid. Sign-in is triggered on request failure. + * * + * @param url + * @returns {*} + */ + getOauthToken(url) { + + // Google is the default provider, don't try to parse host for google URLs + const host = isGoogleURL(url) ? + undefined : + parseUri(url).host; + + // First check the explicit settings (i.e. token set through the API) + let token = this.oauth.getToken(host); + if (token) { + return token + } else if (host === undefined) { + // Now try Google oauth tokens previously obtained. This will return undefined if google oauth is not + // configured. + const googleToken = getCurrentGoogleAccessToken(); + if (googleToken && googleToken.expires_at > Date.now()) { + return googleToken.access_token + } + } + } + + /** + * This method should only be called when it is known the server supports HEAD requests. It is used to recover + * from 416 errors from out-of-spec WRT range request servers. Notably Globus. + * * * + * @param url + * @param options + * @returns {Promise} + */ + async getContentLength(url, options) { + options = options || {}; + options.method = 'HEAD'; + options.requestedHeaders = ['content-length']; + const headerMap = await this._loadURL(url, options); + const contentLengthString = headerMap['content-length']; + return contentLengthString ? Number.parseInt(contentLengthString) : 0 + } + + } + + function isGoogleStorageSigned(url) { + return url.indexOf("X-Goog-Signature") > -1 + } + + + /** + * 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 access_token = await getAccessToken(scope); + return access_token + } 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 access_token = getCurrentAccessToken(); + return access_token + } 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$1(url) { + + if (url.startsWith("https://www.dropbox.com")) { + return url.replace("//www.dropbox.com", "//dl.dropboxusercontent.com") + } else if (url.startsWith("https://drive.google.com")) { + return getDriveDownloadURL(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", "//s3.amazonaws.com/igv.broadinstitute.org") + } else if (url.includes("//igv.genepattern.org")) { + return url.replace("//igv.genepattern.org", "//igv-genepattern-org.s3.amazonaws.com") + } 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_blocks(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 + } + } + + + const igvxhr = new IGVXhr(); + + /* + * 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; + } + + 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})` + } + + function appleCrayonRGBA(name, alpha) { + const {r, g, b} = appleCrayonRGBPalette[name]; + return `rgba(${r},${g},${b},${alpha})` + } + + 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)) + + }; + + new RandomColorGenerator(); + + function randomRGB$1(min, max) { + + min = IGVMath.clamp(min, 0, 255); + max = IGVMath.clamp(max, 0, 255); + + const r = Math.round(Math.random() * (max - min) + min).toString(10); + const g = Math.round(Math.random() * (max - min) + min).toString(10); + const b = Math.round(Math.random() * (max - min) + min).toString(10); + return `rgb(${r},${g},${b})` + + } + + /* + * 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, properties = {}) { + 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(); + } + }, + + drawRandomColorVerticalLines: ctx => { + for (let x = 0; x < ctx.canvas.width; x++) { + IGVGraphics.fillRect(ctx, x, 0, 1, ctx.canvas.height, { fillStyle: randomRGB$1(100, 250) }); + } + }, + + labelTransformWithContext: (ctx, exe) => { + ctx.translate(exe, 0); + ctx.scale(-1, 1); + ctx.translate(-exe, 0); + } + + }; + + 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(); + } + + class Alert { + constructor(parent) { + this.dialog = new AlertDialog(parent); + + } + + present(alert, callback) { + this.dialog.present(alert, callback); + } + } + + 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' + ] + } + }; + + /* + * 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 && features.length > 0) { + min = Number.MAX_VALUE; + max = -Number.MAX_VALUE; + + for(let f of features) { + 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 validateGenomicExtent = function (chromosomeLengthBP, genomicExtent, minimumBP) { + + let ss = genomicExtent.start; + let ee = genomicExtent.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; + } + } + + genomicExtent.start = Math.ceil(ss); + genomicExtent.end = Math.floor(ee); + }; + + /*! + * 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$1(url) { + if (isString$2(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$2(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$2(obj) && obj.startsWith("data:")) + } + + function createColumn(columnContainer, className) { + const column = domUtils.div({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" + } + + // reference: https://pretagteam.com/question/find-element-height-including-margin + function getElementAbsoluteHeight(element) { + + // Get the DOM Node if you pass in a string + element = (typeof element === 'string') ? document.querySelector(element) : element; + + const styles = window.getComputedStyle(element); + const margin = parseFloat(styles['marginTop']) + parseFloat(styles['marginBottom']); + const height = element.offsetHeight; + + return Math.ceil(margin + height); + } + + /** + * 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, + * + * 1 2 3 4 5 6 7 8 9 10 11- + * chrom1 start1 end1 chrom2 start2 end2 name score strand1 strand2 + * + * However there are off spec variants, an important one being a 7 column format with score in place of the standard + * name column. + * + * A 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 + * + * The "hiccups" output is apparently not standardized as this is found at ENCODE, with a non-helpful "tsv" extension + * + * chr1 x1 x2 chr2 y1 y2 color observed expectedBL expectedDonut expectedH expectedV fdrBL fdrDonut fdrH fdrV numCollapsed centroid1 centroid2 radius + * chr9 136790000 136795000 chr9 136990000 136995000 0,255,255 101.0 31.100368 38.40316 56.948116 34.040756 1.1876738E-13 1.05936405E-13 2.5148233E-4 1.7220993E-13 1 136792500 136992500 25590 + * + * The "hiccups" documentation specfies yet another set of column headers + * chromosome1 x1 x2 chromosome2 y1 y2 color observed expected_bottom_left expected_donut expected_horizontal expected_vertical fdr_bottom_left fdr_donut fdr_horizontal fdr_vertical number_collapsed centroid1 centroid2 radius + * + * @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 + } + + // Determine if this is a "hiccups" file. Store result on "header" so it doesn't need repeated for every feature + if(header && header.hiccups === undefined) { + header.hiccups = header.columnNames ? isHiccups(header.columnNames) : false; + } + const hiccups = header && header.hiccups; + const stdColumns = hiccups ? 6 : 10; + + if(!hiccups) { + 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 > stdColumns && header.columnNames && header.columnNames.length === tokens.length) { + feature.extras = tokens.slice(stdColumns); + } + } + + + // 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]) + } + } + + function isHiccups(columns) { + return columns && (columns.includes("fdrDonut") || columns.includes("fdr_donut")) + } + + /* + * 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 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", + "tsv", + "hiccups", + "fasta", + "fa", + "fna", + "pytor" + ]); + + /** + * 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" + case "fasta": + case "fa": + case "fna": + return "fasta" + default: + if (knownFileExtensions.has(ext)) { + return ext + } else { + return undefined + } + + } + + } + + function inferIndexPath(url, extension) { + + if (isString$2(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 "hiccups": + 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" + case "fasta": + return "sequence" + case "pytor": + return "cnvpytor" + 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"; + } + } + + /** + * Attempt to infer the file format by reading a few lines from the header. Currently this only supports "tsv" extensions, + * it was added specifically for "hiccups" type tsv files in ENCODE. Might be expanded in the future. + * + * @param url + * @returns {Promise} + */ + async function inferFileFormatFromHeader(config) { + + if (config.url) { + const firstBytes = await igvxhr.loadString(config.url, buildOptions(config, {range: {start: 0, size: 1000}})); + if(firstBytes) { + const columnNames = firstBytes.split('\n')[0].split('\t'); + if(isHiccups(columnNames)) { + return "hiccups" + } + } + } + + return undefined + + } + + function registerFileFormats(name, fields) { + FileFormats[name] = {fields: fields}; + } + + var TrackUtils = /*#__PURE__*/Object.freeze({ + __proto__: null, + knownFileExtensions: knownFileExtensions, + getFormat: getFormat, + inferFileFormat: inferFileFormat, + inferFileFormatFromHeader: inferFileFormatFromHeader, + inferTrackType: inferTrackType, + inferIndexPath: inferIndexPath, + registerFileFormats: registerFileFormats + }); + + 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 + } + + class Chromosome { + constructor(name, order, bpLength) { + this.name = name; + this.order = order; + this.bpLength = bpLength; + } + } + + /* + * 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(); + + // 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.sequences.has(chr)) { + return undefined + } + + let seqSlice = this.sequences.get(chr).find(ss => ss.contains(start, end)); + if (!seqSlice) { + seqSlice = this.sequences.get(chr).find(ss => ss.overlaps(start, end)); + if (!seqSlice) { + return undefined + } + } + + start -= seqSlice.offset; + end -= seqSlice.offset; + + 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 = seqSlice.sequence; + 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 chrNameSet = new Set(); + const lines = splitLines$4(data); + const len = lines.length; + let lineNo = 0; + let order = 0; + let nextLine; + let current = {}; + while (lineNo < len) { + nextLine = lines[lineNo++].trim(); + if (nextLine.startsWith("#") || nextLine.length === 0) ; else if (nextLine.startsWith(">")) { + // Start the next sequence + if(current && current.seq) { + pushChromosome.call(this, current, order++); + } + + 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(':'); + current.chr = nameParts[0]; + current.seq = ""; + current.offset = 0; + 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) { // TODO this should be an error + current.offset = from - 1; + } + + // Check for chromosome length token + if (parts.length > 1 && parts[1].startsWith("@len=")) { + try { + current.length = parseInt(parts[1].trim().substring(5)); + } catch (e) { + current.length = undefined; + console.error(`Error parsing sequence length for ${nextLine}`); + } + } else { + current.length = undefined; + } + } + } else { + current.seq += nextLine; + } + // add last seq + if (current && current.seq) { + pushChromosome.call(this, current, order); + } + } + + function pushChromosome(current, order) { + const length = current.length || (current.offset + current.seq.length); + if (!chrNameSet.has(current.chr)) { + this.chromosomeNames.push(current.chr); + this.sequences.set(current.chr, []); + this.chromosomes[current.chr] = new Chromosome(current.chr, order, length); + chrNameSet.add(current.chr); + } else { + const c = this.chromosomes[current.chr]; + c.bpLength = Math.max(c.bpLength, length); + } + this.sequences.get(current.chr).push(new SequenceSlice(current.offset, current.seq)); + } + } + } + + class SequenceSlice { + + constructor(offset, sequence) { + this.offset = offset; + this.sequence = sequence; + } + + contains(start, end) { + return this.offset <= start && this.end >= end + } + + overlaps(start, end) { + return this.offset < end && this.end > start + } + + get end() { + return this.offset + this.sequence.length + } + + } + + 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++, 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++, 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 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)" + }; + + /* + * 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; + const BP_PER_PIXEL_THRESHOLD = 1 / 10; + + const bppFeatureFetchThreshold = 10; + + class SequenceTrack { + + constructor(config, browser) { + + this.config = config; + this.browser = browser; + this.type = "sequence"; + this.removable = config.removable === undefined ? true : config.removable; // Defaults to true + this.name = config.name; + this.id = config.id; + 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; + + // Hack for backward compatibility + if(config.url) { + config.fastaURL = config.url; + } + + if(!config.fastaURL) { + // Mark this as the genome reference sequence ==> backward compatibility convention + this.id = config.id || "sequence"; + } + + } + + 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 (!seq) { + seq = "Unknown sequence"; + } else if (this.reversed) { + seq = reverseComplementSequence(seq); + } + this.browser.alert.present(seq); + } + } + ]; + if (isSecureContext()) { + items.push({ + label: 'Copy visible sequence', + click: async () => { + let seq = await this.browser.genome.sequence.getSequence(chr, start, end); + if (!seq) { + seq = "Unknown sequence"; + } else if (this.reversed) { + seq = reverseComplementSequence(seq); + } + try { + await navigator.clipboard.writeText(seq); + } catch (e) { + console.error(e); + this.browser.alert.present(`error copying sequence to clipboard ${e}`); + } + } + + }); + } + items.push('
'); + + 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 + } + + /** + * Return the source for sequence. If an explicit fasta url is defined, use it, otherwise fetch sequence + * from the current genome + * * + * @returns {Promise} + */ + async getSequenceSource() { + if(this.config.fastaURL) { + if(!this.fasta) { + this.fasta = new WrappedFasta(this.config, this.browser.genome); + await this.fasta.init(); + } + return this.fasta + } else { + return this.browser.genome.sequence + } + } + + async getFeatures(chr, start, end, bpPerPixel) { + + start = Math.floor(start); + end = Math.floor(end); + + if (bpPerPixel && bpPerPixel > bppFeatureFetchThreshold) { + return null + } else { + const sequenceSource = await this.getSequenceSource(); + const sequence = await sequenceSource.getSequence(chr, start, end); + return { + bpStart: start, + sequence: sequence + } + } + } + + draw(options) { + + const ctx = options.context; + + if (options.features) { + + let sequence = options.features.sequence; + if(!sequence) { + return + } + + 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 offsetBP = bp - options.bpStart; + const aPixel = offsetBP / options.bpPerPixel; + const pixelWidth = 1 / options.bpPerPixel; + const baseLetter = sequence[seqIdx]; + const color = this.fillColor(baseLetter.toUpperCase()); + + if (options.bpPerPixel > BP_PER_PIXEL_THRESHOLD) { + IGVGraphics.fillRect(ctx, aPixel, FRAME_BORDER, pixelWidth, SEQUENCE_HEIGHT - FRAME_BORDER, {fillStyle: color}); + } else { + const textPixel = aPixel + 0.5 * (pixelWidth - ctx.measureText(baseLetter).width); + + + + + if ('y' === options.axis) { + ctx.save(); + IGVGraphics.labelTransformWithContext(ctx, textPixel); + IGVGraphics.strokeText(ctx, baseLetter, textPixel, SEQUENCE_HEIGHT, {strokeStyle: color}); + ctx.restore(); + } else { + 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' + return defaultNucleotideColors[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 + } + + } + + /** + * Wrapper for a Fasta object that does chr name alias translation. This is not neccessary for the genome fasta, + * as it defines the reference name, but can be neccessary if loading an additional fasta as a track + * + */ + class WrappedFasta { + + constructor(config, genome) { + this.config = config; + this.genome = genome; + } + + async init() { + this.fasta = await loadFasta(this.config); + this.chrNameMap = new Map(); + for(let name of this.fasta.chromosomeNames) { + this.chrNameMap.set(this.genome.getChromosomeName(name), name); + } + } + + async getSequence(chr, start, end) { + const chrName = this.chrNameMap.has(chr) ? this.chrNameMap.get(chr) : chr; + return this.fasta.getSequence(chrName, start, 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. + */ + + class Viewport { + + constructor(trackView, viewportColumn, referenceFrame, width) { + + this.guid = domUtils.guid(); + this.trackView = trackView; + this.referenceFrame = referenceFrame; + + this.browser = trackView.browser; + + this.$viewport = $$1('
'); + 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.contentTop = 0; + this.contentHeight = this.$viewport.height(); + + + 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.$viewport.append($$1(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) { + + this.contentTop = contentTop; + this.$viewport.height(); + + //this.$content.css('top', `${contentTop}px`) + // + // if (undefined === this.canvasVerticalRange || this.canvasVerticalRange.bottom < viewBottom || this.canvasVerticalRange.top > viewTop) { + // console.log("Repaint " + this.canvasVerticalRange) + // this.repaint() + // } + + } + + async loadFeatures() { + return undefined + } + + clearCache() { + + } + + async 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() + let currentContentHeight = this.contentHeight; + if (requiredContentHeight !== currentContentHeight) { + this.setContentHeight(requiredContentHeight); + } + } + } + } + + getContentHeight() { + //return this.$content.height() + return this.contentHeight + } + + setContentHeight(contentHeight) { + this.contentHeight = contentHeight; + } + + isLoading() { + return false + } + + saveSVG() { + + } + + isVisible() { + return this.$viewport.width() + } + + setWidth(width) { + this.$viewport.width(width); + } + + getWidth() { + return this.$viewport.width() + } + + getContentTop() { + return this.contentTop + } + + 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. + */ + + //helper function to format a string + 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   == \xa0 + lookup["\\xa0"] = ' '; + 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(); + this.__currentElement; + 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 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.15.9"; + 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 aliases; + if (aliasURL) { + aliases = await loadAliases(aliasURL, sequence.config); + } + + const genome = new Genome(options, sequence, aliases); + + // Delay loading cytbands untils after genome initialization to use chromosome aliases (1 vs chr1, etc). + if (cytobandUrl) { + genome.cytobands = await loadCytobands(cytobandUrl, sequence.config, genome); + } + + return genome + }, + + 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 (alert, idOrConfig) { + + // idOrConfig might be json + if (isString$2(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$2(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.present(new Error(`Unknown genome id: ${genomeID}`), undefined); + } + return reference + } else { + return idOrConfig + } + } + }; + + + class Genome { + + constructor(config, sequence, 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.featureDB = new Map(); // 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.cytobands ? this.cytobands[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) + } + + addFeaturesToDB(featureList, config) { + + const insertFeature = (name, feature) => { + const current = this.featureDB.get(name); + if (current) { + feature = (feature.end - feature.start) > (current.end - current.start) ? feature : current; + + } + this.featureDB.set(name, feature); + }; + + for (let feature of featureList) { + if (feature.name) { + insertFeature(feature.name.toUpperCase(), feature); + } + if (feature.gene && feature.gene.name) { + insertFeature(feature.gene.name.toUpperCase(), feature); + } + + if (config.searchableFields) { + for (let f of config.searchableFields) { + const value = feature.getAttributeValue(f); + if (value) { + if (value.indexOf(" ") > 0) { + insertFeature(value.replaceAll(" ", "+").toUpperCase(), feature); + } else { + insertFeature(value.toUpperCase(), feature); + } + } + } + } + } + } + } + + async function loadCytobands(cytobandUrl, config, genome) { + + 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 = genome.getChromosomeName(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$2(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 lastHoverUpdateTime = 0; + let popupTimerID; + + class TrackViewport extends Viewport { + + constructor(trackView, viewportColumn, referenceFrame, width) { + super(trackView, viewportColumn, referenceFrame, width); + } + + initializationHelper() { + + this.$spinner = $$1('
', {class: 'igv-loading-spinner-container'}); + this.$viewport.append(this.$spinner); + this.$spinner.append($$1('
')); + + const track = this.trackView.track; + if ('sequence' !== track.type) { + this.$zoomInNotice = this.createZoomInNotice(this.$viewport); + } + + if (track.name && "sequence" !== track.id) { + this.$trackLabel = $$1('
'); + 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 > bppFeatureFetchThreshold) { + $$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.referenceFrame.chr === this.referenceFrame.chr && + this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) { + const pixelOffset = Math.round((this.canvas._data.bpStart - referenceFrame.start) / referenceFrame.bpPerPixel); + this.canvas.style.left = pixelOffset + "px"; + } + } + + /** + * Set the content top of the current view. This is triggered by scrolling. If the current canvas extent is not + * sufficient to cover the new vertical range repaint. + * + * @param contentTop - the "top" property of the virtual content div, 0 unless track is scrolled vertically + * + * + */ + setTop(contentTop) { + + super.setTop(contentTop); + + if (!this.canvas) { + this.repaint(); + } else { + // See if currently painted canvas covers the vertical range of the viewport. If not repaint + const h = this.$viewport.height(); + const vt = contentTop + this.canvas._data.pixelTop; + const vb = vt + this.canvas._data.pixelHeight; + if (vt > 0 || vb < h) { + this.repaint(); + } + } + + // Now offset backing canvas to align with the contentTop visual offset. + let offset = contentTop + this.canvas._data.pixelTop; + this.canvas.style.top = `${offset}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.$viewport.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); + if (features) { + let roiFeatures = []; + if (track.roiSets && track.roiSets.length > 0) { + for (let roiSet of track.roiSets) { + const features = await roiSet.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel); + roiFeatures.push({track: roiSet, features}); + } + } + + 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); + this.browser.alert.present(error); + console.error(error); + } + } finally { + this.loading = false; + this.stopSpinner(); + } + } + + /** + * Compute the genomic extent and needed pixelWidth to repaint the canvas for the current genomic state. + * Normally the canvas is size 3X the width of the viewport, however there is no left-right panning for WGV so + * canvas width is viewport width. + * @returns {{bpEnd: *, pixelWidth: (*|number), bpStart: number}} + */ + repaintDimensions() { + const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr); + const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width(); + const bpPerPixel = this.referenceFrame.bpPerPixel; + const bpStart = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel); + const bpEnd = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel); + return { + bpStart, bpEnd, pixelWidth + } + } + + /** + * Repaint the canvas using the cached features + * + */ + repaint() { + + if (undefined === this.featureCache) { + return + } + + const {features, roiFeatures} = this.featureCache; + + // Canvas dimensions. + // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position + const {bpStart, bpEnd, pixelWidth} = this.repaintDimensions(); + const viewportHeight = this.$viewport.height(); + const contentHeight = this.getContentHeight(); + const maxHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs. + const pixelHeight = Math.min(maxHeight, 3 * viewportHeight); + if (0 === pixelWidth || 0 === pixelHeight) { + if (this.canvas) { + $$1(this.canvas).remove(); + } + return + } + const pixelTop = Math.max(0, -this.contentTop - Math.floor(pixelHeight / 3)); + + const bpPerPixel = this.referenceFrame.bpPerPixel; + const pixelXOffset = Math.round((bpStart - this.referenceFrame.start) / bpPerPixel); + const canvasTop = (this.contentTop || 0) + pixelTop; + const newCanvas = document.createElement('canvas');// $('').get(0) + newCanvas.style.position = 'relative'; + newCanvas.style.display = 'block'; + 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, -pixelTop); + + const drawConfiguration = + { + context: ctx, + pixelXOffset, + pixelWidth, + pixelHeight, + pixelTop, + bpStart, + bpEnd: bpEnd, + bpPerPixel, + referenceFrame: this.referenceFrame, + selection: this.selection, + viewport: this, + viewportWidth: this.$viewport.width() + }; + + this.draw(drawConfiguration, features, roiFeatures); + + if (this.canvas) { + $$1(this.canvas).remove(); + } + newCanvas._data = drawConfiguration; + this.canvas = newCanvas; + this.$viewport.append($$1(newCanvas)); + + } + + refresh() { + if (!(this.canvas && this.featureCache)) return + + const drawConfiguration = this.canvas._data; + drawConfiguration.context.clearRect(0, 0, this.canvas.width, this.canvas.height); + const {features, roiFeatures} = this.featureCache; + this.draw(drawConfiguration, features, roiFeatures); + } + + /** + * Draw the associated track. + * + * @param drawConfiguration + * @param features + * @param roiFeatures + */ + draw(drawConfiguration, features, roiFeatures) { + + if (features) { + drawConfiguration.features = features; + this.trackView.track.draw(drawConfiguration); + } + if (roiFeatures && roiFeatures.length > 0) { + 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.canvas._data; + const canvasTop = canvasMetadata ? canvasMetadata.pixelTop : 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.contentTop - 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 marginTop = 32; + const marginLeft = 32; + + let {width, height} = this.browser.columnContainer.getBoundingClientRect(); + + const h_render = 8000; + + const config = + { + + width, + height: h_render, + + backdropColor: 'white', + + multiLocusGap: 0, + + viewbox: + { + x: 0, + y: 0, + width, + height: h_render + } + + }; + + const context = new ctx(config); + + const delta = + { + deltaX: marginLeft, + deltaY: marginTop + }; + + this.renderViewportToSVG(context, delta); + + // reset height to trim away unneeded svg canvas real estate. Yes, a bit of a hack. + context.setHeight(height); + + const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, ''); + const index = this.browser.referenceFrameList.indexOf(this.referenceFrame); + + const svg = context.getSerializedSvg(true); + const data = URL.createObjectURL(new Blob([svg], {type: "application/octet-stream"})); + + const id = `${str}_referenceFrame_${index}_guid_${domUtils.guid()}`; + download(`${id}.svg`, data); + + } + + // called by trackView.renderSVGContext() when rendering + // entire browser as SVG + + renderViewportToSVG(context, {deltaX, deltaY}) { + + if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) { + return + } + + const {width, height} = this.$viewport.get(0).getBoundingClientRect(); + + 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_${domUtils.guid()}`; + this.drawSVGWithContext(context, width, height, id, deltaX, deltaY + this.contentTop, -this.contentTop); + + } + + renderSVGContext(context, {deltaX, deltaY}) { + + this.renderViewportToSVG(context, {deltaX, deltaY}); + + if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) { + return + } + + if (this.$trackLabel && true === this.browser.trackLabelsVisible) { + const {x, y, width, height} = domUtils.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_${domUtils.guid()}`; + + 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 : undefined; + const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined; + this.draw(config, features, roiFeatures); + + context.restore(); + + } + + get cachedFeatures() { + return this.featureCache ? this.featureCache.features : [] + } + + clearCache() { + this.featureCache = undefined; + if (this.canvas) this.canvas._data = undefined; + } + + 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.bpStart || + this.referenceFrame.end > data.bpEnd || + this.referenceFrame.chr !== data.referenceFrame.chr || + this.referenceFrame.bpPerPixel != data.bpPerPixel + } + + needsReload() { + if (!this.featureCache) return true + const {chr, bpPerPixel} = this.referenceFrame; + const {bpStart, bpEnd} = this.repaintDimensions(); + return (!this.featureCache.containsRange(chr, bpStart, bpEnd, bpPerPixel)) + } + + createZoomInNotice($parent) { + + const $container = $$1('
', {class: 'igv-zoom-in-notice-container'}); + $parent.append($container); + + const $e = $$1('
'); + $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); + + // Mouse down + const md = (event) => { + this.enableClick = true; + this.browser.mouseDownOnViewport(event, this); + domUtils.pageCoordinates(event); + }; + viewport.addEventListener('mousedown', md); + viewport.addEventListener('touchstart', md); + + // Mouse up + 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); + + // Mouse move + if (typeof this.trackView.track.hoverText === 'function') { + viewport.addEventListener('mousemove', (event => { + if (event.buttons === 0 && (Date.now() - lastHoverUpdateTime > 100)) { + lastHoverUpdateTime = Date.now(); + const clickState = this.createClickState(event); + if (clickState) { + const tooltip = this.trackView.track.hoverText(clickState); + if (tooltip) { + this.$viewport[0].setAttribute("title", tooltip); + } else { + this.$viewport[0].removeAttribute("title"); + } + } + } + })); + } + + 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 = this.createClickState(event); + + 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('
')}); + } + + 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 = domUtils.translateMouseCoordinates(event, this.$viewport.get(0)).x; + const mouseXCanvas = domUtils.translateMouseCoordinates(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(({locusSearchString}) => 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 = this.getPopupContent(event); + 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 = `
${track.description}
`; + } + + if (str) { + if (this.popover) { + this.popover.dispose(); + } + this.popover = new Popover(this.browser.columnContainer, (track.name || '')); + this.popover.presentContentWithEvent(event, str); + } + }); + } + + createClickState(event) { + + if (!this.canvas) return // Can happen during initialization + + const referenceFrame = this.referenceFrame; + const viewportCoords = domUtils.translateMouseCoordinates(event, this.$viewport.get(0)); + const canvasCoords = domUtils.translateMouseCoordinates(event, this.canvas); + const genomicLocation = ((referenceFrame.start) + referenceFrame.toBP(viewportCoords.x)); + + return { + event, + viewport: this, + referenceFrame, + genomicLocation, + y: viewportCoords.y - this.contentTop, + canvasX: canvasCoords.x, + canvasY: canvasCoords.y + } + + } + + getPopupContent(event) { + + const clickState = this.createClickState(event); + + if (undefined === clickState) { + return + } + + let track = this.trackView.track; + const dataList = track.popupData(clickState); + + const popupClickHandlerResult = this.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 = `${nameValue.name}   ${nameValue.value}`; + return `
${str}
` + } else if ('
' === nameValue) { // this can be retired if nameValue.html is allowed. + return nameValue + } else if (nameValue.html) { + return nameValue.html + } else { + return `
${nameValue}
` + } + + }); + + return rows.join('') + } + + class FeatureCache { + + constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) { + this.chr = chr; + this.bpStart = tileStart; + this.bpEnd = 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.bpStart && end <= this.bpEnd && chr === this.chr && r > 0.5 && r < 2 + } + + overlapsRange(chr, start, end) { + return this.chr === chr && end >= this.bpStart && start <= this.bpEnd + } + } + + /** + * 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('
'); + } + + 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({start, end}) { + 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('
'); + 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('
'); + 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: decodeGFFAttribute(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, relaxed = false) { + // 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 = stripQuotes(decodeGFFAttribute(kv.substring(0, idx).trim(), relaxed)); + let value = stripQuotes(decodeGFFAttribute(kv.substring(idx + 1).trim(), relaxed)); + attributes.push([key, value]); + } + } + return attributes + } + + function stripQuotes(value) { + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substr(1, value.length - 2); + } + return value + } + + // GFF3 attributes have specific percent encoding rules, the list below are required, all others are forbidden + /* + tab (%09) + newline (%0A) + carriage return (%0D) + % percent (%25) + control characters (%00 through %1F, %7F) + In addition, the following characters have reserved meanings in column 9 and must be escaped when used in other contexts: + ; semicolon (%3B) + = equals (%3D) + & ampersand (%26) + , comma (%2C) + */ + + const encodings = new Map([ + ["%09", "\t"], + ["%0A", "\n"], + ["%0D", "\r"], + ["%25", "%"], + ["%3B", ";"], + ["%3D", "="], + ["%26", "&"], + ["%2C", ","] + ]); + + function decodeGFFAttribute(str, relaxed = false) { + + if (!str.includes("%")) { + return str + } + if (relaxed) { + return decodeURIComponent(str); + } + let decoded = ""; + for (let i = 0; i < str.length; i++) { + + if (str.charCodeAt(i) === 37 && i < str.length - 2) { + const key = str.substring(i, i + 3); + if (encodings.has(key)) { + decoded += encodings.get(key); + } else { + decoded += key; + } + i += 2; + } else { + decoded += str.charAt(i); + } + } + return decoded + + } + + /** + * 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], '=', true); + feature.attributes = {}; + for (let kv of attributeKVs) { + feature.attributes[kv[0]] = kv[1]; + if (header.nameField != undefined && kv[0] === header.nameField) { + feature.name = 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}); + } + if (exons.length > 0) { + 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. + */ + function decodeRepeatMasker(tokens, header) { + + /** + * 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. + */ + 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] + }; + const exons = decodeExons(parseInt(tokens[7 + shift]), tokens[8 + shift], tokens[9 + shift]); + 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] + }; + + const exons = decodeExons(parseInt(tokens[7 + shift]), tokens[8 + shift], tokens[9 + shift]); + 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 + }; + + const exons = decodeExons(parseInt(tokens[8 + shift]), tokens[9 + shift], tokens[10 + shift]); + findUTRs$1(exons, cdStart, cdEnd); + feature.exons = exons; + + return feature + } + + /** + * Decode a UCS PSL record * + * @param tokens + * @param header + * @returns {DecodeError|UCSCBedFeature|undefined} + */ + function decodePSL(tokens, header) { + + /* + * 0 matches - Number of bases that match that aren't repeats + * 1 misMatches - Number of bases that don't match + * 2 repMatches - Number of bases that match but are part of repeats + * 3 nCount - Number of "N" bases + * 4 qNumInsert - Number of inserts in query + * 5 qBaseInsert - Number of bases inserted in query + * 6 tNumInsert - Number of inserts in target + * 7 tBaseInsert - Number of bases inserted in target + * 8 strand - "+" or "-" for query strand. For translated alignments, second "+"or "-" is for target genomic strand. + * 9 qName - Query sequence name + * 10 qSize - Query sequence size. + * 11 qStart - Alignment start position in query + * 12 qEnd - Alignment end position in query + * 13 tName - Target sequence name + * 14 tSize - Target sequence size + * 15 tStart - Alignment start position in target + * 16 tEnd - Alignment end position in target + * 17 blockCount - Number of blocks in the alignment (a block contains no gaps) + * 18 blockSizes - Comma-separated list of sizes of each block. If the query is a protein and the target the genome, blockSizes are in amino acids. See below for more information on protein query PSLs. + * 19 qStarts - Comma-separated list of starting positions of each block in query + * 20 tStarts - Comma-separated list of starting positions of each block in target + */ + + + if (tokens.length < 21) return undefined + + const chr = tokens[13]; + const start = parseInt(tokens[15]); + const end = parseInt(tokens[16]); + const strand = tokens[8].charAt(0); + const exonCount = parseInt(tokens[17]); + const exons = []; + const exonStarts = tokens[20].replace(/,$/, '').split(','); + const exonSizes = tokens[18].replace(/,$/, '').split(','); + + for (let i = 0; i < exonCount; i++) { + const start = parseInt(exonStarts[i]); + const end = start + parseInt(exonSizes[i]); + exons.push({start: start, end: end}); + } + + return new PSLFeature({chr, start, end, strand, exons, tokens}) + } + + + function decodeExons(exonCountToken, exonStartsToken, exonEndsToken) { + + const exonCount = parseInt(exonCountToken); + const exonStarts = exonStartsToken.replace(/,$/, '').split(','); + const exonEnds = exonEndsToken.replace(/,$/, '').split(','); + const exons = []; + for (let i = 0; i < exonCount; i++) { + const start = parseInt(exonStarts[i]); + const end = parseInt(exonEnds[i]); + exons.push({start: start, end: end}); + } + return exons + + } + + 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] + } + } + } + + /* + * 0 matches - Number of bases that match that aren't repeats + * 1 misMatches - Number of bases that don't match + * 2 repMatches - Number of bases that match but are part of repeats + * 3 nCount - Number of "N" bases + * 4 qNumInsert - Number of inserts in query + * 5 qBaseInsert - Number of bases inserted in query + * 6 tNumInsert - Number of inserts in target + * 7 tBaseInsert - Number of bases inserted in target + * 8 strand - "+" or "-" for query strand. For translated alignments, second "+"or "-" is for target genomic strand. + * 9 qName - Query sequence name + * 10 qSize - Query sequence size. + * 11 qStart - Alignment start position in query + * 12 qEnd - Alignment end position in query + * 13 tName - Target sequence name + * 14 tSize - Target sequence size + * 15 tStart - Alignment start position in target + * 16 tEnd - Alignment end position in target + * 17 blockCount - Number of blocks in the alignment (a block contains no gaps) + * 18 blockSizes - Comma-separated list of sizes of each block. If the query is a protein and the target the genome, blockSizes are in amino acids. See below for more information on protein query PSLs. + * 19 qStarts - Comma-separated list of starting positions of each block in query + * 20 tStarts - Comma-separated list of starting positions of each block in target + */ + + class PSLFeature { + + + constructor(properties) { + Object.assign(this, properties); + } + + get score() { + const tokens = this.tokens; + const match = parseInt(tokens[0]); + const repMatch = parseInt(tokens[2]); + const misMatch = parseInt(tokens[1]); + const qGapCount = parseInt(tokens[4]); + const tGapCount = parseInt(tokens[6]); + const qSize = parseInt(tokens[10]); + return Math.floor((1000 * (match + repMatch - misMatch - qGapCount - tGapCount)) / qSize) + } + + get matches() { + return this.tokens[0] + } + + get misMatches() { + return this.tokens[1] + } + + get repMatches() { + return this.tokens[2] + } + + get nCount() { + return this.tokens[3] + } + + get qNumInsert() { + return this.tokens[4] + } + + get qBaseInsert() { + return this.tokens[5] + } + + get tNumInsert() { + return this.tokens[6] + } + + get tBaseInsert() { + return this.tokens[7] + + } + + popupData() { + return [ + {name: 'chr', value: this.chr}, + {name: 'start', value: this.start + 1}, + {name: 'end', value: this.end}, + {name: 'strand', value: this.strand}, + {name: 'score', value: this.score}, + {name: 'match', value: this.matches}, + {name: "mis-match", value: this.misMatches}, + {name: "rep. match", value: this.repMatches}, + {name: "N's", value: this.nCount}, + {name: 'Q gap count', value: this.qNumInsert}, + {name: 'Q gap bases', value: this.qBaseInsert}, + {name: 'T gap count', value: this.tNumInsert}, + {name: 'T gap bases', value: this.tBaseInsert}, + ] + } + + } + + 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'] = `${tokens[7]}`; + } + 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 + + } + + /** + * 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": + case "hiccups": + 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) 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$2(colorString)) { + return (colorString.indexOf(",") > 0 && !(colorString.startsWith("rgb(") || colorString.startsWith("rgba("))) ? + `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 { + + static defaults = { + height: 50, + autoHeight: false, + visibilityWindow: undefined, // Identifies property that should be copied from config + color: undefined, // Identifies property that should be copied from config + altColor: undefined, // Identifies property that should be copied from config + supportHiDPI: true + } + + 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) { + + this.config = config; + + if (config.displayMode) { + config.displayMode = config.displayMode.toUpperCase(); + } + + // Set default properties + const defaults = Object.assign({}, TrackBase.defaults); + if(this.constructor.defaults) { + for(let key of Object.keys(this.constructor.defaults)) { + defaults[key] = this.constructor.defaults[key]; + } + } + for(let key of Object.keys(defaults)) { + this[key] = config.hasOwnProperty(key) ? config[key] : defaults[key]; + if(key === 'color' || key === 'altColor') { + this[key] = fixColor(this[key]); + } + } + + if (config.name || config.label) { + this.name = config.name || config.label; + } else if (isFile(config.url)) { + this.name = config.url.name; + } else if (isString$2(config.url) && !config.url.startsWith("data:")) { + this.name = getFilename$2(config.url); + } + + this.url = config.url; + if(this.config.type) this.type = this.config.type; + this.id = this.config.id === undefined ? this.name : this.config.id; + this.order = config.order; + this.autoscaleGroup = config.autoscaleGroup; + this.removable = config.removable === undefined ? true : config.removable; // Defaults to true + this.minHeight = config.minHeight || Math.min(25, this.height); + this.maxHeight = config.maxHeight || Math.max(1000, this.height); + + 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; + } + } + + // Support for mouse hover text. This can be expensive, off by default. + // this.hoverText = function(clickState) => return tool tip text + if (config.hoverTextFields) { + this.hoverText = hoverText.bind(this); + } else if (typeof this.config.hoverText === 'function') { + this.hoverText = this.config.hoverText; + } + } + + 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); + } + + clearCachedFeatures() { + if (this.trackView) { + this.trackView.clearCachedFeatures(); + } + } + + updateViews() { + if (this.trackView) { + this.trackView.updateViews(); + } + } + + /** + * Used to create session object for bookmarking, sharing. Only simple property values (string, number, boolean) + * are saved. + */ + getState() { + + const jsonable = (v) => !(v === undefined || typeof v === 'function' || v instanceof File || v instanceof Promise); + + // 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("_") && jsonable(this.config[key])) { + 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 user has changed other properties from defaults update state. + const defs = TrackBase.defaults; + if (this.constructor.defaults) { + for (let key of Object.keys(this.constructor.defaults)) { + defs[key] = this.constructor.defaults[key]; + } + } + for (let key of Object.keys(defs)) { + if (undefined !== this[key] && defs[key] !== this[key]) { + state[key] = this[key]; + } + } + + // Flatten dataRange if present + if (!this.autoscale && this.dataRange) { + state.min = this.dataRange.min; + state.max = this.dataRange.max; + } + + 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) { + + if (this.disposed) return // This track was removed during async load + + 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}; + this.viewLimitMin = min; + this.viewLimitMax = 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 an array of features and only considers + * the genomic location. Overriden by most subclasses. + * + * @param clickState + * @param features + * @returns {[]|*[]} + */ + clickedFeatures(clickState) { + + // 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 features = clickState.viewport.cachedFeatures; + + if (!features || !Array.isArray(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('
'); + data.push({html: cravatLink}); + data.push('
'); + } + } + } + } + } + } + + 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) => `
${k}: ${v}
`; + + let str = '
'; + 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 += '
'; + return str + } + + /** + * Return color for a specific feature of this track. This default implementation is overriden by subclasses* + * @param f + * @returns {*|string|string} + */ + getColorForFeature(f) { + return (typeof this.color === "function") ? this.color(feature) : this.color + } + + /** + * Track has been permanently removed. Release resources and other cleanup + */ + dispose() { + + this.disposed = true; + + // This should not be neccessary, but in case there is some unknown reference holding onto this track object, + // for example in client code, release any resources here. + for (let key of Object.keys(this)) { + this[key] = undefined; + } + } + + static getCravatLink(chr, position, ref, alt, genomeID) { + + if ("hg38" === genomeID || "GRCh38" === genomeID) { + + const cravatChr = chr.startsWith("chr") ? chr : "chr" + chr; + return `Cravat ${ref}->${alt}` + + } else { + return undefined + } + } + } + + function hoverText(clickState) { + + if (!this.hoverTextFields) return + + const features = this.clickedFeatures(clickState); + + if (features && features.length > 0) { + let str = ""; + for (let i = 0; i < features.length; i++) { + if (i === 10) { + str += "; ..."; + break + } + if (!features[i]) continue + + const f = features[i]._f || features[i]; + if (str.length > 0) str += "\n"; + + str = ""; + for (let field of this.hoverTextFields) { + if (str.length > 0) str += "\n"; + if (f.hasOwnProperty(field)) { + str += f[field]; + } else if (typeof f.getAttribute === "function") { + str += f.getAttribute(field); + } + } + + } + return str + } + } + + /** + * 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} + */ + const UCSCTypeMappings = new Map([ + ["wiggle_0", "wig"], + ["bed", "bed"], + ["bigBed", "bigBed"], + ["bigWig", "bigWig"] + ]); + + /* + * 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({sample, chr, start, end, value, valueColumnName}) { + this.sample = sample; + this.chr = chr; + this.start = start; + this.end = end; + this.value = value; + this.valueColumnName = valueColumnName; + } + + setAttributes({names, values}) { + 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('
'); + pd.push({html: l}); + pd.push('
'); + } + } + + 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: "ID", value: this.names ? this.names : ""}, + {name: "Ref", value: this.referenceBases}, + {name: "Alt", value: this.alternateBases.replace("<", "<")}, + {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('
'); + fields.push({html: l}); + } + } + } + } + } + + if (this.hasOwnProperty("heterozygosity")) { + fields.push({name: "Heterozygosity", value: this.heterozygosity}); + } + + if (this.info) { + fields.push({html: '
'}); + for (let key of Object.keys(this.info)) { + fields.push({name: key, value: arrayToString(decodeURIComponent(this.info[key]))}); + } + } + + return fields + } + + getInfo(tag) { + return this.info ? this.info[tag] : undefined; + } + + 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 === "" || + 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 "" === 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= + // ##FILTER= + // ##FORMAT= + + 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("", 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 this is a structural variant create a complement of this variant for the other end + // The test for "SV" is not comprehensive, there is not yet a standard for this + 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.valueCol = config.columns.value - 1; + } else { + // Defaults -- can be overriden in header + this.posCol = 2; + this.chrCol = 1; + this.valueCol = 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({chr, start, end, value, line, columns}) { + 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) 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) + } + + isEqualTo(vp) { + return this.block === vp.block && this.offset === vp.offset + } + + print() { + return "" + this.block + ":" + this.offset + } + } + + function optimizeChunks(chunks, lowest) { + + 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 + } + }); + + if(chunks.length <= 1) { + return chunks + } + + // console.log("Before trimming " + chunks.length) + // for (let c of chunks) { + // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`) + // } + + if (lowest) { + chunks = chunks.filter(c => c.maxv.isGreaterThan(lowest)); + } + + // console.log("Before merging " + chunks.length) + // for (let c of chunks) { + // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`) + // } + + const mergedChunks = []; + let lastChunk; + for (let chunk of chunks) { + + if (!lastChunk) { + 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; + } + } + } + + // console.log("After merging " + mergedChunks.length) + // for (let c of mergedChunks) { + // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`) + // } + + return mergedChunks + } + + + /** + * Merge 2 blocks if the file position gap between them is < 16 kb, and the total size is < ~5 mb + * @param chunk1 + * @param chunk2 + * @returns {boolean|boolean} + */ + function canMerge(chunk1, chunk2) { + chunk2.minv.block - chunk1.maxv.block; + const sizeEstimate = chunk1.maxv.block - chunk1.minv.block; + return sizeEstimate < 5000000 + } + + // Represents a CSI Bam or Tabix 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}} + */ + chunksForRange(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]; + for (let c of binChunks) { + const cs = c[0]; + const ce = c[1]; + chunks.push({minv: cs, maxv: ce, bin: bin}); + } + } + } + } + + const lowestOffset = ba.loffset[overlappingBins[0]]; + + return optimizeChunks(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 ** 34) 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); + // + // ITS NOT CLEAR WHERE THIS TEST CAME FROM, but maxBinNumber is never set, and its not clear what it represents. + // 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`, + // ) + // + bins.push([b, e]); + } + return bins + } + + + bin_limit() { + return ((1 << (this.depth + 1) * 3) - 1) / 7 + } + + } + + // Represents a BAM or Tabix index. + + const BAI_MAGIC$1 = 21578050; + const TABIX_MAGIC$1 = 21578324; + + + 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 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; + } + } + + // 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 chunks 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 objects representing chunks (file spans) {minv: {block, offset}, {maxv: {block, offset}} + */ + chunksForRange(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 + + //console.log("bin ranges") + //for(let b of overlappingBins) { + // console.log(`${b[0]} - ${b[1]}`) + //} + + 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]; + for (let c of binChunks) { + const cs = c[0]; + const ce = c[1]; + chunks.push({minv: cs, maxv: ce}); + } + } + } + } + + // 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; + const minLin = Math.min(min >> 14, nintv - 1); // i.e. min / 16384 + const maxLin = Math.min(max >> 14, nintv - 1); + for (let i = minLin; i <= maxLin; i++) { + const vp = ba.linearIndex[i]; + if (vp) { + lowest = vp; // lowest file offset that contains alignments overlapping (min, max) + break + } + } + + return optimizeChunks(chunks, lowest) + } + } + } + + + + /** + * 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 + */ + chunksForRange(queryChr, min, max) { //function (refId, 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. + */ + + 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 to iterate line-by-line over a BGZipped text file. This class is useful for iterating from the start of + * the file. Not useful for indexed queries. + */ + + class BGZLineReader { + + 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$1(abuffer); + + 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); + if(this.buffer.byteLength == 0) { + this.eof = true; + } + this.bufferPtr = 0; + this.filePtr += data.byteLength; //data.byteLength; + } + } + + } + + function concatenateArrayBuffers(arrayBuffers) { + + if (arrayBuffers.length === 1) { + return arrayBuffers[0] + } + + let len = 0; + for (const b of arrayBuffers) { + len += b.byteLength; + } + const c = new Uint8Array(len); + let offset = 0; + for (const b of arrayBuffers) { + c.set(new Uint8Array(b), offset); + offset += b.byteLength; + } + return c.buffer + } + + /** + * Return the block size for the data buffer. + * @param data + * @returns {number} + */ + const bgzBlockSize = (data) => { + const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data); + const bsize = (ba[17] << 8 | ba[16]) + 1; + return bsize + }; + + class BGZBlockLoader { + + constructor(config) { + this.config = config; + this.cacheBlocks = false != config.cacheBlocks; // Default to true + this.cache = undefined; + } + + /** + * Return inflated data from startBlock through endBlock as an UInt8Array + * + * @param minv minimum virtual pointer {block, offset} + * @param maxv maximum virtual pointer {block, offset} + * @returns {Promise} + */ + async getData(minv, maxv) { + + const startBlock = minv.block; + const endBlock = maxv.block; + const skipEnd = maxv.offset === 0; + + const blocks = await this.getInflatedBlocks(startBlock, endBlock, skipEnd); + if (blocks.length === 1) { + return blocks[0] + } + + let len = 0; + for (const b of blocks) { + len += b.byteLength; + } + const c = new Uint8Array(len); + let offset = 0; + for (const b of blocks) { + c.set(b, offset); + offset += b.byteLength; + } + return c + } + + /** + * Return the inflated data for the specified blocks as an array of Uint8Arrays. This method is public so + * it can be unit tested. * + * @param startBlock + * @param endBlock + * @returns {Promise<*[Uint8Array]>} + */ + async getInflatedBlocks(startBlock, endBlock, skipEnd) { + + if (!this.cacheBlocks) { + const buffer = await this.loadBLockData(startBlock, endBlock, {skipEnd}); + return inflateBlocks(buffer) + } else { + + const c = this.cache; + if (c && + c.startBlock <= startBlock && + (c.endBlock >= endBlock || skipEnd && c.nextEndBlock === endBlock)) { + //console.log("Complete overlap") + const startOffset = startBlock - c.startBlock; + const endOffset = endBlock - c.startBlock; + return inflateBlocks(c.buffer, startOffset, endOffset) + // Don't update cache, still valid + } else { + + let buffer; + if (!c || (c.startBlock > endBlock || c.endBlock < startBlock)) { + // no overlap with cache + buffer = await this.loadBLockData(startBlock, endBlock, {skipEnd}); + } else { + + //console.log("Some overlap") + const arrayBuffers = []; + + // Load blocks preceding cache start, if any + if (startBlock < c.startBlock) { + // load first blocks + const startBuffer = await this.loadBLockData(startBlock, c.startBlock, {skipEnd: true}); + arrayBuffers.push(startBuffer); + } + + // Slice cached buffer as needed + let cachedBuffer; + if (startBlock <= c.startBlock && endBlock >= c.endBlock) { + cachedBuffer = c.buffer; + } else { + const start = Math.max(0, startBlock - c.startBlock); + let end; + if (endBlock >= c.endBlock) { + end = c.buffer.byteLength; + } else { + // We need to find the byte position of the end of "endBlock" + const boundaries = findBlockBoundaries(c.buffer); + for (let i = 0; i < boundaries.length - 1; i++) { + if (c.startBlock + boundaries[i] === endBlock) { + end = boundaries[i + 1]; + break + } + } + // Do something if end not found + } + cachedBuffer = c.buffer.slice(start, end); + } + arrayBuffers.push(cachedBuffer); + + // Load end blocks, if any + if (endBlock > c.endBlock) { + const endBuffer = await this.loadBLockData(c.endBlock, endBlock, {skipStart: true, skipEnd}); + arrayBuffers.push(endBuffer); + } + + buffer = concatenateArrayBuffers(arrayBuffers); + } + + // If skipEnd === true we need to find boundary of last block in cache + let nextEndBlock = endBlock; + if(skipEnd) { + const boundaries = findBlockBoundaries(buffer); + endBlock = boundaries[boundaries.length - 1]; + } + + this.cache = {startBlock, endBlock, nextEndBlock, buffer}; + return inflateBlocks(buffer) + } + } + } + + async loadBLockData(startBlock, endBlock, options) { + + const config = this.config; + const skipStart = options && options.skipStart; + const skipEnd = options && options.skipEnd; + + // Get size of last block if not skipped + let lastBlockSize = 0; + if (!skipEnd) { + const bsizeOptions = buildOptions(config, {range: {start: endBlock, size: 26}}); + const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions); + lastBlockSize = bgzBlockSize(abuffer); + } + + if (skipStart) { + const bsizeOptions = buildOptions(config, {range: {start: startBlock, size: 26}}); + const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions); + startBlock += bgzBlockSize(abuffer); + } + + // Load data for all blocks + const loadOptions = buildOptions(config, { + range: { + start: startBlock, + size: endBlock + lastBlockSize - startBlock + } + }); + + //console.log(`${this.config.name} Loaded ${startBlock} - ${endBlock + lastBlockSize} (${(endBlock + lastBlockSize - startBlock) / 1000} kb)`) + + return igvxhr.loadArrayBuffer(config.url, loadOptions) + } + } + + function findBlockBoundaries(arrayBuffer) { + + const byteLengh = arrayBuffer.byteLength; + let offset = 0; + const blockBoundaries = [0]; + while (offset < byteLengh) { + //console.log("Cache block " + offset) + const ba = new Uint8Array(arrayBuffer, offset); + const bsize = (ba[17] << 8 | ba[16]) + 1; + offset += bsize; + if (offset < byteLengh) { + blockBoundaries.push(offset); + } + } + return blockBoundaries + } + + + /** + * Inflate compressed blocks within the data buffer* + * @param data + * @param startBlock - optional file location for start block. Default == 0 + * @param endBlock - optional file location for last block to decompress. + * @returns {*[]} + */ + function inflateBlocks(data, startBlock, endBlock) { + + startBlock = startBlock || 0; + + const oBlockList = []; + let ptr = startBlock; + + const lim = data.byteLength - 18; + while (ptr < lim) { + try { + //console.log(113873 + ptr) + const header = new Uint8Array(data, ptr, 18); + const xlen = (header[11] << 8) | (header[10]); + const bsize = ((header[17] << 8) | (header[16])); // Total block size, including header, minus 1 + const start = 12 + xlen + ptr; // Start of CDATA + const bytesLeft = data.byteLength - start; + const cDataSize = bsize - xlen - 18; + + if (bytesLeft < cDataSize || cDataSize <= 0) { + // This is unexpected. Throw error? + break + } + + const cdata = new Uint8Array(data, start, cDataSize); + const unc = inflateRaw_1(cdata); + oBlockList.push(unc); + + if (endBlock === ptr) { + break + } else { + // Advance to next block + ptr += bsize + 1; + } + + } catch (e) { + console.error(e); + break + } + } + return oBlockList + } + + /* + * 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 || this.indexURL !== undefined; + this.queryable = this.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) { + this._blockLoader = new BGZBlockLoader(this.config); + dataWrapper = new BGZLineReader(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) { + + // insure that header has been loaded -- tabix _blockLoader is initialized as side effect + if(!this.dataURI && !this.header) { + await this.readHeader(); + } + + //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 chunks = this.index.chunksForRange(refId, start, end); + if (!chunks || chunks.length === 0) { + return [] + } else { + const allFeatures = []; + for (let chunk of chunks) { + + let inflated; + if (tabix) { + inflated = await this._blockLoader.getData(chunk.minv, chunk.maxv); + } else { + const options = buildOptions(config, { + range: { + start: chunk.minv.block, + size: chunk.maxv.block - chunk.minv.block + 1 + } + }); + inflated = await igvxhr.loadString(config.url, options); + } + + const slicedData = chunk.minv.offset ? inflated.slice(chunk.minv.offset) : 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$2; + + + 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. + */ + + // Experimental class for fetching features from an mpg webservice. + // 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 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. + */ + + 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 "" + text + "" + } + + } + } + + /* + * 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 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$1(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 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; + + } + + } + + // 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; + } + } + + const DEFAULT_MAX_WG_COUNT$1 = 10000; + + /** + * Return a collection of "whole genome" features wrapping the supplied features, possibly downsampled. The purpose + * is to support painting features in "whole genome view". + * + * @param allFeatures - dictionary (object), keys are chromosome names, values are lists of features + * @param genome + * @param maxWGCount - optional, maximum # of whole genome features to computer + * @returns {*[]} + */ + function computeWGFeatures(allFeatures, genome, maxWGCount) { + + const max = maxWGCount || DEFAULT_MAX_WG_COUNT$1; + + 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 wgChromosomeNames = new Set(genome.wgChromosomeNames); + const wgFeatures = []; + let count = 0; + for (let c of genome.wgChromosomeNames) { + + if(Array.isArray(allFeatures)) { + const featureDict = {}; + for(let f of allFeatures) { + const chr = genome.getChromosomeName(f.chr); + if(!featureDict.hasOwnProperty(chr)) { + featureDict[chr] = []; + } + featureDict[chr].push(f); + } + allFeatures = featureDict; + } + + 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); + } + } + + /* + * 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"]); + + this.queryable = config.indexURL || config.queryable === true; // False by default, unless explicitly set + 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); + this.queryable = true; + } 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 ; + } + + // Set searchable unless explicitly turned off, or track uses in indexed or otherwise queryable + // feature source. queryable => features loaded on demand (by query) + this.searchable = config.searchable === true || config.searchableFields || (config.searchable !== false && !this.queryable); + + } + + 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. + * + * @param chr + * @param start + * @param end + * @param bpPerPixel + */ + async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) { + + const genome = this.genome; + const queryChr = genome ? genome.getChromosomeName(chr) : chr; + const isWholeGenome = ("all" === queryChr.toLowerCase()); + + // Various conditions that can require a feature load + // * view is "whole genome" but no features are loaded + // * cache is disabled + // * cache does not contain requested range + // const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end)) + if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) || + 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.supportsWholeGenome()) { + this.wgFeatures = computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, this.maxWGCount); + } else { + this.wgFeatures = []; + } + } + return this.wgFeatures + } else { + return this.featureCache.queryFeatures(queryChr, start, end) + } + } + + supportsWholeGenome() { + return !this.queryable // queryable (indexed, web services) sources don't support whole genome view + } + + // TODO -- experimental, will only work for non-indexed sources + getAllFeatures() { + if (this.queryable || !this.featureCache) { // queryable sources don't support all features + return [] + } else { + return 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.searchable) { + this.genome.addFeaturesToDB(features, this.config); + } + } else { + this.featureCache = new FeatureCache$1([], genomicInterval); // Empty 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. + */ + + 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, retries = 0) { + try { + + 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; + } + if (this.contentLength) { + bufferSize = Math.min(bufferSize, this.contentLength - requestedRange.start); + } + 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) + } catch (e) { + if (retries === 0 && e.message && e.message.startsWith("416")) { + try { + this.contentLength = await igvxhr.getContentLength(this.path, buildOptions(this.config)); + return this.dataViewForRange(requestedRange, asUint8, ++retries) + } catch (e1) { + console.error(e1); + } + throw e + } + } + } + } + + 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 + } + } + + /* + * 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 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_1$3(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(); + const blockStart = binaryParser.getInt(); + let chromStart = blockStart; + 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) { + + let idx = 0; + 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(); + chromStart = blockStart + idx * itemStep; + chromEnd = chromStart + itemSpan; + idx++; + 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 = {}; + this.queryable = true; + } + + async getFeatures({chr, start, end, bpPerPixel, windowFunction}) { + + const isBigWig = this.reader.type === "bigwig"; + + const features = (chr.toLowerCase() === "all") ? + (isBigWig ? await this.getWGValues(windowFunction) : []) : + await this.reader.readFeatures(chr, start, chr, end, bpPerPixel, windowFunction); + + 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_1$3(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_1$3(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); + this.queryable = true; + } + + async getFeatures({chr, start, end, bpPerPixel}) { + + 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. + */ + + /** + * feature source for features supplied directly, as opposed to reading and parsing from a file or webservice + * + * @param config + * @constructor + */ + class StaticFeatureSource { + + constructor(config, genome) { + + this.config = config; + this.genome = genome; + this.queryable = false; + this.searchable = config.searchable !== false; // searchable by default + this.updateFeatures(config.features); + } + + updateFeatures(features) { + features = fixFeatures(features, this.genome); + packFeatures(features); + if (this.config.mappings) { + mapProperties(features, this.config.mappings); + } + this.featureCache = new FeatureCache$1(features, this.genome); + + if (this.searchable || this.config.searchableFields) { + this.genome.addFeaturesToDB(features, this.config); + } + } + + /** + * Required function for all data source objects. Fetches features for the + * range requested. + * + * This function is complex due to the variety of reader types backing it, some indexed, some queryable, + * some not. + * + * @param chr + * @param start + * @param end + * @param bpPerPixel + */ + async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) { + + const genome = this.genome; + const queryChr = genome ? genome.getChromosomeName(chr) : chr; + const isWholeGenome = ("all" === queryChr.toLowerCase()); + + // Various conditions that can require a feature load + // * view is "whole genome" but no features are loaded + // * cache is disabled + // * cache does not contain requested range + if (isWholeGenome) { + return computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, this.maxWGCount) + } else { + return this.featureCache.queryFeatures(queryChr, start, end) + } + } + + // + // supportsWholeGenome() { + // return true + // } + + getAllFeatures() { + return this.featureCache.getAllFeatures() + } + + supportsWholeGenome() { + return true + } + + + } + + + /** + * 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 (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]]; + }); + }); + } + + /* + * 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 (config.features) { + return new StaticFeatureSource(config, genome) + } else 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 appleCrayonColorName = 'nickel'; + + const ROI_DEFAULT_ALPHA = 2 / 16; + + const ROI_DEFAULT_COLOR = appleCrayonRGBA(appleCrayonColorName, ROI_DEFAULT_ALPHA); + const ROI_DEFAULT_HEADER_COLOR = 'rgb(190,190,190)'; + + const ROI_USER_HEADER_DEFINED_COLOR = 'rgba(155,185,129)'; + const ROI_USER_DEFINED_COLOR = ROI_DEFAULT_COLOR; + + class ROISet { + + constructor(config, genome) { + + this.url = config.url; + + if (config.name) { + this.name = config.name; + } else if (config.url && isFile(config.url)) { + this.name = config.url.name; + } else if (config.url && isString$2(config.url) && !config.url.startsWith("data:")) { + this.name = getFilename$2(config.url); + } + + this.isUserDefined = config.isUserDefined; + + if (config.features) { + this.featureSource = new DynamicFeatureSource(config.features, genome); + } else { + if (config.format) { + config.format = config.format.toLowerCase(); + } else { + const filename = getFilename$2(config.url); + config.format = inferFileFormat(filename); + } + this.featureSource = config.featureSource || FeatureSource(config, genome); + } + + if (true === this.isUserDefined) { + this.color = config.color || ROI_USER_DEFINED_COLOR; + this.headerColor = ROI_USER_HEADER_DEFINED_COLOR; + + } else { + + this.color = config.color || ROI_DEFAULT_COLOR; + this.headerColor = ROI_DEFAULT_HEADER_COLOR; + + // Use body color with alpha pinned to 1 + // const [ r, g, b, discard ] = rgbaStringTokens(this.color) + // this.headerColor = `rgba(${ r },${ g },${ b },${ 1.0 })` + + } + } + + async getFeatures(chr, start, end) { + return this.featureSource.getFeatures({chr, start, end}) + } + + async getAllFeatures() { + return typeof this.featureSource.getAllFeatures === 'function' ? await this.featureSource.getAllFeatures() : {} + } + + addFeature(feature) { + if (this.isUserDefined) { + this.featureSource.addFeature(feature); + } else { + console.error("Attempt to add ROI to non user-defined set"); + } + } + + removeFeature(feature) { + if (this.isUserDefined) { + this.featureSource.removeFeature(feature); + } else { + console.error("Attempt to remove ROI from non user-defined set"); + } + } + + toJSON() { + if (this.url) { + return {name: this.name, color: this.color, url: this.url, isUserDefined: this.isUserDefined} + } else { + const featureMap = this.featureSource.getAllFeatures(); + const features = []; + for (let chr of Object.keys(featureMap)) { + for (let f of featureMap[chr]) { + features.push(f); + } + } + return {name: this.name, color: this.color, features: features, isUserDefined: this.isUserDefined} + } + } + + dispose() { + for (let key of Object.keys(this)) { + this[key] = undefined; + } + } + + } + + const SCREEN_COORDS_WIDTH_THRESHOLD = 3; + + function screenCoordinates(regionStartBP, regionEndBP, bpStart, bpp) { + + let xStart = Math.round((regionStartBP - bpStart) / bpp); + const xEnd = Math.round((regionEndBP - bpStart) / bpp); + + let width = xEnd - xStart; + + if (width < SCREEN_COORDS_WIDTH_THRESHOLD) { + width = SCREEN_COORDS_WIDTH_THRESHOLD; + xStart -= 1; + } + + return {x: xStart, width} + } + + + /** + * Special feature source that allows addition of features dynamically + */ + class DynamicFeatureSource { + + constructor(features, genome) { + this.featureMap = {}; + this.genome = genome; + + for (let feature of features) { + + // Store as canonical chr name (i.e. translate aliases) + const chrKey = genome ? genome.getChromosomeName(feature.chr) : feature.chr; + + let featureList = this.featureMap[chrKey]; + if (!featureList) { + featureList = []; + this.featureMap[chrKey] = featureList; + } + featureList.push(feature); + } + + for (let key of Object.keys(this.featureMap)) { + this.featureMap[key].sort((a, b) => a.start - b.start); + } + } + + getFeatures({chr, start, end}) { + if (chr.toLowerCase() === 'all') { + return computeWGFeatures(this.featureMap, this.genome) + } else { + // TODO -- this use of filter is O(N), and might not scale well for large feature lists. + const featureList = this.featureMap[chr]; + return featureList ? featureList.filter(feature => feature.end > start && feature.start < end) : [] + } + } + + getAllFeatures() { + return this.featureMap + } + + supportsWholeGenome() { + return true + } + + addFeature(feature) { + let featureList = this.featureMap[feature.chr]; + if (!featureList) { + featureList = []; + this.featureMap[feature.chr] = featureList; + } + featureList.push(feature); + featureList.sort((a, b) => a.start - b.start); + } + + removeFeature({chr, start, end}) { + + if (this.featureMap[chr]) { + const match = `${chr}-${start}-${end}`; + this.featureMap[chr] = this.featureMap[chr].filter(feature => match !== `${feature.chr}-${feature.start}-${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. + */ + + const RULER_SWEEPER_COLOR = 'rgba(68, 134, 247, 0.25)'; + + class RulerSweeper { + + constructor(rulerViewport, column, browser, referenceFrame) { + + this.rulerViewport = rulerViewport; + + this.rulerSweeper = domUtils.div({class: 'igv-ruler-sweeper'}); + column.appendChild(this.rulerSweeper); + + this.browser = browser; + this.referenceFrame = referenceFrame; + + this.isMouseHandlers = undefined; + + this.addBrowserObserver(); + } + + addBrowserObserver() { + + const observerHandler = () => { + if (this.referenceFrame) { + GenomeUtils.isWholeGenomeView(this.referenceFrame.chr) ? this.removeMouseHandlers() : this.addMouseHandlers(); + } + }; + + // Viewport Content + this.boundObserverHandler = observerHandler.bind(this); + this.browser.on('locuschange', this.boundObserverHandler); + + } + + removeBrowserObserver() { + this.browser.off('locuschange', this.boundObserverHandler); + } + + addMouseHandlers() { + + if (true === this.isMouseHandlers) { + return + } + + const threshold = 1; + + let isMouseDown; + let isMouseIn; + let mouseDownX; + let left; + let width; + let dx; + + // Viewport Content + this.boundContentMouseDownHandler = contentMouseDownHandler.bind(this); + this.rulerViewport.contentDiv.addEventListener('mousedown', this.boundContentMouseDownHandler); + + function contentMouseDownHandler(event) { + + isMouseDown = true; + isMouseIn = true; + + const {x} = domUtils.translateMouseCoordinates(event, this.rulerViewport.contentDiv); + left = mouseDownX = x; + + width = threshold; + + + this.rulerSweeper.style.display = 'block'; + this.rulerSweeper.style.backgroundColor = true === event.shiftKey ? ROI_USER_DEFINED_COLOR : RULER_SWEEPER_COLOR; + + 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) { + + let mouseCurrentX; + + if (isMouseDown && isMouseIn) { + + const {x} = domUtils.translateMouseCoordinates(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) { + + let genomicExtent; + + if (true === isMouseDown && true === isMouseIn) { + + isMouseDown = isMouseIn = undefined; + + this.rulerSweeper.style.display = 'none'; + + if (width > threshold) { + + genomicExtent = + { + start: Math.floor(this.referenceFrame.calculateEnd(left)), + end: Math.floor(this.referenceFrame.calculateEnd(left + width)), + }; + + + const shiftKeyPressed = event.shiftKey; + + if (true === shiftKeyPressed) { + this.browser.roiManager.updateUserDefinedROISet(Object.assign({chr: this.referenceFrame.chr}, genomicExtent)); + } else { + + validateGenomicExtent(this.browser.genome.getChromosome(this.referenceFrame.chr).bpLength, genomicExtent, this.browser.minimumBases()); + updateReferenceFrame(this.referenceFrame, genomicExtent, this.rulerViewport.contentDiv.clientWidth); + this.browser.updateViews(this.referenceFrame); + + } + + } + + } + + } + + 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 updateReferenceFrame(referenceFrame, genomicExtent, pixelWidth) { + referenceFrame.start = Math.round(genomicExtent.start); + referenceFrame.end = Math.round(genomicExtent.end); + referenceFrame.bpPerPixel = (referenceFrame.end - referenceFrame.start) / pixelWidth; + } + + /* + * 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. + */ + + 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 + + } + + getSortValue({position, option, tag}, alignmentContainer) { + + if (!option) option = "BASE"; + + const alignment = this.findAlignment(position); + if (undefined === alignment) { // This condition should never occur + return Number.MAX_VALUE + } + + 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": { + return alignment.tags()[tag] + } + case "READ_NAME": + return alignment.readName + case "INSERT_SIZE": + return -Math.abs(alignment.fragmentLength) + case "GAP_SIZE": + return -alignment.gapSizeAt(position) + case "MATE_CHR": + return alignment.mate + case "MQ": + return alignment.mq === undefined ? Number.MAX_VALUE : -alignment.mq + case "ALIGNED_READ_LENGTH": + return -alignment.lengthOnRef + case "SOFT_CLIPPED": + var regix = RegExp(/S/); + return alignment.cigar.match(regix) == "S" ? -1 : 1 + 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 + } + } + + + } + + const alignmentSpace = 2; + + 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) { + + //console.log(`packAlignmentRows ${start} ${end}`) + //const t0 = Date.now() + + 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 + }); + + const packedAlignmentRows = []; + let alignmentRow; + let nextStart = 0; + let nextIDX = 0; + const allocated = new Set(); + const startNewRow = () => { + alignmentRow = new BamAlignmentRow(); + packedAlignmentRows.push(alignmentRow); + nextStart = 0; + nextIDX = 0; + allocated.clear(); + }; + startNewRow(); + + while (alignments.length > 0) { + if (nextIDX >= 0 && nextIDX < alignments.length) { + const alignment = alignments[nextIDX]; + allocated.add(alignment); + alignmentRow.alignments.push(alignment); + nextStart = showSoftClips ? + alignment.scStart + alignment.scLengthOnRef + alignmentSpace : + alignment.start + alignment.lengthOnRef + alignmentSpace; + nextIDX = binarySearch$1(alignments, (a) => (showSoftClips ? a.scStart : a.start) > nextStart, nextIDX); + } else { + // Remove allocated alignments and start new row + alignments = alignments.filter(a => !allocated.has(a)); + startNewRow(); + } + } + //console.log(`Done in ${Date.now() - t0} ms`) + return packedAlignmentRows + } + } + + + /** + * Return 0 <= i <= array.length such that !pred(array[i - 1]) && pred(array[i]). + * + * returns an index 0 ≤ i ≤ array.length such that the given predicate is false for array[i - 1] and true for array[i]* * + */ + function binarySearch$1(array, pred, min) { + let lo = min - 1, hi = array.length; + while (1 + lo < hi) { + const mi = lo + ((hi - lo) >> 1); + if (pred(array[mi])) { + hi = mi; + } else { + lo = mi; + } + } + return hi + } + + /* + * 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, {samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold}) { + + 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) + } + + sortRows(options) { + + const newRows = []; + const undefinedRow = []; + for (let row of this.packedAlignmentRows) { + const alignment = row.findAlignment(options.position); + if (undefined !== alignment) { + newRows.push(row); + } else { + undefinedRow.push(row); + } + } + + newRows.sort((rowA, rowB) => { + const direction = options.direction; + const rowAValue = rowA.getSortValue(options, this); + const rowBValue = rowB.getSortValue(options, this); + + if (rowBValue === undefined && rowBValue !== undefined) return 1 + else if (rowAValue !== undefined && rowBValue === undefined) return -1 + + const i = rowAValue > rowBValue ? 1 : (rowAValue < rowBValue ? -1 : 0); + return true === direction ? i : -i + }); + + for (let row of undefinedRow) { + newRows.push(row); + } + + this.packedAlignmentRows = newRows; + } + + } + + + 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; + } + + hoverText() { + const pos = this.posA + this.posT + this.posC + this.posG + this.posN; + const neg = this.negA + this.negT + this.negC + this.negG + this.negN; + return `${this.total} (${pos}+, ${neg}-)` + } + + 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('
'); + + // 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('
'); + 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('
'); + nameValues.push({name: 'Supplementary Alignments', value: ''}); + const sa = createSupplementaryAlignments(tagDict['SA']); + if (sa) { + nameValues.push('
    '); + for (let s of sa) { + nameValues.push(`
  • ${s.printString()}
  • `); + } + nameValues.push('
'); + } + } + + const hiddenTags = new Set(['SA', 'MD']); + nameValues.push('
'); + 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('
'); + 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 + } + + /** + * Return soft clipped blocks, if they exist, keyed by alignment end (left or right) + */ + softClippedBlocks() { + let left; + let right; + let interiorSeen; + for(let b of this.blocks) { + if('S' === b.type) { + if(interiorSeen) { + right = b; + } else { + left = b; + } + } else if('H' !== b.type) { + interiorSeen = true; + } + } + return {left, right} + } + + } + + 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 + } + } + + /* + * 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. + */ + + + /** + * 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 + } + + /* + * 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 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) 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); + + this._blockLoader = new BGZBlockLoader(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.chunksForRange(chrId, bpStart, bpEnd); + + if (!chunks || chunks.length === 0) { + return alignmentContainer + } + + for (let c of chunks) { + const ba = await this._blockLoader.getData(c.minv, c.maxv); + const done = BamUtils.decodeBamRecords(ba, c.minv.offset, alignmentContainer, this.indexToChr, chrId, bpStart, bpEnd, this.filter); + if (done) { + 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$1(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®ion=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 + "" + + "®ion=" + 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 + + }) + } + + } + + /* + * 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 + + } + + } + + /*! For license information please see cram-bundle.js.LICENSE.txt */ + const fn = ()=>{var e={368:function(e,t,r){var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function s(e){try{f(n.next(e));}catch(e){o(e);}}function a(e){try{f(n.throw(e));}catch(e){o(e);}}function f(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t);}))).then(s,a);}f((n=n.apply(e,t||[])).next());}))},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const o=i(r(4105)),s=i(r(1269)),a=r(597),f=r(3427),h=r(8577);function l(e,t){if(t.some((e=>void 0===e)))throw new h.CramMalformedError("invalid .crai index file");const[r,n,i,o,s,a]=t;e[r]||(e[r]=[]),e[r].push({start:n,span:i,containerStart:o,sliceStart:s,sliceBytes:a});}t.default=class{constructor(e){this.filehandle=(0, f.open)(e.url,e.path,e.filehandle),this._parseCache=new o.default({cache:new s.default({maxSize:1}),fill:(e,t)=>this.parseIndex()});}parseIndex(){const e={};return this.filehandle.readFile().then((e=>31===e[0]&&139===e[1]?(0, a.unzip)(e):e)).then((t=>{if(t.length>4&&21578050===t.readUInt32LE(0))throw new h.CramMalformedError("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");let r=[],n="";for(let i=0;i=48&&o<=57||!n&&45===o)n+=String.fromCharCode(o);else if(9===o)r.push(Number.parseInt(n,10)),n="";else if(10===o)r.push(Number.parseInt(n,10)),n="",l(e,r),r=[];else if(13!==o&&32!==o)throw new h.CramMalformedError("invalid .crai index file")}return n&&r.push(Number.parseInt(n,10)),6===r.length&&l(e,r),Object.entries(e).forEach((([t,r])=>{e[t]=r.sort(((e,t)=>e.start-t.start||e.span-t.span));})),e}))}getIndex(e={}){return this._parseCache.get("index",null,e.signal)}hasDataForReferenceSequence(e){return n(this,void 0,void 0,(function*(){return !!(yield this.getIndex())[e]}))}getEntriesForRange(e,t,r){return n(this,void 0,void 0,(function*(){const n=(yield this.getIndex())[e];if(!n)return [];const i=e=>{const n=e.start,i=e.start+e.span;return n>r?-1:i<=t?1:0},o=[];for(let e=0;e{Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(e,t){this.parameters=e,this.dataType=t;}};},4863:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(8577),o=n(r(1050)),s=r(1074);class a extends o.default{constructor(e,t){if(super(e,t),"int"!==this.dataType)throw new i.CramUnimplementedError(`${this.dataType} decoding not yet implemented by BETA codec`)}decode(e,t,r,n){return (0, s.getBits)(t.content,n.coreBlock,this.parameters.length)-this.parameters.offset}}t.default=a;},1738:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(9488),o=n(r(1050));class s extends o.default{constructor(e,t,r){if(super(e,t),this.instantiateCodec=r,"byteArray"!==t)throw new TypeError(`byteArrayLength does not support data type ${t}`)}decode(e,t,r,n){const i=this._getLengthCodec().decode(e,t,r,n),o=this._getDataCodec(),s=new Uint8Array(i);for(let a=0;a(0, i.tinyMemoize)(s,e)));},1405:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(8577),o=n(r(1050)),s=r(1074);class a extends o.default{constructor(e,t){if(super(e,t),"byteArray"!==t)throw new TypeError(`byteArrayStop codec does not support data type ${t}`)}decode(e,t,r,n){const{blockContentId:o}=this.parameters,s=r[o];if(!s)throw new i.CramMalformedError(`no block found with content ID ${o}`);const a=n.externalBlocks.getCursor(o);return this._decodeByteArray(s,a)}_decodeByteArray(e,t){const r=e.content,{stopByte:n}=this.parameters,i=t.bytePosition;let o=t.bytePosition;for(;r[o]!==n&&o=e.content.length)throw new a.CramBufferOverrunError("attempted to read beyond end of block. this file seems truncated.");return e.content[t.bytePosition++]}}t.default=f;},4229:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(8577),o=n(r(1050)),s=r(1074);class a extends o.default{constructor(e,t){if(super(e,t),"int"!==this.dataType)throw new i.CramUnimplementedError(`${this.dataType} decoding not yet implemented by GAMMA codec`)}decode(e,t,r,n){let i=1;for(;0===(0, s.getBits)(t.content,n.coreBlock,1);)i+=1;return ((0, s.getBits)(t.content,n.coreBlock,i-1)|1<{Object.defineProperty(t,"__esModule",{value:!0}),t.getBits=t.CramBufferOverrunError=void 0;class r extends Error{}t.CramBufferOverrunError=r,t.getBits=function(e,t,n){let i=0;if(t.bytePosition+(7-t.bitPosition+n)/8>e.length)throw new r("read error during decoding. the file seems to be truncated.");for(let r=n;r;r--)i<<=1,i|=e[t.bytePosition]>>t.bitPosition&1,t.bitPosition-=1,t.bitPosition<0&&(t.bytePosition+=1),t.bitPosition&=7;return i};},2082:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(8577),o=n(r(1050)),s=r(1074);class a extends o.default{constructor(e,t){if(super(e,t),this.codes={},this.codeBook={},this.sortedByValue=[],this.sortedCodes=[],this.sortedValuesByBitCode=[],this.sortedBitCodes=[],this.sortedBitLengthsByBitCode=[],this.bitCodeToValue=[],!["byte","int"].includes(this.dataType))throw new TypeError(`${this.dataType} decoding not yet implemented by HUFFMAN_INT codec`);this.buildCodeBook(),this.buildCodes(),this.buildCaches(),0===this.sortedCodes[0].bitLength&&(this._decode=this._decodeZeroLengthCode);}buildCodeBook(){let e=new Array(this.parameters.numCodes);for(let t=0;te.bitLength-t.bitLength||e.symbol-t.symbol)),this.codeBook={},e.forEach((e=>{this.codeBook[e.bitLength]||(this.codeBook[e.bitLength]=[]),this.codeBook[e.bitLength].push(e.symbol);}));}buildCodes(){this.codes={};let e=0,t=-1;Object.entries(this.codeBook).forEach((([r,n])=>{const o=parseInt(r,10);n.forEach((r=>{const n={bitLength:o,value:r,bitCode:0};t+=1;const s=o-e;if(t<<=s,n.bitCode=t,e+=s,function(e){let t=e-(e>>1)&1431655765;return t=(858993459&t)+(t>>2&858993459),16843009*(t+(t>>4)&252645135)>>24}(t)>o)throw new i.CramMalformedError("Symbol out of range");this.codes[r]=n;}));}));}buildCaches(){this.sortedCodes=Object.values(this.codes).sort(((e,t)=>e.bitLength-t.bitLength||e.bitCode-t.bitCode)),this.sortedByValue=Object.values(this.codes).sort(((e,t)=>e.value-t.value)),this.sortedValuesByBitCode=this.sortedCodes.map((e=>e.value)),this.sortedBitCodes=this.sortedCodes.map((e=>e.bitCode)),this.sortedBitLengthsByBitCode=this.sortedCodes.map((e=>e.bitLength));const e=Math.max(...this.sortedBitCodes);this.bitCodeToValue=new Array(e+1).fill(-1);for(let e=0;e-1&&this.sortedBitLengthsByBitCode[r]===t)return this.sortedValuesByBitCode[r];for(let r=e;this.sortedCodes[r+1].bitLength===t&&r{Object.defineProperty(t,"__esModule",{value:!0}),t.default={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};},8543:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=r(8772),i=r(8577),o={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"};t.default=class{constructor(e){this.dataSeriesCodecCache={},this.tagCodecCache={},this.tagEncoding={},this.readNamesIncluded=e.preservation.RN,this.APdelta=e.preservation.AP,this.referenceRequired=!!e.preservation.RR,this.tagIdsDictionary=e.preservation.TD,this.substitutionMatrix=function(e){const t=new Array(5);for(let e=0;e<5;e+=1)t[e]=new Array(4);return t[0][e[0]>>6&3]="C",t[0][e[0]>>4&3]="G",t[0][e[0]>>2&3]="T",t[0][e[0]>>0&3]="N",t[1][e[1]>>6&3]="A",t[1][e[1]>>4&3]="G",t[1][e[1]>>2&3]="T",t[1][e[1]>>0&3]="N",t[2][e[2]>>6&3]="A",t[2][e[2]>>4&3]="C",t[2][e[2]>>2&3]="T",t[2][e[2]>>0&3]="N",t[3][e[3]>>6&3]="A",t[3][e[3]>>4&3]="C",t[3][e[3]>>2&3]="G",t[3][e[3]>>0&3]="N",t[4][e[4]>>6&3]="A",t[4][e[4]>>4&3]="C",t[4][e[4]>>2&3]="G",t[4][e[4]>>0&3]="T",t}(e.preservation.SM),this.dataSeriesEncoding=e.dataSeriesEncoding,this.tagEncoding=e.tagEncoding,this.preservation=e.preservation,this._size=e._size,this._endPosition=e._endPosition;}getCodecForTag(e){if(!this.tagCodecCache[e]){const t=this.tagEncoding[e];t&&(this.tagCodecCache[e]=(0, n.instantiateCodec)(t,"byteArray"));}return this.tagCodecCache[e]}getTagNames(e){return this.tagIdsDictionary[e]}getCodecForDataSeries(e){let t=this.dataSeriesCodecCache[e];if(void 0===t){const r=this.dataSeriesEncoding[e];if(r){const s=o[e];if(!s)throw new i.CramMalformedError(`data series name ${e} not defined in file compression header`);t=(0, n.instantiateCodec)(r,s),this.dataSeriesCodecCache[e]=t;}}return t}toJSON(){const e={};return Object.keys(this).forEach((t=>{/Cache$/.test(t)||(e[t]=this[t]);})),e}};},6284:function(e,t,r){var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function s(e){try{f(n.next(e));}catch(e){o(e);}}function a(e){try{f(n.throw(e));}catch(e){o(e);}}function f(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t);}))).then(s,a);}f((n=n.apply(e,t||[])).next());}))},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const o=r(8577),s=r(9488),a=i(r(6601)),f=i(r(8543));class h{constructor(e,t){this.file=e,this.filePosition=t;}getHeader(){return this._readContainerHeader(this.filePosition)}getCompressionHeaderBlock(){return n(this,void 0,void 0,(function*(){if(!(yield this.getHeader()).numRecords)return null;const e=yield this.file.getSectionParsers(),t=yield this.getFirstBlock();if(void 0===t)return;if("COMPRESSION_HEADER"!==t.contentType)throw new o.CramMalformedError(`invalid content type ${t.contentType} in what is supposed to be the compression header block`);const r=(0, s.parseItem)(t.content,e.cramCompressionHeader.parser,0,t.contentPosition);return Object.assign(Object.assign({},t),{parsedContent:r})}))}getFirstBlock(){return n(this,void 0,void 0,(function*(){const e=yield this.getHeader();return this.file.readBlock(e._endPosition)}))}getCompressionScheme(){return n(this,void 0,void 0,(function*(){const e=yield this.getCompressionHeaderBlock();if(e)return new f.default(e.parsedContent)}))}getSlice(e,t){return new a.default(this,e,t)}_readContainerHeader(e){return n(this,void 0,void 0,(function*(){const t=yield this.file.getSectionParsers(),{cramContainerHeader1:r,cramContainerHeader2:n}=t,{size:i}=yield this.file.stat();if(e>=i)return;const o=Buffer.allocUnsafe(r.maxLength);yield this.file.read(o,0,r.maxLength,e);const a=(0, s.parseItem)(o,r.parser),f=(0, s.itf8Size)(a.numLandmarks);if(e+a.length>=i)return void console.warn(`${this.file}: container header at ${e} indicates that the container has length ${a.length}, which extends beyond the length of the file. Skipping this container.`);const h=Buffer.allocUnsafe(n.maxLength(a.numLandmarks));yield this.file.read(h,0,n.maxLength(a.numLandmarks),e+a._size-f);const l=(0, s.parseItem)(h,n.parser);return this.file.validateChecksums&&void 0!==l.crc32&&(yield this.file.checkCrc32(e,a._size+l._size-f-4,l.crc32,`container header beginning at position ${e}`)),Object.assign(a,l,{_size:a._size+l._size-f,_endPosition:a._size+l._size-f+e})}))}}t.default=h,"getHeader getCompressionHeaderBlock getCompressionScheme".split(" ").forEach((e=>(0, s.tinyMemoize)(h,e)));},5457:function(e,t,r){var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function s(e){try{f(n.next(e));}catch(e){o(e);}}function a(e){try{f(n.throw(e));}catch(e){o(e);}}function f(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t);}))).then(s,a);}f((n=n.apply(e,t||[])).next());}))},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const o=r(597),s=i(r(2779)),a=i(r(1269)),f=r(8577),h=i(r(9675)),l=r(6141),u=i(r(3498)),c=i(r(6284)),d=r(3427),p=r(9488),g=r(7578);class m{constructor(e){var t;if(this.file=(0, d.open)(e.url,e.path,e.filehandle),this.validateChecksums=!0,this.fetchReferenceSequenceCallback=e.seqFetch,this.options={checkSequenceMD5:e.checkSequenceMD5,cacheSize:null!==(t=e.cacheSize)&&void 0!==t?t:2e4},this.featureCache=new a.default({maxSize:this.options.cacheSize}),function(){const e=new Uint32Array([287454020]),t=new Uint8Array(e.buffer);return 68===t[0]?0:17===t[0]?1:2}()>0)throw new Error("Detected big-endian machine, may be unable to run")}read(e,t,r,n){return this.file.read(e,t,r,n)}stat(){return this.file.stat()}getDefinition(){return n(this,void 0,void 0,(function*(){const e=Buffer.allocUnsafe(l.cramFileDefinition.maxLength);yield this.file.read(e,0,l.cramFileDefinition.maxLength,0);const t=l.cramFileDefinition.parser.parse(e).result;if(2!==t.majorVersion&&3!==t.majorVersion)throw new f.CramUnimplementedError(`CRAM version ${t.majorVersion} not supported`);return t}))}getSamHeader(){return n(this,void 0,void 0,(function*(){const e=yield this.getContainerById(0);if(!e)throw new f.CramMalformedError("file contains no containers");const t=yield e.getFirstBlock();if(void 0===t)return (0, g.parseHeaderText)("");const r=t.content,n=r.readInt32LE(0),i=r.toString("utf8",4,4+n);return this.header=i,(0, g.parseHeaderText)(i)}))}getHeaderText(){return n(this,void 0,void 0,(function*(){return yield this.getSamHeader(),this.header}))}getSectionParsers(){return n(this,void 0,void 0,(function*(){const{majorVersion:e}=yield this.getDefinition();return (0, l.getSectionParsers)(e)}))}getContainerById(e){return n(this,void 0,void 0,(function*(){const t=yield this.getSectionParsers();let r=t.cramFileDefinition.maxLength;const{size:n}=yield this.file.stat(),{cramContainerHeader1:i}=t;let o;for(let t=0;t<=e;t+=1){if(r+i.maxLength+8>=n)return;o=this.getContainerAtPosition(r);const s=yield o.getHeader();if(!s)throw new f.CramMalformedError(`container ${e} not found in file`);if(0===t){r=s._endPosition;for(let e=0;e=n)return;const i=Buffer.allocUnsafe(r.maxLength);return yield this.file.read(i,0,r.maxLength,e),(0, p.parseItem)(i,r.parser,0,e)}))}_parseSection(e,t,r=e.maxLength,i){return n(this,void 0,void 0,(function*(){let n;if(i)n=i;else {const{size:e}=yield this.file.stat();if(t+r>=e)return;n=Buffer.allocUnsafe(r),yield this.file.read(n,0,r,t);}const o=(0, p.parseItem)(n,e.parser,0,t);if(o._size!==r)throw new f.CramMalformedError(`section read error: requested size ${r} does not equal parsed size ${o._size}`);return o}))}_uncompress(e,t,r){if("gzip"===e)(0, o.unzip)(t).copy(r);else if("bzip2"===e){const e=bzip2.array(t);let n,i=bzip2.header(e),o=0;do{n=bzip2.decompress(e,i),-1!=n&&(Buffer.from(n).copy(r,o),o+=n.length,i-=n.length);}while(-1!=n)}else if("rans"===e)(0, h.default)(t,r);else if("rans4x16"===e)u.default.r4x16_uncompress(t,r);else if("arith"===e)u.default.arith_uncompress(t,r);else if("fqzcomp"===e)u.default.fqzcomp_uncompress(t,r);else {if("tok3"!==e)throw new f.CramUnimplementedError(`${e} decompression not yet implemented`);u.default.tok3_uncompress(t,r);}}readBlock(e){return n(this,void 0,void 0,(function*(){const{majorVersion:t}=yield this.getDefinition(),r=yield this.getSectionParsers(),n=yield this.readBlockHeader(e);if(void 0===n)return;const i=n._endPosition,o=Buffer.allocUnsafe(n.uncompressedSize),s=Object.assign(Object.assign({},n),{_endPosition:i,contentPosition:i,content:o});if("raw"!==n.compressionMethod){const e=Buffer.allocUnsafe(n.compressedSize);yield this.read(e,0,n.compressedSize,i),this._uncompress(n.compressionMethod,e,o);}else yield this.read(o,0,n.uncompressedSize,i);if(t>=3){const t=yield this._parseSection(r.cramBlockCrc32,i+n.compressedSize);if(void 0===t)return;s.crc32=t.crc32,this.validateChecksums&&(yield this.checkCrc32(e,n._size+n.compressedSize,t.crc32,"block data")),s._endPosition=t._endPosition,s._size=s.compressedSize+r.cramBlockCrc32.maxLength;}else s._endPosition=i+s.compressedSize,s._size=s.compressedSize;return s}))}}t.default=m,"getDefinition getSectionParsers getSamHeader".split(" ").forEach((e=>(0, p.tinyMemoize)(m,e)));},8222:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.CramRecord=void 0;const i=n(r(5457));var o=r(8631);Object.defineProperty(t,"CramRecord",{enumerable:!0,get:function(){return n(o).default}}),t.default=i.default;},8631:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MateFlagsDecoder=t.CramFlagsDecoder=t.BamFlagsDecoder=t.MateFlags=t.CramFlags=t.BamFlags=void 0;const i=n(r(2615)),o={a:0,A:0,c:1,C:1,g:2,G:2,t:3,T:3,n:4,N:4};function s(e){const t={};for(const[r,n]of e)t["is"+n]=e=>!!(e&r),t["set"+n]=e=>e|r;return t}t.BamFlags=[[1,"Paired"],[2,"ProperlyPaired"],[4,"SegmentUnmapped"],[8,"MateUnmapped"],[16,"ReverseComplemented"],[32,"MateReverseComplemented"],[64,"Read1"],[128,"Read2"],[256,"Secondary"],[512,"FailedQc"],[1024,"Duplicate"],[2048,"Supplementary"]],t.CramFlags=[[1,"PreservingQualityScores"],[2,"Detached"],[4,"WithMateDownstream"],[8,"DecodeSequenceAsStar"]],t.MateFlags=[[1,"OnNegativeStrand"],[2,"Unmapped"]],t.BamFlagsDecoder=s(t.BamFlags),t.CramFlagsDecoder=s(t.CramFlags),t.MateFlagsDecoder=s(t.MateFlags),t.default=class{constructor({flags:e,cramFlags:t,readLength:r,mappingQuality:n,lengthOnRef:i,qualityScores:o,mateRecordNumber:s,readBases:a,readFeatures:f,mateToUse:h,readGroupId:l,readName:u,sequenceId:c,uniqueId:d,templateSize:p,alignmentStart:g,tags:m}){this.flags=e,this.cramFlags=t,this.readLength=r,this.mappingQuality=n,this.lengthOnRef=i,this.qualityScores=o,a&&(this.readBases=a),this.readGroupId=l,this.readName=u,this.sequenceId=c,this.uniqueId=d,this.templateSize=p,this.alignmentStart=g,this.tags=m,f&&(this.readFeatures=f),h&&(this.mate={flags:h.mateFlags,readName:h.mateReadName,sequenceId:h.mateSequenceId,alignmentStart:h.mateAlignmentStart}),s&&(this.mateRecordNumber=s);}isPaired(){return !!(this.flags&i.default.BAM_FPAIRED)}isProperlyPaired(){return !!(this.flags&i.default.BAM_FPROPER_PAIR)}isSegmentUnmapped(){return !!(this.flags&i.default.BAM_FUNMAP)}isMateUnmapped(){return !!(this.flags&i.default.BAM_FMUNMAP)}isReverseComplemented(){return !!(this.flags&i.default.BAM_FREVERSE)}isMateReverseComplemented(){return !!(this.flags&i.default.BAM_FMREVERSE)}isRead1(){return !!(this.flags&i.default.BAM_FREAD1)}isRead2(){return !!(this.flags&i.default.BAM_FREAD2)}isSecondary(){return !!(this.flags&i.default.BAM_FSECONDARY)}isFailedQc(){return !!(this.flags&i.default.BAM_FQCFAIL)}isDuplicate(){return !!(this.flags&i.default.BAM_FDUP)}isSupplementary(){return !!(this.flags&i.default.BAM_FSUPPLEMENTARY)}isDetached(){return !!(this.cramFlags&i.default.CRAM_FLAG_DETACHED)}hasMateDownStream(){return !!(this.cramFlags&i.default.CRAM_FLAG_MATE_DOWNSTREAM)}isPreservingQualityScores(){return !!(this.cramFlags&i.default.CRAM_FLAG_PRESERVE_QUAL_SCORES)}isUnknownBases(){return !!(this.cramFlags&i.default.CRAM_FLAG_NO_SEQ)}getReadBases(){if(!this.readBases&&this._refRegion){const e=function(e,t){if(!e.lengthOnRef&&!e.readLength)return null;if(e.isUnknownBases())return null;const r=e.alignmentStart-t.start;if(!e.readFeatures)return t.seq.substr(r,e.lengthOnRef).toUpperCase();let n="",i=r,o=0;for(;n.lengththis.mate.alignmentStart&&o>0&&(o=-o),o>0?(i[0]=e,i[1]=r,i[2]=t,i[3]=n):(i[2]=e,i[3]=r,i[0]=t,i[1]=n),i.join("")}return null}addReferenceSequence(e,t){this.readFeatures&&this.readFeatures.forEach((r=>{"X"===r.code&&function(e,t,r,n){if(!t)return;const i=n.refPos-t.start,s=t.seq.charAt(i);s&&(n.ref=s);let a=o[s];void 0===a&&(a=4);const f=r.substitutionMatrix[a][n.data];f&&(n.sub=f);}(0,e,t,r);})),!this.readBases&&e.start<=this.alignmentStart&&e.end>=this.alignmentStart+(this.lengthOnRef||this.readLength)-1&&(this._refRegion=e);}toJSON(){const e={};return Object.keys(this).forEach((t=>{"_"!==t.charAt(0)&&(e[t]=this[t]);})),e.readBases=this.getReadBases(),e}};},6141:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getSectionParsers=t.cramFileDefinition=t.isMappedSliceHeader=void 0;const n=r(9996),i=(new n.Parser).itf8(),o={parser:(new n.Parser).string("magic",{length:4}).uint8("majorVersion").uint8("minorVersion").string("fileId",{length:20,stripNull:!0}),maxLength:26};t.cramFileDefinition=o;const s={parser:(new n.Parser).uint8("compressionMethod",{formatter:e=>{const t=["raw","gzip","bzip2","lzma","rans","rans4x16","arith","fqzcomp","tok3"][e];if(!t)throw new Error(`compression method number ${e} not implemented`);return t}}).uint8("contentType",{formatter:e=>{const t=["FILE_HEADER","COMPRESSION_HEADER","MAPPED_SLICE_HEADER","UNMAPPED_SLICE_HEADER","EXTERNAL_DATA","CORE_DATA"][e];if(!t)throw new Error(`invalid block content type id ${e}`);return t}}).itf8("contentId").itf8("compressedSize").itf8("uncompressedSize"),maxLength:17},a={parser:(new n.Parser).uint32("crc32"),maxLength:4},f=(new n.Parser).itf8("size").buffer("ents",{length:"size",formatter:e=>{function t(t,r){const n=e.toString("utf8",t,r),i=[];for(let e=0;ei&&n.push(t(i,r)),n}}),h=(new n.Parser).uint8(null,{formatter:e=>!!e}),l=(new n.Parser).itf8("mapSize").itf8("mapCount").array("ents",{length:"mapCount",type:(new n.Parser).string("key",{length:2,stripNull:!1}).choice("value",{tag:"key",choices:{MI:h,UI:h,PI:h,RN:h,AP:h,RR:h,SM:(new n.Parser).array(null,{type:"uint8",length:5}),TD:(new n.Parser).nest(null,{type:f,formatter:e=>e.ents})}})});function u(e){const t={};for(let r=0;r=3?(r=r.ltf8("recordCounter"),t+=9):2===e&&(r=r.itf8("recordCounter"),t+=5),r=r.itf8("numBlocks").itf8("numContentIds").array("contentIds",{type:i,length:"numContentIds"}),t+=10,e>=2&&(r=r.array("md5",{type:"uint8",length:16}),t+=16),{parser:r,maxLength:e=>t+5*e}},cramMappedSliceHeader(e){let t=(new n.Parser).itf8("refSeqId").itf8("refSeqStart").itf8("refSeqSpan").itf8("numRecords"),r=20;return e>=3?(t=t.ltf8("recordCounter"),r+=9):2===e&&(t=t.itf8("recordCounter"),r+=5),t=t.itf8("numBlocks").itf8("numContentIds").array("contentIds",{type:i,length:"numContentIds"}).itf8("refBaseBlockId"),r+=15,e>=2&&(t=t.array("md5",{type:"uint8",length:16}),r+=16),{parser:t,maxLength:e=>r+5*e}},cramEncoding:e=>({parser:(new n.Parser).namely("cramEncoding").itf8("codecId").itf8("parametersBytes").choice("parameters",{tag:"codecId",choices:{0:new n.Parser,1:(new n.Parser).itf8("blockContentId"),2:(new n.Parser).itf8("offset").itf8("M"),3:n.Parser.start().itf8("numCodes").array("symbols",{length:"numCodes",type:i}).itf8("numLengths").array("bitLengths",{length:"numLengths",type:i}),4:n.Parser.start().nest("lengthsEncoding",{type:"cramEncoding"}).nest("valuesEncoding",{type:"cramEncoding"}),5:(new n.Parser).uint8("stopByte").itf8("blockContentId"),6:(new n.Parser).itf8("offset").itf8("length"),7:(new n.Parser).itf8("offset").itf8("K"),8:(new n.Parser).itf8("offset").itf8("log2m"),9:(new n.Parser).itf8("offset")}})}),cramDataSeriesEncodingMap(e){return (new n.Parser).itf8("mapSize").itf8("mapCount").array("ents",{length:"mapCount",type:(new n.Parser).string("key",{length:2,stripNull:!1}).nest("value",{type:this.cramEncoding(e).parser})})},cramTagEncodingMap(e){return (new n.Parser).itf8("mapSize").itf8("mapCount").array("ents",{length:"mapCount",type:(new n.Parser).itf8("key",{formatter:e=>String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e)}).nest("value",{type:this.cramEncoding(e).parser})})},cramCompressionHeader(e){let t=new n.Parser;return t=t.nest("preservation",{type:l,formatter:u}).nest("dataSeriesEncoding",{type:this.cramDataSeriesEncodingMap(e),formatter:u}).nest("tagEncoding",{type:this.cramTagEncodingMap(e),formatter:u}),{parser:t}},cramContainerHeader1(e){let t=(new n.Parser).int32("length").itf8("refSeqId").itf8("refSeqStart").itf8("alignmentSpan").itf8("numRecords"),r=24;return e>=3?(t=t.ltf8("recordCounter"),r+=9):2===e&&(t=t.itf8("recordCounter"),r+=5),e>1&&(t=t.ltf8("numBases"),r+=9),t=t.itf8("numBlocks").itf8("numLandmarks"),r+=10,{parser:t,maxLength:r}},cramContainerHeader2(e){let t=(new n.Parser).itf8("numLandmarks").array("landmarks",{type:(new n.Parser).itf8(),length:"numLandmarks"}),r=0;return e>=3&&(t=t.uint32("crc32"),r=4),{parser:t,maxLength:e=>5+5*e+r}}};t.getSectionParsers=function(e){const t=Object.assign({},c);return Object.keys(d).forEach((r=>{t[r]=d[r](e);})),t};},3757:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(r(3720)),o=r(8577),s=r(8631),a=r(6141);function f(e){let t="";for(let r=0;r1&&-2===n.parsedContent.refSeqId?t("RI"):n.parsedContent.refSeqId;const y=t("RL");let b=t("AP");r.APdelta&&(b+=u.lastAlignmentStart),u.lastAlignmentStart=b;const v=t("RG");let w,_,B,E;if(r.readNamesIncluded&&(w=f(t("RN"))),s.CramFlagsDecoder.isDetached(g)){const e=t("MF");let n;r.readNamesIncluded||(n=f(t("RN")),w=n);const i=t("NS"),o=t("NP");(e||i>-1)&&(_={mateFlags:e,mateSequenceId:i,mateAlignmentStart:o,mateReadName:n}),B=t("TS"),s.MateFlagsDecoder.isUnmapped(e)&&(p=s.BamFlagsDecoder.setMateUnmapped(p)),s.MateFlagsDecoder.isOnNegativeStrand(e)&&(p=s.BamFlagsDecoder.setMateReverseComplemented(p));}else s.CramFlagsDecoder.isWithMateDownstream(g)&&(E=t("NF")+d+1);const S=t("TL");if(S<0)throw new o.CramMalformedError("invalid TL index");const A={},C=r.getTagNames(S),R=C.length;for(let t=0;t1?"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"]}[t];if(!l)throw new o.CramMalformedError(`invalid read feature code "${t}"`);let u=h(l);const c={B:["number","QS"]}[t];c&&(u=[u,h(c)]),s+=n;const d=s;a+=n;const p=a;"D"===t||"N"===t?a+=u:"I"===t||"S"===t?a-=u.length:"i"===t&&(a-=1),f[e]={code:t,pos:d,refPos:p,data:u};}return f}(b,e,t,0,c)),x=y,k)for(const{code:e,data:t}of k)"D"===e||"N"===e?x+=t:"I"===e||"S"===e?x-=t.length:"i"===e&&(x-=1);if(Number.isNaN(x)&&(console.warn(`${w||`${m}:${b}`} record has invalid read features`),x=y),I=t("MQ"),s.CramFlagsDecoder.isPreservingQualityScores(g)){M=new Array(y);for(let e=0;e=0){const i=e[r.mateRecordNumber];if(!i)throw new o.CramMalformedError("intra-slice mate record not found, this file seems malformed");n.push(...t(i));}return n}(r),i=n.map((e=>e.alignmentStart)),s=n.map((e=>e.alignmentStart+e.readLength-1)),a=Math.max(...s)-Math.min(...i)+1;a>=0&&n.forEach((e=>{if(void 0!==e.templateLength)throw new o.CramMalformedError("mate pair group has some members that have template lengths already, this file seems malformed");e.templateLength=a;}));}(e,0,r):function(e,t){const r=Math.min(e.alignmentStart,t.alignmentStart),n=Math.max(e.alignmentStart+e.readLength-1,t.alignmentStart+t.readLength-1)-r+1;e.templateLength=n,t.templateLength=n;}(r,n)),delete r.mateRecordNumber;}class d{constructor(e,t,r){this.container=e,this.containerPosition=t,this.file=e.file;}getHeader(){return n(this,void 0,void 0,(function*(){const e=yield this.file.getSectionParsers(),t=yield this.container.getHeader(),r=yield this.file.readBlock(t._endPosition+this.containerPosition);if(void 0===r)throw new Error;if("MAPPED_SLICE_HEADER"===r.contentType){const n=(0, s.parseItem)(r.content,e.cramMappedSliceHeader.parser,0,t._endPosition);return Object.assign(Object.assign({},r),{parsedContent:n})}if("UNMAPPED_SLICE_HEADER"===r.contentType){const n=(0, s.parseItem)(r.content,e.cramUnmappedSliceHeader.parser,0,t._endPosition);return Object.assign(Object.assign({},r),{parsedContent:n})}throw new o.CramMalformedError(`error reading slice header block, invalid content type ${r.contentType}`)}))}getBlocks(){return n(this,void 0,void 0,(function*(){const e=yield this.getHeader();let t=e._endPosition;const r=new Array(e.parsedContent.numBlocks);for(let e=0;e{"EXTERNAL_DATA"===e.contentType&&(t[e.contentId]=e);})),t}))}getBlockByContentId(e){return n(this,void 0,void 0,(function*(){return (yield this._getBlocksContentIdIndex())[e]}))}getReferenceRegion(){return n(this,void 0,void 0,(function*(){const e=(yield this.getHeader()).parsedContent;if(!(0, l.isMappedSliceHeader)(e))throw new Error;if(e.refSeqId<0)return;const t=yield this.container.getCompressionScheme();if(void 0===t)throw new Error;if(e.refBaseBlockId>=0){const t=yield this.getBlockByContentId(e.refBaseBlockId);if(!t)throw new o.CramMalformedError("embedded reference specified, but reference block does not exist");return {seq:t.data.toString("utf8"),start:e.refSeqStart,end:e.refSeqStart+e.refSeqSpan-1,span:e.refSeqSpan}}if(t.referenceRequired||this.file.fetchReferenceSequenceCallback){if(!this.file.fetchReferenceSequenceCallback)throw new Error("reference sequence not embedded, and seqFetch callback not provided, cannot fetch reference sequence");const t=yield this.file.fetchReferenceSequenceCallback(e.refSeqId,e.refSeqStart,e.refSeqStart+e.refSeqSpan-1);if(t.length!==e.refSeqSpan)throw new o.CramArgumentError("seqFetch callback returned a reference sequence of the wrong length");return {seq:t,start:e.refSeqStart,end:e.refSeqStart+e.refSeqSpan-1,span:e.refSeqSpan}}}))}getAllRecords(){return this.getRecords((()=>!0))}_fetchRecords(){return n(this,void 0,void 0,(function*(){const{majorVersion:e}=yield this.file.getDefinition(),t=yield this.container.getCompressionScheme();if(void 0===t)throw new Error;const r=yield this.getHeader();if(void 0===r)throw new Error;const n=yield this._getBlocksContentIdIndex();if(e>1&&this.file.options.checkSequenceMD5&&(0, l.isMappedSliceHeader)(r.parsedContent)&&r.parsedContent.refSeqId>=0&&"0000000000000000"!==r.parsedContent.md5.join("")){const e=yield this.getReferenceRegion();if(e){const{seq:t,start:n,end:i}=e,a=(0, s.sequenceMD5)(t),f=r.parsedContent.md5.map((e=>(e<16?"0":"")+e.toString(16))).join("");if(a!==f)throw new o.CramMalformedError(`MD5 checksum reference mismatch for ref ${r.parsedContent.refSeqId} pos ${n}..${i}. recorded MD5: ${f}, calculated MD5: ${a}`)}}const i=yield this.getCoreDataBlock(),a={lastAlignmentStart:(0, l.isMappedSliceHeader)(r.parsedContent)?r.parsedContent.refSeqStart:0,coreBlock:{bitPosition:7,bytePosition:0},externalBlocks:{map:new Map,getCursor(e){let t=this.map.get(e);return void 0===t&&(t={bitPosition:7,bytePosition:0},this.map.set(e,t)),t}}},d=e=>{const r=t.getCodecForDataSeries(e);if(!r)throw new o.CramMalformedError(`no codec defined for ${e} data series`);return r.decode(this,i,n,a)};let p=new Array(r.parsedContent.numRecords);for(let o=0;o!!e));break}throw e}for(let e=0;e=0&&c(p,e,p[e],p[t]);}return p}))}getRecords(e){return n(this,void 0,void 0,(function*(){const t=this.container.filePosition+this.containerPosition;let r=this.file.featureCache.get(t.toString());r||(r=this._fetchRecords(),this.file.featureCache.set(t.toString(),r));const i=(yield r).filter(e);if(i.length&&this.file.fetchReferenceSequenceCallback){const e=yield this.getHeader();if((0, l.isMappedSliceHeader)(e.parsedContent)&&(e.parsedContent.refSeqId>=0||-2===e.parsedContent.refSeqId)){const t=e.parsedContent.refSeqId>=0?e.parsedContent.refSeqId:void 0,r=yield this.container.getCompressionScheme();if(void 0===r)throw new Error;const o={};for(let e=0;en.end&&(n.end=s),i[e].alignmentStartn(this,void 0,void 0,(function*(){-1!==e.id&&e.start<=e.end&&(e.seq=yield this.file.fetchReferenceSequenceCallback(e.id,e.start,e.end));})))));for(let e=0;e(0, s.tinyMemoize)(d,e)));},9488:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.sequenceMD5=t.tinyMemoize=t.parseItem=t.parseItf8=t.itf8Size=void 0;const i=n(r(2568)),o=r(1074);t.itf8Size=function(e){return -128&e?-16384&e?-2097152&e?-268435456&e?5:4:3:2:1},t.parseItf8=function(e,t){let r=t;const n=e[r];let i;if(n<128?(i=n,r+=1):n<192?(i=16383&(n<<8|e[r+1]),r+=2):n<224?(i=2097151&(n<<16|e[r+1]<<8|e[r+2]),r+=3):n<240?(i=268435455&(n<<24|e[r+1]<<16|e[r+2]<<8|e[r+3]),r+=4):(i=(15&n)<<28|e[r+1]<<20|e[r+2]<<12|e[r+3]<<4|15&e[r+4],r+=5),r>e.length)throw new o.CramBufferOverrunError("Attempted to read beyond end of buffer; this file seems truncated.");return [i,r-t]},t.parseItem=function(e,t,r=0,n=0){const{offset:i,result:o}=t.parse(e);return Object.assign(Object.assign({},o),{_endPosition:i+n,_size:i-r})},t.tinyMemoize=function(e,t){const r=e.prototype[t],n=`_memo_${t}`;e.prototype[t]=function(){if(!(n in this)){const e=r.call(this);this[n]=e,Promise.resolve(e).catch((()=>{delete this[n];}));}return this[n]};},t.sequenceMD5=function(e){return (0, i.default)(e.toUpperCase().replace(/[^\x21-\x7e]/g,""))};},8577:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CramArgumentError=t.CramSizeLimitError=t.CramMalformedError=t.CramUnimplementedError=t.CramError=void 0;class r extends Error{}t.CramError=r;class n extends Error{}t.CramUnimplementedError=n,t.CramMalformedError=class extends r{},t.CramSizeLimitError=class extends r{},t.CramArgumentError=class extends r{};},5590:function(e,t,r){var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var i=Object.getOwnPropertyDescriptor(t,r);i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,i);}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r];}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t});}:function(e,t){e.default=t;}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&n(t,e,r);return i(t,e),t},s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.CramRecord=t.CraiIndex=t.IndexedCramFile=t.CramFile=void 0;const a=o(r(8222));t.CramFile=a.default,Object.defineProperty(t,"CramRecord",{enumerable:!0,get:function(){return a.CramRecord}});const f=s(r(946));t.IndexedCramFile=f.default;const h=s(r(368));t.CraiIndex=h.default;},946:function(e,t,r){var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function s(e){try{f(n.next(e));}catch(e){o(e);}}function a(e){try{f(n.throw(e));}catch(e){o(e);}}function f(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t);}))).then(s,a);}f((n=n.apply(e,t||[])).next());}))},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const o=r(8577),s=i(r(8222));t.default=class{constructor(e){if(e.cram?this.cram=e.cram:this.cram=new s.default({url:e.cramUrl,path:e.cramPath,filehandle:e.cramFilehandle,seqFetch:e.seqFetch,checkSequenceMD5:e.checkSequenceMD5,cacheSize:e.cacheSize}),!(this.cram instanceof s.default))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;}getRecordsForRange(e,t,r,i={}){return n(this,void 0,void 0,(function*(){if(i.viewAsPairs=i.viewAsPairs||!1,i.pairAcrossChr=i.pairAcrossChr||!1,i.maxInsertSize=i.maxInsertSize||2e5,"string"==typeof e)throw new o.CramUnimplementedError("string sequence names not yet supported");const n=e,s=yield this.index.getEntriesForRange(n,t,r),a=s.map((e=>e.sliceBytes)).reduce(((e,t)=>e+t),0);if(a>this.fetchSizeLimit)throw new o.CramSizeLimitError(`data size of ${a.toLocaleString()} bytes exceeded fetch size limit of ${this.fetchSizeLimit.toLocaleString()} bytes`);const f=n=>n.sequenceId===e&&n.alignmentStart<=r&&void 0!==n.lengthOnRef&&n.alignmentStart+n.lengthOnRef-1>=t,h=yield Promise.all(s.map((e=>this.getRecordsInSlice(e,f))));let l=Array.prototype.concat(...h);if(i.viewAsPairs){const e={},t={};for(let r=0;r{1===t&&(r[e]=!0);}));const o=[];for(let e=0;ee.toString().localeCompare(t.toString()))).filter(((e,t,r)=>!t||e.toString()!==r[t-1].toString()));const h=[],u=a.map((e=>e.sliceBytes)).reduce(((e,t)=>e+t),0);if(u>this.fetchSizeLimit)throw new Error(`mate data size of ${u.toLocaleString()} bytes exceeded fetch size limit of ${this.fetchSizeLimit.toLocaleString()} bytes`);a.forEach((e=>{let n=this.cram.featureCache.get(e.toString());n||(n=this.getRecordsInSlice(e,(()=>!0)),this.cram.featureCache.set(e.toString(),n));const i=n.then((e=>{const n=[];for(let i=0;ie.concat(t)));l=l.concat(e);}}return l}))}getRecordsInSlice({containerStart:e,sliceStart:t,sliceBytes:r},n){return this.cram.getContainerAtPosition(e).getSlice(t,r).getRecords(n)}hasDataForReferenceSequence(e){return this.index.hasDataForReferenceSequence(e)}};},3427:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.open=t.fromUrl=t.RemoteFile=t.LocalFile=void 0;const i=n(r(8575)),o=r(4319),s=r(2949);function a(e){const{protocol:t,pathname:r}=i.default.parse(e);return "file:"===t?new s.LocalFile(unescape((0, o.ensureNotNullish)(r))):new s.RemoteFile(e)}Object.defineProperty(t,"LocalFile",{enumerable:!0,get:function(){return s.LocalFile}}),Object.defineProperty(t,"RemoteFile",{enumerable:!0,get:function(){return s.RemoteFile}}),t.fromUrl=a,t.open=function(e,t,r){if(r)return r;if(e)return a(e);if(t)return new s.LocalFile(t);throw new Error("no url, path, or filehandle provided, cannot open")};},5702:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.RANS_BYTE_L=t.TOTFREQ=t.TF_SHIFT=void 0,t.TF_SHIFT=12,t.TOTFREQ=4096,t.RANS_BYTE_L=1<<23;},6484:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(8577),o=r(5702),s=n(r(7634));t.default=function(e,t,r,n){let a=e.getInt(),f=e.getInt(),h=e.getInt(),l=e.getInt();const u=n.remaining(),c=-4&u;for(let i=0;i>2;let c=0,d=u,p=2*u,g=3*u,m=0,y=0,b=0,v=0;for(;c{Object.defineProperty(t,"__esModule",{value:!0});const n=r(8577),i=r(5702);class o{constructor(){this.F=void 0,this.C=void 0;}}function s(e,t,r,n){return r*(e>>n)+(e&(1<>o)+(e&(1<=128&&(t.fc[f].F&=-129,t.fc[f].F=(127&t.fc[f].F)<<8|255&e.get()),t.fc[f].C=i,s.default.symbolInit(r[f],t.fc[f].C,t.fc[f].F),t.R||(t.R=new Array(o.TOTFREQ)),t.R.fill(f,i,i+t.fc[f].F),i+=t.fc[f].F,0===n&&f+1===(255&e.getByteAt(e.position()))?(f=255&e.get(),n=255&e.get()):0!==n?(n-=1,f+=1):f=255&e.get();}while(0!==f);a(i=128&&(t[i].fc[l].F&=-129,t[i].fc[l].F=(127&t[i].fc[l].F)<<8|255&e.get()),t[i].fc[l].C=h,0===t[i].fc[l].F&&(t[i].fc[l].F=o.TOTFREQ),null==r[i][l]&&(r[i][l]=new s.default.RansDecSymbol),s.default.symbolInit(r[i][l],t[i].fc[l].C,t[i].fc[l].F),null==t[i].R&&(t[i].R=new Array(o.TOTFREQ)),t[i].R.fill(l,h,h+t[i].fc[l].F),h+=t[i].fc[l].F,a(h<=o.TOTFREQ),0===f&&l+1===(255&e.getByteAt(e.position()))?(l=255&e.get(),f=255&e.get()):0!==f?(f-=1,l+=1):l=255&e.get();}while(0!==l);0===n&&i+1===(255&e.getByteAt(e.position()))?(i=255&e.get(),n=255&e.get()):0!==n?(n-=1,i+=1):i=255&e.get();}while(0!==i)};},9675:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=r(8577),o=n(r(7634)),s=r(696),a=n(r(6484)),f=n(r(7121));class h{constructor(e,t=0){this._buffer=e,this._position=t,this.length=e.length;}get(){const e=this._buffer[this._position];return this._position+=1,e}getByte(){return this.get()}getByteAt(e){return this._buffer[e]}position(){return this._position}put(e){return this._buffer[this._position]=e,this._position+=1,e}putAt(e,t){return this._buffer[e]=t,t}setPosition(e){return this._position=e,e}getInt(){const e=this._buffer.readInt32LE(this._position);return this._position+=4,e}remaining(){return this._buffer.length-this._position}}t.default=function(e,t,r=0){if(0===e.length)return t.fill(0),t;const n=new h(e,r),l=n.get();if(0!==l&&1!==l)throw new i.CramMalformedError(`Invalid rANS order ${l}`);if(n.getInt()!==n.remaining()-4)throw new i.CramMalformedError("Incorrect input length.");const u=n.getInt(),c=new h(t||Buffer.allocUnsafe(u));if(c.length{Object.defineProperty(t,"__esModule",{value:!0}),t.parseHeaderText=void 0,t.parseHeaderText=function(e){const t=e.split(/\r?\n/),r=[];return t.forEach((e=>{const[t,...n]=e.split(/\t/),i=n.map((e=>{const[t,r]=e.split(":",2);return {tag:t,value:r}}));t&&r.push({tag:t.substr(1),data:i});})),r};},4319:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ensureNotNullish=void 0,t.ensureNotNullish=function(e){if(null==e)throw new Error("Value must not be nullish.");return e};},597:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.unzip=void 0;const n=r(9591);t.unzip=function(e){return Buffer.from((0, n.inflate)(e))};},9996:(e,t,r)=>{function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}var i=r(8764).Buffer,o=r(22),s=r(2961)._,a=r(3720);"undefined"!=typeof window&&(window.Buffer=i),"undefined"!=typeof self&&(self.Buffer=i);var f={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},h={},l="___parser_",u=[];!function(){var e;for(e=1;e<=32;e++)u.push(e);}();var c={};Object.keys(f).concat(Object.keys({String:null,Buffer:null,Array:null,Skip:null,Choice:null,Nest:null,Bit:null,Itf8:null,Ltf8:null})).forEach((function(e){c[e.toLowerCase()]=e;}));var d=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;};d.start=function(){return new d},Object.keys(f).forEach((function(e){d.prototype[e.toLowerCase()]=function(t,r){return this.setNextParser(e.toLowerCase(),t,r)};var t=e.replace(/BE|LE/,"").toLowerCase();t in d.prototype||(d.prototype[t]=function(e,r){return this[t+this.endian](e,r)});})),u.forEach((function(e){d.prototype["bit".concat(e.toString())]=function(t,r){return r||(r={}),r.length=e,this.setNextParser("bit",t,r)};})),d.prototype.namely=function(e){return h[e]=this,this.alias=e,this},d.prototype.skip=function(e,t){if(t&&t.assert)throw new Error("assert option on skip is not allowed.");return this.setNextParser("skip","",{length:e})},d.prototype.string=function(e,t){if(!t.zeroTerminated&&!t.length&&!t.greedy)throw new Error("Neither length, zeroTerminated, nor greedy is defined for string.");if((t.zeroTerminated||t.length)&&t.greedy)throw new Error("greedy is mutually exclusive with length and zeroTerminated for string.");if(t.stripNull&&!t.length&&!t.greedy)throw new Error("Length or greedy must be defined if stripNull is defined.");return t.encoding=t.encoding||"utf8",this.setNextParser("string",e,t)},d.prototype.buffer=function(e,t){if(!t.length&&!t.readUntil)throw new Error("Length nor readUntil is defined in buffer parser");return this.setNextParser("buffer",e,t)},d.prototype.array=function(e,t){if(!t.readUntil&&!t.length&&!t.lengthInBytes)throw new Error("Length option of array is not defined.");if(!t.type)throw new Error("Type option of array is not defined.");if("string"==typeof t.type&&!h[t.type]&&Object.keys(f).indexOf(c[t.type])<0)throw new Error('Specified primitive type "'.concat(t.type,'" is not supported.'));return this.setNextParser("array",e,t)},d.prototype.choice=function(e,t){if(1===arguments.length&&"object"===n(e)&&(t=e,e=null),!t.tag)throw new Error("Tag option of array is not defined.");if(!t.choices)throw new Error("Choices option of array is not defined.");return Object.keys(t.choices).forEach((function(r){if(!t.choices[r])throw new Error("Choice Case ".concat(r," of ").concat(e," is not valid."));if("string"==typeof t.choices[r]&&!h[t.choices[r]]&&Object.keys(f).indexOf(c[t.choices[r]])<0)throw new Error('Specified primitive type "'.concat(t.choices[r],'" is not supported.'))}),this),this.setNextParser("choice",e,t)},d.prototype.nest=function(e,t){if(1===arguments.length&&"object"===n(e)&&(t=e,e=null),!t.type)throw new Error("Type option of nest is not defined.");if(!(t.type instanceof d||h[t.type]))throw new Error("Type option of nest must be a Parser object.");if(!(t.type instanceof d||e))throw new Error("options.type must be a object if variable name is omitted.");return this.setNextParser("nest",e,t)},d.prototype.endianess=function(e){switch(e.toLowerCase()){case"little":this.endian="le";break;case"big":this.endian="be";break;default:throw new Error("Invalid endianess: ".concat(e))}return this},d.prototype.create=function(e){if(!(e instanceof Function))throw new Error("Constructor must be a Function object.");return this.constructorFn=e,this},d.prototype.getCode=function(){var e=new s;return e.pushCode("if (!Buffer.isBuffer(buffer)) {"),e.generateError('"argument buffer is not a Buffer object"'),e.pushCode("}"),this.alias?this.addAliasedCode(e):this.addRawCode(e),this.alias?e.pushCode("return {0}(0)",l+this.alias):e.pushCode("return { offset: offset, result: vars };"),e.code},d.prototype.addRawCode=function(e){e.pushCode("var offset = 0;"),this.constructorFn?e.pushCode("var vars = new constructorFn();"):e.pushCode("var vars = {};"),this.generate(e),this.resolveReferences(e),e.pushCode("return { offset: offset, result: vars };");},d.prototype.addAliasedCode=function(e){return e.pushCode("function {0}(offset) {",l+this.alias),this.constructorFn?e.pushCode("var vars = new constructorFn();"):e.pushCode("var vars = {};"),this.generate(e),e.markResolved(this.alias),this.resolveReferences(e),e.pushCode("return { offset: offset, result: vars };"),e.pushCode("}"),e},d.prototype.resolveReferences=function(e){var t=e.getUnresolvedReferences();e.markRequested(t),t.forEach((function(t){h[t].addAliasedCode(e);}));},d.prototype.compile=function(){var e="(function(buffer, constructorFn, Long) { ".concat(this.getCode()," })");this.compiled=o.runInThisContext(e);},d.prototype.sizeOf=function(){var e=NaN;if(Object.keys(f).indexOf(this.type)>=0)e=f[this.type];else if("String"===this.type&&"number"==typeof this.options.length)e=this.options.length;else if("Buffer"===this.type&&"number"==typeof this.options.length)e=this.options.length;else if("Array"===this.type&&"number"==typeof this.options.length){var t=NaN;"string"==typeof this.options.type?t=f[c[this.options.type]]:this.options.type instanceof d&&(t=this.options.type.sizeOf()),e=this.options.length*t;}else "Skip"===this.type?e=this.options.length:"Nest"===this.type?e=this.options.type.sizeOf():this.type||(e=0);return this.next&&(e+=this.next.sizeOf()),e},d.prototype.parse=function(e){return this.compiled||this.compile(),this.compiled(e,this.constructorFn,a)},d.prototype.setNextParser=function(e,t,r){var n=new d;return n.type=c[e],n.varName=t,n.options=r||n.options,n.endian=this.endian,this.head?this.head.next=n:this.next=n,this.head=n,this},d.prototype.generate=function(e){this.type&&(this["generate".concat(this.type)](e),this.generateAssert(e));var t=e.generateVariable(this.varName);return this.options.formatter&&this.generateFormatter(e,t,this.options.formatter),this.generateNext(e)},d.prototype.generateAssert=function(e){if(this.options.assert){var t=e.generateVariable(this.varName);switch(n(this.options.assert)){case"function":e.pushCode("if (!({0}).call(vars, {1})) {",this.options.assert,t);break;case"number":e.pushCode("if ({0} !== {1}) {",this.options.assert,t);break;case"string":e.pushCode('if ("{0}" !== {1}) {',this.options.assert,t);break;default:throw new Error("Assert option supports only strings, numbers and assert functions.")}e.generateError('"Assert error: {0} is " + {0}',t),e.pushCode("}");}},d.prototype.generateNext=function(e){return this.next&&(e=this.next.generate(e)),e},Object.keys(f).forEach((function(e){d.prototype["generate".concat(e)]=function(t){"UInt64"===e?t.pushCode("{0} = Long.fromBytes(buffer.slice(offset,offset+8), true, this.endian === 'le').toNumber();",t.generateVariable(this.varName),e):"Int64"===e?t.pushCode("{0} = Long.fromBytes(buffer.slice(offset,offset+8), false, this.endian === 'le').toNumber();",t.generateVariable(this.varName),e):t.pushCode("{0} = buffer.read{1}(offset);",t.generateVariable(this.varName),e),t.pushCode("offset += {0};",f[e]);};})),d.prototype.generateBit=function(e){var t=JSON.parse(JSON.stringify(this));if(t.varName=e.generateVariable(t.varName),e.bitFields.push(t),!this.next||this.next&&["Bit","Nest"].indexOf(this.next.type)<0){var r=0;e.bitFields.forEach((function(e){r+=e.options.length;}));var n=e.generateTmpVariable();if(r<=8)e.pushCode("var {0} = buffer.readUInt8(offset);",n),r=8;else if(r<=16)e.pushCode("var {0} = buffer.readUInt16BE(offset);",n),r=16;else if(r<=24){var i=e.generateTmpVariable(),o=e.generateTmpVariable();e.pushCode("var {0} = buffer.readUInt16BE(offset);",i),e.pushCode("var {0} = buffer.readUInt8(offset + 2);",o),e.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.");e.pushCode("var {0} = buffer.readUInt32BE(offset);",n),r=32;}e.pushCode("offset += {0};",r/8);var s=0,a="be"===this.endian;e.bitFields.forEach((function(t){e.pushCode("{0} = {1} >> {2} & {3};",t.varName,n,a?r-s-t.options.length:s,(1< offset++);"),e.pushCode("{0} = buffer.toString('{1}', {2}, offset);",t,this.options.encoding,r)),this.options.stripNull&&e.pushCode("{0} = {0}.replace(/\\x00+$/g, '')",t);},d.prototype.generateBuffer=function(e){"eof"===this.options.readUntil?e.pushCode("{0} = buffer.slice(offset);",e.generateVariable(this.varName)):(e.pushCode("{0} = buffer.slice(offset, offset + {1});",e.generateVariable(this.varName),e.generateOption(this.options.length)),e.pushCode("offset += {0};",e.generateOption(this.options.length))),this.options.clone&&e.pushCode("{0} = Buffer.from({0});",e.generateVariable(this.varName));},d.prototype.generateArray=function(e){var t=e.generateOption(this.options.length),r=e.generateOption(this.options.lengthInBytes),n=this.options.type,i=e.generateTmpVariable(),o=e.generateVariable(this.varName),s=e.generateTmpVariable(),a=this.options.key,u="string"==typeof a;if(u?e.pushCode("{0} = {};",o):e.pushCode("{0} = [];",o),"function"==typeof this.options.readUntil?e.pushCode("do {"):"eof"===this.options.readUntil?e.pushCode("for (var {0} = 0; offset < buffer.length; {0}++) {",i):void 0!==r?e.pushCode("for (var {0} = offset; offset - {0} < {1}; ) {",i,r):e.pushCode("for (var {0} = 0; {0} < {1}; {0}++) {",i,t),"string"==typeof n)if(h[n]){var p=e.generateTmpVariable();e.pushCode("var {0} = {1}(offset);",p,l+n),e.pushCode("var {0} = {1}.result; offset = {1}.offset;",s,p),n!==this.alias&&e.addReference(n);}else e.pushCode("var {0} = buffer.read{1}(offset);",s,c[n]),e.pushCode("offset += {0};",f[c[n]]);else n instanceof d&&(e.pushCode("var {0} = {};",s),e.pushScope(s),n.generate(e),e.popScope());u?e.pushCode("{0}[{2}.{1}] = {2};",o,a,s):e.pushCode("{0}.push({1});",o,s),e.pushCode("}"),"function"==typeof this.options.readUntil&&e.pushCode(" while (!({0}).call(this, {1}, buffer.slice(offset)));",this.options.readUntil,s);},d.prototype.generateChoiceCase=function(e,t,r){if("string"==typeof r)if(h[r]){var n=e.generateTmpVariable();e.pushCode("var {0} = {1}(offset);",n,l+r),e.pushCode("{0} = {1}.result; offset = {1}.offset;",e.generateVariable(this.varName),n),r!==this.alias&&e.addReference(r);}else e.pushCode("{0} = buffer.read{1}(offset);",e.generateVariable(this.varName),c[r]),e.pushCode("offset += {0};",f[c[r]]);else r instanceof d&&(e.pushPath(t),r.generate(e),e.popPath(t));},d.prototype.generateChoice=function(e){var t=e.generateOption(this.options.tag);this.varName&&e.pushCode("{0} = {};",e.generateVariable(this.varName)),e.pushCode("switch({0}) {",t),Object.keys(this.options.choices).forEach((function(t){var r=this.options.choices[t];Number.isNaN(parseInt(t,10))?e.pushCode("case '{0}':",t):e.pushCode("case {0}:",t),this.generateChoiceCase(e,this.varName,r),e.pushCode("break;");}),this),e.pushCode("default:"),this.options.defaultChoice?this.generateChoiceCase(e,this.varName,this.options.defaultChoice):e.generateError('"Met undefined tag value " + {0} + " at choice"',t),e.pushCode("}");},d.prototype.generateNest=function(e){var t=e.generateVariable(this.varName);if(this.options.type instanceof d)this.varName&&e.pushCode("{0} = {};",t),e.pushPath(this.varName),this.options.type.generate(e),e.popPath(this.varName);else if(h[this.options.type]){var r=e.generateTmpVariable();e.pushCode("var {0} = {1}(offset);",r,l+this.options.type),e.pushCode("{0} = {1}.result; offset = {1}.offset;",t,r),this.options.type!==this.alias&&e.addReference(this.options.type);}},d.prototype.generateFormatter=function(e,t,r){"function"==typeof r&&e.pushCode("{0} = ({1}).call(this, {0});",t,r);},d.prototype.isInteger=function(){return !!this.type.match(/U?Int[8|16|32][BE|LE]?|Bit\d+/)},d.prototype.itf8=function(e,t){return this.setNextParser("itf8",e,t)},d.prototype.itf8=function(e,t){return this.setNextParser("itf8",e,t)},d.prototype.generateItf8=function(e){var t=e.generateVariable(this.varName),r=e.generateTmpVariable();e.pushCode("\n var ".concat(r," = buffer[offset];\n if (").concat(r," < 0x80) {\n ").concat(t," = ").concat(r,";\n offset += 1;\n } else if (").concat(r," < 0xc0) {\n ").concat(t," = ((").concat(r,"<<8) | buffer[offset+1]) & 0x3fff;\n offset += 2;\n } else if (").concat(r," < 0xe0) {\n ").concat(t," = ((").concat(r,"<<16) | (buffer[offset+1]<< 8) | buffer[offset+2]) & 0x1fffff;\n offset += 3;\n } else if (").concat(r," < 0xf0) {\n ").concat(t," = ((").concat(r,"<<24) | (buffer[offset+1]<<16) | (buffer[offset+2]<<8) | buffer[offset+3]) & 0x0fffffff;\n offset += 4\n } else {\n ").concat(t," = ((").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 "));},d.prototype.ltf8=function(e,t){return this.setNextParser("ltf8",e,t)},d.prototype.generateLtf8=function(e){var t=e.generateVariable(this.varName),r=e.generateTmpVariable();e.pushCode("\n var ".concat(r," = buffer[offset];\n if (").concat(r," < 0x80) {\n ").concat(t," = ").concat(r,";\n offset += 1;\n } else if (").concat(r," < 0xc0) {\n ").concat(t," = ((buffer[offset]<<8) | buffer[offset+1]) & 0x3fff;\n offset += 2;\n } else if (").concat(r," < 0xe0) {\n ").concat(t," = ((buffer[offset]<<16) | (buffer[offset+1]<<8) | buffer[offset+2]) & 0x1fffff;\n ").concat(t," = (((").concat(r," & 63) << 16) | buffer.readUInt16LE(offset + 1));\n offset += 3;\n } else if (").concat(r," < 0xf0) {\n ").concat(t," = ((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(t," = (((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(t," = ((((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(t," = ((((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(t," = Long.fromBytesBE(buffer.slice(offset+1,offset+8));\n if (").concat(t,".greaterThan(Number.MAX_SAFE_INTEGER) || ").concat(t,".lessThan(Number.MIN_SAFE_INTEGER))\n throw new Error('integer overflow')\n ").concat(t," = ").concat(t,".toNumber()\n offset += 8;\n } else {\n ").concat(t," = Long.fromBytesBE(buffer.slice(offset+1,offset+9));\n if (").concat(t,".greaterThan(Number.MAX_SAFE_INTEGER) || ").concat(t,".lessThan(Number.MIN_SAFE_INTEGER))\n throw new Error('integer overflow')\n ").concat(t," = ").concat(t,".toNumber()\n offset += 9;\n }\n "));},t.Parser=d;},2961:(e,t)=>{function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}var n=function(){this.code="",this.scopes=[["vars"]],this.isAsync=!1,this.bitFields=[],this.tmpVariableCount=0,this.references={};};n.prototype.generateVariable=function(e){var t=[];for(Array.prototype.push.apply(t,this.scopes[this.scopes.length-1]);/^\$parent\./.test(e);)t.pop(),e=e.replace(/^\$parent\./,"");return e&&t.push(e),t.join(".")},n.prototype.generateOption=function(e){switch(r(e)){case"number":return e.toString();case"string":return this.generateVariable(e);case"function":return "(".concat(e,").call(").concat(this.generateVariable(),", vars)");default:return}},n.prototype.generateError=function(){var e=Array.prototype.slice.call(arguments),t=n.interpolate.apply(this,e);this.isAsync?this.pushCode("return process.nextTick(function() { callback(new Error(".concat(t,"), vars); });")):this.pushCode("throw new Error(".concat(t,");"));},n.prototype.generateTmpVariable=function(){return "$tmp".concat(this.tmpVariableCount++)},n.prototype.pushCode=function(){var e=Array.prototype.slice.call(arguments);this.code+="".concat(n.interpolate.apply(this,e),"\n");},n.prototype.pushPath=function(e){e&&this.scopes[this.scopes.length-1].push(e);},n.prototype.popPath=function(e){e&&this.scopes[this.scopes.length-1].pop();},n.prototype.pushScope=function(e){this.scopes.push([e]);},n.prototype.popScope=function(){this.scopes.pop();},n.prototype.addReference=function(e){this.references[e]||(this.references[e]={resolved:!1,requested:!1});},n.prototype.markResolved=function(e){this.references[e].resolved=!0;},n.prototype.markRequested=function(e){e.forEach(function(e){this.references[e].requested=!0;}.bind(this));},n.prototype.getUnresolvedReferences=function(){var e=this.references;return Object.keys(this.references).filter((function(t){return !e[t].resolved&&!e[t].requested}))},n.interpolate=function(e){var t=/{\d+}/g,r=e.match(t),n=Array.prototype.slice.call(arguments,1);return r&&r.forEach((function(t){var r=parseInt(t.substr(1,t.length-2),10);e=e.replace(t,n[r].toString());})),e},t._=n;},22:e=>{e.exports.runInThisContext=function(e){return new Function("code","return eval(code);").call(globalThis,e)};},445:(e,t,r)=>{const n=r(7381),i=r(9260),o=r(576),s=r(4693),a=128;e.exports=class{decode(e){return this.stream=new i(e),this.decodeStream(this.stream)}decodeStream(e,t=0){var r=this.stream.ReadByte();16&r||(t=this.stream.ReadUint7());var n,i=t,o=1&r;if(8&r)return this.decodeStripe(this.stream,t);if(r&a&&([n,i]=this.decodePackMeta(this.stream)),32&r)var s=this.decodeCat(this.stream,i);else s=4&r?this.decodeExt(this.stream,i):64&r?o?this.decodeRLE1(this.stream,i):this.decodeRLE0(this.stream,i):o?this.decode1(this.stream,i):this.decode0(this.stream,i);return r&a&&(s=this.decodePack(s,n,t)),s}encode(e,t){if(this.stream=new i("",0,1.1*e.length+100),this.stream.WriteByte(t),16&t||this.stream.WriteUint7(e.length),8&t)return Buffer.concat([this.stream.buf.slice(0,this.stream.pos),this.encodeStripe(this.stream,e,t>>8)]);var r,n=1&t,o=e.length;return t&a&&([r,e,o]=this.encodePack(e)),t&a&&this.stream.WriteStream(r),64&t?n?this.encodeRLE1(e,o,this.stream):this.encodeRLE0(e,o,this.stream):n?this.encode1(e,o,this.stream):this.encode0(e,o,this.stream)}decode0(e,t){var r=new Buffer.allocUnsafe(t),i=e.ReadByte();0==i&&(i=256);var s=new o(i),a=new n(e);a.RangeStartDecode(e);for(var f=0;f=3?3:l;for(f[u].ModelEncode(r,h,c),l-=c,u=256;3==c;)c=l>=3?3:l,f[u].ModelEncode(r,h,c),u=257,l-=c;}return h.RangeFinishEncode(r),r.buf.slice(0,r.pos)}decodeRLE1(e,t){var r=new Buffer.allocUnsafe(t),i=e.ReadByte();0==i&&(i=256);for(var s=new Array(i),a=0;a=3?3:u;for(f[c].ModelEncode(r,h,d),u-=d,c=256;3==d;)d=u>=3?3:u,f[c].ModelEncode(r,h,d),c=257,u-=d;}return h.RangeFinishEncode(r),r.buf.slice(0,r.pos)}decodePackMeta(e){this.nsym=e.ReadByte();for(var t=new Array(this.nsym),r=0;r>=1;}}else if(this.nsym<=4)for(i=0,o=0;i>=2;else {if(!(this.nsym<=16))return e;for(i=0,o=0;i>=4;}return n}packMeta(e){for(var t=new i("",0,1024),r=new Array(256),n=0;ns),n[s]=new Array(o[s]);for(var a=0,f=0;fo),s[o]=this.decodeStream(e,i[o]);var a=new Buffer.allocUnsafe(t);for(o=0;o{e.exports=class{constructor(e){this.low=0,this.range=4294967295,this.code=0,this.FFnum=0,this.carry=0,this.cache=0;}RangeStartDecode(e){for(var t=0;t<5;t++)this.code=(this.code<<8)+e.ReadByte();this.code&=4294967295,this.code>>>=0;}RangeGetFrequency(e){return this.range=Math.floor(this.range/e),Math.floor(this.code/this.range)}RangeDecode(e,t,r,n){for(this.code-=t*this.range,this.range*=r;this.range<1<<24;)this.range*=256,this.code=256*this.code+e.ReadByte();}RangeShiftLow(e){if(this.low<4278190080|this.carry){for(e.WriteByte(this.cache+this.carry);this.FFnum;)e.WriteByte(this.carry-1),this.FFnum--;this.cache=this.low>>>24,this.carry=0;}else this.FFnum++;this.low<<=8,this.low>>>=0;}RangeEncode(e,t,r,n){var i=this.low;for(this.range=Math.floor(this.range/n),this.low+=t*this.range,this.low>>>=0,this.range*=r,this.low{e.exports=class{constructor(e=256){this.total_freq=e,this.max_sym=e-1,this.S=new Array,this.F=new Array;for(var t=0;t<=this.max_sym;t++)this.S[t]=t,this.F[t]=1;}ModelDecode(e,t){for(var r=t.RangeGetFrequency(this.total_freq),n=0,i=0;n+this.F[i]<=r;)n+=this.F[i++];t.RangeDecode(e,n,this.F[i],this.total_freq),this.F[i]+=16,this.total_freq+=16,this.total_freq>65519&&this.ModelRenormalise();var o=this.S[i];if(i>0&&this.F[i]>this.F[i-1]){var s=this.F[i];this.F[i]=this.F[i-1],this.F[i-1]=s,s=this.S[i],this.S[i]=this.S[i-1],this.S[i-1]=s;}return o}ModelRenormalise(){this.total_freq=0;for(var e=0;e<=this.max_sym;e++)this.F[e]-=Math.floor(this.F[e]/2),this.total_freq+=this.F[e];}ModelEncode(e,t,r){for(var n=0,i=0;this.S[i]!=r;i++)n+=this.F[i];if(t.RangeEncode(e,n,this.F[i],this.total_freq),this.F[i]+=16,this.total_freq+=16,this.total_freq>65519&&this.ModelRenormalise(),r=this.S[i],i>0&&this.F[i]>this.F[i-1]){var o=this.F[i];this.F[i]=this.F[i-1],this.F[i-1]=o,o=this.S[i],this.S[i]=this.S[i-1],this.S[i-1]=o;}}};},5260:(e,t,r)=>{const n=r(9260),i=r(576),o=r(7381);function s(e,t,r){for(var n=0,i=0,o=-1,s=new Array(1024);i>4,t.qshift=15&r,r=e.ReadByte(),t.qloc=r>>4,t.sloc=15&r,r=e.ReadByte(),t.ploc=r>>4,t.dloc=15&r,t.qmap=new Array(256),16&t.pflags)for(var n=0;n0&&128&t.pflags)s(e,t.qtab,256);else for(n=0;n<256;n++)t.qtab[n]=n;return t.ptab=new Array(1024),32&t.pflags&&s(e,t.ptab,1024),t.dtab=new Array(256),64&t.pflags&&s(e,t.dtab,256),t}function h(e,t,r,n,i,o){r.max_sel>0?i.s=n.sel.ModelDecode(e,t):i.s=0,i.x=r.stab[i.s];var s=r.params[i.x];if(s.fixed_len>=0){var a=n.len[0].ModelDecode(e,t);a|=n.len[1].ModelDecode(e,t)<<8,a|=n.len[2].ModelDecode(e,t)<<16,a|=n.len[3].ModelDecode(e,t)<<24,s.fixed_len>0&&(s.fixed_len=-a);}else a=-s.fixed_len;i.len=a,r.do_rev&&(o[i.rec]=n.rev.ModelDecode(e,t)),i.is_dup=0,2&s.pflags&&n.dup.ModelDecode(e,t)&&(i.is_dup=1),i.p=a,i.delta=0,i.qctx=0,i.prevq=0,i.rec++;}function l(e,t,r){for(var n=0,i=0,o=new Array(2*r),s=0;n1?r.nparam-1:0,o=new Array(256);if(2&r)i=e.ReadByte(),s(e,o,256);else {for(var a=0;a0&&(t.sel=new i(e.max_sel+1)),t}(n),d=new o(e);d.RangeStartDecode(e);for(var p=new Buffer.allocUnsafe(r),g={qctx:0,prevq:0,delta:0,p:0,s:0,x:0,len:0,is_dup:0,rec:0},m=0;m0&&c.dup.ModelDecode(e,d)){for(var y=0;y4),qshift:u,qloc:7,pbits:7,pshift:t[0]>128?1:0,ploc:0,dbits:u>4?0:1,dshift:3,dloc:15,sbits:0,sloc:15,do_stab:0,context:0,max_sym:f,nsym:a,do_qmap:c,do_dedup:0,fixed_len:1==t.length?1:0,do_sel:0,do_rev:0,do_pos:1,do_delta:u<=4?1:0,do_qtab:0,qbits:8+(u>4)-(0==s),sbits:1,sloc:15-(u<=4),do_stab:1,do_sel:1}]}(e,t,r,s),p=function(e,t,r,n,i,o,s){for(var a=[0,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7],f=0;f1?1:0)|(t[0].do_stab?2:0);if(e.WriteByte(h),1&h&&e.WriteByte(t.length),2&h){var u=1<0&&u--,e.WriteByte(u),l(e,s,256);}for(var c=0;c0){for(f=0;f<256;f++)n[c][f]=f;t[c].do_qtab&&l(e,n[c],256);}if(t[c].pbits>0){for(f=0;f<1024;f++)i[c][f]=Math.min((1<>t[c].pshift);l(e,i[c],1024);}if(t[c].dbits>0){for(f=0;f<256;f++)a[f]>(1<>t[c].dshift)];l(e,o[c],256);}}return e}(p,d,s,a,f,h,u);return function(e,t,r,n,s,a,f,h,l,u){var c=1<0&&c--;for(var d=t.length,p=0,g=0;g0&&v.ModelEncode(e,w,B);var E=u[B],S=r[Math.min(r.length-1,_++)];s[E].fixed_len?s[E].fixed_len>0&&(b[0].ModelEncode(e,w,255&S),b[1].ModelEncode(e,w,S>>8&255),b[2].ModelEncode(e,w,S>>16&255),b[3].ModelEncode(e,w,S>>24&255),s[E].fixed_len=-1):(b[0].ModelEncode(e,w,255&S),b[1].ModelEncode(e,w,S>>8&255),b[2].ModelEncode(e,w,S>>16&255),b[3].ModelEncode(e,w,S>>24&255)),s[E].do_dedup&&process.exit(1),g=S;var A=0,C=s[E].context,R=0,k=0;}var x=t[y++],I=a[E][x];m[C].ModelEncode(e,w,I),R=(R<0&&(C+=h[E][Math.min(g,1023)]<0&&(C+=l[E][Math.min(A,255)]<{var n=r(4459),i=r(594),o=r(445),s=r(5260),a=r(2881);e.exports={r4x8_uncompress:function(e,t){n.decode(e).copy(t,0,0);},r4x16_uncompress:function(e,t){i.decode(e).copy(t,0,0);},arith_uncompress:function(e,t){o.decode(e).copy(t,0,0);},fqzcomp_uncompress:function(e,t){var r=new Array;s.decode(e,r).copy(t,0,0);},tok3_uncompress:function(e,t){var r=a.decode(e,0,"\0");Buffer.from(r,"binary").copy(t,0,0);}};},9260:e=>{e.exports=class{constructor(e,t=0,r=0){0!=r?(this.buf=Buffer.allocUnsafe(r),this.length=r):(this.buf=e,this.length=e.length),this.pos=t;}EOF(){return this.pos>=this.length}ReadData(e){var t=this.buf.slice(this.pos,this.pos+e);return this.pos+=e,t}ReadByte(){const e=this.buf[this.pos];return this.pos++,e}ReadChar(){const e=this.buf[this.pos];return this.pos++,String.fromCharCode(e)}ReadUint16(){return this.ReadByte()|this.ReadByte()<<8}ReadUint32(){const e=this.buf.readInt32LE(this.pos);return this.pos+=4,e}ReadString(){var e="";do{var t=this.buf[this.pos++];t&&(e+=String.fromCharCode(t));}while(t);return e}ReadUint7(){var e=0;do{var t=this.ReadByte();e=e<<7|127&t;}while(128&t);return e}ReadITF8(){var e=this.buf[this.pos];return this.pos++,e>=240?(e=(15&e)<<28,e+=(this.buf[this.pos+0]<<20)+(this.buf[this.pos+1]<<12)+(this.buf[this.pos+2]<<4)+(this.buf[this.pos+3]>>4),this.pos+=4):e>=224?(e=(15&e)<<24,e+=(this.buf[this.pos+0]<<16)+(this.buf[this.pos+1]<<8)+(this.buf[this.pos+2]<<0),this.pos+=3):e>=192?(e=(31&e)<<16,e+=(this.buf[this.pos+0]<<8)+(this.buf[this.pos+1]<<0),this.pos+=2):e>=128&&(e=(63&e)<<8,e+=this.buf[this.pos],this.pos++),e}WriteByte(e){this.buf[this.pos++]=e;}WriteChar(e){this.buf[this.pos++]=e.charCodeAt(0);}WriteString(e){for(var t=0;t>8&255);}WriteUint32(e){this.buf.writeInt32LE(e,this.pos),this.pos+=4;}WriteUint7(e){var t=0,r=e;do{t+=7,r>>=7;}while(r>0);do{t-=7,this.WriteByte((e>>t&127)+((t>0)<<7));}while(t>0)}WriteITF8(e){e<0&&(e=1+e),e<=127?this.buf[this.pos++]=e:e<=16383?(this.buf[this.pos++]=128|Math.floor(e/256),this.buf[this.pos++]=255&e):e<131071?(this.buf[this.pos++]=192|Math.floor(e/65536),this.buf[this.pos++]=255&Math.floor(e/256),this.buf[this.pos++]=255&e):e<268435455?(this.buf[this.pos++]=224|Math.floor(e/16777216),this.buf[this.pos++]=255&Math.floor(e/65536),this.buf[this.pos++]=255&Math.floor(e/256),this.buf[this.pos++]=255&e):(this.buf[this.pos++]=240|Math.floor(e/268435456),this.buf[this.pos++]=255&Math.floor(e/1048576),this.buf[this.pos++]=255&Math.floor(e/4096),this.buf[this.pos++]=255&Math.floor(e/4),this.buf[this.pos++]=15&e);}WriteByteNeg(e){this.buf[--this.pos]=e;}};},4459:(e,t,r)=>{const n=r(9260);function i(e){return 4095&e}function o(e,t){for(var r=0;t>=e[r+1];)r++;return r}function s(e){for(var t=new Array(4096),r=0,n=0;n<4096;n++){for(;n>=e[r+1];)r++;t[n]=r;}return t}function a(e,t,r){return r*(e>>12)+(4095&e)-t}function f(e,t){for(;t<1<<23;)t=(t<<8)+e.ReadByte();return t}function h(e,t){t.WriteByteNeg(e>>24&255),t.WriteByteNeg(e>>16&255),t.WriteByteNeg(e>>8&255),t.WriteByteNeg(e>>0&255);}function l(e,t,r,n,i){return e=function(e,t,r,n){for(var i=(1<<23>>n<<8)*r;e>=i;)t.WriteByteNeg(255&e),e>>=8;return e}(e,t,n,i),(Math.floor(e/n)<0?(s--,i++):(i=e.ReadByte())==o+1&&(s=e.ReadByte()),o=i;}while(0!=i);for(r[0]=0,n=0;n<=255;n++)r[n+1]=r[n]+t[n];}function c(e){for(var t=0,r=0;r<256;r++)t+=e[r];const n=4096;var i=n/t;do{var o=0,s=0,a=0;for(t=0,r=0;r<256;r++)0!=e[r]&&(o2?e[s]-=t-n:t!=n&&(i*=.99,a=1);}while(a)}function d(e,t){for(var r=0,n=0;n<256;n++)if(t[n]){if(r>0)r--;else if(e.WriteByte(n),n>0&&t[n-1]>0){for(r=n+1;r<256&&t[r];r++);r-=n+1,e.WriteByte(r);}e.WriteITF8(t[n]);}e.WriteByte(0);}e.exports={decode:function(e){var t=new n(e),r=t.ReadByte(),h=(t.ReadUint32(),t.ReadUint32());return 0==r?function(e,t){var r=new Array(256),n=new Array(256);u(e,r,n);for(var o=s(n),h=new Array(4),l=0;l<4;l++)h[l]=e.ReadUint32();var c=new Buffer.allocUnsafe(t);for(l=0;l0?(a--,o++):(o=e.ReadByte())==s+1&&(a=e.ReadByte()),s=o;}while(0!=o)}(e,r,n);for(var h=new Array(256),l=0;l<256;l++)h[l]=s(n[l]);for(var c=new Array(4),d=new Array(4),p=0;p<4;p++)c[p]=e.ReadUint32(),d[p]=0;var g=new Buffer.allocUnsafe(t),m=Math.floor(t/4);for(l=0;l=0;s--)a[s%4]=l(a[s%4],u,o[e[s]],i[e[s]],12);for(s=3;s>=0;s--)h(a[s],u);var p=r.pos;return r.buf.writeInt32LE(p-9+(u.length-u.pos),1),r.buf.writeInt32LE(t,5),Buffer.concat([r.buf.slice(0,r.pos),u.buf.slice(u.pos,u.length)],r.pos+u.length-u.pos)}(e):function(e){const t=e.length;var r=new n("",0,198156);r.WriteByte(1),r.WriteUint32(0),r.WriteUint32(0);for(var i=new Array(256),o=new Array(256),s=new Array(256),a=0;a<256;a++)o[a]=new Array(256),s[a]=new Array(256);for(function(e,t,r){for(var n=0;n<256;n++){r[n]=0;for(var i=0;i<256;i++)t[n][i]=0;}var o=0;for(n=0;n>2)]]++,t[0][e[2*(e.length>>2)]]++,t[0][e[3*(e.length>>2)]]++,r[0]+=3;}(e,o,i),function(e,t){for(var r=0;r<256;r++)t[r]&&c(e[r]);}(o,i),function(e,t,r){for(var n=0,i=0;i<256;i++)if(r[i]){if(n>0)n--;else if(e.WriteByte(i),i>0&&r[i-1]>0){for(n=i+1;n<256&&r[n];n++);n-=i+1,e.WriteByte(n);}d(e,t[i]);}e.WriteByte(0);}(r,o,i),a=0;a<256;a++)if(i[a]){s[a][0]=0;for(var f=1;f<256;f++)s[a][f]=s[a][f-1]+o[a][f-1];}var u=new Array(4),p=new Array(4);for(f=0;f<4;f++)u[f]=1<<23,p[f]=0;var g=new n("",t,t),m=Math.floor(t/4),y=new Array(4),b=new Array(4);for(f=0;f<4;f++)y[f]=(f+1)*m-2,b[f]=e[y[f]+1];for(b[3]=e[t-1],a=t-2;a>4*m-2;a--)u[3]=l(u[3],g,s[e[a]][b[3]],o[e[a]][b[3]],12),b[3]=e[a];for(;y[0]>=0;)for(f=3;f>=0;f--){var v=e[y[f]];u[f]=l(u[f],g,s[v][b[f]],o[v][b[f]],12),b[f]=v,y[f]--;}for(f=3;f>=0;f--)u[f]=l(u[f],g,s[0][b[f]],o[0][b[f]],12);for(a=3;a>=0;a--)h(u[a],g);var w=r.pos;return r.buf.writeInt32LE(w-9+(g.length-g.pos),1),r.buf.writeInt32LE(t,5),Buffer.concat([r.buf.slice(0,r.pos),g.buf.slice(g.pos,g.length)],r.pos+g.length-g.pos)}(e)}};},594:(e,t,r)=>{const n=r(9260);function i(e,t){return e&(1<=e[r+1];)r++;return r}function s(e,t){for(var r=1<=e[i+1];)i++;n[o]=i;}return n}function a(e,t,r,n){return r*(e>>n)+(e&(1<>24&255),t.WriteByteNeg(e>>16&255),t.WriteByteNeg(e>>8&255),t.WriteByteNeg(e>>0&255);}function l(e,t,r,n,i){return e=function(e,t,r,n){for(var i=(1<<31-n)*r;e>=i;)t.WriteByteNeg(e>>8&255),t.WriteByteNeg(255&e),e>>=16;return e}(e,t,n,i),(Math.floor(e/n)<s),i[s]=new Array(o[s]);for(var a=0,f=0;fo),s[o]=c(e,i[o]);var a=new Buffer.allocUnsafe(t);for(o=0;o>4,h=e;if(1&c){var l=e.ReadUint7(),u=e.ReadUint7(),c=new n(e.ReadData(u));h=new n(g(c,l));}var d=new Array(256),m=new Array(256);!function(e,t,r,n){for(var i=0;i<256;i++){t[i]=new Array(256),r[i]=new Array(256);for(var o=0;o<256;o++)t[i][o]=0;}var s=p(e);for(i=0;i<256;i++)if(s[i]){var a=0;for(o=0;o<256;o++)s[o]&&(a>0?a--:(t[i][o]=e.ReadUint7(),0==t[i][o]&&(a=e.ReadByte())));for(y(t[i],n),r[i][0]=0,o=0;o<256;o++)r[i][o+1]=r[i][o]+t[i][o];}}(h,d,m,r);for(var b=new Array(256),v=0;v<256;v++)b[v]=s(m[v],r);for(var w=new Array(4),_=new Array(4),B=0;B<4;B++)w[B]=e.ReadUint32(),_[B]=0;var E=new Buffer.allocUnsafe(t),S=Math.floor(t/4);for(v=0;v>=1;}else if(r<=4)for(s=0;s>=2;else if(r<=16)for(s=0;s>=4;return i}(S,v,w,b)),S}function d(e,t){var r=new n("",0,10);r.WriteByte(t);var i=1&t,o=8&t,s=32&t,a=64&t,f=128&t,c=t>>8;if(16&t||r.WriteUint7(e.length),o)return Buffer.concat([r.buf.slice(0,r.pos),u(0,e,c)]);var d=new Buffer.alloc(0);f&&([d,e]=function(e){for(var t=new Array(256),r=0;r<256;r++)t[r]=0;for(r=0;r0&&(i[r]=o++);if(!(o>16)){if(o<=1)var s=new Buffer.allocUnsafe(0);else if(o<=2){s=new Buffer.allocUnsafe(Math.ceil(e.length/8));var a=-1;for(r=0;r0&&(t[r]=a++,f.WriteByte(r));return f.WriteUint7(s.length),[f.buf.slice(0,f.pos),s]}}(e));var p=new Buffer.alloc(0);if(a&&([p,e]=function(e){for(var t=new Array(256),r=0;r<256;r++)t[r]=0;var i=-1;for(r=0;r0&&o++;for(o||(o=1,t[0]=1),(h=new n("",0,o+1+e.length)).WriteByte(o),r=0;r<256;r++)t[r]>0&&h.WriteByte(r);var s=new Buffer.allocUnsafe(e.length),a=0;for(r=0;r0){i=e[r];for(var f=0;r+f+1>2)]]++,t[0][e[2*(e.length>>2)]]++,t[0][e[3*(e.length>>2)]]++,r[0]+=3;}))(e,o,i),function(e,t,r){for(var n=0;n<256;n++)if(t[n]){var i=Math.ceil(Math.log2(t[n]));i>12&&(i=12),m(e[n],i);}}(o,i);var u=new n("",0,198156);!function(e,t,r){b(e,r);for(var n=0;n<256;n++)if(r[n])for(var i=0,o=0;o<256;o++)if(r[o])if(i)i--;else if(e.WriteUint7(t[n][o]),!t[n][o]){for(var s=o+1;s<256;s++)if(r[s]){if(0!=t[n][s])break;i++;}e.WriteByte(i);}}(u,o,i);var c=v(u.buf.slice(0,u.pos));for(c.length>0,1.05*t+100>>0),_=Math.floor(t/4),B=new Array(4),E=new Array(4);for(d=0;d<4;d++)B[d]=(d+1)*_-2,E[d]=e[B[d]+1];for(E[3]=e[t-1],a=t-2;a>4*_-2;a--)p[3]=l(p[3],w,s[e[a]][E[3]],o[e[a]][E[3]],f),E[3]=e[a];for(;B[0]>=0;)for(d=3;d>=0;d--){var S=e[B[d]];p[d]=l(p[d],w,s[S][E[d]],o[S][E[d]],f),E[d]=S,B[d]--;}for(d=3;d>=0;d--)p[d]=l(p[d],w,s[0][E[d]],o[0][E[d]],f);for(a=3;a>=0;a--)h(p[a],w);return Buffer.concat([r.buf.slice(0,r.pos),w.buf.slice(w.pos,w.length)],r.pos+w.length-w.pos)}(e);return Buffer.concat([r.buf.slice(0,r.pos),d,p,g])}function p(e){for(var t=new Array(256),r=0;r<256;r++)t[r]=0;var n=0,i=e.ReadByte(),o=i;do{t[i]=1,n>0?(n--,i++):(i=e.ReadByte())==o+1&&(n=e.ReadByte()),o=i;}while(0!=i);return t}function g(e,t){var r=new Array(256),n=new Array(256);!function(e,t,r){for(var n=0;n<256;n++)t[n]=0;var i=p(e);for(n=0;n<256;n++)i[n]>0&&(t[n]=e.ReadUint7());for(y(t,12),r[0]=0,n=0;n<=255;n++)r[n+1]=r[n]+t[n];}(e,r,n);for(var o=s(n,12),h=new Array(4),l=0;l<4;l++)h[l]=e.ReadUint32();var u=new Buffer.allocUnsafe(t);for(l=0;l2?e[a]-=r-i:r!=i&&(o=i/r,f=1);}while(f)}function y(e,t){for(var r=0,n=0;n<256;n++)r+=e[n];if(0!=r&&r!=1<0)r--;else if(e.WriteByte(n),n>0&&t[n-1]>0){for(r=n+1;r<256&&t[r];r++);r-=n+1,e.WriteByte(r);}e.WriteByte(0);}function v(e){const t=e.length;var r=new n("",0,780),i=new Array(256);!function(e,t){for(var r=0;r<256;r++)t[r]=0;for(r=0;r12&&(o=12),m(i,o),function(e,t){b(e,t);for(var r=0;r<256;r++)t[r]&&e.WriteUint7(t[r]);}(r,i),m(i,12);var s=new Array(256);s[0]=0;for(var a=1;a<256;a++)s[a]=s[a-1]+i[a-1];var f=new Array(4);for(a=0;a<4;a++)f[a]=32768;var u=new n("",1.05*t+100>>0,1.05*t+100>>0);for(a=t-1;a>=0;a--)f[a%4]=l(f[a%4],u,s[e[a]],i[e[a]],12);for(a=3;a>=0;a--)h(f[a],u);return Buffer.concat([r.buf.slice(0,r.pos),u.buf.slice(u.pos,u.length)],r.pos+u.length-u.pos)}e.exports={decode:function(e){return c(new n(e),0)},encode:d};},2881:(e,t,r)=>{const n=r(9260),i=r(594);var o=new(r(445));function s(e,t){for(var r=e+"";r.length>0)+e[a][8].ReadByte();break;case 9:f=(r[o][a]>>0)+e[a][9].ReadByte(),h=r[o][a].length,r[n][a]=s(f,h);break;case 10:r[n][a]=r[o][a];break;default:r[n][a]="";}t[n]+=r[n][a++];}while(12!=i);return t[n]}function f(e,t,r,n,i,o){for(var s=0;s0&&5==t[s][0].type)&&t[s][r])switch(e[0].WriteByte(t[s][r].type),t[s][r].type){case 6:e[6].WriteUint32(t[s][r].val);break;case 5:e[5].WriteUint32(t[s][r].val);break;case 1:e[1].WriteString(t[s][r].val);break;case 2:e[2].WriteChar(t[s][r].val);break;case 7:e[7].WriteUint32(t[s][r].val);break;case 3:e[3].WriteUint32(t[s][r].val),e[4].WriteByte(t[s][r].val.length);break;case 8:case 9:e[t[s][r].type].WriteByte(t[s][r].val);}}function h(e,t,r,n){for(var i=0;i<=12;i++)if(!(e[i].pos<=0)){n.WriteByte(i+(0==i?128:0)),e[i]=e[i].buf.slice(0,e[i].pos);var o=l(e[i],r);n.WriteUint7(o.length),n.WriteData(o,o.length);}}function l(e,t){var r,n=1<<30,s=[0,1,64,65,128,129,201];for(var a in s){var f=s[a];if(!(1&f&&e.length<100||8&f&&e.length%4!=0)){try{var h=t?o.encode(e,f):i.encode(e,f);}catch(e){h=0;}h&&n>h.length&&(n=h.length,r=h);}}return r}function u(e,t,r,n,i){var o=0,s=i-1;e[i]=new Array(256),t[n]?e[i][0]={type:5,val:i-t[n]}:e[i][0]={type:6,val:0==i?0:1},t[n]=i;for(var a=n.match(/([a-zA-Z0-9]{1,9})|([^a-zA-Z0-9]+)/g),f=0;f=0&&e[s][h])if(e[s][h].str==a[f])l=10,u="";else if(7==e[s][h].type||8==e[s][h].type){var c=u-e[s][h].str;r[h]++,c>=0&&c<256&&r[h]>i/2&&(l=8,u=c);}else 3!=e[s][h].type&&9!=e[s][h].type||e[s][h].str.length!=u.length||(c=u-e[s][h].str,r[h]++,c>=0&&c<256&&r[h]>i/2&&(l=9,u=c));e[i][h]={str:a[f],val:u,type:l},o{a.callback(e);})),settled:!1,statusReporter:a,get aborted(){return this.aborter.signal.aborted}};f.aborter.addSignal(r),f.aborter.signal.addEventListener("abort",(()=>{f.settled||this.evict(e,f);})),f.promise.then((()=>{f.settled=!0;}),(()=>{f.settled=!0,this.evict(e,f);})).catch((e=>{throw console.error(e),e})),this.cache.set(e,f);}static checkSinglePromise(e,t){function r(){if(t&&t.aborted)throw Object.assign(new Error("aborted"),{code:"ERR_ABORTED"})}return e.then((e=>(r(),e)),(e=>{throw r(),e}))}has(e){return this.cache.has(e)}get(e,t,r,n){if(!r&&t instanceof i.AbortSignal)throw new TypeError("second get argument appears to be an AbortSignal, perhaps you meant to pass `null` for the fill data?");const o=this.cache.get(e);return o?o.aborted&&!o.settled?(this.evict(e,o),this.get(e,t,r,n)):o.settled?o.promise:(o.aborter.addSignal(r),o.statusReporter.addCallback(n),a.checkSinglePromise(o.promise,r)):(this.fill(e,t,r,n),a.checkSinglePromise(this.cache.get(e).promise,r))}delete(e){const t=this.cache.get(e);t&&(t.settled||t.aborter.abort(),this.cache.delete(e));}clear(){const e=this.cache.keys();let t=0;for(let r=e.next();!r.done;r=e.next())this.delete(r.value),t+=1;return t}}t.default=a;},9049:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=r(8904);class i{}t.default=class{constructor(){this.signals=new Set,this.abortController=new n.AbortController;}addSignal(e=new i){if(this.signal.aborted)throw new Error("cannot add a signal, already aborted!");this.signals.add(e),e.aborted?this.handleAborted(e):"function"==typeof e.addEventListener&&e.addEventListener("abort",(()=>{this.handleAborted(e);}));}handleAborted(e){this.signals.delete(e),0===this.signals.size&&this.abortController.abort();}get signal(){return this.abortController.signal}abort(){this.abortController.abort();}};},450:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(){this.callbacks=new Set;}addCallback(e=(()=>{})){this.callbacks.add(e),e(this.currentMessage);}callback(e){this.currentMessage=e,this.callbacks.forEach((t=>{t(e);}));}};},8904:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AbortSignal=t.AbortController=void 0;const n=r(5988);var i=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==r.g)return r.g;throw new Error("unable to locate global object")};let o=void 0===i().AbortController?n.AbortController:i().AbortController;t.AbortController=o;let s=void 0===i().AbortController?n.AbortSignal:i().AbortSignal;t.AbortSignal=s;},4105:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const i=n(r(1422));t.default=i.default;},5988:(e,t)=>{function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var r=0;r{t.byteLength=function(e){var t=f(e),r=t[0],n=t[1];return 3*(r+n)/4-n},t.toByteArray=function(e){var t,r,o=f(e),s=o[0],a=o[1],h=new i(function(e,t,r){return 3*(t+r)/4-r}(0,s,a)),l=0,u=a>0?s-4:s;for(r=0;r>16&255,h[l++]=t>>8&255,h[l++]=255&t;return 2===a&&(t=n[e.charCodeAt(r)]<<2|n[e.charCodeAt(r+1)]>>4,h[l++]=255&t),1===a&&(t=n[e.charCodeAt(r)]<<10|n[e.charCodeAt(r+1)]<<4|n[e.charCodeAt(r+2)]>>2,h[l++]=t>>8&255,h[l++]=255&t),h},t.fromByteArray=function(e){for(var t,n=e.length,i=n%3,o=[],s=16383,a=0,f=n-i;af?f:a+s));return 1===i?(t=e[n-1],o.push(r[t>>2]+r[t<<4&63]+"==")):2===i&&(t=(e[n-2]<<8)+e[n-1],o.push(r[t>>10]+r[t>>4&63]+r[t<<2&63]+"=")),o.join("")};for(var r=[],n=[],i="undefined"!=typeof Uint8Array?Uint8Array:Array,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0,a=o.length;s0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");return -1===r&&(r=t),[r,r===t?0:4-r%4]}function h(e,t,n){for(var i,o,s=[],a=t;a>18&63]+r[o>>12&63]+r[o>>6&63]+r[63&o]);return s.join("")}n["-".charCodeAt(0)]=62,n["_".charCodeAt(0)]=63;},2779:(e,t,r)=>{var n=r(8764).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(e){if(n.isBuffer(e))return e;var t="function"==typeof n.alloc&&"function"==typeof n.from;if("number"==typeof e)return t?n.alloc(e):new n(e);if("string"==typeof e)return t?n.from(e):new n(e);throw new Error("input must be buffer, number, or string, received "+typeof e)}function s(e){var t=o(4);return t.writeInt32BE(e,0),t}function a(e,t){e=o(e),n.isBuffer(t)&&(t=t.readUInt32BE(0));for(var r=-1^~~t,s=0;s>>8;return -1^r}function f(){return s(a.apply(null,arguments))}"undefined"!=typeof Int32Array&&(i=new Int32Array(i)),f.signed=function(){return a.apply(null,arguments)},f.unsigned=function(){return a.apply(null,arguments)>>>0},e.exports=f;},8764:(e,t,r)=>{const n=r(9742),i=r(645),o="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=f,t.SlowBuffer=function(e){return +e!=e&&(e=0),f.alloc(+e)},t.INSPECT_MAX_BYTES=50;const s=2147483647;function a(e){if(e>s)throw new RangeError('The value "'+e+'" is invalid for option "size"');const t=new Uint8Array(e);return Object.setPrototypeOf(t,f.prototype),t}function f(e,t,r){if("number"==typeof e){if("string"==typeof t)throw new TypeError('The "string" argument must be of type string. Received type number');return u(e)}return h(e,t,r)}function h(e,t,r){if("string"==typeof e)return function(e,t){if("string"==typeof t&&""!==t||(t="utf8"),!f.isEncoding(t))throw new TypeError("Unknown encoding: "+t);const r=0|g(e,t);let n=a(r);const i=n.write(e,t);return i!==r&&(n=n.slice(0,i)),n}(e,t);if(ArrayBuffer.isView(e))return function(e){if(Q(e,Uint8Array)){const t=new Uint8Array(e);return d(t.buffer,t.byteOffset,t.byteLength)}return c(e)}(e);if(null==e)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);if(Q(e,ArrayBuffer)||e&&Q(e.buffer,ArrayBuffer))return d(e,t,r);if("undefined"!=typeof SharedArrayBuffer&&(Q(e,SharedArrayBuffer)||e&&Q(e.buffer,SharedArrayBuffer)))return d(e,t,r);if("number"==typeof e)throw new TypeError('The "value" argument must not be of type number. Received type number');const n=e.valueOf&&e.valueOf();if(null!=n&&n!==e)return f.from(n,t,r);const i=function(e){if(f.isBuffer(e)){const t=0|p(e.length),r=a(t);return 0===r.length||e.copy(r,0,0,t),r}return void 0!==e.length?"number"!=typeof e.length||Y(e.length)?a(0):c(e):"Buffer"===e.type&&Array.isArray(e.data)?c(e.data):void 0}(e);if(i)return i;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof e[Symbol.toPrimitive])return f.from(e[Symbol.toPrimitive]("string"),t,r);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e)}function l(e){if("number"!=typeof e)throw new TypeError('"size" argument must be of type number');if(e<0)throw new RangeError('The value "'+e+'" is invalid for option "size"')}function u(e){return l(e),a(e<0?0:0|p(e))}function c(e){const t=e.length<0?0:0|p(e.length),r=a(t);for(let n=0;n=s)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s.toString(16)+" bytes");return 0|e}function g(e,t){if(f.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||Q(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);const r=e.length,n=arguments.length>2&&!0===arguments[2];if(!n&&0===r)return 0;let i=!1;for(;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":return V(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return Z(e).length;default:if(i)return n?-1:V(e).length;t=(""+t).toLowerCase(),i=!0;}}function m(e,t,r){let n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return "";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return "";if((r>>>=0)<=(t>>>=0))return "";for(e||(e="utf8");;)switch(e){case"hex":return I(this,t,r);case"utf8":case"utf-8":return C(this,t,r);case"ascii":return k(this,t,r);case"latin1":case"binary":return x(this,t,r);case"base64":return A(this,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return M(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0;}}function y(e,t,r){const n=e[t];e[t]=e[r],e[r]=n;}function b(e,t,r,n,i){if(0===e.length)return -1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),Y(r=+r)&&(r=i?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(i)return -1;r=e.length-1;}else if(r<0){if(!i)return -1;r=0;}if("string"==typeof t&&(t=f.from(t,n)),f.isBuffer(t))return 0===t.length?-1:v(e,t,r,n,i);if("number"==typeof t)return t&=255,"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):v(e,[t],r,n,i);throw new TypeError("val must be string, number or Buffer")}function v(e,t,r,n,i){let o,s=1,a=e.length,f=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return -1;s=2,a/=2,f/=2,r/=2;}function h(e,t){return 1===s?e[t]:e.readUInt16BE(t*s)}if(i){let n=-1;for(o=r;oa&&(r=a-f),o=r;o>=0;o--){let r=!0;for(let n=0;ni&&(n=i):n=i;const o=t.length;let s;for(n>o/2&&(n=o/2),s=0;s>8,i=r%256,o.push(i),o.push(n);return o}(t,e.length-r),e,r,n)}function A(e,t,r){return 0===t&&r===e.length?n.fromByteArray(e):n.fromByteArray(e.slice(t,r))}function C(e,t,r){r=Math.min(e.length,r);const n=[];let i=t;for(;i239?4:t>223?3:t>191?2:1;if(i+s<=r){let r,n,a,f;switch(s){case 1:t<128&&(o=t);break;case 2:r=e[i+1],128==(192&r)&&(f=(31&t)<<6|63&r,f>127&&(o=f));break;case 3:r=e[i+1],n=e[i+2],128==(192&r)&&128==(192&n)&&(f=(15&t)<<12|(63&r)<<6|63&n,f>2047&&(f<55296||f>57343)&&(o=f));break;case 4:r=e[i+1],n=e[i+2],a=e[i+3],128==(192&r)&&128==(192&n)&&128==(192&a)&&(f=(15&t)<<18|(63&r)<<12|(63&n)<<6|63&a,f>65535&&f<1114112&&(o=f));}}null===o?(o=65533,s=1):o>65535&&(o-=65536,n.push(o>>>10&1023|55296),o=56320|1023&o),n.push(o),i+=s;}return function(e){const t=e.length;if(t<=R)return String.fromCharCode.apply(String,e);let r="",n=0;for(;nn.length?(f.isBuffer(t)||(t=f.from(t)),t.copy(n,i)):Uint8Array.prototype.set.call(n,t,i);else {if(!f.isBuffer(t))throw new TypeError('"list" argument must be an Array of Buffers');t.copy(n,i);}i+=t.length;}return n},f.byteLength=g,f.prototype._isBuffer=!0,f.prototype.swap16=function(){const e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let t=0;tr&&(e+=" ... "),""},o&&(f.prototype[o]=f.prototype.inspect),f.prototype.compare=function(e,t,r,n,i){if(Q(e,Uint8Array)&&(e=f.from(e,e.offset,e.byteLength)),!f.isBuffer(e))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===i&&(i=this.length),t<0||r>e.length||n<0||i>this.length)throw new RangeError("out of range index");if(n>=i&&t>=r)return 0;if(n>=i)return -1;if(t>=r)return 1;if(this===e)return 0;let o=(i>>>=0)-(n>>>=0),s=(r>>>=0)-(t>>>=0);const a=Math.min(o,s),h=this.slice(n,i),l=e.slice(t,r);for(let e=0;e>>=0,isFinite(r)?(r>>>=0,void 0===n&&(n="utf8")):(n=r,r=void 0);}const i=this.length-t;if((void 0===r||r>i)&&(r=i),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");let o=!1;for(;;)switch(n){case"hex":return w(this,e,t,r);case"utf8":case"utf-8":return _(this,e,t,r);case"ascii":case"latin1":case"binary":return B(this,e,t,r);case"base64":return E(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,r);default:if(o)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),o=!0;}},f.prototype.toJSON=function(){return {type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const R=4096;function k(e,t,r){let n="";r=Math.min(e.length,r);for(let i=t;in)&&(r=n);let i="";for(let n=t;nr)throw new RangeError("Trying to access beyond buffer length")}function O(e,t,r,n,i,o){if(!f.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>i||te.length)throw new RangeError("Index out of range")}function T(e,t,r,n,i){j(t,n,i,e,r,7);let o=Number(t&BigInt(4294967295));e[r++]=o,o>>=8,e[r++]=o,o>>=8,e[r++]=o,o>>=8,e[r++]=o;let s=Number(t>>BigInt(32)&BigInt(4294967295));return e[r++]=s,s>>=8,e[r++]=s,s>>=8,e[r++]=s,s>>=8,e[r++]=s,r}function U(e,t,r,n,i){j(t,n,i,e,r,7);let o=Number(t&BigInt(4294967295));e[r+7]=o,o>>=8,e[r+6]=o,o>>=8,e[r+5]=o,o>>=8,e[r+4]=o;let s=Number(t>>BigInt(32)&BigInt(4294967295));return e[r+3]=s,s>>=8,e[r+2]=s,s>>=8,e[r+1]=s,s>>=8,e[r]=s,r+8}function P(e,t,r,n,i,o){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function N(e,t,r,n,o){return t=+t,r>>>=0,o||P(e,0,r,4),i.write(e,t,r,n,23,4),r+4}function L(e,t,r,n,o){return t=+t,r>>>=0,o||P(e,0,r,8),i.write(e,t,r,n,52,8),r+8}f.prototype.slice=function(e,t){const r=this.length;(e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t>>=0,t>>>=0,r||F(e,t,this.length);let n=this[e],i=1,o=0;for(;++o>>=0,t>>>=0,r||F(e,t,this.length);let n=this[e+--t],i=1;for(;t>0&&(i*=256);)n+=this[e+--t]*i;return n},f.prototype.readUint8=f.prototype.readUInt8=function(e,t){return e>>>=0,t||F(e,1,this.length),this[e]},f.prototype.readUint16LE=f.prototype.readUInt16LE=function(e,t){return e>>>=0,t||F(e,2,this.length),this[e]|this[e+1]<<8},f.prototype.readUint16BE=f.prototype.readUInt16BE=function(e,t){return e>>>=0,t||F(e,2,this.length),this[e]<<8|this[e+1]},f.prototype.readUint32LE=f.prototype.readUInt32LE=function(e,t){return e>>>=0,t||F(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},f.prototype.readUint32BE=f.prototype.readUInt32BE=function(e,t){return e>>>=0,t||F(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},f.prototype.readBigUInt64LE=X((function(e){H(e>>>=0,"offset");const t=this[e],r=this[e+7];void 0!==t&&void 0!==r||W(e,this.length-8);const n=t+256*this[++e]+65536*this[++e]+this[++e]*2**24,i=this[++e]+256*this[++e]+65536*this[++e]+r*2**24;return BigInt(n)+(BigInt(i)<>>=0,"offset");const t=this[e],r=this[e+7];void 0!==t&&void 0!==r||W(e,this.length-8);const n=t*2**24+65536*this[++e]+256*this[++e]+this[++e],i=this[++e]*2**24+65536*this[++e]+256*this[++e]+r;return (BigInt(n)<>>=0,t>>>=0,r||F(e,t,this.length);let n=this[e],i=1,o=0;for(;++o=i&&(n-=Math.pow(2,8*t)),n},f.prototype.readIntBE=function(e,t,r){e>>>=0,t>>>=0,r||F(e,t,this.length);let n=t,i=1,o=this[e+--n];for(;n>0&&(i*=256);)o+=this[e+--n]*i;return i*=128,o>=i&&(o-=Math.pow(2,8*t)),o},f.prototype.readInt8=function(e,t){return e>>>=0,t||F(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},f.prototype.readInt16LE=function(e,t){e>>>=0,t||F(e,2,this.length);const r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},f.prototype.readInt16BE=function(e,t){e>>>=0,t||F(e,2,this.length);const r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},f.prototype.readInt32LE=function(e,t){return e>>>=0,t||F(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},f.prototype.readInt32BE=function(e,t){return e>>>=0,t||F(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},f.prototype.readBigInt64LE=X((function(e){H(e>>>=0,"offset");const t=this[e],r=this[e+7];void 0!==t&&void 0!==r||W(e,this.length-8);const n=this[e+4]+256*this[e+5]+65536*this[e+6]+(r<<24);return (BigInt(n)<>>=0,"offset");const t=this[e],r=this[e+7];void 0!==t&&void 0!==r||W(e,this.length-8);const n=(t<<24)+65536*this[++e]+256*this[++e]+this[++e];return (BigInt(n)<>>=0,t||F(e,4,this.length),i.read(this,e,!0,23,4)},f.prototype.readFloatBE=function(e,t){return e>>>=0,t||F(e,4,this.length),i.read(this,e,!1,23,4)},f.prototype.readDoubleLE=function(e,t){return e>>>=0,t||F(e,8,this.length),i.read(this,e,!0,52,8)},f.prototype.readDoubleBE=function(e,t){return e>>>=0,t||F(e,8,this.length),i.read(this,e,!1,52,8)},f.prototype.writeUintLE=f.prototype.writeUIntLE=function(e,t,r,n){e=+e,t>>>=0,r>>>=0,n||O(this,e,t,r,Math.pow(2,8*r)-1,0);let i=1,o=0;for(this[t]=255&e;++o>>=0,r>>>=0,n||O(this,e,t,r,Math.pow(2,8*r)-1,0);let i=r-1,o=1;for(this[t+i]=255&e;--i>=0&&(o*=256);)this[t+i]=e/o&255;return t+r},f.prototype.writeUint8=f.prototype.writeUInt8=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,1,255,0),this[t]=255&e,t+1},f.prototype.writeUint16LE=f.prototype.writeUInt16LE=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},f.prototype.writeUint16BE=f.prototype.writeUInt16BE=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},f.prototype.writeUint32LE=f.prototype.writeUInt32LE=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},f.prototype.writeUint32BE=f.prototype.writeUInt32BE=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},f.prototype.writeBigUInt64LE=X((function(e,t=0){return T(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),f.prototype.writeBigUInt64BE=X((function(e,t=0){return U(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),f.prototype.writeIntLE=function(e,t,r,n){if(e=+e,t>>>=0,!n){const n=Math.pow(2,8*r-1);O(this,e,t,r,n-1,-n);}let i=0,o=1,s=0;for(this[t]=255&e;++i>0)-s&255;return t+r},f.prototype.writeIntBE=function(e,t,r,n){if(e=+e,t>>>=0,!n){const n=Math.pow(2,8*r-1);O(this,e,t,r,n-1,-n);}let i=r-1,o=1,s=0;for(this[t+i]=255&e;--i>=0&&(o*=256);)e<0&&0===s&&0!==this[t+i+1]&&(s=1),this[t+i]=(e/o>>0)-s&255;return t+r},f.prototype.writeInt8=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},f.prototype.writeInt16LE=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},f.prototype.writeInt16BE=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},f.prototype.writeInt32LE=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},f.prototype.writeInt32BE=function(e,t,r){return e=+e,t>>>=0,r||O(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},f.prototype.writeBigInt64LE=X((function(e,t=0){return T(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),f.prototype.writeBigInt64BE=X((function(e,t=0){return U(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),f.prototype.writeFloatLE=function(e,t,r){return N(this,e,t,!0,r)},f.prototype.writeFloatBE=function(e,t,r){return N(this,e,t,!1,r)},f.prototype.writeDoubleLE=function(e,t,r){return L(this,e,t,!0,r)},f.prototype.writeDoubleBE=function(e,t,r){return L(this,e,t,!1,r)},f.prototype.copy=function(e,t,r,n){if(!f.isBuffer(e))throw new TypeError("argument should be a Buffer");if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n=this.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(i=t;i=n+4;r-=3)t=`_${e.slice(r-3,r)}${t}`;return `${e.slice(0,r)}${t}`}function j(e,t,r,n,i,o){if(e>r||e3?0===t||t===BigInt(0)?`>= 0${n} and < 2${n} ** ${8*(o+1)}${n}`:`>= -(2${n} ** ${8*(o+1)-1}${n}) and < 2 ** ${8*(o+1)-1}${n}`:`>= ${t}${n} and <= ${r}${n}`,new z.ERR_OUT_OF_RANGE("value",i,e)}!function(e,t,r){H(t,"offset"),void 0!==e[t]&&void 0!==e[t+r]||W(t,e.length-(r+1));}(n,i,o);}function H(e,t){if("number"!=typeof e)throw new z.ERR_INVALID_ARG_TYPE(t,"number",e)}function W(e,t,r){if(Math.floor(e)!==e)throw H(e,r),new z.ERR_OUT_OF_RANGE(r||"offset","an integer",e);if(t<0)throw new z.ERR_BUFFER_OUT_OF_BOUNDS;throw new z.ERR_OUT_OF_RANGE(r||"offset",`>= ${r?1:0} and <= ${t}`,e)}D("ERR_BUFFER_OUT_OF_BOUNDS",(function(e){return e?`${e} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"}),RangeError),D("ERR_INVALID_ARG_TYPE",(function(e,t){return `The "${e}" argument must be of type number. Received type ${typeof t}`}),TypeError),D("ERR_OUT_OF_RANGE",(function(e,t,r){let n=`The value of "${e}" is out of range.`,i=r;return Number.isInteger(r)&&Math.abs(r)>2**32?i=q(String(r)):"bigint"==typeof r&&(i=String(r),(r>BigInt(2)**BigInt(32)||r<-(BigInt(2)**BigInt(32)))&&(i=q(i)),i+="n"),n+=` It must be ${t}. Received ${i}`,n}),RangeError);const $=/[^+/0-9A-Za-z-_]/g;function V(e,t){let r;t=t||1/0;const n=e.length;let i=null;const o=[];for(let s=0;s55295&&r<57344){if(!i){if(r>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(s+1===n){(t-=3)>-1&&o.push(239,191,189);continue}i=r;continue}if(r<56320){(t-=3)>-1&&o.push(239,191,189),i=r;continue}r=65536+(i-55296<<10|r-56320);}else i&&(t-=3)>-1&&o.push(239,191,189);if(i=null,r<128){if((t-=1)<0)break;o.push(r);}else if(r<2048){if((t-=2)<0)break;o.push(r>>6|192,63&r|128);}else if(r<65536){if((t-=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((t-=4)<0)break;o.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128);}}return o}function Z(e){return n.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace($,"")).length<2)return "";for(;e.length%4!=0;)e+="=";return e}(e))}function G(e,t,r,n){let i;for(i=0;i=t.length||i>=e.length);++i)t[i+r]=e[i];return i}function Q(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function Y(e){return e!=e}const K=function(){const e="0123456789abcdef",t=new Array(256);for(let r=0;r<16;++r){const n=16*r;for(let i=0;i<16;++i)t[n+i]=e[r]+e[i];}return t}();function X(e){return "undefined"==typeof BigInt?J:e}function J(){throw new Error("BigInt not supported")}},4693:e=>{var t={array:function(e){var t=0,r=0,n=[0,1,3,7,15,31,63,127,255];return function(i){for(var o=0;i>0;){var s=8-t;i>=s?(o<<=s,o|=n[s]&e[r++],t=0,i-=s):(o<<=i,o|=(e[r]&n[i]<<8-i-t)>>8-i-t,t+=i,i=0);}return o}},simple:function(e){var r,n,i=t.header(e),o=[],s=0;do{-1!=(n=t.decompress(e,i))&&(o.push(n),s+=n.byteLength);}while(-1!=n);r=new Uint8Array(s),s=0;for(var a=0;a9)throw "Not a BZIP archive";return t},decompress:function(e,t,r){for(var n=9e5,i="",o=0;o<6;o++)i+=e(8).toString(16);if("177245385090"==i)return -1;if("314159265359"!=i)throw "eek not valid bzip data";if(e(32),e(1))throw "unsupported obsolete version";var s=e(24);if(s>n)throw "Initial position larger than buffer size";var a=e(16),f=new Uint8Array(256),h=0;for(o=0;o<16;o++)if(a&1<<15-o){var l=e(16);for(g=0;g<16;g++)l&1<<15-g&&(f[h++]=16*o+g);}var u=e(3);if(u<2||u>6)throw "another error";var c=e(15);if(0==c)throw "meh";var d=[];for(o=0;o=u)throw "whoops another error";var m=d[g];d.splice(g,1),d.splice(0,0,m),p[o]=m;}var y=h+2,b=[];for(g=0;g20)throw "I gave up a while ago on writing error messages";if(!e(1))break;e(1)?a--:a++;}B[o]=a;}for(v=w=B[0],o=1;ow?w=B[o]:B[o]=c)throw "meow i'm a kitty, that's an error";S=(_=b[p[x++]]).base.subarray(1),A=_.limit.subarray(1);}for(g=e(o=_.minLen);;){if(o>_.maxLen)throw "rawr i'm a dinosaur";if(g<=A[o])break;o++,g=g<<1|e(1);}if((g-=S[o])<0||g>=258)throw "moo i'm a cow";var F=_.permute[g];if(0!=F&&1!=F){if(R){if(R=0,k+a>=n)throw "Boom.";for(I[m=f[d[0]]]+=a;a--;)M[k++]=m;}if(F>h)break;if(k>=n)throw "I can't think of anything. Error";m=d[o=F-1],d.splice(o,1),d.splice(0,0,m),I[m=f[m]]++,M[k++]=m;}else R||(R=1,a=0),a+=0==F?R:2*R,R<<=1;}if(s<0||s>=k)throw "I'm a monkey and I'm throwing something at someone, namely you";for(g=0,o=0;o<256;o++)l=g+I[o],I[o]=g,g=l;for(o=0;o>=8,U=-1);var P,N,L,z=new Uint8Array(n),D=0;for(r||(r=1/0);k;){for(k--,N=T,T=255&(O=M[O]),O>>=8,3==U++?(P=T,L=N,T=-1):(P=1,L=T);P--;)if(z[D++]=L,!--r)return z;T!=N&&(U=0);}return z.subarray(0,D)}};e.exports=t;},487:e=>{var t={utf8:{stringToBytes:function(e){return t.bin.stringToBytes(unescape(encodeURIComponent(e)))},bytesToString:function(e){return decodeURIComponent(escape(t.bin.bytesToString(e)))}},bin:{stringToBytes:function(e){for(var t=[],r=0;r{var t,r;t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r={rotl:function(e,t){return e<>>32-t},rotr:function(e,t){return e<<32-t|e>>>t},endian:function(e){if(e.constructor==Number)return 16711935&r.rotl(e,8)|4278255360&r.rotl(e,24);for(var t=0;t0;e--)t.push(Math.floor(256*Math.random()));return t},bytesToWords:function(e){for(var t=[],r=0,n=0;r>>5]|=e[r]<<24-n%32;return t},wordsToBytes:function(e){for(var t=[],r=0;r<32*e.length;r+=8)t.push(e[r>>>5]>>>24-r%32&255);return t},bytesToHex:function(e){for(var t=[],r=0;r>>4).toString(16)),t.push((15&e[r]).toString(16));return t.join("")},hexToBytes:function(e){for(var t=[],r=0;r>>6*(3-o)&63)):r.push("=");return r.join("")},base64ToBytes:function(e){e=e.replace(/[^A-Z0-9+\/]/gi,"");for(var r=[],n=0,i=0;n>>6-2*i);return r}},e.exports=r;},2949:(e,t,r)=>{r.r(t),r.d(t,{BlobFile:()=>h,LocalFile:()=>i(),RemoteFile:()=>a,fromUrl:()=>l,open:()=>u});var n=r(7067),i=r.n(n),o=r(8764);const s="undefined"!=typeof window?window:"undefined"!=typeof self?self:{fetch:void 0};class a{constructor(e,t={}){this.baseOverrides={},this.url=e;const r=t.fetch||s.fetch&&s.fetch.bind(s);if(!r)throw new TypeError("no fetch function supplied, and none found in global environment");t.overrides&&(this.baseOverrides=t.overrides),this.fetchImplementation=r;}async getBufferFromResponse(e){if("function"==typeof e.buffer)return e.buffer();if("function"==typeof e.arrayBuffer){const t=await e.arrayBuffer();return o.Buffer.from(t)}throw new TypeError("invalid HTTP response object, has no buffer method, and no arrayBuffer method")}async fetch(e,t){let r;try{r=await this.fetchImplementation(e,t);}catch(n){if(!`${n}`.includes("Failed to fetch"))throw n;console.warn(`generic-filehandle: refetching ${e} to attempt to work around chrome CORS header caching bug`),r=await this.fetchImplementation(e,{...t,cache:"reload"});}return r}async read(e,t=0,r,n=0,i={}){const{headers:o={},signal:s,overrides:a={}}=i;r<1/0?o.range=`bytes=${n}-${n+r}`:r===1/0&&0!==n&&(o.range=`bytes=${n}-`);const f={...this.baseOverrides,...a,headers:{...o,...a.headers,...this.baseOverrides.headers},method:"GET",redirect:"follow",mode:"cors",signal:s},h=await this.fetch(this.url,f);if(!h.ok)throw new Error(`HTTP ${h.status} ${h.statusText} ${this.url}`);if(200===h.status&&0===n||206===h.status){const n=await this.getBufferFromResponse(h),i=n.copy(e,t,0,Math.min(r,n.length)),o=h.headers.get("content-range"),s=/\/(\d+)$/.exec(o||"");return s&&s[1]&&(this._stat={size:parseInt(s[1],10)}),{bytesRead:i,buffer:e}}if(200===h.status)throw new Error("${this.url} fetch returned status 200, expected 206");throw new Error(`HTTP ${h.status} fetching ${this.url}`)}async readFile(e={}){let t,r;"string"==typeof e?(t=e,r={}):(t=e.encoding,r=e,delete r.encoding);const{headers:n={},signal:i,overrides:o={}}=r,s={headers:n,method:"GET",redirect:"follow",mode:"cors",signal:i,...this.baseOverrides,...o},a=await this.fetch(this.url,s);if(!a)throw new Error("generic-filehandle failed to fetch");if(200!==a.status)throw Object.assign(new Error(`HTTP ${a.status} fetching ${this.url}`),{status:a.status});if("utf8"===t)return a.text();if(t)throw new Error(`unsupported encoding: ${t}`);return this.getBufferFromResponse(a)}async stat(){if(!this._stat){const e=o.Buffer.allocUnsafe(10);if(await this.read(e,0,10,0),!this._stat)throw new Error(`unable to determine size of file at ${this.url}`)}return this._stat}async close(){}}function f(e){const t=new FileReader;return new Promise(((r,n)=>{t.onerror=()=>{t.abort(),n(new Error("problem reading blob"));},t.onabort=()=>{n(new Error("blob reading was aborted"));},t.onload=()=>{t.result&&"string"!=typeof t.result?r(t.result):n(new Error("unknown error reading blob"));},t.readAsArrayBuffer(e);}))}class h{constructor(e){this.blob=e,this.size=e.size;}async read(e,t=0,r,n=0){if(!r)return {bytesRead:0,buffer:e};const i=n,s=i+r,a=await f(this.blob.slice(i,s)),h=o.Buffer.from(a);return {bytesRead:h.copy(e,t),buffer:h}}async readFile(e){let t;if(t="string"==typeof e?e:e&&e.encoding,"utf8"===t)return function(e){const t=new FileReader;return new Promise(((r,n)=>{t.onerror=()=>{t.abort(),n(new Error("problem reading blob"));},t.onabort=()=>{n(new Error("blob reading was aborted"));},t.onload=()=>{t.result&&"string"==typeof t.result?r(t.result):n(new Error("unknown error reading blob"));},t.readAsText(e);}))}(this.blob);if(t)throw new Error(`unsupported encoding: ${t}`);const r=await f(this.blob);return o.Buffer.from(r)}async stat(){return {size:this.size}}async close(){}}function l(e,t={}){return new a(e,t)}function u(e,t,r,n={}){if(void 0!==r)return r;if(void 0!==e)return l(e,n);if(void 0!==t)return new(i())(t,n);throw new Error("no url, path, or filehandle provided, cannot open")}},645:(e,t)=>{t.read=function(e,t,r,n,i){var o,s,a=8*i-n-1,f=(1<>1,l=-7,u=r?i-1:0,c=r?-1:1,d=e[t+u];for(u+=c,o=d&(1<<-l)-1,d>>=-l,l+=a;l>0;o=256*o+e[t+u],u+=c,l-=8);for(s=o&(1<<-l)-1,o>>=-l,l+=n;l>0;s=256*s+e[t+u],u+=c,l-=8);if(0===o)o=1-h;else {if(o===f)return s?NaN:1/0*(d?-1:1);s+=Math.pow(2,n),o-=h;}return (d?-1:1)*s*Math.pow(2,o-n)},t.write=function(e,t,r,n,i,o){var s,a,f,h=8*o-i-1,l=(1<>1,c=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,d=n?0:o-1,p=n?1:-1,g=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(a=isNaN(t)?1:0,s=l):(s=Math.floor(Math.log(t)/Math.LN2),t*(f=Math.pow(2,-s))<1&&(s--,f*=2),(t+=s+u>=1?c/f:c*Math.pow(2,1-u))*f>=2&&(s++,f/=2),s+u>=l?(a=0,s=l):s+u>=1?(a=(t*f-1)*Math.pow(2,i),s+=u):(a=t*Math.pow(2,u-1)*Math.pow(2,i),s=0));i>=8;e[r+d]=255&a,d+=p,a/=256,i-=8);for(s=s<0;e[r+d]=255&s,d+=p,s/=256,h-=8);e[r+d-p]|=128*g;};},8738:e=>{function t(e){return !!e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}e.exports=function(e){return null!=e&&(t(e)||function(e){return "function"==typeof e.readFloatLE&&"function"==typeof e.slice&&t(e.slice(0,0))}(e)||!!e._isBuffer)};},3720:e=>{e.exports=r;var t=null;try{t=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(e){}function r(e,t,r){this.low=0|e,this.high=0|t,this.unsigned=!!r;}function n(e){return !0===(e&&e.__isLong__)}r.prototype.__isLong__,Object.defineProperty(r.prototype,"__isLong__",{value:!0}),r.isLong=n;var i={},o={};function s(e,t){var r,n,s;return t?(s=0<=(e>>>=0)&&e<256)&&(n=o[e])?n:(r=f(e,(0|e)<0?-1:0,!0),s&&(o[e]=r),r):(s=-128<=(e|=0)&&e<128)&&(n=i[e])?n:(r=f(e,e<0?-1:0,!1),s&&(i[e]=r),r)}function a(e,t){if(isNaN(e))return t?y:m;if(t){if(e<0)return y;if(e>=d)return B}else {if(e<=-p)return E;if(e+1>=p)return _}return e<0?a(-e,t).neg():f(e%c|0,e/c|0,t)}function f(e,t,n){return new r(e,t,n)}r.fromInt=s,r.fromNumber=a,r.fromBits=f;var h=Math.pow;function l(e,t,r){if(0===e.length)throw Error("empty string");if("NaN"===e||"Infinity"===e||"+Infinity"===e||"-Infinity"===e)return m;if("number"==typeof t?(r=t,t=!1):t=!!t,(r=r||10)<2||360)throw Error("interior hyphen");if(0===n)return l(e.substring(1),t,r).neg();for(var i=a(h(r,8)),o=m,s=0;s>>0:this.low},S.toNumber=function(){return this.unsigned?(this.high>>>0)*c+(this.low>>>0):this.high*c+(this.low>>>0)},S.toString=function(e){if((e=e||10)<2||36>>0).toString(e);if((o=f).isZero())return l+s;for(;l.length<6;)l="0"+l;s=""+l+s;}},S.getHighBits=function(){return this.high},S.getHighBitsUnsigned=function(){return this.high>>>0},S.getLowBits=function(){return this.low},S.getLowBitsUnsigned=function(){return this.low>>>0},S.getNumBitsAbs=function(){if(this.isNegative())return this.eq(E)?64:this.neg().getNumBitsAbs();for(var e=0!=this.high?this.high:this.low,t=31;t>0&&0==(e&1<=0},S.isOdd=function(){return 1==(1&this.low)},S.isEven=function(){return 0==(1&this.low)},S.equals=function(e){return n(e)||(e=u(e)),(this.unsigned===e.unsigned||this.high>>>31!=1||e.high>>>31!=1)&&this.high===e.high&&this.low===e.low},S.eq=S.equals,S.notEquals=function(e){return !this.eq(e)},S.neq=S.notEquals,S.ne=S.notEquals,S.lessThan=function(e){return this.comp(e)<0},S.lt=S.lessThan,S.lessThanOrEqual=function(e){return this.comp(e)<=0},S.lte=S.lessThanOrEqual,S.le=S.lessThanOrEqual,S.greaterThan=function(e){return this.comp(e)>0},S.gt=S.greaterThan,S.greaterThanOrEqual=function(e){return this.comp(e)>=0},S.gte=S.greaterThanOrEqual,S.ge=S.greaterThanOrEqual,S.compare=function(e){if(n(e)||(e=u(e)),this.eq(e))return 0;var t=this.isNegative(),r=e.isNegative();return t&&!r?-1:!t&&r?1:this.unsigned?e.high>>>0>this.high>>>0||e.high===this.high&&e.low>>>0>this.low>>>0?-1:1:this.sub(e).isNegative()?-1:1},S.comp=S.compare,S.negate=function(){return !this.unsigned&&this.eq(E)?E:this.not().add(b)},S.neg=S.negate,S.add=function(e){n(e)||(e=u(e));var t=this.high>>>16,r=65535&this.high,i=this.low>>>16,o=65535&this.low,s=e.high>>>16,a=65535&e.high,h=e.low>>>16,l=0,c=0,d=0,p=0;return d+=(p+=o+(65535&e.low))>>>16,c+=(d+=i+h)>>>16,l+=(c+=r+a)>>>16,l+=t+s,f((d&=65535)<<16|(p&=65535),(l&=65535)<<16|(c&=65535),this.unsigned)},S.subtract=function(e){return n(e)||(e=u(e)),this.add(e.neg())},S.sub=S.subtract,S.multiply=function(e){if(this.isZero())return m;if(n(e)||(e=u(e)),t)return f(t.mul(this.low,this.high,e.low,e.high),t.get_high(),this.unsigned);if(e.isZero())return m;if(this.eq(E))return e.isOdd()?E:m;if(e.eq(E))return this.isOdd()?E:m;if(this.isNegative())return e.isNegative()?this.neg().mul(e.neg()):this.neg().mul(e).neg();if(e.isNegative())return this.mul(e.neg()).neg();if(this.lt(g)&&e.lt(g))return a(this.toNumber()*e.toNumber(),this.unsigned);var r=this.high>>>16,i=65535&this.high,o=this.low>>>16,s=65535&this.low,h=e.high>>>16,l=65535&e.high,c=e.low>>>16,d=65535&e.low,p=0,y=0,b=0,v=0;return b+=(v+=s*d)>>>16,y+=(b+=o*d)>>>16,b&=65535,y+=(b+=s*c)>>>16,p+=(y+=i*d)>>>16,y&=65535,p+=(y+=o*c)>>>16,y&=65535,p+=(y+=s*l)>>>16,p+=r*d+i*c+o*l+s*h,f((b&=65535)<<16|(v&=65535),(p&=65535)<<16|(y&=65535),this.unsigned)},S.mul=S.multiply,S.divide=function(e){if(n(e)||(e=u(e)),e.isZero())throw Error("division by zero");var r,i,o;if(t)return this.unsigned||-2147483648!==this.high||-1!==e.low||-1!==e.high?f((this.unsigned?t.div_u:t.div_s)(this.low,this.high,e.low,e.high),t.get_high(),this.unsigned):this;if(this.isZero())return this.unsigned?y:m;if(this.unsigned){if(e.unsigned||(e=e.toUnsigned()),e.gt(this))return y;if(e.gt(this.shru(1)))return v;o=y;}else {if(this.eq(E))return e.eq(b)||e.eq(w)?E:e.eq(E)?b:(r=this.shr(1).div(e).shl(1)).eq(m)?e.isNegative()?b:w:(i=this.sub(e.mul(r)),o=r.add(i.div(e)));if(e.eq(E))return this.unsigned?y:m;if(this.isNegative())return e.isNegative()?this.neg().div(e.neg()):this.neg().div(e).neg();if(e.isNegative())return this.div(e.neg()).neg();o=m;}for(i=this;i.gte(e);){r=Math.max(1,Math.floor(i.toNumber()/e.toNumber()));for(var s=Math.ceil(Math.log(r)/Math.LN2),l=s<=48?1:h(2,s-48),c=a(r),d=c.mul(e);d.isNegative()||d.gt(i);)d=(c=a(r-=l,this.unsigned)).mul(e);c.isZero()&&(c=b),o=o.add(c),i=i.sub(d);}return o},S.div=S.divide,S.modulo=function(e){return n(e)||(e=u(e)),t?f((this.unsigned?t.rem_u:t.rem_s)(this.low,this.high,e.low,e.high),t.get_high(),this.unsigned):this.sub(this.div(e).mul(e))},S.mod=S.modulo,S.rem=S.modulo,S.not=function(){return f(~this.low,~this.high,this.unsigned)},S.and=function(e){return n(e)||(e=u(e)),f(this.low&e.low,this.high&e.high,this.unsigned)},S.or=function(e){return n(e)||(e=u(e)),f(this.low|e.low,this.high|e.high,this.unsigned)},S.xor=function(e){return n(e)||(e=u(e)),f(this.low^e.low,this.high^e.high,this.unsigned)},S.shiftLeft=function(e){return n(e)&&(e=e.toInt()),0==(e&=63)?this:e<32?f(this.low<>>32-e,this.unsigned):f(0,this.low<>>e|this.high<<32-e,this.high>>e,this.unsigned):f(this.high>>e-32,this.high>=0?0:-1,this.unsigned)},S.shr=S.shiftRight,S.shiftRightUnsigned=function(e){if(n(e)&&(e=e.toInt()),0==(e&=63))return this;var t=this.high;return e<32?f(this.low>>>e|t<<32-e,t>>>e,this.unsigned):f(32===e?t:t>>>e-32,0,this.unsigned)},S.shru=S.shiftRightUnsigned,S.shr_u=S.shiftRightUnsigned,S.toSigned=function(){return this.unsigned?f(this.low,this.high,!1):this},S.toUnsigned=function(){return this.unsigned?this:f(this.low,this.high,!0)},S.toBytes=function(e){return e?this.toBytesLE():this.toBytesBE()},S.toBytesLE=function(){var e=this.high,t=this.low;return [255&t,t>>>8&255,t>>>16&255,t>>>24,255&e,e>>>8&255,e>>>16&255,e>>>24]},S.toBytesBE=function(){var e=this.high,t=this.low;return [e>>>24,e>>>16&255,e>>>8&255,255&e,t>>>24,t>>>16&255,t>>>8&255,255&t]},r.fromBytes=function(e,t,n){return n?r.fromBytesLE(e,t):r.fromBytesBE(e,t)},r.fromBytesLE=function(e,t){return new r(e[0]|e[1]<<8|e[2]<<16|e[3]<<24,e[4]|e[5]<<8|e[6]<<16|e[7]<<24,t)},r.fromBytesBE=function(e,t){return new r(e[4]<<24|e[5]<<16|e[6]<<8|e[7],e[0]<<24|e[1]<<16|e[2]<<8|e[3],t)};},2568:(e,t,r)=>{var n,i,o,s,a;n=r(1012),i=r(487).utf8,o=r(8738),s=r(487).bin,(a=function(e,t){e.constructor==String?e=t&&"binary"===t.encoding?s.stringToBytes(e):i.stringToBytes(e):o(e)?e=Array.prototype.slice.call(e,0):Array.isArray(e)||e.constructor===Uint8Array||(e=e.toString());for(var r=n.bytesToWords(e),f=8*e.length,h=1732584193,l=-271733879,u=-1732584194,c=271733878,d=0;d>>24)|4278255360&(r[d]<<24|r[d]>>>8);r[f>>>5]|=128<>>9<<4)]=f;var p=a._ff,g=a._gg,m=a._hh,y=a._ii;for(d=0;d>>0,l=l+v>>>0,u=u+w>>>0,c=c+_>>>0;}return n.endian([h,l,u,c])})._ff=function(e,t,r,n,i,o,s){var a=e+(t&r|~t&n)+(i>>>0)+s;return (a<>>32-o)+t},a._gg=function(e,t,r,n,i,o,s){var a=e+(t&n|r&~n)+(i>>>0)+s;return (a<>>32-o)+t},a._hh=function(e,t,r,n,i,o,s){var a=e+(t^r^n)+(i>>>0)+s;return (a<>>32-o)+t},a._ii=function(e,t,r,n,i,o,s){var a=e+(r^(t|~n))+(i>>>0)+s;return (a<>>32-o)+t},a._blocksize=16,a._digestsize=16,e.exports=function(e,t){if(null==e)throw new Error("Illegal argument "+e);var r=n.wordsToBytes(a(e,t));return t&&t.asBytes?r:t&&t.asString?s.bytesToString(r):n.bytesToHex(r)};},9591:(e,t,r)=>{var n={};(0, r(4236).assign)(n,r(4555),r(8843),r(1619)),e.exports=n;},4555:(e,t,r)=>{var n=r(405),i=r(4236),o=r(9373),s=r(8898),a=r(2292),f=Object.prototype.toString;function h(e){if(!(this instanceof h))return new h(e);this.options=i.assign({level:-1,method:8,chunkSize:16384,windowBits:15,memLevel:8,strategy:0,to:""},e||{});var t=this.options;t.raw&&t.windowBits>0?t.windowBits=-t.windowBits:t.gzip&&t.windowBits>0&&t.windowBits<16&&(t.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new a,this.strm.avail_out=0;var r=n.deflateInit2(this.strm,t.level,t.method,t.windowBits,t.memLevel,t.strategy);if(0!==r)throw new Error(s[r]);if(t.header&&n.deflateSetHeader(this.strm,t.header),t.dictionary){var l;if(l="string"==typeof t.dictionary?o.string2buf(t.dictionary):"[object ArrayBuffer]"===f.call(t.dictionary)?new Uint8Array(t.dictionary):t.dictionary,0!==(r=n.deflateSetDictionary(this.strm,l)))throw new Error(s[r]);this._dict_set=!0;}}function l(e,t){var r=new h(t);if(r.push(e,!0),r.err)throw r.msg||s[r.err];return r.result}h.prototype.push=function(e,t){var r,s,a=this.strm,h=this.options.chunkSize;if(this.ended)return !1;s=t===~~t?t:!0===t?4:0,"string"==typeof e?a.input=o.string2buf(e):"[object ArrayBuffer]"===f.call(e)?a.input=new Uint8Array(e):a.input=e,a.next_in=0,a.avail_in=a.input.length;do{if(0===a.avail_out&&(a.output=new i.Buf8(h),a.next_out=0,a.avail_out=h),1!==(r=n.deflate(a,s))&&0!==r)return this.onEnd(r),this.ended=!0,!1;0!==a.avail_out&&(0!==a.avail_in||4!==s&&2!==s)||("string"===this.options.to?this.onData(o.buf2binstring(i.shrinkBuf(a.output,a.next_out))):this.onData(i.shrinkBuf(a.output,a.next_out)));}while((a.avail_in>0||0===a.avail_out)&&1!==r);return 4===s?(r=n.deflateEnd(this.strm),this.onEnd(r),this.ended=!0,0===r):2!==s||(this.onEnd(0),a.avail_out=0,!0)},h.prototype.onData=function(e){this.chunks.push(e);},h.prototype.onEnd=function(e){0===e&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=i.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg;},t.Deflate=h,t.deflate=l,t.deflateRaw=function(e,t){return (t=t||{}).raw=!0,l(e,t)},t.gzip=function(e,t){return (t=t||{}).gzip=!0,l(e,t)};},8843:(e,t,r)=>{var n=r(7948),i=r(4236),o=r(9373),s=r(1619),a=r(8898),f=r(2292),h=r(2401),l=Object.prototype.toString;function u(e){if(!(this instanceof u))return new u(e);this.options=i.assign({chunkSize:16384,windowBits:0,to:""},e||{});var t=this.options;t.raw&&t.windowBits>=0&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(t.windowBits>=0&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),t.windowBits>15&&t.windowBits<48&&0==(15&t.windowBits)&&(t.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var r=n.inflateInit2(this.strm,t.windowBits);if(r!==s.Z_OK)throw new Error(a[r]);if(this.header=new h,n.inflateGetHeader(this.strm,this.header),t.dictionary&&("string"==typeof t.dictionary?t.dictionary=o.string2buf(t.dictionary):"[object ArrayBuffer]"===l.call(t.dictionary)&&(t.dictionary=new Uint8Array(t.dictionary)),t.raw&&(r=n.inflateSetDictionary(this.strm,t.dictionary))!==s.Z_OK))throw new Error(a[r])}function c(e,t){var r=new u(t);if(r.push(e,!0),r.err)throw r.msg||a[r.err];return r.result}u.prototype.push=function(e,t){var r,a,f,h,u,c=this.strm,d=this.options.chunkSize,p=this.options.dictionary,g=!1;if(this.ended)return !1;a=t===~~t?t:!0===t?s.Z_FINISH:s.Z_NO_FLUSH,"string"==typeof e?c.input=o.binstring2buf(e):"[object ArrayBuffer]"===l.call(e)?c.input=new Uint8Array(e):c.input=e,c.next_in=0,c.avail_in=c.input.length;do{if(0===c.avail_out&&(c.output=new i.Buf8(d),c.next_out=0,c.avail_out=d),(r=n.inflate(c,s.Z_NO_FLUSH))===s.Z_NEED_DICT&&p&&(r=n.inflateSetDictionary(this.strm,p)),r===s.Z_BUF_ERROR&&!0===g&&(r=s.Z_OK,g=!1),r!==s.Z_STREAM_END&&r!==s.Z_OK)return this.onEnd(r),this.ended=!0,!1;c.next_out&&(0!==c.avail_out&&r!==s.Z_STREAM_END&&(0!==c.avail_in||a!==s.Z_FINISH&&a!==s.Z_SYNC_FLUSH)||("string"===this.options.to?(f=o.utf8border(c.output,c.next_out),h=c.next_out-f,u=o.buf2string(c.output,f),c.next_out=h,c.avail_out=d-h,h&&i.arraySet(c.output,c.output,f,h,0),this.onData(u)):this.onData(i.shrinkBuf(c.output,c.next_out)))),0===c.avail_in&&0===c.avail_out&&(g=!0);}while((c.avail_in>0||0===c.avail_out)&&r!==s.Z_STREAM_END);return r===s.Z_STREAM_END&&(a=s.Z_FINISH),a===s.Z_FINISH?(r=n.inflateEnd(this.strm),this.onEnd(r),this.ended=!0,r===s.Z_OK):a!==s.Z_SYNC_FLUSH||(this.onEnd(s.Z_OK),c.avail_out=0,!0)},u.prototype.onData=function(e){this.chunks.push(e);},u.prototype.onEnd=function(e){e===s.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=i.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg;},t.Inflate=u,t.inflate=c,t.inflateRaw=function(e,t){return (t=t||{}).raw=!0,c(e,t)},t.ungzip=c;},4236:(e,t)=>{var r="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.assign=function(e){for(var t=Array.prototype.slice.call(arguments,1);t.length;){var r=t.shift();if(r){if("object"!=typeof r)throw new TypeError(r+"must be non-object");for(var i in r)n(r,i)&&(e[i]=r[i]);}}return e},t.shrinkBuf=function(e,t){return e.length===t?e:e.subarray?e.subarray(0,t):(e.length=t,e)};var i={arraySet:function(e,t,r,n,i){if(t.subarray&&e.subarray)e.set(t.subarray(r,r+n),i);else for(var o=0;o{var n=r(4236),i=!0,o=!0;try{String.fromCharCode.apply(null,[0]);}catch(e){i=!1;}try{String.fromCharCode.apply(null,new Uint8Array(1));}catch(e){o=!1;}for(var s=new n.Buf8(256),a=0;a<256;a++)s[a]=a>=252?6:a>=248?5:a>=240?4:a>=224?3:a>=192?2:1;function f(e,t){if(t<65534&&(e.subarray&&o||!e.subarray&&i))return String.fromCharCode.apply(null,n.shrinkBuf(e,t));for(var r="",s=0;s>>6,t[s++]=128|63&r):r<65536?(t[s++]=224|r>>>12,t[s++]=128|r>>>6&63,t[s++]=128|63&r):(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63,t[s++]=128|r>>>6&63,t[s++]=128|63&r);return t},t.buf2binstring=function(e){return f(e,e.length)},t.binstring2buf=function(e){for(var t=new n.Buf8(e.length),r=0,i=t.length;r4)h[n++]=65533,r+=o-1;else {for(i&=2===o?31:3===o?15:7;o>1&&r1?h[n++]=65533:i<65536?h[n++]=i:(i-=65536,h[n++]=55296|i>>10&1023,h[n++]=56320|1023&i);}return f(h,n)},t.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;r>=0&&128==(192&e[r]);)r--;return r<0||0===r?t:r+s[e[r]]>t?r:t};},6069:e=>{e.exports=function(e,t,r,n){for(var i=65535&e|0,o=e>>>16&65535|0,s=0;0!==r;){r-=s=r>2e3?2e3:r;do{o=o+(i=i+t[n++]|0)|0;}while(--s);i%=65521,o%=65521;}return i|o<<16|0};},1619:e=>{e.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};},2869:e=>{var t=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e;}return t}();e.exports=function(e,r,n,i){var o=t,s=i+n;e^=-1;for(var a=i;a>>8^o[255&(e^r[a])];return -1^e};},405:(e,t,r)=>{var n,i=r(4236),o=r(342),s=r(6069),a=r(2869),f=r(8898),h=-2,l=258,u=262,c=103,d=113,p=666;function g(e,t){return e.msg=f[t],t}function m(e){return (e<<1)-(e>4?9:0)}function y(e){for(var t=e.length;--t>=0;)e[t]=0;}function b(e){var t=e.state,r=t.pending;r>e.avail_out&&(r=e.avail_out),0!==r&&(i.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0));}function v(e,t){o._tr_flush_block(e,e.block_start>=0?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,b(e.strm);}function w(e,t){e.pending_buf[e.pending++]=t;}function _(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t;}function B(e,t,r,n){var o=e.avail_in;return o>n&&(o=n),0===o?0:(e.avail_in-=o,i.arraySet(t,e.input,e.next_in,o,r),1===e.state.wrap?e.adler=s(e.adler,t,o,r):2===e.state.wrap&&(e.adler=a(e.adler,t,o,r)),e.next_in+=o,e.total_in+=o,o)}function E(e,t){var r,n,i=e.max_chain_length,o=e.strstart,s=e.prev_length,a=e.nice_match,f=e.strstart>e.w_size-u?e.strstart-(e.w_size-u):0,h=e.window,c=e.w_mask,d=e.prev,p=e.strstart+l,g=h[o+s-1],m=h[o+s];e.prev_length>=e.good_match&&(i>>=2),a>e.lookahead&&(a=e.lookahead);do{if(h[(r=t)+s]===m&&h[r+s-1]===g&&h[r]===h[o]&&h[++r]===h[o+1]){o+=2,r++;do{}while(h[++o]===h[++r]&&h[++o]===h[++r]&&h[++o]===h[++r]&&h[++o]===h[++r]&&h[++o]===h[++r]&&h[++o]===h[++r]&&h[++o]===h[++r]&&h[++o]===h[++r]&&os){if(e.match_start=t,s=n,n>=a)break;g=h[o+s-1],m=h[o+s];}}}while((t=d[t&c])>f&&0!=--i);return s<=e.lookahead?s:e.lookahead}function S(e){var t,r,n,o,s,a=e.w_size;do{if(o=e.window_size-e.lookahead-e.strstart,e.strstart>=a+(a-u)){i.arraySet(e.window,e.window,a,a,0),e.match_start-=a,e.strstart-=a,e.block_start-=a,t=r=e.hash_size;do{n=e.head[--t],e.head[t]=n>=a?n-a:0;}while(--r);t=r=a;do{n=e.prev[--t],e.prev[t]=n>=a?n-a:0;}while(--r);o+=a;}if(0===e.strm.avail_in)break;if(r=B(e.strm,e.window,e.strstart+e.lookahead,o),e.lookahead+=r,e.lookahead+e.insert>=3)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=3&&(e.ins_h=(e.ins_h<=3)if(n=o._tr_tally(e,e.strstart-e.match_start,e.match_length-3),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=3){e.match_length--;do{e.strstart++,e.ins_h=(e.ins_h<=3&&(e.ins_h=(e.ins_h<4096)&&(e.match_length=2)),e.prev_length>=3&&e.match_length<=e.prev_length){i=e.strstart+e.lookahead-3,n=o._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-3),e.lookahead-=e.prev_length-1,e.prev_length-=2;do{++e.strstart<=i&&(e.ins_h=(e.ins_h<15&&(a=2,n-=16),o<1||o>9||8!==r||n<8||n>15||t<0||t>9||s<0||s>4)return g(e,h);8===n&&(n=9);var f=new k;return e.state=f,f.strm=e,f.wrap=a,f.gzhead=null,f.w_bits=n,f.w_size=1<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(S(e),0===e.lookahead&&0===t)return 1;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,v(e,!1),0===e.strm.avail_out))return 1;if(e.strstart-e.block_start>=e.w_size-u&&(v(e,!1),0===e.strm.avail_out))return 1}return e.insert=0,4===t?(v(e,!0),0===e.strm.avail_out?3:4):(e.strstart>e.block_start&&(v(e,!1),e.strm.avail_out),1)})),new R(4,4,8,4,A),new R(4,5,16,8,A),new R(4,6,32,32,A),new R(4,4,16,16,C),new R(8,16,32,32,C),new R(8,16,128,128,C),new R(8,32,128,256,C),new R(32,128,258,1024,C),new R(32,258,258,4096,C)],t.deflateInit=function(e,t){return M(e,t,8,15,8,0)},t.deflateInit2=M,t.deflateReset=I,t.deflateResetKeep=x,t.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?h:(e.state.gzhead=t,0):h},t.deflate=function(e,t){var r,i,s,f;if(!e||!e.state||t>5||t<0)return e?g(e,h):h;if(i=e.state,!e.output||!e.input&&0!==e.avail_in||i.status===p&&4!==t)return g(e,0===e.avail_out?-5:h);if(i.strm=e,r=i.last_flush,i.last_flush=t,42===i.status)if(2===i.wrap)e.adler=0,w(i,31),w(i,139),w(i,8),i.gzhead?(w(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)),w(i,255&i.gzhead.time),w(i,i.gzhead.time>>8&255),w(i,i.gzhead.time>>16&255),w(i,i.gzhead.time>>24&255),w(i,9===i.level?2:i.strategy>=2||i.level<2?4:0),w(i,255&i.gzhead.os),i.gzhead.extra&&i.gzhead.extra.length&&(w(i,255&i.gzhead.extra.length),w(i,i.gzhead.extra.length>>8&255)),i.gzhead.hcrc&&(e.adler=a(e.adler,i.pending_buf,i.pending,0)),i.gzindex=0,i.status=69):(w(i,0),w(i,0),w(i,0),w(i,0),w(i,0),w(i,9===i.level?2:i.strategy>=2||i.level<2?4:0),w(i,3),i.status=d);else {var u=8+(i.w_bits-8<<4)<<8;u|=(i.strategy>=2||i.level<2?0:i.level<6?1:6===i.level?2:3)<<6,0!==i.strstart&&(u|=32),u+=31-u%31,i.status=d,_(i,u),0!==i.strstart&&(_(i,e.adler>>>16),_(i,65535&e.adler)),e.adler=1;}if(69===i.status)if(i.gzhead.extra){for(s=i.pending;i.gzindex<(65535&i.gzhead.extra.length)&&(i.pending!==i.pending_buf_size||(i.gzhead.hcrc&&i.pending>s&&(e.adler=a(e.adler,i.pending_buf,i.pending-s,s)),b(e),s=i.pending,i.pending!==i.pending_buf_size));)w(i,255&i.gzhead.extra[i.gzindex]),i.gzindex++;i.gzhead.hcrc&&i.pending>s&&(e.adler=a(e.adler,i.pending_buf,i.pending-s,s)),i.gzindex===i.gzhead.extra.length&&(i.gzindex=0,i.status=73);}else i.status=73;if(73===i.status)if(i.gzhead.name){s=i.pending;do{if(i.pending===i.pending_buf_size&&(i.gzhead.hcrc&&i.pending>s&&(e.adler=a(e.adler,i.pending_buf,i.pending-s,s)),b(e),s=i.pending,i.pending===i.pending_buf_size)){f=1;break}f=i.gzindexs&&(e.adler=a(e.adler,i.pending_buf,i.pending-s,s)),0===f&&(i.gzindex=0,i.status=91);}else i.status=91;if(91===i.status)if(i.gzhead.comment){s=i.pending;do{if(i.pending===i.pending_buf_size&&(i.gzhead.hcrc&&i.pending>s&&(e.adler=a(e.adler,i.pending_buf,i.pending-s,s)),b(e),s=i.pending,i.pending===i.pending_buf_size)){f=1;break}f=i.gzindexs&&(e.adler=a(e.adler,i.pending_buf,i.pending-s,s)),0===f&&(i.status=c);}else i.status=c;if(i.status===c&&(i.gzhead.hcrc?(i.pending+2>i.pending_buf_size&&b(e),i.pending+2<=i.pending_buf_size&&(w(i,255&e.adler),w(i,e.adler>>8&255),e.adler=0,i.status=d)):i.status=d),0!==i.pending){if(b(e),0===e.avail_out)return i.last_flush=-1,0}else if(0===e.avail_in&&m(t)<=m(r)&&4!==t)return g(e,-5);if(i.status===p&&0!==e.avail_in)return g(e,-5);if(0!==e.avail_in||0!==i.lookahead||0!==t&&i.status!==p){var B=2===i.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(S(e),0===e.lookahead)){if(0===t)return 1;break}if(e.match_length=0,r=o._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(v(e,!1),0===e.strm.avail_out))return 1}return e.insert=0,4===t?(v(e,!0),0===e.strm.avail_out?3:4):e.last_lit&&(v(e,!1),0===e.strm.avail_out)?1:2}(i,t):3===i.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=l){if(S(e),e.lookahead<=l&&0===t)return 1;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=3&&e.strstart>0&&(n=a[i=e.strstart-1])===a[++i]&&n===a[++i]&&n===a[++i]){s=e.strstart+l;do{}while(n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&ie.lookahead&&(e.match_length=e.lookahead);}if(e.match_length>=3?(r=o._tr_tally(e,1,e.match_length-3),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=o._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(v(e,!1),0===e.strm.avail_out))return 1}return e.insert=0,4===t?(v(e,!0),0===e.strm.avail_out?3:4):e.last_lit&&(v(e,!1),0===e.strm.avail_out)?1:2}(i,t):n[i.level].func(i,t);if(3!==B&&4!==B||(i.status=p),1===B||3===B)return 0===e.avail_out&&(i.last_flush=-1),0;if(2===B&&(1===t?o._tr_align(i):5!==t&&(o._tr_stored_block(i,0,0,!1),3===t&&(y(i.head),0===i.lookahead&&(i.strstart=0,i.block_start=0,i.insert=0))),b(e),0===e.avail_out))return i.last_flush=-1,0}return 4!==t?0:i.wrap<=0?1:(2===i.wrap?(w(i,255&e.adler),w(i,e.adler>>8&255),w(i,e.adler>>16&255),w(i,e.adler>>24&255),w(i,255&e.total_in),w(i,e.total_in>>8&255),w(i,e.total_in>>16&255),w(i,e.total_in>>24&255)):(_(i,e.adler>>>16),_(i,65535&e.adler)),b(e),i.wrap>0&&(i.wrap=-i.wrap),0!==i.pending?0:1)},t.deflateEnd=function(e){var t;return e&&e.state?42!==(t=e.state.status)&&69!==t&&73!==t&&91!==t&&t!==c&&t!==d&&t!==p?g(e,h):(e.state=null,t===d?g(e,-3):0):h},t.deflateSetDictionary=function(e,t){var r,n,o,a,f,l,u,c,d=t.length;if(!e||!e.state)return h;if(2===(a=(r=e.state).wrap)||1===a&&42!==r.status||r.lookahead)return h;for(1===a&&(e.adler=s(e.adler,t,d,0)),r.wrap=0,d>=r.w_size&&(0===a&&(y(r.head),r.strstart=0,r.block_start=0,r.insert=0),c=new i.Buf8(r.w_size),i.arraySet(c,t,d-r.w_size,r.w_size,0),t=c,d=r.w_size),f=e.avail_in,l=e.next_in,u=e.input,e.avail_in=d,e.next_in=0,e.input=t,S(r);r.lookahead>=3;){n=r.strstart,o=r.lookahead-2;do{r.ins_h=(r.ins_h<{e.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1;};},4264:e=>{e.exports=function(e,t){var r,n,i,o,s,a,f,h,l,u,c,d,p,g,m,y,b,v,w,_,B,E,S,A,C;r=e.state,n=e.next_in,A=e.input,i=n+(e.avail_in-5),o=e.next_out,C=e.output,s=o-(t-e.avail_out),a=o+(e.avail_out-257),f=r.dmax,h=r.wsize,l=r.whave,u=r.wnext,c=r.window,d=r.hold,p=r.bits,g=r.lencode,m=r.distcode,y=(1<>>=w=v>>>24,p-=w,0==(w=v>>>16&255))C[o++]=65535&v;else {if(!(16&w)){if(0==(64&w)){v=g[(65535&v)+(d&(1<>>=w,p-=w),p<15&&(d+=A[n++]<>>=w=v>>>24,p-=w,!(16&(w=v>>>16&255))){if(0==(64&w)){v=m[(65535&v)+(d&(1<f){e.msg="invalid distance too far back",r.mode=30;break e}if(d>>>=w,p-=w,B>(w=o-s)){if((w=B-w)>l&&r.sane){e.msg="invalid distance too far back",r.mode=30;break e}if(E=0,S=c,0===u){if(E+=h-w,w<_){_-=w;do{C[o++]=c[E++];}while(--w);E=o-B,S=C;}}else if(u2;)C[o++]=S[E++],C[o++]=S[E++],C[o++]=S[E++],_-=3;_&&(C[o++]=S[E++],_>1&&(C[o++]=S[E++]));}else {E=o-B;do{C[o++]=C[E++],C[o++]=C[E++],C[o++]=C[E++],_-=3;}while(_>2);_&&(C[o++]=C[E++],_>1&&(C[o++]=C[E++]));}break}}break}}while(n>3,d&=(1<<(p-=_<<3))-1,e.next_in=n,e.next_out=o,e.avail_in=n{var n=r(4236),i=r(6069),o=r(2869),s=r(4264),a=r(9241),f=-2,h=12,l=30;function u(e){return (e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function c(){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 d(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=1,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new n.Buf32(852),t.distcode=t.distdyn=new n.Buf32(592),t.sane=1,t.back=-1,0):f}function p(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,d(e)):f}function g(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||t>15)?f:(null!==n.window&&n.wbits!==t&&(n.window=null),n.wrap=r,n.wbits=t,p(e))):f}function m(e,t){var r,n;return e?(n=new c,e.state=n,n.window=null,0!==(r=g(e,t))&&(e.state=null),r):f}var y,b,v=!0;function w(e){if(v){var t;for(y=new n.Buf32(512),b=new n.Buf32(32),t=0;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(a(1,e.lens,0,288,y,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;a(2,e.lens,0,32,b,0,e.work,{bits:5}),v=!1;}e.lencode=y,e.lenbits=9,e.distcode=b,e.distbits=5;}function _(e,t,r,i){var o,s=e.state;return null===s.window&&(s.wsize=1<=s.wsize?(n.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):((o=s.wsize-s.wnext)>i&&(o=i),n.arraySet(s.window,t,r-i,o,s.wnext),(i-=o)?(n.arraySet(s.window,t,r-i,i,0),s.wnext=i,s.whave=s.wsize):(s.wnext+=o,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=o(r.check,L,2,0),b=0,v=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&b)<<8)+(b>>8))%31){e.msg="incorrect header check",r.mode=l;break}if(8!=(15&b)){e.msg="unknown compression method",r.mode=l;break}if(v-=4,O=8+(15&(b>>>=4)),0===r.wbits)r.wbits=O;else if(O>r.wbits){e.msg="invalid window size",r.mode=l;break}r.dmax=1<>8&1),512&r.flags&&(L[0]=255&b,L[1]=b>>>8&255,r.check=o(r.check,L,2,0)),b=0,v=0,r.mode=3;case 3:for(;v<32;){if(0===m)break e;m--,b+=c[p++]<>>8&255,L[2]=b>>>16&255,L[3]=b>>>24&255,r.check=o(r.check,L,4,0)),b=0,v=0,r.mode=4;case 4:for(;v<16;){if(0===m)break e;m--,b+=c[p++]<>8),512&r.flags&&(L[0]=255&b,L[1]=b>>>8&255,r.check=o(r.check,L,2,0)),b=0,v=0,r.mode=5;case 5:if(1024&r.flags){for(;v<16;){if(0===m)break e;m--,b+=c[p++]<>>8&255,r.check=o(r.check,L,2,0)),b=0,v=0;}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&((S=r.length)>m&&(S=m),S&&(r.head&&(O=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),n.arraySet(r.head.extra,c,p,S,O)),512&r.flags&&(r.check=o(r.check,c,S,p)),m-=S,p+=S,r.length-=S),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===m)break e;S=0;do{O=c[p+S++],r.head&&O&&r.length<65536&&(r.head.name+=String.fromCharCode(O));}while(O&&S>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=h;break;case 10:for(;v<32;){if(0===m)break e;m--,b+=c[p++]<>>=7&v,v-=7&v,r.mode=27;break}for(;v<3;){if(0===m)break e;m--,b+=c[p++]<>>=1)){case 0:r.mode=14;break;case 1:if(w(r),r.mode=20,6===t){b>>>=2,v-=2;break e}break;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=l;}b>>>=2,v-=2;break;case 14:for(b>>>=7&v,v-=7&v;v<32;){if(0===m)break e;m--,b+=c[p++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=l;break}if(r.length=65535&b,b=0,v=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(S=r.length){if(S>m&&(S=m),S>y&&(S=y),0===S)break e;n.arraySet(d,c,p,S,g),m-=S,p+=S,y-=S,g+=S,r.length-=S;break}r.mode=h;break;case 17:for(;v<14;){if(0===m)break e;m--,b+=c[p++]<>>=5,v-=5,r.ndist=1+(31&b),b>>>=5,v-=5,r.ncode=4+(15&b),b>>>=4,v-=4,r.nlen>286||r.ndist>30){e.msg="too many length or distance symbols",r.mode=l;break}r.have=0,r.mode=18;case 18:for(;r.have>>=3,v-=3;}for(;r.have<19;)r.lens[z[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,U={bits:r.lenbits},T=a(0,r.lens,0,19,r.lencode,0,r.work,U),r.lenbits=U.bits,T){e.msg="invalid code lengths set",r.mode=l;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,x=65535&N,!((R=N>>>24)<=v);){if(0===m)break e;m--,b+=c[p++]<>>=R,v-=R,r.lens[r.have++]=x;else {if(16===x){for(P=R+2;v>>=R,v-=R,0===r.have){e.msg="invalid bit length repeat",r.mode=l;break}O=r.lens[r.have-1],S=3+(3&b),b>>>=2,v-=2;}else if(17===x){for(P=R+3;v>>=R)),b>>>=3,v-=3;}else {for(P=R+7;v>>=R)),b>>>=7,v-=7;}if(r.have+S>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=l;break}for(;S--;)r.lens[r.have++]=O;}}if(r.mode===l)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=l;break}if(r.lenbits=9,U={bits:r.lenbits},T=a(1,r.lens,0,r.nlen,r.lencode,0,r.work,U),r.lenbits=U.bits,T){e.msg="invalid literal/lengths set",r.mode=l;break}if(r.distbits=6,r.distcode=r.distdyn,U={bits:r.distbits},T=a(2,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,U),r.distbits=U.bits,T){e.msg="invalid distances set",r.mode=l;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(m>=6&&y>=258){e.next_out=g,e.avail_out=y,e.next_in=p,e.avail_in=m,r.hold=b,r.bits=v,s(e,E),g=e.next_out,d=e.output,y=e.avail_out,p=e.next_in,c=e.input,m=e.avail_in,b=r.hold,v=r.bits,r.mode===h&&(r.back=-1);break}for(r.back=0;k=(N=r.lencode[b&(1<>>16&255,x=65535&N,!((R=N>>>24)<=v);){if(0===m)break e;m--,b+=c[p++]<>I)])>>>16&255,x=65535&N,!(I+(R=N>>>24)<=v);){if(0===m)break e;m--,b+=c[p++]<>>=I,v-=I,r.back+=I;}if(b>>>=R,v-=R,r.back+=R,r.length=x,0===k){r.mode=26;break}if(32&k){r.back=-1,r.mode=h;break}if(64&k){e.msg="invalid literal/length code",r.mode=l;break}r.extra=15&k,r.mode=22;case 22:if(r.extra){for(P=r.extra;v>>=r.extra,v-=r.extra,r.back+=r.extra;}r.was=r.length,r.mode=23;case 23:for(;k=(N=r.distcode[b&(1<>>16&255,x=65535&N,!((R=N>>>24)<=v);){if(0===m)break e;m--,b+=c[p++]<>I)])>>>16&255,x=65535&N,!(I+(R=N>>>24)<=v);){if(0===m)break e;m--,b+=c[p++]<>>=I,v-=I,r.back+=I;}if(b>>>=R,v-=R,r.back+=R,64&k){e.msg="invalid distance code",r.mode=l;break}r.offset=x,r.extra=15&k,r.mode=24;case 24:if(r.extra){for(P=r.extra;v>>=r.extra,v-=r.extra,r.back+=r.extra;}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=l;break}r.mode=25;case 25:if(0===y)break e;if(S=E-y,r.offset>S){if((S=r.offset-S)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=l;break}S>r.wnext?(S-=r.wnext,A=r.wsize-S):A=r.wnext-S,S>r.length&&(S=r.length),C=r.window;}else C=d,A=g-r.offset,S=r.length;S>y&&(S=y),y-=S,r.length-=S;do{d[g++]=C[A++];}while(--S);0===r.length&&(r.mode=21);break;case 26:if(0===y)break e;d[g++]=r.length,y--,r.mode=21;break;case 27:if(r.wrap){for(;v<32;){if(0===m)break e;m--,b|=c[p++]<{var n=r(4236),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],s=[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],a=[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];e.exports=function(e,t,r,f,h,l,u,c){var d,p,g,m,y,b,v,w,_,B=c.bits,E=0,S=0,A=0,C=0,R=0,k=0,x=0,I=0,M=0,F=0,O=null,T=0,U=new n.Buf16(16),P=new n.Buf16(16),N=null,L=0;for(E=0;E<=15;E++)U[E]=0;for(S=0;S=1&&0===U[C];C--);if(R>C&&(R=C),0===C)return h[l++]=20971520,h[l++]=20971520,c.bits=1,0;for(A=1;A0&&(0===e||1!==C))return -1;for(P[1]=0,E=1;E<15;E++)P[E+1]=P[E]+U[E];for(S=0;S852||2===e&&M>592)return 1;for(;;){v=E-x,u[S]b?(w=N[L+u[S]],_=O[T+u[S]]):(w=96,_=0),d=1<>x)+(p-=d)]=v<<24|w<<16|_|0;}while(0!==p);for(d=1<>=1;if(0!==d?(F&=d-1,F+=d):F=0,S++,0==--U[E]){if(E===C)break;E=t[r+u[S]];}if(E>R&&(F&m)!==g){for(0===x&&(x=R),y+=A,I=1<<(k=E-x);k+x852||2===e&&M>592)return 1;h[g=F&m]=R<<24|k<<16|y-l|0;}}return 0!==F&&(h[y+F]=E-x<<24|64<<16|0),c.bits=R,0};},8898:e=>{e.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"};},342:(e,t,r)=>{var n=r(4236);function i(e){for(var t=e.length;--t>=0;)e[t]=0;}var o=[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],s=[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],a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],f=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],h=new Array(576);i(h);var l=new Array(60);i(l);var u=new Array(512);i(u);var c=new Array(256);i(c);var d=new Array(29);i(d);var p,g,m,y=new Array(30);function b(e,t,r,n,i){this.static_tree=e,this.extra_bits=t,this.extra_base=r,this.elems=n,this.max_length=i,this.has_stree=e&&e.length;}function v(e,t){this.dyn_tree=e,this.max_code=0,this.stat_desc=t;}function w(e){return e<256?u[e]:u[256+(e>>>7)]}function _(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255;}function B(e,t,r){e.bi_valid>16-r?(e.bi_buf|=t<>16-e.bi_valid,e.bi_valid+=r-16):(e.bi_buf|=t<>>=1,r<<=1;}while(--t>0);return r>>>1}function A(e,t,r){var n,i,o=new Array(16),s=0;for(n=1;n<=15;n++)o[n]=s=s+r[n-1]<<1;for(i=0;i<=t;i++){var a=e[2*i+1];0!==a&&(e[2*i]=S(o[a]++,a));}}function C(e){var t;for(t=0;t<286;t++)e.dyn_ltree[2*t]=0;for(t=0;t<30;t++)e.dyn_dtree[2*t]=0;for(t=0;t<19;t++)e.bl_tree[2*t]=0;e.dyn_ltree[512]=1,e.opt_len=e.static_len=0,e.last_lit=e.matches=0;}function R(e){e.bi_valid>8?_(e,e.bi_buf):e.bi_valid>0&&(e.pending_buf[e.pending++]=e.bi_buf),e.bi_buf=0,e.bi_valid=0;}function k(e,t,r,n){var i=2*t,o=2*r;return e[i]>1;r>=1;r--)x(e,o,r);i=f;do{r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],x(e,o,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,o[2*i]=o[2*r]+o[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,o[2*r+1]=o[2*n+1]=i,e.heap[1]=i++,x(e,o,1);}while(e.heap_len>=2);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,o,s,a,f=t.dyn_tree,h=t.max_code,l=t.stat_desc.static_tree,u=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,g=0;for(o=0;o<=15;o++)e.bl_count[o]=0;for(f[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<573;r++)(o=f[2*f[2*(n=e.heap[r])+1]+1]+1)>p&&(o=p,g++),f[2*n+1]=o,n>h||(e.bl_count[o]++,s=0,n>=d&&(s=c[n-d]),a=f[2*n],e.opt_len+=a*(o+s),u&&(e.static_len+=a*(l[2*n+1]+s)));if(0!==g){do{for(o=p-1;0===e.bl_count[o];)o--;e.bl_count[o]--,e.bl_count[o+1]+=2,e.bl_count[p]--,g-=2;}while(g>0);for(o=p;0!==o;o--)for(n=e.bl_count[o];0!==n;)(i=e.heap[--r])>h||(f[2*i+1]!==o&&(e.opt_len+=(o-f[2*i+1])*f[2*i],f[2*i+1]=o),n--);}}(e,t),A(o,h,e.bl_count);}function F(e,t,r){var n,i,o=-1,s=t[1],a=0,f=7,h=4;for(0===s&&(f=138,h=3),t[2*(r+1)+1]=65535,n=0;n<=r;n++)i=s,s=t[2*(n+1)+1],++a>=7;n<30;n++)for(y[n]=i<<7,e=0;e<1<0?(2===e.strm.data_type&&(e.strm.data_type=function(e){var t,r=4093624447;for(t=0;t<=31;t++,r>>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return 0;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return 1;for(t=32;t<256;t++)if(0!==e.dyn_ltree[2*t])return 1;return 0}(e)),M(e,e.l_desc),M(e,e.d_desc),s=function(e){var t;for(F(e,e.dyn_ltree,e.l_desc.max_code),F(e,e.dyn_dtree,e.d_desc.max_code),M(e,e.bl_desc),t=18;t>=3&&0===e.bl_tree[2*f[t]+1];t--);return e.opt_len+=3*(t+1)+5+5+4,t}(e),i=e.opt_len+3+7>>>3,(o=e.static_len+3+7>>>3)<=i&&(i=o)):i=o=r+5,r+4<=i&&-1!==t?U(e,t,r,n):4===e.strategy||o===i?(B(e,2+(n?1:0),3),I(e,h,l)):(B(e,4+(n?1:0),3),function(e,t,r,n){var i;for(B(e,t-257,5),B(e,r-1,5),B(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(c[r]+256+1)]++,e.dyn_dtree[2*w(t)]++),e.last_lit===e.lit_bufsize-1},t._tr_align=function(e){B(e,2,3),E(e,256,h),function(e){16===e.bi_valid?(_(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):e.bi_valid>=8&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8);}(e);};},2292:e=>{e.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;};},2587:e=>{function t(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,r,n,i){r=r||"&",n=n||"=";var o={};if("string"!=typeof e||0===e.length)return o;var s=/\+/g;e=e.split(r);var a=1e3;i&&"number"==typeof i.maxKeys&&(a=i.maxKeys);var f=e.length;a>0&&f>a&&(f=a);for(var h=0;h=0?(l=p.substr(0,g),u=p.substr(g+1)):(l=p,u=""),c=decodeURIComponent(l),d=decodeURIComponent(u),t(o,c)?Array.isArray(o[c])?o[c].push(d):o[c]=[o[c],d]:o[c]=d;}return o};},2361:e=>{var t=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return ""}};e.exports=function(e,r,n,i){return r=r||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?Object.keys(e).map((function(i){var o=encodeURIComponent(t(i))+n;return Array.isArray(e[i])?e[i].map((function(e){return o+encodeURIComponent(t(e))})).join(r):o+encodeURIComponent(t(e[i]))})).join(r):i?encodeURIComponent(t(i))+n+encodeURIComponent(t(e)):""};},7673:(e,t,r)=>{t.decode=t.parse=r(2587),t.encode=t.stringify=r(2361);},1269:e=>{class t{constructor(e={}){if(!(e.maxSize&&e.maxSize>0))throw new TypeError("`maxSize` must be a number greater than 0");this.maxSize=e.maxSize,this.cache=new Map,this.oldCache=new Map,this._size=0;}_set(e,t){this.cache.set(e,t),this._size++,this._size>=this.maxSize&&(this._size=0,this.oldCache=this.cache,this.cache=new Map);}get(e){if(this.cache.has(e))return this.cache.get(e);if(this.oldCache.has(e)){const t=this.oldCache.get(e);return this.oldCache.delete(e),this._set(e,t),t}}set(e,t){return this.cache.has(e)?this.cache.set(e,t):this._set(e,t),this}has(e){return this.cache.has(e)||this.oldCache.has(e)}peek(e){return this.cache.has(e)?this.cache.get(e):this.oldCache.has(e)?this.oldCache.get(e):void 0}delete(e){const t=this.cache.delete(e);return t&&this._size--,this.oldCache.delete(e)||t}clear(){this.cache.clear(),this.oldCache.clear(),this._size=0;}*keys(){for(const[e]of this)yield e;}*values(){for(const[,e]of this)yield e;}*[Symbol.iterator](){for(const e of this.cache)yield e;for(const e of this.oldCache){const[t]=e;this.cache.has(t)||(yield e);}}get size(){let e=0;for(const t of this.oldCache.keys())this.cache.has(t)||e++;return this._size+e}}e.exports=t;},2511:function(e,t,r){var n;e=r.nmd(e),function(i){t&&t.nodeType,e&&e.nodeType;var o="object"==typeof r.g&&r.g;o.global!==o&&o.window!==o&&o.self;var s,a=2147483647,f=36,h=/^xn--/,l=/[^\x20-\x7E]/,u=/[\x2E\u3002\uFF0E\uFF61]/g,c={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},d=Math.floor,p=String.fromCharCode;function g(e){throw RangeError(c[e])}function m(e,t){for(var r=e.length,n=[];r--;)n[r]=t(e[r]);return n}function y(e,t){var r=e.split("@"),n="";return r.length>1&&(n=r[0]+"@",e=r[1]),n+m((e=e.replace(u,".")).split("."),t).join(".")}function b(e){for(var t,r,n=[],i=0,o=e.length;i=55296&&t<=56319&&i65535&&(t+=p((e-=65536)>>>10&1023|55296),e=56320|1023&e),t+p(e)})).join("")}function w(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function _(e,t,r){var n=0;for(e=r?d(e/700):e>>1,e+=d(e/t);e>455;n+=f)e=d(e/35);return d(n+36*e/(e+38))}function B(e){var t,r,n,i,o,s,h,l,u,c,p,m=[],y=e.length,b=0,w=128,B=72;for((r=e.lastIndexOf("-"))<0&&(r=0),n=0;n=128&&g("not-basic"),m.push(e.charCodeAt(n));for(i=r>0?r+1:0;i=y&&g("invalid-input"),((l=(p=e.charCodeAt(i++))-48<10?p-22:p-65<26?p-65:p-97<26?p-97:f)>=f||l>d((a-b)/s))&&g("overflow"),b+=l*s,!(l<(u=h<=B?1:h>=B+26?26:h-B));h+=f)s>d(a/(c=f-u))&&g("overflow"),s*=c;B=_(b-o,t=m.length+1,0==o),d(b/t)>a-w&&g("overflow"),w+=d(b/t),b%=t,m.splice(b++,0,w);}return v(m)}function E(e){var t,r,n,i,o,s,h,l,u,c,m,y,v,B,E,S=[];for(y=(e=b(e)).length,t=128,r=0,o=72,s=0;s=t&&md((a-r)/(v=n+1))&&g("overflow"),r+=(h-t)*v,t=h,s=0;sa&&g("overflow"),m==t){for(l=r,u=f;!(l<(c=u<=o?1:u>=o+26?26:u-o));u+=f)E=l-c,B=f-c,S.push(p(w(c+E%B,0))),l=d(E/B);S.push(p(w(l,0))),o=_(r,v,n==i),r=0,++n;}++r,++t;}return S.join("")}s={version:"1.3.2",ucs2:{decode:b,encode:v},decode:B,encode:E,toASCII:function(e){return y(e,(function(e){return l.test(e)?"xn--"+E(e):e}))},toUnicode:function(e){return y(e,(function(e){return h.test(e)?B(e.slice(4).toLowerCase()):e}))}},void 0===(n=function(){return s}.call(t,r,t,e))||(e.exports=n);}();},8575:(e,t,r)=>{var n=r(2511),i=r(2502);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;}t.parse=v,t.resolve=function(e,t){return v(e,!1,!0).resolve(t)},t.resolveObject=function(e,t){return e?v(e,!1,!0).resolveObject(t):t},t.format=function(e){return i.isString(e)&&(e=v(e)),e instanceof o?e.format():o.prototype.format.call(e)},t.Url=o;var s=/^([a-z0-9.+-]+:)/i,a=/:[0-9]*$/,f=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,h=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),l=["'"].concat(h),u=["%","/","?",";","#"].concat(l),c=["/","?","#"],d=/^[+a-z0-9A-Z_-]{0,63}$/,p=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,g={javascript:!0,"javascript:":!0},m={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},b=r(7673);function v(e,t,r){if(e&&i.isObject(e)&&e instanceof o)return e;var n=new o;return n.parse(e,t,r),n}o.prototype.parse=function(e,t,r){if(!i.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),a=-1!==o&&o127?O+="x":O+=F[T];if(!O.match(d)){var P=I.slice(0,R),N=I.slice(R+1),L=F.match(p);L&&(P.push(L[1]),N.unshift(L[2])),N.length&&(v="/"+N.join(".")+v),this.hostname=P.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),x||(this.hostname=n.toASCII(this.hostname));var z=this.port?":"+this.port:"",D=this.hostname||"";this.host=D+z,this.href+=this.host,x&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==v[0]&&(v="/"+v));}if(!g[B])for(R=0,M=l.length;R0)&&r.host.split("@"))&&(r.auth=x.shift(),r.host=r.hostname=x.shift())),r.search=e.search,r.query=e.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 A=E.slice(-1)[0],C=(r.host||e.host||E.length>1)&&("."===A||".."===A)||""===A,R=0,k=E.length;k>=0;k--)"."===(A=E[k])?E.splice(k,1):".."===A?(E.splice(k,1),R++):R&&(E.splice(k,1),R--);if(!_&&!B)for(;R--;R)E.unshift("..");!_||""===E[0]||E[0]&&"/"===E[0].charAt(0)||E.unshift(""),C&&"/"!==E.join("/").substr(-1)&&E.push("");var x,I=""===E[0]||E[0]&&"/"===E[0].charAt(0);return S&&(r.hostname=r.host=I?"":E.length?E.shift():"",(x=!!(r.host&&r.host.indexOf("@")>0)&&r.host.split("@"))&&(r.auth=x.shift(),r.host=r.hostname=x.shift())),(_=_||r.host&&E.length)&&!I&&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=e.auth||r.auth,r.slashes=r.slashes||e.slashes,r.href=r.format(),r},o.prototype.parseHost=function(){var e=this.host,t=a.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e);};},2502:e=>{e.exports={isString:function(e){return "string"==typeof e},isObject:function(e){return "object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}};},7067:()=>{}},t={};function r(n){var i=t[n];if(void 0!==i)return i.exports;var o=t[n]={id:n,loaded:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.loaded=!0,o.exports}r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]});},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0});},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e);var n=r(5590); + //window.gmodCRAM=n + return n; + }; + + const gmodCRAM = fn(); + + /* + * 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} + */ + + 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?"; + } + this.browser.alert.present(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 + // Ignore + } + } + } + + // 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, offset = 0, length = Infinity, position = 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({fetch, size = 10000000, chunkSize = 32768}) { + + 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(({data, chunkNumber}) => { + 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 + } + } + + class QuickLRU { + constructor(options = {}) { + 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. + */ + + 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)) { + 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$2(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 + + } + } + + 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) + } + } + } + + class Locus { + + constructor({chr, start, end}) { + 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 || '
'; + 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 = '    '; // <- 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 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 baseColor = track.color || 'rgb(0,0,255)'; + + const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? baseColor : getChrColor(refFrame.chr), alpha); + const trackColor = IGVColor.addAlpha(baseColor, 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, {minTLENPercentile, maxTLENPercentile}) { + 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] + + } + + 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.color; + ctx.strokeStyle = this.color; + + const color = this.getColorForFeature(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; // typical 3*viewportWidth + + 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 in pixel coordinates + * @param featureX1 feature end in pixel coordinates + * @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); + let centerX = (t1 + t2) / 2; + + let transform; + if (this.displayMode === "COLLAPSED" && this.labelDisplayMode === "SLANT") { + transform = {rotate: {angle: 45}}; + } + const labelY = getFeatureLabelY(featureY, transform); + + let color = this.getColorForFeature(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; + const lastLabelX = options.rowLastLabelX[feature.row] || -Number.MAX_SAFE_INTEGER; + if (options.labelAllFeatures || xleft > lastLabelX || gtexSelection) { + options.rowLastLabelX[feature.row] = xright; + + if ('y' === options.axis) { + ctx.save(); + IGVGraphics.labelTransformWithContext(ctx, centerX); + IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform); + ctx.restore(); + } else { + IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform); + } + } + + } finally { + ctx.restore(); + } + } + + function getFeatureLabelY(featureY, transform) { + return transform ? featureY + 20 : featureY + 25 + } + + // 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(); + } + } + + const DEFAULT_COLOR$2 = 'rgb(0, 0, 150)'; + + + class FeatureTrack extends TrackBase { + + static defaults = { + type: "annotation", + maxRows: 1000, // protects against pathological feature packing cases (# of rows of overlapping feaures) + displayMode: "EXPANDED", // COLLAPSED | EXPANDED | SQUISHED + margin: 10, + featureHeight: 14, + autoHeight: false, + useScore: false + } + + + constructor(config, browser) { + super(config, browser); + } + + init(config) { + super.init(config); + + + // Obscure option, not common or supoorted, included for backward compatibility + 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); + } + + 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"); + } + } + } + } + + async postInit() { + + if (typeof this.featureSource.getHeader === "function") { + this.header = await this.featureSource.getHeader(); + if (this.disposed) return // This track was removed during async load + } + + // 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 maxFeatureCount = Math.max(1, Math.max(...rowFeatureCount)); + const pixelsPerFeature = pixelWidth / maxFeatureCount; + + 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) { + + const y = clickState.y - this.margin; + const allFeatures = super.clickedFeatures(clickState); + + 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) { + + if (features === undefined) features = this.clickedFeatures(clickState); + 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("

"); + } + + // 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$2(fd.value) && + !fd.value.startsWith("<")) { + const href = infoURL.replace("$$", feature.name); + fd.value = `${fd.value}`; + } + } + + + //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('
'); + data.push({name: "Exon Number", value: exonNumber}); + } + break + } + } + } + } + } + + return data + + } + + menuItemList() { + + const menuItems = []; + + if (this.render === renderSnp) { + menuItems.push('
'); + for (let colorScheme of ["function", "class"]) { + menuItems.push({ + object: $$1(createCheckbox('Color by ' + colorScheme, colorScheme === this.colorBy)), + click: () => { + this.colorBy = colorScheme; + this.trackView.repaintViews(); + } + }); + } + } + + menuItems.push('
'); + for (let displayMode of ["COLLAPSED", "SQUISHED", "EXPANDED"]) { + const lut = + { + "COLLAPSED": "Collapse", + "SQUISHED": "Squish", + "EXPANDED": "Expand" + }; + + menuItems.push( + { + object: $$1(createCheckbox(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 (undefined === features || 0 === features.length) { + return undefined + } + + 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 (!seq) { + seq = "Unknown sequence"; + } else if (f.strand === '-') { + seq = reverseComplementSequence(seq); + } + this.browser.alert.present(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 (!seq) { + seq = "Unknown sequence"; + } else if (f.strand === '-') { + seq = reverseComplementSequence(seq); + } + try { + await navigator.clipboard.writeText(seq); + } catch (e) { + console.error(e); + this.browser.alert.present(`error copying sequence to clipboard ${e}`); + } + } + } + ); + } + list.push('
'); + return list + } else { + return undefined + } + } + + description() { + + // if('snp' === this.type) { + if (renderSnp === this.render) { + let desc = "" + this.name + '
'; + desc += 'Color By Function:
'; + desc += 'Red: Coding-Non-Synonymous, Splice Site
'; + desc += 'Green: Coding-Synonymous
'; + desc += 'Blue: Untranslated
'; + desc += 'Black: Intron, Locus, Unknown

'; + desc += 'Color By Class:
'; + desc += 'Red: Deletion
'; + desc += 'Green: MNP
'; + desc += 'Blue: Microsatellite, Named
'; + desc += 'Black: Indel, Insertion, SNP'; + desc += ""; + return desc + } else { + return super.description() + } + + }; + + /** + * Return color for feature. + * @param feature + * @returns {string} + */ + + 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 + } + + // If no explicit setting use the default + if (!color) { + color = DEFAULT_COLOR$2; // 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 : this.viewLimitMin ? this.viewLimitMin : 0; + const max = this.config.max ? this.config.max : this.viewLimitMax ? this.viewLimitMax : 1000; + 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 + } + + + /** + * 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.updateViews(); // 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); + } + } + + } + + class RegionTableBase { + constructor(config) { + + this.config = config; + + this.browser = config.browser; + + this.columnFormat = config.columnFormat; + + this.tableRowSelectionList = []; + + this.tableDOM = domUtils.div({ class: 'igv-roi-table' }); + // if(config.width) { + // let [ w ] = config.width.split('px') + // w = parseInt(w, 10) + // this.tableDOM.style.width = `${Math.min(w, 1600)}px` + // + // } + + config.parent.appendChild(this.tableDOM); + + this.headerDOM = config; + + this.tableColumnTitles = this.tableDOM; + + this.tableRowContainer = this.tableDOM; + + this.footerDOM = config.gotoButtonHandler; + + } + + set headerDOM({ browser, parent, headerTitle, dismissHandler }) { + + // header + const dom = domUtils.div(); + this.tableDOM.appendChild(dom); + + // header title + const div = domUtils.div(); + dom.appendChild(div); + div.innerHTML = headerTitle; + + // table dismiss button + const dismiss = domUtils.div(); + dom.appendChild(dismiss); + dismiss.appendChild(icons$1.createIcon('times')); + + this.boundDismissHandler = mouseClickHandler.bind(this); + + dismiss.addEventListener('click', this.boundDismissHandler); + + function mouseClickHandler (event) { + event.stopPropagation(); + dismissHandler(); + } + + const { y:y_root } = browser.root.getBoundingClientRect(); + const { y:y_parent } = parent.getBoundingClientRect(); + const constraint = -(y_parent - y_root); + makeDraggable(this.tableDOM, dom, { minX:0, minY:constraint }); + + this.tableDOM.style.display = 'none'; + + this._headerDOM = dom; + + } + + set tableColumnTitles(tableDOM) { + + const tblColumnTitles = domUtils.div({ class: 'igv-roi-table-column-titles' }); + tableDOM.appendChild(tblColumnTitles); + + for (const { label, width } of this.columnFormat) { + const col = domUtils.div(); + tblColumnTitles.appendChild(col); + col.style.width = width; + col.innerText = label; + } + + this._tableColumnTitlesDOM = tblColumnTitles; + + } + + get tableColumnTitles() { + return this._tableColumnTitlesDOM + } + + set tableRowContainer(container) { + + const tblRowContainer = domUtils.div({ class: 'igv-roi-table-row-container' }); + container.appendChild(tblRowContainer); + + this._tableRowContainerDOM = tblRowContainer; + + } + + get tableRowContainer() { + return this._tableRowContainerDOM + } + + set footerDOM(gotoButtonHandler) { + + const dom = domUtils.div(); + this.tableDOM.appendChild(dom); + + // Go To Button + const gotoButton = domUtils.div({class: 'igv-roi-table-button'}); + dom.appendChild(gotoButton); + + gotoButton.id = 'igv-roi-table-view-button'; + gotoButton.textContent = 'Go To'; + gotoButton.style.pointerEvents = 'none'; + + this._footerDOM = dom; + + this.gotoButton = gotoButton; + + this.boundGotoButtonHandler = gotoButtonHandler.bind(this); + + this.gotoButton.addEventListener('click', this.boundGotoButtonHandler); + + } + + tableRowDOMHelper(dom) { + + dom.addEventListener('mousedown', event => { + event.stopPropagation(); + + dom.classList.toggle('igv-roi-table-row-selected'); + dom.classList.contains('igv-roi-table-row-selected') ? dom.classList.remove('igv-roi-table-row-hover') : dom.classList.add('igv-roi-table-row-hover'); + + this.setTableRowSelectionState(dom.classList.contains('igv-roi-table-row-selected')); + }); + + dom.addEventListener('mouseover', e => { + dom.classList.contains('igv-roi-table-row-selected') ? dom.classList.remove('igv-roi-table-row-hover') : dom.classList.add('igv-roi-table-row-hover'); + }); + + dom.addEventListener('mouseout', e => { + dom.classList.remove('igv-roi-table-row-hover'); + }); + + } + + clearTable() { + const elements = this.tableRowContainer.querySelectorAll('.igv-roi-table-row'); + for (let el of elements) { + el.remove(); + } + } + + setTableRowSelectionState(isTableRowSelected) { + isTableRowSelected ? this.tableRowSelectionList.push(1) : this.tableRowSelectionList.pop(); + this.gotoButton.style.pointerEvents = this.tableRowSelectionList.length > 0 ? 'auto' : 'none'; + } + + present() { + this.tableDOM.style.left = `${ 0 }px`; + + const { y:y_root } = this.browser.root.getBoundingClientRect(); + const { y:y_parent } = this.config.parent.getBoundingClientRect(); + + this.tableDOM.style.top = `${ y_root - y_parent }px`; + this.tableDOM.style.display = 'flex'; + } + + dismiss() { + this.tableDOM.style.display = 'none'; + } + + dispose() { + + this.tableDOM.innerHTML = ''; + this.tableDOM.remove(); + + for (const key of Object.keys(this)) { + this[key] = undefined; + } + + document.removeEventListener('click', this.boundDismissHandler); + + } + + } + + class BlatTable extends RegionTableBase { + + constructor(config) { + + const cooked = Object.assign({ 'width':'1024px' }, config); + super(cooked); + + this.descriptionDOM = config; + + } + + set descriptionDOM(config) { + + if (config.description) { + + let dom; + + // BLAT result for query sequence + dom = domUtils.div({ class: 'igv-roi-table-description' }); + this.tableDOM.insertBefore(dom, this.tableColumnTitles); + dom.style.height = 'auto'; + dom.innerHTML = `BLAT result for query sequence:`; + + // CTAATCAtctacactggtttctactg ... + dom = domUtils.div({ class: 'igv-roi-table-description' }); + this.tableDOM.insertBefore(dom, this.tableColumnTitles); + dom.style.height = 'auto'; + dom.style.maxHeight = '128px'; + dom.innerHTML = config.description; + + // Select one or more rows ... + dom = domUtils.div({ class: 'igv-roi-table-goto-explainer' }); + this.tableDOM.insertBefore(dom, this.tableColumnTitles); + dom.innerHTML = `Select one or more rows and click Go To to view the regions`; + + } + + } + + tableRowDOM(record) { + + const dom = domUtils.div({ class: 'igv-roi-table-row' }); + + const pretty = record.map(item => isFinite(item) ? numberFormatter$1(item) : item); + + for (let i = 0; i < pretty.length; i++) { + + const el = domUtils.div(); + dom.appendChild(el); + + const format = this.columnFormat[ i ]; + el.style.width = format.width || 'fit-content'; + el.innerText = pretty[ i ]; + } + + this.tableRowDOMHelper(dom); + + return dom + } + + renderTable(records) { + + Array.from(this.tableRowContainer.querySelectorAll('.igv-roi-table-row')).forEach(el => el.remove()); + + if (records.length > 0) { + + for (let record of records) { + const row = this.tableRowDOM(record); + this.tableRowContainer.appendChild(row); + } + + } + + } + + static getColumnFormatConfiguration() { + + /* + return [ + { label: 'chr', width: '60px' }, + { label: 'start', width: '100px' }, + { label: 'end', width: '100px' }, + { label: 'strand', width: '50px' }, + { label: 'score', width: '50px' }, + { label: 'match', width: '50px' }, + { label: "mis-match", width: '70px' }, + { label: "rep. match", width: '70px' }, + { label: "N's", width: '32px' }, + { label: 'Q gap count', width: '90px' }, + { label: 'Q gap bases', width: '90px' }, + { label: 'T gap count', width: '90px' }, + { label: 'T gap bases', width: '90px' }, + ] + */ + + return [ + { label: 'chr', width: '7%' }, + { label: 'start', width: '12%' }, + { label: 'end', width: '12%' }, + { label: 'strand', width: '5%' }, + { label: 'score', width: '5%' }, + { label: 'match', width: '5%' }, + { label: "mis-match", width: '7%' }, + { label: "rep. match", width: '7%' }, + { label: "N's", width: '3%' }, + { label: 'Q gap count', width: '9%' }, + { label: 'Q gap bases', width: '9%' }, + { label: 'T gap count', width: '9%' }, + { label: 'T gap bases', width: '9%' }, + ] + } + + static gotoButtonHandler (event) { + + event.stopPropagation(); + + const selectedRows = this.tableDOM.querySelectorAll('.igv-roi-table-row-selected'); + + const loci = []; + for (const row of selectedRows) { + + const record = []; + row.querySelectorAll('div').forEach(el => record.push(el.innerText)); + + const [ chr, start, end ] = record; + loci.push(`${ chr }:${ start }-${ end }`); + } + + for (const el of this.tableDOM.querySelectorAll('.igv-roi-table-row')) { + el.classList.remove('igv-roi-table-row-selected'); + } + + this.setTableRowSelectionState(false); + + this.browser.search(loci.join(' ')); + + // console.log(`browser search( ${loci.join(' ')} )`) + + } + + } + + /* + http://genome.ucsc.edu/cgi-bin/hgBlat + ?userSeq=CTAATCAtctacactggtttctactgaaatgtctgttgtcatagacttaattgtgtcttcagatacagcagttctgttatttctgagttttacctggggcaagagaatctttagcaagtttaaaggcacctatatctggaatcacccctccctccagatgaatatcacagactctcccattaaaggtcttgccTTCCTTGATAGCATCATCACTCCA + &type=DNA + &db=hg38 + &output=json + */ + + //const blatServer = "https://genome.ucsc.edu/cgi-bin/hgBlat" + const defaultBlatServer = "https://igv.org/services/blatUCSC.php"; + //const blatServer = "http://localhost:8000/blatUCSC.php" + + + async function blat({url, userSeq, db}) { + + url = url || defaultBlatServer; + + const results = await postData(url, userSeq, db); + + results.fields; + + const features = results.blat.map(decodePSL); + + return features + } + + async function postData(url = "", userSeq, db) { + + const data = new URLSearchParams(); + data.append("userSeq", userSeq); + data.append("db", db); + + const response = await fetch(url, { method: "post", body: data }); + return response.json(); // parses JSON response into native JavaScript objects + } + + const maxSequenceSize = 25000; + + class BlatTrack extends FeatureTrack { + + constructor(config, browser) { + super(config, browser); + if (!this.name) { + this.name = 'Blat Results'; + } + this.sequence = config.sequence; + this.table = undefined; + } + + openTableView() { + + if (undefined === this.table) { + + const rows = this.config.features.map(f => [ + f.chr, + (f.start + 1), + f.end, + f.strand, + f.score, + f.matches, + f.misMatches, + f.repMatches, + f.nCount, + f.qNumInsert, + f.qBaseInsert, + f.tNumInsert, + f.tBaseInsert + ]); + + const config = + { + browser: this.browser, + parent: this.browser.parent, + headerTitle: this.config.title, + description: this.sequence, + dismissHandler: () => { + this.table.dismiss(); + this.table.dispose(); + this.table = undefined; + }, + columnFormat: BlatTable.getColumnFormatConfiguration(), + gotoButtonHandler: BlatTable.gotoButtonHandler + }; + + this.table = new BlatTable(config); + this.table.renderTable(rows); + } + + this.table.present(); + + } + + menuItemList() { + + const menuItems = super.menuItemList(); + + menuItems.push('
'); + menuItems.push({ + label: 'Open table view', + click: () => this.openTableView() + }); + return menuItems + } + + + /** + * Track has been permanently removed. Release resources and other cleanup + */ + dispose() { + super.dispose(); + // Release DOM element for table + if (this.table) { + this.table.popover.parentElement.removeChild(this.table.popover); + + } + + + } + } + + + async function createBlatTrack({sequence, browser, name, title}) { + + if (sequence.length > maxSequenceSize) { + browser.alert.present(`Sequence size exceeds maximum allowed length (${sequence.length} > ${maxSequenceSize})`); + return + } + + const db = browser.genome.id; // TODO -- blat specific property + + const url = browser.config["blatServerURL"]; + + try { + + const features = await blat({url, userSeq: sequence, db}); + const trackConfig = { + type: 'blat', + name: name || 'blat results', + title: title || 'blat results', + sequence: sequence, + altColor: 'rgb(176, 176, 236)', + color: 'rgb(236, 176, 176)', + features: features + }; + + const track = await browser.loadTrack(trackConfig); + track.openTableView(); + + } catch (e) { + browser.alert.present(`Error performing blat search: ${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. + */ + + const alignmentStartGap = 5; + const downsampleRowHeight = 5; + const DEFAULT_ALIGNMENT_COLOR = "rgb(185, 185, 185)"; + const DEFAULT_COVERAGE_COLOR = "rgb(150, 150, 150)"; + const DEFAULT_CONNECTOR_COLOR = "rgb(200, 200, 200)"; + const MINIMUM_BLAT_LENGTH = 20; + + class BAMTrack extends TrackBase { + + static defaults = { + alleleFreqThreshold: 0.2, + visibilityWindow: 30000, + showCoverage: true, + showAlignments: true, + viewAsPairs: false, + pairsSupported: true, + showSoftClips: false, + showAllBases: false, + showInsertions: true, + showMismatches: true, + height: 300, + coverageTrackHeight: 50 + } + + constructor(config, browser) { + super(config, browser); + } + + init(config) { + + this.type = "alignment"; + this.featureSource = new BamSource(config, this.browser); + this.coverageTrack = new CoverageTrack(config, this); + this.alignmentTrack = new AlignmentTrack(config, this); + + super.init(config); + + this.alignmentTrack.setTop(this.coverageTrack, this.showCoverage); + + if(!this.showAlignments) { + this._height = this.coverageTrackHeight; + } + + // 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); + } + } + + } + + set height(h) { + this._height = h; + if (this.showAlignments) { + this.alignmentTrack.height = this.showCoverage ? h - this.coverageTrackHeight : 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) { + alignmentContainer.sortRows(options); + 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) { + alignmentContainer.sortRows(sort); + } + } + + 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.coverageTrackHeight : 0) + + (this.showAlignments ? this.alignmentTrack.computePixelHeight(alignmentContainer) : 0) + } + + draw(options) { + + IGVGraphics.fillRect(options.context, 0, options.pixelTop, options.pixelWidth, options.pixelHeight, {'fillStyle': "rgb(255, 255, 255)"}); + + if (true === this.showCoverage && this.coverageTrackHeight > 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.coverageTrackHeight); + + // if (this.browser.isMultiLocusMode()) { + // ctx.clearRect(0, 0, pixelWidth, pixelHeight); + // } else { + // this.coverageTrack.paintAxis(ctx, pixelWidth, this.coverageTrackHeight); + // } + } + + contextMenuItemList(config) { + return this.alignmentTrack.contextMenuItemList(config) + } + + popupData(clickState) { + if (true === this.showCoverage && clickState.y >= this.coverageTrack.top && clickState.y < this.coverageTrackHeight) { + 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) { + + let clickedObject; + if (true === this.showCoverage && clickState.y >= this.coverageTrack.top && clickState.y < this.coverageTrackHeight) { + clickedObject = this.coverageTrack.getClickedObject(clickState); + } else { + clickedObject = this.alignmentTrack.getClickedObject(clickState); + } + return clickedObject ? [clickedObject] : undefined + } + + hoverText(clickState) { + if (true === this.showCoverage && clickState.y >= this.coverageTrack.top && clickState.y < this.coverageTrackHeight) { + const clickedObject = this.coverageTrack.getClickedObject(clickState); + if (clickedObject) { + return clickedObject.hoverText() + } + } + + } + + menuItemList() { + + + // Start with overage track items + let menuItems = []; + + menuItems = menuItems.concat(MenuUtils.numericDataMenuItems(this.trackView)); + + // Color by items + menuItems.push('
'); + const $e = $$1('
'); + $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 = + (this.showCoverage ? this.coverageTrackHeight : 0) + + (this.showAlignments ? this.alignmentTrack.height : 0); + this.trackView.setTrackHeight(h); + } + }; + + menuItems.push('
'); + menuItems.push({ + object: $$1(createCheckbox("Show Coverage", this.showCoverage)), + click: () => { + this.showCoverage = !this.showCoverage; + adjustTrackHeight(); + this.trackView.checkContentHeight(); + this.trackView.repaintViews(); + } + }); + menuItems.push({ + object: $$1(createCheckbox("Show Alignments", this.showAlignments)), + click: () => { + this.showAlignments = !this.showAlignments; + adjustTrackHeight(); + this.trackView.checkContentHeight(); + this.trackView.repaintViews(); + } + }); + + // Show all bases + menuItems.push('
'); + menuItems.push({ + object: $$1(createCheckbox("Show all bases", this.showAllBases)), + click: () => { + this.showAllBases = !this.showAllBases; + this.config.showAllBases = this.showAllBases; + this.trackView.repaintViews(); + } + }); + + // Show mismatches + menuItems.push('
'); + menuItems.push({ + object: $$1(createCheckbox("Show mismatches", this.showMismatches)), + click: () => { + this.showMismatches = !this.showMismatches; + this.config.showMismatches = this.showMismatches; + this.trackView.repaintViews(); + } + }); + + // Insertions + menuItems.push({ + object: $$1(createCheckbox("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("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('
'); + menuItems.push({ + object: $$1(createCheckbox("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('
'); + 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('
'); + const $dml = $$1('
'); + $dml.text('Display mode:'); + menuItems.push({name: undefined, object: $dml, click: undefined, init: undefined}); + + menuItems.push({ + object: $$1(createCheckbox("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("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(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} + } + + /** + * 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); + } + + 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); + } + } + + + class CoverageTrack { + + constructor(config, parent) { + this.parent = parent; + this.featureSource = parent.featureSource; + + 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 + }; + } + + } + + get height() { + return this.parent.coverageTrackHeight + } + + 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 && typeof this.parent.color !== "function") { + 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('
'); + 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.insertionTextColor = config.insertionTextColor || "white"; + this.showInsertionText = config.showInsertionText === undefined ? false : !!config.showInsertionText; + this.deletionColor = config.deletionColor || "black"; + this.deletionTextColor = config.deletionTextColor || "black"; + this.showDeletionText = config.showDeletionText === undefined ? false : !!config.showDeletionText; + 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}); + + // Save bases to draw into an array for later drawing, so they can be overlaid on insertion blocks, + // which is relevant if we have insertions with size label + const basesToDraw = []; + + 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 returns bases to draw, which are drawn on top of insertion blocks (if they're wider than + // the space between two bases) like in Java IGV + basesToDraw.push(...drawBlock.call(this, block, b)); + + if ((block.start + block.len) > bpEnd) { + // Do this after drawBlock to insure gaps are drawn + break + } + } + + 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 lineWidth = ePixel - sPixel; + const gapLenText = gap.len.toString(); + const gapTextWidth = gapLenText.length * 6; + const gapCenter = sPixel + (lineWidth / 2); + + const color = ("D" === gap.type) ? this.deletionColor : this.skippedColor; + + IGVGraphics.strokeLine(ctx, sPixel, yStrokedLine, ePixel, yStrokedLine, { + strokeStyle: color, + lineWidth: 2, + }); + + // Add gap width as text like Java IGV if it fits nicely and is a multi-base gap + if (this.showDeletionText && gap.len > 1 && lineWidth >= gapTextWidth + 8) { + const textStart = gapCenter - (gapTextWidth / 2); + IGVGraphics.fillRect(ctx, textStart - 1, yRect - 1, gapTextWidth + 2, 12, {fillStyle: "white"}); + IGVGraphics.fillText(ctx, gapLenText, textStart, yRect + 10, { + 'font': 'normal 10px monospace', + 'fillStyle': this.deletionTextColor, + }); + } + } + } + + 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 insertLenText = insertionBlock.len.toString(); + + const textPixelWidth = 2 + (insertLenText.length * 6); + const basePixelWidth = (!this.showInsertionText || insertionBlock.len === 1) + ? 2 + : Math.round(insertionBlock.len / bpPerPixel); + const widthBlock = Math.max(Math.min(textPixelWidth, basePixelWidth), 2); + + const xBlockStart = (refOffset / bpPerPixel) - (widthBlock / 2); + if ((xBlockStart - lastXBlockStart) > 2) { + const props = {fillStyle: this.insertionColor}; + + // Draw decorations like Java IGV to make an 'I' shape + IGVGraphics.fillRect(ctx, xBlockStart - 2, yRect, widthBlock + 4, 2, props); + IGVGraphics.fillRect(ctx, xBlockStart, yRect + 2, widthBlock, alignmentHeight - 4, props); + IGVGraphics.fillRect(ctx, xBlockStart - 2, yRect + alignmentHeight - 2, widthBlock + 4, 2, props); + + // Show # of inserted bases as text if it's a multi-base insertion and the insertion block + // is wide enough to hold text (its size is capped at the text label size, but can be smaller + // if the browser is zoomed out and the insertion is small) + if (this.showInsertionText && insertionBlock.len > 1 && basePixelWidth > textPixelWidth) { + IGVGraphics.fillText(ctx, insertLenText, xBlockStart + 1, yRect + 10, { + 'font': 'normal 10px monospace', + 'fillStyle': this.insertionTextColor, + }); + } + lastXBlockStart = xBlockStart; + } + } + } + + basesToDraw.forEach(({bbox, baseColor, readChar}) => { + renderBlockOrReadChar(ctx, bpPerPixel, bbox, baseColor, readChar); + }); + + + function drawBlock(block, b) { + // Collect bases to draw for later rendering + const blockBasesToDraw = []; + + 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 + + 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; + const widthPixel = Math.max(1, 1 / bpPerPixel); + + + for (let i = 0, len = block.len; i < len; i++) { + + const xPixel = ((block.start + i) - bpStart) / bpPerPixel; + + if (xPixel + widthPixel < 0) continue // Off left edge + if (xPixel > pixelWidth) break // Off right edge + + let readChar = seq ? seq.charAt(seqOffset + i) : ''; + const refChar = offsetBP + i >= 0 ? 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) { + blockBasesToDraw.push({ + bbox: { + x: xPixel, + y: yRect, + width: widthPixel, + height: alignmentHeight + }, + baseColor, + readChar, + }); + } + } + } + } + + return blockBasesToDraw + } + + 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; + viewport.cachedFeatures.sortRows(newSortObject); + viewport.repaint(); + }; + list.push('Sort by...'); + list.push({label: '  base', click: () => sortByOption("BASE")}); + list.push({label: '  read strand', click: () => sortByOption("STRAND")}); + list.push({label: '  insert size', click: () => sortByOption("INSERT_SIZE")}); + list.push({label: '  gap size', click: () => sortByOption("GAP_SIZE")}); + list.push({label: '  chromosome of mate', click: () => sortByOption("MATE_CHR")}); + list.push({label: '  mapping quality', click: () => sortByOption("MQ")}); + list.push({label: '  read name', click: () => sortByOption("READ_NAME")}); + list.push({label: '  aligned read length', click: () => sortByOption("ALIGNED_READ_LENGTH")}); + list.push({label: '  soft clipped reads', click: () => sortByOption("SOFT_CLIPPED")}); + list.push({ + label: '  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; + viewport.cachedFeatures.sortRows(newSortObject); + viewport.repaint(); + } + } + }; + this.browser.inputDialog.present(config, clickState.event); + } + }); + list.push('
'); + + 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 { + this.browser.alert.present(`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) { + this.browser.alert.present("Read sequence: *"); + } else { + this.browser.alert.present(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); + this.browser.alert.present(`error copying sequence to clipboard ${e}`); + } + + } + }); + } + + // TODO if genome supports blat + const seqstring = clickedAlignment.seq; + if (seqstring && "*" != seqstring) { + + if (seqstring.length < maxSequenceSize) { + list.push({ + label: 'BLAT read sequence', + click: () => { + const sequence = clickedAlignment.isNegativeStrand() ? reverseComplementSequence(seqstring) : seqstring; + const name = `${clickedAlignment.readName} - blat`; + const title = `${this.parent.name} - ${name}`; + createBlatTrack({sequence, browser: this.browser, name, title}); + } + }); + } + + const softClips = clickedAlignment.softClippedBlocks(); + if (softClips.left && softClips.left.len > MINIMUM_BLAT_LENGTH && softClips.left.len < maxSequenceSize) { + list.push({ + label: 'BLAT left soft-clipped sequence', + click: () => { + const clippedSequence = seqstring.substr(softClips.left.seqOffset, softClips.left.len); + const sequence = clickedAlignment.isNegativeStrand() ? reverseComplementSequence(clippedSequence) : clippedSequence; + const name = `${clickedAlignment.readName} - blat left clip`; + const title = `${this.parent.name} - ${name}`; + createBlatTrack({sequence, browser: this.browser, name, title}); + } + }); + } + if (softClips.right && softClips.right.len > MINIMUM_BLAT_LENGTH && softClips.right.len < maxSequenceSize) { + list.push({ + label: 'BLAT right soft-clipped sequence', + click: () => { + const clippedSequence = seqstring.substr(softClips.right.seqOffset, softClips.right.len); + const sequence = clickedAlignment.isNegativeStrand() ? reverseComplementSequence(clippedSequence) : clippedSequence; + const name = `${clickedAlignment.readName} - blat right clip`; + const title = `${this.parent.name} - ${name}`; + createBlatTrack({sequence, browser: this.browser, name, title}); + } + }); + } + } + + list.push('
'); + } + } + + // 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('
'); + } + + 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": + if (this.parent.color) { + return (typeof this.parent.color === "function") ? this.parent.color(alignment) : this.parent.color + } else { + return DEFAULT_CONNECTOR_COLOR + } + default: + return this.getAlignmentColor(alignment) + + } + } + + getAlignmentColor(alignment) { + + let color = DEFAULT_ALIGNMENT_COLOR; // The default color if nothing else applies + if (this.parent.color) { + color = (typeof this.parent.color === "function") ? this.parent.color(alignment) : this.parent.color; + } else { + color = DEFAULT_ALIGNMENT_COLOR; + } + 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 + } + + return color + + } + } + + 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); + } + + get contentDiv() { + return this.$viewport.get(0) + } + + initializationHelper() { + + this.$multiLocusCloseButton = $$1('
', {class: 'igv-multi-locus-close-button'}); + this.$viewport.append(this.$multiLocusCloseButton); + this.$multiLocusCloseButton.get(0).appendChild(icons$1.createIcon("times-circle")); + + this.$multiLocusCloseButton.click(() => { + this.browser.removeMultiLocusPanel(this.referenceFrame); + }); + + this.$rulerLabel = $$1('
', {class: 'igv-multi-locus-ruler-label'}); + this.$viewport.append(this.$rulerLabel); + + let div; + div = document.createElement('div'); + this.$rulerLabel.append($$1(div)); + + this.$rulerLabel.get(0).addEventListener('click', async event => { + event.stopPropagation(); + await this.browser.gotoMultilocusPanel(this.referenceFrame); + }); + + this.$tooltip = $$1('
', {class: 'igv-ruler-tooltip'}); + this.$tooltip.height(this.$viewport.height()); + + this.$viewport.append(this.$tooltip); + + this.$tooltipContent = $$1('
'); + this.$tooltip.append(this.$tooltipContent); + + this.rulerSweeper = new RulerSweeper(this, this.$viewport.get(0).parentElement, this.browser, this.referenceFrame); + + this.attachMouseHandlers(GenomeUtils.isWholeGenomeView(this.referenceFrame.chr)); + + this.$tooltip.hide(); + + this.dismissLocusLabel(); + } + + presentLocusLabel(viewportWidth) { + + this.$multiLocusCloseButton.show(); + + this.$rulerLabel.show(); + this.$rulerLabel.get(0).style.backgroundColor = getChrColor(this.referenceFrame.chr); + + const textDiv = this.$rulerLabel.get(0).querySelector('div'); + + const { width } = this.$rulerLabel.get(0).getBoundingClientRect(); + + textDiv.innerHTML = `${ this.referenceFrame.getMultiLocusLabel(viewportWidth) }`; + const { width:textDivWidth } = textDiv.getBoundingClientRect(); + + if (textDivWidth/width > 0.5) { + textDiv.innerHTML = `${ this.referenceFrame.getMultiLocusLabelBPLengthOnly(viewportWidth) }`; + } + + //console.log(`${ Date.now() } textDiv ${ StringUtils.numberFormatter(Math.floor(textDivWidth)) }`) + + } + + // Use in conjuction with .igv-multi-locus-ruler-label-square-dot css class (_dom-misc.scss) + 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); + + if (true === isWholeGenomeView) { + + const index = this.browser.referenceFrameList.indexOf(this.referenceFrame); + + const click = `click${this.namespace}`; + this.$viewport.on(click, (e) => { + + const {x: pixel} = domUtils.translateMouseCoordinates(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(({locusSearchString}) => 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} = domUtils.translateMouseCoordinates(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($(this.canvas)) + this.$viewport.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} = domUtils.translateMouseCoordinates(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({referenceFrame}) { + + 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 { + const viewportObject = new TrackViewport(trackView, column, referenceFrame, width); + + // if ('sequence' === trackView.track.type) { + // viewportObject.$viewport.get(0).style.marginTop = '0px' + // } + + return viewportObject + } + } + + 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 = domUtils.guid(); + this.trackView = trackView; + + this.browser = trackView.browser; + + this.viewport = domUtils.div({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({context, samples}) { + + 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, {deltaX, deltaY}) { + + 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_${domUtils.guid()}`; + + 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, {textAlign, textBaseline, strokeStyle, fillStyle}, sampleHeight) { + const pixels = Math.min(sampleHeight, maxFontSize); + ctx.font = `${pixels}px sans-serif`; + ctx.textAlign = textAlign; + ctx.textBaseline = textBaseline; + ctx.fillStyle = fillStyle; + } + + const MenuPopup = function (parent) { + + this.popover = domUtils.div({class: 'igv-menu-popup'}); + parent.appendChild(this.popover); + + const header = domUtils.div({class: 'igv-menu-popup-header'}); + this.popover.appendChild(header); + + uiUtils.attachDialogCloseHandlerWithParent(header, () => this.hide()); + + this.popoverContent = domUtils.div(); + this.popover.appendChild(this.popoverContent); + + makeDraggable(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' && '
' === item) { + el = document.createElement('hr'); + } else if (typeof item === 'string') { + el = domUtils.div({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("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 = domUtils.div({class: 'context-menu'}); + if (typeof item.label === 'string') { + el.innerHTML = item.label; + } + const clickHandler = e => { + colorPicker.show(); + domUtils.hide(popover); + e.preventDefault(); + e.stopPropagation(); + }; + el.addEventListener('click', clickHandler); + el.addEventListener('touchend', clickHandler); + el.addEventListener('mouseup', function (e) { + e.preventDefault(); + e.stopPropagation(); + }); + } else { + el = domUtils.div({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(); + domUtils.hide(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} = domUtils.translateMouseCoordinates(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 igv_axis_column_width = 50; + const scrollbarExclusionTypes = new Set(['ruler', '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 = domUtils.div(); + 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, {deltaX, deltaY}) { + + 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$2(color)) { + trackColors.push(color); + } + if (this.track.altColor && isString$2(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 = this.maxViewportContentHeight(); + 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 = this.maxViewportContentHeight(); + 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} + */ + 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 + } + + // Filter zoomed out views. This has the side effect or turning off or no the zoomed out notice + const viewportsToRepaint = visibleViewports.filter(vp => vp.needsRepaint()).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(visibleViewport.getWidth()); + if (visibleViewport.featureCache && visibleViewport.featureCache.features) { + // If the "features" object has a getMax function use it. Currently only alignmentContainer implements this, for coverage. + if (typeof visibleViewport.featureCache.features.getMax === 'function') { + const max = visibleViewport.featureCache.features.getMax(start, end); + allFeatures.push({value: max}); + } else { + const viewFeatures = FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end); + for (let f of viewFeatures) { + allFeatures.push(f); + } + } + } + } + if (typeof this.track.doAutoscale === 'function') { + this.track.dataRange = this.track.doAutoscale(allFeatures); + } else { + this.track.dataRange = doAutoscale(allFeatures); + } + } + + const refreshView = (this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler'); + for (let vp of visibleViewports) { + if (viewportsToRepaint.includes(vp)) { + vp.repaint(); + } else if (refreshView) { + vp.refresh(); + } + } + + this.adjustTrackHeight(); + + // Repaint sample names last + this.repaintSamples(); + + this.updateRulerViewportLabels(); + } + + clearCachedFeatures() { + for (let viewport of this.viewports) { + viewport.clearCache(); + } + } + + 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 = []; + const visibleViewports = this.viewports.filter(viewport => viewport.isVisible()); + for (let vp of visibleViewports) { + + const referenceFrame = vp.referenceFrame; + const {chr, start, bpPerPixel} = vp.referenceFrame; + const end = start + referenceFrame.toBP(vp.getWidth()); + const needsReload = !vp.featureCache || !vp.featureCache.containsRange(chr, start, end, bpPerPixel); + + if (needsReload) { + await vp.loadFeatures(); + } + if (vp.featureCache && vp.featureCache.features) { + + if (typeof vp.featureCache.features.getMax === 'function') { + const max = vp.featureCache.features.getMax(start, end); + allFeatures.push({value: max}); + } else { + const vpFeatures = typeof vp.featureCache.queryFeatures === 'function' ? + vp.featureCache.queryFeatures(chr, start, end) : + FeatureUtils.findOverlapping(vp.featureCache.features, start, end); + allFeatures = allFeatures.concat(vpFeatures); + } + } + } + return allFeatures + } + + checkContentHeight() { + + for (let viewport of this.viewports) { + viewport.checkContentHeight(); + } + this.adjustTrackHeight(); + + } + + adjustTrackHeight() { + + var contentHeight = this.maxViewportContentHeight(); + if (this.track.autoHeight) { + this.setTrackHeight(contentHeight, false); + } else if (this.track.paintAxis) { // Avoid duplication, paintAxis is already called in setTrackHeight + this.paintAxis(); + } + + if (false === scrollbarExclusionTypes.has(this.track.type)) { + + // Adjust scrollbar, if needed, to insure content is in view + const currentTop = this.viewports[0].getContentTop(); + const viewportHeight = this.viewports[0].$viewport.height(); + const minTop = Math.min(0, viewportHeight - contentHeight); + if(currentTop < minTop) { + for (let viewport of this.viewports) { + viewport.setTop(minTop); + } + } + this.updateScrollbar(); + } + } + + createTrackScrollbar(browser) { + + const outerScroll = domUtils.div(); + 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 = domUtils.div(); + 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 = domUtils.div({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 = domUtils.div(); + 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 = domUtils.div(); + this.gearContainer.appendChild(this.gear); + this.gear.appendChild(icons$1.createIcon('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} = domUtils.pageCoordinates(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} = domUtils.pageCoordinates(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) { + + 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(); + } + + 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); + } + } + + maxViewportContentHeight() { + return Math.max(this.viewports.map(viewport => viewport.getContentHeight())) + } + + } + + 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_${domUtils.guid()}`; + + context.saveWithTranslationAndClipRect(id, deltaX, y + deltaY, width, height, 0); + + track.paintAxis(context, width, height); + + context.restore(); + } + + } + + /* + * 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$1 = 'rgb(150, 150, 150)'; + + class WigTrack extends TrackBase { + + static defaults = { + height: 50, + flipAxis: false, + logScale: false, + windowFunction: 'mean', + graphType: 'bar', + autoscale: true, + normalize: undefined, + scaleFactor: undefined + } + + constructor(config, browser) { + super(config, browser); + } + + init(config) { + + super.init(config); + + this.type = "wig"; + this.featureType = 'numeric'; + this.paintAxis = paintAxis; + + const format = config.format ? config.format.toLowerCase() : config.format; + if (config.featureSource) { + this.featureSource = config.featureSource; + delete config.featureSource; + } else 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); + } + + + // Override autoscale default + if(config.max === undefined || config.autoscale === true) { + this.autoscale = true; + } else { + this.dataRange = { + min: config.min || 0, + max: config.max + }; + } + } + + async postInit() { + const header = await this.getHeader(); + if (this.disposed) return // This track was removed during async load + if (header) this.setTrackProperties(header); + } + + async getFeatures(chr, start, end, bpPerPixel) { + const features = await this.featureSource.getFeatures({ + chr, + start, + end, + bpPerPixel, + visibilityWindow: this.visibilityWindow, + 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('
'); + 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; + const posColor = this.color || DEFAULT_COLOR$1; + + 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) { + + let lastPixelEnd = -1; + let lastY; + 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); + + const color = this.getColorForFeature(f); + + 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 if (this.graphType === "line") { + if (lastY != undefined) { + IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, { + "fillStyle": color, + "strokeStyle": color + }); + } + IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color}); + } else { + const height = y - y0; + IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color}); + + } + lastPixelEnd = x + width; + lastY = y; + } + + // 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) { + + if (features === undefined) features = this.clickedFeatures(clickState); + + 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('
'); + } + 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:     ", + value: numberFormatter$1(selectedFeature.value) + }); + } + } + if (displayFeatures.length < features.length) { + popupData.push("
..."); + } + + return popupData + + } else { + return [] + } + } + + get supportsWholeGenome() { + return !this.config.indexURL && this.config.supportsWholeGenome !== false + } + + /** + * Return color for feature. + * @param feature + * @returns {string} + */ + + getColorForFeature(f) { + let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$1; + return (typeof c === "function") ? c(f.value) : c + } + + /** + * Called when the track is removed. Do any needed cleanup here + */ + dispose() { + this.trackView = undefined; + } + + } + + /** + * + * @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(); + if(this.disposed) return; // This track was removed during async load + } + // 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('
'); + menuItems.push("DisplayMode:"); + + const displayOptions = this.type === 'seg' ? ["SQUISHED", "EXPANDED", "FILL"] : ["SQUISHED", "EXPANDED"]; + + for (let displayMode of displayOptions) { + + const checkBox = createCheckbox(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({context, renderSVG, pixelTop, pixelWidth, pixelHeight, features, bpPerPixel, bpStart}) { + + IGVGraphics.fillRect(context, 0, pixelTop, 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) { + + const allFeatures = super.clickedFeatures(clickState); + const y = clickState.y; + return allFeatures.filter(function (feature) { + const rect = feature.pixelRect; + return rect && y >= rect.y && y <= (rect.y + rect.h) + }) + + } + + hoverText(clickState) { + const features = this.clickedFeatures(clickState); + if(features && features.length > 0) { + return `${features[0].sample}: ${features[0].value}` + } + } + + popupData(clickState, featureList) { + + if(featureList === undefined) featureList = this.clickedFeatures(clickState); + + const items = []; + + for (let feature of featureList) { + + // Double line divider between features + if (items.length > 0) { + items.push('
'); + items.push('
'); + } + + // 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; + this.graphType = config.graphType; + } + + 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 + } + + /** + * Returns a MergedFeatureCollection containing an array of features for the specified range, 1 for each track. + */ + async getFeatures(chr, bpStart, bpEnd, bpPerPixel) { + + const promises = this.tracks.map((t) => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel)); + const featureArrays = await Promise.all(promises); + return new MergedFeatureCollection(featureArrays) + } + + draw(options) { + + const mergedFeatures = options.features; // A MergedFeatureCollection + + for (let i = 0, len = this.tracks.length; i < len; i++) { + const trackOptions = Object.assign({}, options); + trackOptions.features = mergedFeatures.featureArrays[i]; + this.tracks[i].dataRange = this.dataRange; + this.tracks[i].flipAxis = this.flipAxis; + this.tracks[i].logScale = this.logScale; + if (this.graphType) { + this.tracks[i].graphType = this.graphType; + } + this.tracks[i].draw(trackOptions); + } + } + + popupData(clickState) { + + if(clickState.viewport && clickState.viewport.cachedFeatures) { + + const featuresArray = clickState.viewport.cachedFeatures.featureArrays; + + 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('
'); + popupData.push(`
${this.tracks[i].name}
`); + const trackPopupData = this.tracks[i].popupData(clickState, featuresArray[i]); + popupData.push(...trackPopupData); + + } + return popupData + } + } + } + + clickedFeatures(clickState) { + + + // 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 mergedFeaturesCollection = clickState.viewport.cachedFeatures; + + if(!mergedFeaturesCollection) { + return []; + } + + const genomicLocation = clickState.genomicLocation; + const clickedFeatures = []; + for(let features of mergedFeaturesCollection.featureArrays) { + // 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; + const tmp = (FeatureUtils.findOverlapping(features, ss, ee)); + for(let f of tmp) { + clickedFeatures.push(f); + } + } + return clickedFeatures; + } + + + get supportsWholeGenome() { + return this.tracks.every(track => track.supportsWholeGenome) + } + } + + + class MergedFeatureCollection { + + constructor(featureArrays) { + this.featureArrays = featureArrays; + } + + getMax(start, end) { + let max = -Number.MAX_VALUE; + for (let a of this.featureArrays) { + for (let f of a) { + if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) { + if (f.end < start) { + continue + } + if (f.start > end) { + break + } + max = Math.max(max, f.value); + } + } + } + return 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 + } + } + + const DEFAULT_ARC_COLOR = "rgb(180,25,137)"; + + class InteractionTrack extends TrackBase { + + static defaults = { + height: 250, + theta: Math.PI / 4, + arcOrientation: true, + showBlocks: true, + blockHeight: 3, + thickness: 1, + alpha: 0.02, + logScale: true, + } + + constructor(config, browser) { + super(config, browser); + } + + init(config) { + + super.init(config); + + this.sinTheta = Math.sin(this.theta); + this.cosTheta = Math.cos(this.theta); + this.arcType = getArcType(config); // nested | proportional | inView | partialInView + 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"; + } + + 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 + if (config.featureSource) { + this.featureSource = config.featureSource; + delete config._featureSource; + } else { + 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(); + if (this.disposed) return; // This track was removed during async load + } + + // 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; + if (typeof this.color === 'function') { + color = this.color(feature); + } else { + color = this.color || feature.color || DEFAULT_ARC_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 (pixelStart >= 0 && pixelEnd <= 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 = []; + + if (this.hasValue) { + items.push("
"); + const lut = + { + "nested": "Nested", + "proportional": "Proportional - All", + "inView": "Proportional - Both Ends in View", + "partialInView": "Proportional - One End in View" + }; + items.push("Arc Type"); + for (let arcType of ["nested", "proportional", "inView", "partialInView"]) { + items.push( + { + object: $$1(createCheckbox(lut[arcType], arcType === this.arcType)), + click: () => { + this.arcType = arcType; + this.trackView.repaintViews(); + } + }); + } + } + items.push("
"); + + 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('
'); + 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('
'); + 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) { + + if (features === undefined) 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; + const stdColumns = this.header.hiccups ? 6 : 10; + for (let i = stdColumns; i < columnNames.length; i++) { + if (this.header.colorColumn === i) continue; + if (columnNames[i] === 'info') { + extractInfoColumn(data, f.extras[i - stdColumns]); + } else { + data.push({name: columnNames[i], value: f.extras[i - stdColumns]}); + } + } + } + // For now just return the top hit + break + + //if (data.length > 0) { + // data.push("
"); + // } + } + return data + } + + clickedFeatures(clickState) { + + // 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 = 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) + } + } + + 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 Math.min(Math.PI/2, (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.max(0, 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]}); + } + } + + } + + /* + * 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 = isString$2; + + const DEFAULT_COLOR = "rgb(0,0,150)"; + 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 { + + static defaults = { + displayMode: "EXPANDED", + sortDirection: "ASC", + showGenotypes: true, + squishedVariantHeight: 2, + squishedCallHeight: 1, + expandedCallHeight: 10, + expandedVGap: 2, + squishedVGap: 1, + expandedGroupGap: 10, + squishedGroupGap: 5, + featureHeight: 14, + noGenotypeColor: "rgb(200,180,180)", + noCallColor: "rgb(225, 225, 225)", + nonRefColor: "rgb(200, 200, 215)", + mixedColor: "rgb(200, 220, 200)", + homrefColor: "rgb(200, 200, 200)", + homvarColor: "rgb(17,248,254)", + hetvarColor: "rgb(34,12,253)", + colorBy: undefined, + visibilityWindow: undefined, + labelDisplayMode: undefined, + type: "variant" + } + + constructor(config, browser) { + super(config, browser); + } + + init(config) { + + super.init(config); + + this.expandedVariantHeight = config.expandedVariantHeight || config.variantHeight || 10; + + this.featureSource = FeatureSource(config, this.browser.genome); + + this._initColorBy = config.colorBy; + if (config.colorTable) { + this.colorTables = new Map(); + this.colorTables.set(config.colorBy, new ColorTable(config.colorTable)); + } + this._color = config.color; + this._strokecolor = config.strokecolor; + this._context_hook = config.context_hook; + + + // 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 (this.disposed) return // This track was removed during async load + 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 || DEFAULT_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({context, pixelWidth, pixelHeight, bpPerPixel, bpStart, pixelTop, features}) { + + 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); + + let callSets = this.callSets; + if (!callSets && this._f) { + callSets = this._f.callSets; // "Complementary" variant for structural variants + } + 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.getColorForFeature(variant); + context.fillRect(x, y, w, h); + + //only paint stroke if a color is defined + let strokecolor = this.getVariantStrokecolor(variant); + if (strokecolor) { + context.strokeStyle = strokecolor; + context.strokeRect(x, y, w, h); + } + + // call hook if _context_hook fn is defined + this.callContextHook(variant, context, 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; + if (callSets && variant.calls) { + 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"); + } + }; + + + getColorForFeature(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.color; + } + return variantColor + } + + + getVariantStrokecolor(variant) { + + const v = variant._f || variant; + let variantStrokeColor; + + if (this._strokecolor) { + variantStrokeColor = (typeof this._strokecolor === "function") ? this._strokecolor(v) : this._strokecolor; + } else { + variantStrokeColor = undefined; + } + return variantStrokeColor + } + + callContextHook(variant, context, x, y, w, h) { + if (this._context_hook) { + if (typeof this._context_hook === "function") { + const v = variant._f || variant; + + context.save(); + this._context_hook(v, context, x, y, w, h); + context.restore(); + } + } + } + + clickedFeatures(clickState) { + + let featureList = super.clickedFeatures(clickState); + + 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, featureList) { + + if (featureList === undefined) featureList = this.clickedFeatures(clickState); + 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: '
'}); + } + + 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('
'); + } + 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('
'); + const $e = $$1('
'); + $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('
'); + } + } + } + + if (this.getCallsetsLength() > 0) { + menuItems.push({object: $$1('
')}); + menuItems.push({ + object: $$1(createCheckbox("Show Genotypes", this.showGenotypes)), + click: () => { + this.showGenotypes = !this.showGenotypes; + //adjustTrackHeight(); + this.trackView.checkContentHeight(); + this.trackView.repaintViews(); + } + }); + } + + menuItems.push({object: $$1('
')}); + for (let displayMode of ["COLLAPSED", "SQUISHED", "EXPANDED"]) { + var lut = + { + "COLLAPSED": "Collapse", + "SQUISHED": "Squish", + "EXPANDED": "Expand" + }; + + menuItems.push( + { + object: $$1(createCheckbox(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('
'); + 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('
'); + return list + } + } + + + sendChordsForViewport(viewport) { + const refFrame = viewport.referenceFrame; + let inView; + if ("all" === refFrame.chr) { + const all = this.featureSource.getAllFeatures(); + const arrays = Object.keys(all).map(k => all[k]); + inView = [].concat(...arrays); + } else { + inView = 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(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("<", "<"); + 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, features) { + + if(features === undefined) 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('
'); + } + 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 GWASColors = { + "X": "rgb(204, 153, 0)", + "Y": "rgb(153, 204, 0)", + "Un": "darkGray)", + "1": "rgb(80, 80, 255)", + "2": "rgb(206, 61, 50)", + "2a": "rgb(210, 65, 55)", + "2b": "rgb(215, 70, 60)", + "3": "rgb(116, 155, 88)", + "4": "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(GWASColors)) { + const altName = "chr" + key; + GWASColors[altName] = GWASColors[key]; + } + + // romanizing + for(let a = 1; a <= 48; a++) { + if(a === 10) continue // Don't overide "X" + const roman = romanize(a); + GWASColors[roman] = GWASColors[a.toString()]; + } + + + function romanize (num) { + if (!+num) return false; + var digits = String(+num).split(''); + var key = ['','C','CC','CCC','CD','D','DC','DCC','DCCC','CM', + '','X','XX','XXX','XL','L','LX','LXX','LXXX','XC', + '','I','II','III','IV','V','VI','VII','VIII','IX']; + var roman = '', i = 3; + while (i--) roman = (key[+digits.pop() + (i * 10)] || '') + roman; + return Array(+digits.join('') + 1).join('M') + roman; + } + + /* + * 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); + + // Color settings + if (this.useChrColors) { + this.colorScale = new ColorTable(config.colorTable || GWASColors); + } else if (config.color) { + this.colorScale = new ConstantColorScale(config.color); + } else { + this.colorScale = + 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(); + if (this.disposed) return // This track was removed during async load + } + + // 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 + + let val; + if (this.posteriorProbability) { + val = variant[this.valueProperty]; + } else { + const pvalue = variant[this.valueProperty]; + if (!pvalue) continue + val = -Math.log10(pvalue); + } + + const colorKey = this.useChrColors ? + variant._f ? variant._f.chr : variant.chr : + val; + + const color = this.colorScale.getColor(colorKey); + 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; + } + } + } + + 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, features) { + + if (features === undefined) features = clickState.viewport.cachedFeatures; + + let data = []; + const track = clickState.viewport.trackView.track; + + 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("
"); + } + 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, max: this.config.max || 1}; + } else { + this.dataRange = {min: this.config.max || 25, max: 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(); + if(this.disposed) return; // This track was removed during async load + 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, features) { + + if(features === undefined) features = this.clickedFeatures(clickState); + + const items = []; + features.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) { + + const features = super.clickedFeatures(clickState); + + 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) { + + if(features === undefined) features = this.clickedFeatures(clickState); + + if (features && features.length > 0) { + + return this.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({chr, start, end, bpPerPixel, visibilityWindow}) { + + + 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({context, referenceFrame, pixelWidth, pixelHeight}) { + + 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({ctx, chr, referenceFrame, genome, width, height, stainColors}) { + + 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 + + } + } + + /* + * 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 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(); + if (this.disposed) return // This track was removed during async load + } + + // 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(options.viewport.getWidth()); + + // 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) { + + const allFeatures = super.clickedFeatures(clickState); + + 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) { + + if (features === undefined) features = this.clickedFeatures(clickState); + 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("

"); + } + + Array.prototype.push.apply(data, featureData); + } + } + + return data + } + + /** + * Called when the track is removed. Do any needed cleanup here + */ + dispose() { + this.trackView = undefined; + } + } + + class RemoteFile { + + constructor(args) { + this.config = args; + this.url = mapUrl(args.path || args.url); + } + + + async read(position, length) { + + //console.log(`${position} - ${position + length} (${length})`) + + const headers = this.config.headers || {}; + + if (position !== undefined && length) { + const rangeString = "bytes=" + position + "-" + (position + length - 1); + headers['Range'] = rangeString; + } + + let url = this.url.slice(); // slice => copy + if (this.config.oauthToken) { + const token = resolveToken(this.config.oauthToken); + headers['Authorization'] = `Bearer ${token}`; + } + + if (this.config.apiKey) { + url = addParameter(url, "key", this.config.apiKey); + } + + const response = await fetch(url, { + method: 'GET', + headers: headers, + redirect: 'follow', + mode: 'cors', + }); + + const status = response.status; + + if (status >= 400) { + const err = Error(response.statusText); + err.code = status; + throw err + } else { + return response.arrayBuffer() + } + + /** + * token can be a string, a function that returns a string, or a function that returns a Promise for a string + * @param token + * @returns {Promise<*>} + */ + async function resolveToken(token) { + if (typeof token === 'function') { + return await Promise.resolve(token()) // Normalize the result to a promise, since we don't know what the function returns + } else { + return token + } + } + + } + } + + + function mapUrl(url) { + + if (url.includes("//www.dropbox.com")) { + return url.replace("//www.dropbox.com", "//dl.dropboxusercontent.com") + } else if (url.startsWith("ftp://ftp.ncbi.nlm.nih.gov")) { + return url.replace("ftp://", "https://") + } else { + return url + } + } + + + function addParameter(url, name, value) { + const paramSeparator = url.includes("?") ? "&" : "?"; + return url + paramSeparator + name + "=" + value + } + + class BufferedFile { + + constructor(args) { + this.file = args.file; + this.fetchSize = args.fetchSize || 16000; + this.maxSize = args.maxSize || 1000000; + this.buffers = []; + } + + async read(position, length) { + + + let overlappingBuffers = this.buffers.filter( b => b.overlaps(position, position + length)); + + // See if any buffers completely contain request, if so we're done + for(let buffer of overlappingBuffers) { + if(buffer.contains(position, position + length)) { + return buffer.slice(position, position + length) + } + } + + + if(overlappingBuffers.length === 0) { + + // No overlap with any existing buffer + let size = Math.max(length, this.fetchSize); + + // Find index of first buffer to the right, if any, to potentially limit size + this.buffers.sort((a, b) => a.start - b.start); + const idx = binarySearch(this.buffers, (b) => b.start > position, 0); + if(idx < this.buffers.length) { + size = Math.min(size, this.buffers[idx].start - position); + } + + const bufferStart = position; + const bufferData = await this.file.read(bufferStart, size); + const buffer = new Buffer$1(bufferStart, bufferData); + this.addBuffer(buffer); + + return buffer.slice(position, position + length) + } else { + + // console.log("Cache hit") + // Some overlap. Fill gaps + overlappingBuffers.sort((a, b) => a.start - b.start); + const allBuffers = []; + let currentEnd = position; + for (let ob of overlappingBuffers) { + if (currentEnd < ob.start) { + const bufferStart = currentEnd; + const bufferSize = ob.start - currentEnd; + const bufferData = await this.file.read(bufferStart, bufferSize); + const buffer = new Buffer$1(bufferStart, bufferData); + allBuffers.push(buffer); + } + allBuffers.push(ob); + currentEnd = ob.end; + } + + // Check end + const requestedEnd = position + length; + if (requestedEnd > currentEnd) { + const bufferStart = currentEnd; + const bufferSize = requestedEnd - bufferStart; + const bufferData = await this.file.read(bufferStart, bufferSize); + const buffer = new Buffer$1(bufferStart, bufferData); + allBuffers.push(buffer); + } + + const newStart = allBuffers[0].start; + const newArrayBuffer = concatArrayBuffers(allBuffers.map(b => b.buffer)); + const newBuffer = new Buffer$1(newStart, newArrayBuffer); + + // Replace the overlapping buffers with the new composite one + const tmp = new Set(overlappingBuffers); + this.buffers = this.buffers.filter(b => !tmp.has(b)); + this.addBuffer(newBuffer); + + return newBuffer.slice(position, position + length) + } + + } + + addBuffer(buffer) { + + const size = this.buffers.reduce((a,b) => a + b.size, 0) + buffer.size; + if(size > this.maxSize) { + // console.log(`max buffer size exceeded`) + const overage = size - this.maxSize; + this.buffers.sort((a,b) => a.creationTime - b.creationTime); + let sum = 0; + let i; + for(i=0; i overage) { + break + } + } + // console.log('removing buffers') + // for(let j=0; j this.buffer.byteLength) { + throw Error("buffer bounds error") + } + return this.buffer.slice(start - this.start, end - this.start) + } + + get end() { + return this.start + this.buffer.byteLength + } + + get size() { + return this.buffer.byteLength + } + + contains(start, end) { + return start >= this.start && end <= this.end + } + + overlaps(start, end) { + return (start > this.start && start < this.end) || (end > this.start && end < this.end) + } + + toString() { + return `Buffer ${this.creationTime} ${this.start} - ${this.end}` + } + + } + + /** + * concatenates 2 array buffers. + * Credit: https://gist.github.com/72lions/4528834 + * + * @private + * @param {ArrayBuffers} buffer1 The first buffer. + * @param {ArrayBuffers} buffer2 The second buffer. + * @return {ArrayBuffers} The new ArrayBuffer created out of the two. + */ + function concatArrayBuffers(buffers) { + const size = buffers.reduce((a,b) => a + b.byteLength, 0); + const tmp = new Uint8Array(size); + let offset = 0; + for(let b of buffers) { + tmp.set(new Uint8Array(b), offset); + offset += b.byteLength; + } + return tmp.buffer + } + + /** + * Return 0 <= i <= array.length such that !pred(array[i - 1]) && pred(array[i]). + * + * returns an index 0 ≤ i ≤ array.length such that the given predicate is false for array[i - 1] and true for array[i]* * + */ + function binarySearch(array, pred, min) { + let lo = min - 1, hi = array.length; + while (1 + lo < hi) { + const mi = lo + ((hi - lo) >> 1); + if (pred(array[mi])) { + hi = mi; + } else { + lo = mi; + } + } + return hi + } + + class BlobFile { + + constructor(blob) { + this.file = blob; + } + + async read(position, length) { + + if(length === 0) { + return new ArrayBuffer() + } + + const blob = (position != undefined && length) ? + this.file.slice(position, position + length) : + this.file; + + return blob.arrayBuffer() + } + } + + // esm/core.js + async function _unpack_struct_from_async(structure, async_buf, offset = 0) { + var output = /* @__PURE__ */ new Map(); + for (let [key, fmt] of structure.entries()) { + let value = await struct.unpack_from_async("<" + fmt, async_buf, offset); + offset += struct.calcsize(fmt); + if (value.length == 1) { + value = value[0]; + } + output.set(key, value); + } + return output; + } + function _unpack_struct_from(structure, buf, offset = 0) { + var output = /* @__PURE__ */ new Map(); + for (let [key, fmt] of structure.entries()) { + let value = struct.unpack_from("<" + fmt, buf, offset); + offset += struct.calcsize(fmt); + if (value.length == 1) { + value = value[0]; + } + output.set(key, value); + } + return output; + } + function assert(thing) { + if (!thing) { + thing(); + } + } + function _structure_size(structure) { + var fmt = "<" + Array.from(structure.values()).join(""); + return struct.calcsize(fmt); + } + function _padded_size(size, padding_multiple = 8) { + return Math.ceil(size / padding_multiple) * padding_multiple; + } + var dtype_to_format = { + "u": "Uint", + "i": "Int", + "f": "Float" + }; + function dtype_getter(dtype_str) { + var big_endian = struct._is_big_endian(dtype_str); + var getter, nbytes; + if (/S/.test(dtype_str)) { + getter = "getString"; + nbytes = ((dtype_str.match(/S(\d*)/) || [])[1] || 1) | 0; + } else { + let [_, fstr, bytestr] = dtype_str.match(/[<>=!@]?(i|u|f)(\d*)/); + nbytes = parseInt(bytestr || 4, 10); + let nbits = nbytes * 8; + getter = "get" + dtype_to_format[fstr] + nbits.toFixed(); + } + return [getter, big_endian, nbytes]; + } + var Struct = class { + constructor() { + this.big_endian = isBigEndian(); + this.getters = { + "s": "getUint8", + "b": "getInt8", + "B": "getUint8", + "h": "getInt16", + "H": "getUint16", + "i": "getInt32", + "I": "getUint32", + "l": "getInt32", + "L": "getUint32", + "q": "getInt64", + "Q": "getUint64", + "f": "getFloat32", + "d": "getFloat64" + }; + this.byte_lengths = { + "s": 1, + "b": 1, + "B": 1, + "h": 2, + "H": 2, + "i": 4, + "I": 4, + "l": 4, + "L": 4, + "q": 8, + "Q": 8, + "f": 4, + "d": 8 + }; + let all_formats = Object.keys(this.byte_lengths).join(""); + this.fmt_size_regex = "(\\d*)([" + all_formats + "])"; + } + calcsize(fmt) { + var size = 0; + var match; + var regex = new RegExp(this.fmt_size_regex, "g"); + while ((match = regex.exec(fmt)) !== null) { + let n = parseInt(match[1] || 1, 10); + let f = match[2]; + let subsize = this.byte_lengths[f]; + size += n * subsize; + } + return size; + } + _is_big_endian(fmt) { + var big_endian; + if (/^)/.test(fmt)) { + big_endian = true; + } else { + big_endian = this.big_endian; + } + return big_endian; + } + async unpack_from_async(fmt, async_buf, offset) { + var offset = Number(offset || 0); + const total_size = this.calcsize(fmt); + const local_buffer = await async_buf.slice(offset, offset + total_size); + let local_offset = 0; + var view = new DataView64(local_buffer); + var output = []; + var big_endian = this._is_big_endian(fmt); + var match; + var regex = new RegExp(this.fmt_size_regex, "g"); + while ((match = regex.exec(fmt)) !== null) { + let n = parseInt(match[1] || 1, 10); + let f = match[2]; + let getter = this.getters[f]; + let size = this.byte_lengths[f]; + if (f == "s") { + output.push(new TextDecoder().decode(local_buffer.slice(local_offset, local_offset + n))); + local_offset += n; + } else { + for (var i = 0; i < n; i++) { + output.push(view[getter](local_offset, !big_endian)); + local_offset += size; + } + } + } + return output; + } + unpack_from(fmt, buffer, offset) { + var offset = Number(offset || 0); + const total_size = this.calcsize(fmt); + const local_buffer = buffer.slice(offset, offset + total_size); + let local_offset = 0; + var view = new DataView64(local_buffer); + var output = []; + var big_endian = this._is_big_endian(fmt); + var match; + var regex = new RegExp(this.fmt_size_regex, "g"); + while ((match = regex.exec(fmt)) !== null) { + let n = parseInt(match[1] || 1, 10); + let f = match[2]; + let getter = this.getters[f]; + let size = this.byte_lengths[f]; + if (f == "s") { + output.push(new TextDecoder().decode(local_buffer.slice(local_offset, local_offset + n))); + local_offset += n; + } else { + for (var i = 0; i < n; i++) { + output.push(view[getter](local_offset, !big_endian)); + local_offset += size; + } + } + } + return output; + } + }; + var struct = new Struct(); + function isBigEndian() { + const array = new Uint8Array(4); + const view = new Uint32Array(array.buffer); + return !((view[0] = 1) & array[0]); + } + var DataView64 = class extends DataView { + getUint64(byteOffset, littleEndian) { + const left = BigInt(this.getUint32(byteOffset, littleEndian)); + const right = BigInt(this.getUint32(byteOffset + 4, littleEndian)); + let combined = littleEndian ? left + (right << 32n) : (left << 32n) + right; + return Number(combined); + } + getInt64(byteOffset, littleEndian) { + var low, high; + if (littleEndian) { + low = this.getUint32(byteOffset, true); + high = this.getInt32(byteOffset + 4, true); + } else { + high = this.getInt32(byteOffset, false); + low = this.getUint32(byteOffset + 4, false); + } + let combined = BigInt(low) + (BigInt(high) << 32n); + return Number(combined); + } + getString(byteOffset, littleEndian, length) { + const str_buffer = this.buffer.slice(byteOffset, byteOffset + length); + const decoder = new TextDecoder(); + return decoder.decode(str_buffer); + } + getVLENStruct(byteOffset, littleEndian, length) { + let item_size = this.getUint32(byteOffset, littleEndian); + let collection_address = this.getUint64(byteOffset + 4, littleEndian); + let object_index = this.getUint32(byteOffset + 12, littleEndian); + return [item_size, collection_address, object_index]; + } + }; + function bitSize(integer) { + return integer.toString(2).length; + } + function _unpack_integer(nbytes, buf, offset = 0, littleEndian = true) { + const local_buffer = buf.slice(offset, offset + nbytes); + let bytes = new Uint8Array(local_buffer); + if (!littleEndian) { + bytes.reverse(); + } + let integer = bytes.reduce((accumulator, currentValue, index) => accumulator + (currentValue << index * 8), 0); + return integer; + } + + // esm/datatype-msg.js + var DatatypeMessage = class { + constructor(buf, offset) { + this.buf = buf; + this.offset = offset; + this.dtype = this.determine_dtype(); + } + async determine_dtype() { + let datatype_msg = await _unpack_struct_from_async(DATATYPE_MSG, this.buf, this.offset); + this.offset += DATATYPE_MSG_SIZE; + let datatype_class = datatype_msg.get("class_and_version") & 15; + if (datatype_class == DATATYPE_FIXED_POINT) { + return this._determine_dtype_fixed_point(datatype_msg); + } else if (datatype_class == DATATYPE_FLOATING_POINT) { + return this._determine_dtype_floating_point(datatype_msg); + } else if (datatype_class == DATATYPE_TIME) { + throw "Time datatype class not supported."; + } else if (datatype_class == DATATYPE_STRING) { + return this._determine_dtype_string(datatype_msg); + } else if (datatype_class == DATATYPE_BITFIELD) { + throw "Bitfield datatype class not supported."; + } else if (datatype_class == DATATYPE_OPAQUE) { + return { + datatype_class: DATATYPE_OPAQUE, + size: datatype_msg.get("size") + }; + } else if (datatype_class == DATATYPE_COMPOUND) { + return this._determine_dtype_compound(datatype_msg); + } else if (datatype_class == DATATYPE_REFERENCE) { + return ["REFERENCE", datatype_msg.get("size")]; + } else if (datatype_class == DATATYPE_ENUMERATED) { + return this.determine_dtype(); + } else if (datatype_class == DATATYPE_ARRAY) { + throw "Array datatype class not supported."; + } else if (datatype_class == DATATYPE_VARIABLE_LENGTH) { + let vlen_type = this._determine_dtype_vlen(datatype_msg); + if (vlen_type[0] == "VLEN_SEQUENCE") { + let base_type = this.determine_dtype(); + vlen_type = ["VLEN_SEQUENCE", base_type]; + } + return vlen_type; + } else { + throw "Invalid datatype class " + datatype_class; + } + } + _determine_dtype_fixed_point(datatype_msg) { + let length_in_bytes = datatype_msg.get("size"); + if (![1, 2, 4, 8].includes(length_in_bytes)) { + throw "Unsupported datatype size"; + } + let signed = datatype_msg.get("class_bit_field_0") & 8; + var dtype_char; + if (signed > 0) { + dtype_char = "i"; + } else { + dtype_char = "u"; + } + let byte_order = datatype_msg.get("class_bit_field_0") & 1; + var byte_order_char; + if (byte_order == 0) { + byte_order_char = "<"; + } else { + byte_order_char = ">"; + } + this.offset += 4; + return byte_order_char + dtype_char + length_in_bytes.toFixed(); + } + _determine_dtype_floating_point(datatype_msg) { + let length_in_bytes = datatype_msg.get("size"); + if (![1, 2, 4, 8].includes(length_in_bytes)) { + throw "Unsupported datatype size"; + } + let dtype_char = "f"; + let byte_order = datatype_msg.get("class_bit_field_0") & 1; + var byte_order_char; + if (byte_order == 0) { + byte_order_char = "<"; + } else { + byte_order_char = ">"; + } + this.offset += 12; + return byte_order_char + dtype_char + length_in_bytes.toFixed(); + } + _determine_dtype_string(datatype_msg) { + return "S" + datatype_msg.get("size").toFixed(); + } + _determine_dtype_vlen(datatype_msg) { + let vlen_type = datatype_msg.get("class_bit_field_0") & 1; + if (vlen_type != 1) { + return ["VLEN_SEQUENCE", 0, 0]; + } + let padding_type = datatype_msg.get("class_bit_field_0") >> 4; + let character_set = datatype_msg.get("class_bit_field_1") & 1; + return ["VLEN_STRING", padding_type, character_set]; + } + _determine_dtype_compound(datatype_msg) { + throw "Compound type not yet implemented!"; + } + }; + var DATATYPE_MSG = /* @__PURE__ */ new Map([ + ["class_and_version", "B"], + ["class_bit_field_0", "B"], + ["class_bit_field_1", "B"], + ["class_bit_field_2", "B"], + ["size", "I"] + ]); + var DATATYPE_MSG_SIZE = _structure_size(DATATYPE_MSG); + var COMPOUND_PROP_DESC_V1 = /* @__PURE__ */ new Map([ + ["offset", "I"], + ["dimensionality", "B"], + ["reserved_0", "B"], + ["reserved_1", "B"], + ["reserved_2", "B"], + ["permutation", "I"], + ["reserved_3", "I"], + ["dim_size_1", "I"], + ["dim_size_2", "I"], + ["dim_size_3", "I"], + ["dim_size_4", "I"] + ]); + _structure_size(COMPOUND_PROP_DESC_V1); + var DATATYPE_FIXED_POINT = 0; + var DATATYPE_FLOATING_POINT = 1; + var DATATYPE_TIME = 2; + var DATATYPE_STRING = 3; + var DATATYPE_BITFIELD = 4; + var DATATYPE_OPAQUE = 5; + var DATATYPE_COMPOUND = 6; + var DATATYPE_REFERENCE = 7; + var DATATYPE_ENUMERATED = 8; + var DATATYPE_VARIABLE_LENGTH = 9; + var DATATYPE_ARRAY = 10; + function zero$1(buf) { + let len = buf.length; + while (--len >= 0) { + buf[len] = 0; + } + } + var MIN_MATCH$1 = 3; + var MAX_MATCH$1 = 258; + var LENGTH_CODES$1 = 29; + var LITERALS$1 = 256; + var L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1; + var D_CODES$1 = 30; + var DIST_CODE_LEN = 512; + var static_ltree = new Array((L_CODES$1 + 2) * 2); + zero$1(static_ltree); + var static_dtree = new Array(D_CODES$1 * 2); + zero$1(static_dtree); + var _dist_code = new Array(DIST_CODE_LEN); + zero$1(_dist_code); + var _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1); + zero$1(_length_code); + var base_length = new Array(LENGTH_CODES$1); + zero$1(base_length); + var base_dist = new Array(D_CODES$1); + zero$1(base_dist); + var adler32 = (adler, buf, len, pos) => { + let s1 = adler & 65535 | 0, s2 = adler >>> 16 & 65535 | 0, n = 0; + while (len !== 0) { + n = len > 2e3 ? 2e3 : 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; + var makeTable = () => { + let c, table = []; + for (var n = 0; n < 256; n++) { + c = n; + for (var k = 0; k < 8; k++) { + c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1; + } + table[n] = c; + } + return table; + }; + var crcTable = new Uint32Array(makeTable()); + var 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]) & 255]; + } + return crc ^ -1; + }; + var crc32_1 = crc32; + var messages = { + 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" + }; + var constants$2 = { + 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_MEM_ERROR: -4, + 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 + }; + var _has = (obj, key) => { + return Object.prototype.hasOwnProperty.call(obj, key); + }; + var assign = function(obj) { + 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; + }; + var flattenChunks = (chunks) => { + let len = 0; + for (let i = 0, l = chunks.length; i < l; i++) { + len += chunks[i].length; + } + 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, + flattenChunks + }; + var STR_APPLY_UIA_OK = true; + try { + String.fromCharCode.apply(null, new Uint8Array(1)); + } catch (__) { + STR_APPLY_UIA_OK = false; + } + var _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; + 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; + for (m_pos = 0; m_pos < str_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 64512) === 55296 && m_pos + 1 < str_len) { + c2 = str.charCodeAt(m_pos + 1); + if ((c2 & 64512) === 56320) { + c = 65536 + (c - 55296 << 10) + (c2 - 56320); + m_pos++; + } + } + buf_len += c < 128 ? 1 : c < 2048 ? 2 : c < 65536 ? 3 : 4; + } + buf = new Uint8Array(buf_len); + for (i = 0, m_pos = 0; i < buf_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 64512) === 55296 && m_pos + 1 < str_len) { + c2 = str.charCodeAt(m_pos + 1); + if ((c2 & 64512) === 56320) { + c = 65536 + (c - 55296 << 10) + (c2 - 56320); + m_pos++; + } + } + if (c < 128) { + buf[i++] = c; + } else if (c < 2048) { + buf[i++] = 192 | c >>> 6; + buf[i++] = 128 | c & 63; + } else if (c < 65536) { + buf[i++] = 224 | c >>> 12; + buf[i++] = 128 | c >>> 6 & 63; + buf[i++] = 128 | c & 63; + } else { + buf[i++] = 240 | c >>> 18; + buf[i++] = 128 | c >>> 12 & 63; + buf[i++] = 128 | c >>> 6 & 63; + buf[i++] = 128 | c & 63; + } + } + return buf; + }; + var buf2binstring = (buf, len) => { + 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; + }; + 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; + const utf16buf = new Array(len * 2); + for (out = 0, i = 0; i < len; ) { + let c = buf[i++]; + if (c < 128) { + utf16buf[out++] = c; + continue; + } + let c_len = _utf8len[c]; + if (c_len > 4) { + utf16buf[out++] = 65533; + i += c_len - 1; + continue; + } + c &= c_len === 2 ? 31 : c_len === 3 ? 15 : 7; + while (c_len > 1 && i < len) { + c = c << 6 | buf[i++] & 63; + c_len--; + } + if (c_len > 1) { + utf16buf[out++] = 65533; + continue; + } + if (c < 65536) { + utf16buf[out++] = c; + } else { + c -= 65536; + utf16buf[out++] = 55296 | c >> 10 & 1023; + utf16buf[out++] = 56320 | c & 1023; + } + } + return buf2binstring(utf16buf, out); + }; + var utf8border = (buf, max) => { + max = max || buf.length; + if (max > buf.length) { + max = buf.length; + } + let pos = max - 1; + while (pos >= 0 && (buf[pos] & 192) === 128) { + pos--; + } + if (pos < 0) { + return max; + } + if (pos === 0) { + return max; + } + return pos + _utf8len[buf[pos]] > max ? pos : max; + }; + var strings = { + string2buf, + buf2string, + utf8border + }; + function ZStream() { + 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; + } + var zstream = ZStream; + var BAD$1 = 30; + var TYPE$1 = 12; + var inffast = function inflate_fast(strm, start) { + let _in; + let last; + let _out; + let beg; + let end; + let dmax; + let wsize; + let whave; + let wnext; + let s_window; + let hold; + let bits; + let lcode; + let dcode; + let lmask; + let dmask; + let here; + let op; + let len; + let dist; + let from; + let from_source; + let input, output; + const state = strm.state; + _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); + dmax = state.dmax; + 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; + top: + do { + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + for (; ; ) { + op = here >>> 24; + hold >>>= op; + bits -= op; + op = here >>> 16 & 255; + if (op === 0) { + output[_out++] = here & 65535; + } else if (op & 16) { + len = here & 65535; + op &= 15; + if (op) { + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + len += hold & (1 << op) - 1; + hold >>>= op; + bits -= op; + } + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + for (; ; ) { + op = here >>> 24; + hold >>>= op; + bits -= op; + op = here >>> 16 & 255; + if (op & 16) { + dist = here & 65535; + op &= 15; + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + } + dist += hold & (1 << op) - 1; + if (dist > dmax) { + strm.msg = "invalid distance too far back"; + state.mode = BAD$1; + break top; + } + hold >>>= op; + bits -= op; + op = _out - beg; + if (dist > op) { + op = dist - op; + if (op > whave) { + if (state.sane) { + strm.msg = "invalid distance too far back"; + state.mode = BAD$1; + break top; + } + } + from = 0; + from_source = s_window; + if (wnext === 0) { + from += wsize - op; + if (op < len) { + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; + from_source = output; + } + } else if (wnext < op) { + from += wsize + wnext - op; + op -= wnext; + if (op < len) { + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = 0; + if (wnext < len) { + op = wnext; + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; + from_source = output; + } + } + } else { + from += wnext - op; + if (op < len) { + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; + 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; + do { + 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) { + here = dcode[(here & 65535) + (hold & (1 << op) - 1)]; + continue dodist; + } else { + strm.msg = "invalid distance code"; + state.mode = BAD$1; + break top; + } + break; + } + } else if ((op & 64) === 0) { + here = lcode[(here & 65535) + (hold & (1 << op) - 1)]; + continue dolen; + } else if (op & 32) { + state.mode = TYPE$1; + break top; + } else { + strm.msg = "invalid literal/length code"; + state.mode = BAD$1; + break top; + } + break; + } + } while (_in < last && _out < end); + len = bits >> 3; + _in -= len; + bits -= len << 3; + hold &= (1 << bits) - 1; + 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; + }; + var MAXBITS = 15; + var ENOUGH_LENS$1 = 852; + var ENOUGH_DISTS$1 = 592; + var CODES$1 = 0; + var LENS$1 = 1; + var DISTS$1 = 2; + var lbase = new Uint16Array([ + 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 + ]); + var lext = new Uint8Array([ + 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 + ]); + var dbase = new Uint16Array([ + 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 + ]); + var dext = new Uint8Array([ + 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 + ]); + var inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => { + const bits = opts.bits; + let len = 0; + let sym = 0; + let min = 0, max = 0; + let root = 0; + let curr = 0; + let drop = 0; + let left = 0; + let used = 0; + let huff = 0; + let incr; + let fill; + let low; + let mask; + let next; + let base = null; + let base_index = 0; + let end; + const count = new Uint16Array(MAXBITS + 1); + const offs = new Uint16Array(MAXBITS + 1); + let extra = null; + let extra_index = 0; + let here_bits, here_op, here_val; + for (len = 0; len <= MAXBITS; len++) { + count[len] = 0; + } + for (sym = 0; sym < codes; sym++) { + count[lens[lens_index + sym]]++; + } + root = bits; + for (max = MAXBITS; max >= 1; max--) { + if (count[max] !== 0) { + break; + } + } + if (root > max) { + root = max; + } + if (max === 0) { + table[table_index++] = 1 << 24 | 64 << 16 | 0; + table[table_index++] = 1 << 24 | 64 << 16 | 0; + opts.bits = 1; + return 0; + } + for (min = 1; min < max; min++) { + if (count[min] !== 0) { + break; + } + } + if (root < min) { + root = min; + } + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) { + return -1; + } + } + if (left > 0 && (type === CODES$1 || max !== 1)) { + return -1; + } + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) { + offs[len + 1] = offs[len] + count[len]; + } + for (sym = 0; sym < codes; sym++) { + if (lens[lens_index + sym] !== 0) { + work[offs[lens[lens_index + sym]]++] = sym; + } + } + if (type === CODES$1) { + base = extra = work; + end = 19; + } else if (type === LENS$1) { + base = lbase; + base_index -= 257; + extra = lext; + extra_index -= 257; + end = 256; + } else { + base = dbase; + extra = dext; + end = -1; + } + huff = 0; + sym = 0; + len = min; + next = table_index; + curr = root; + drop = 0; + low = -1; + used = 1 << root; + mask = used - 1; + if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) { + return 1; + } + for (; ; ) { + 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; + here_val = 0; + } + incr = 1 << len - drop; + fill = 1 << curr; + min = fill; + do { + fill -= incr; + table[next + (huff >> drop) + fill] = here_bits << 24 | here_op << 16 | here_val | 0; + } while (fill !== 0); + incr = 1 << len - 1; + while (huff & incr) { + incr >>= 1; + } + if (incr !== 0) { + huff &= incr - 1; + huff += incr; + } else { + huff = 0; + } + sym++; + if (--count[len] === 0) { + if (len === max) { + break; + } + len = lens[lens_index + work[sym]]; + } + if (len > root && (huff & mask) !== low) { + if (drop === 0) { + drop = root; + } + next += min; + curr = len - drop; + left = 1 << curr; + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) { + break; + } + curr++; + left <<= 1; + } + used += 1 << curr; + if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) { + return 1; + } + low = huff & mask; + table[low] = root << 24 | curr << 16 | next - table_index | 0; + } + } + if (huff !== 0) { + table[next + huff] = len - drop << 24 | 64 << 16 | 0; + } + opts.bits = root; + return 0; + }; + var inftrees = inflate_table; + var CODES = 0; + var LENS = 1; + var DISTS = 2; + var { + 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; + var HEAD = 1; + var FLAGS = 2; + var TIME = 3; + var OS = 4; + var EXLEN = 5; + var EXTRA = 6; + var NAME = 7; + var COMMENT = 8; + var HCRC = 9; + var DICTID = 10; + var DICT = 11; + var TYPE = 12; + var TYPEDO = 13; + var STORED = 14; + var COPY_ = 15; + var COPY = 16; + var TABLE = 17; + var LENLENS = 18; + var CODELENS = 19; + var LEN_ = 20; + var LEN = 21; + var LENEXT = 22; + var DIST = 23; + var DISTEXT = 24; + var MATCH = 25; + var LIT = 26; + var CHECK = 27; + var LENGTH = 28; + var DONE = 29; + var BAD = 30; + var MEM = 31; + var SYNC = 32; + var ENOUGH_LENS = 852; + var ENOUGH_DISTS = 592; + var MAX_WBITS = 15; + var DEF_WBITS = MAX_WBITS; + var zswap32 = (q) => { + return (q >>> 24 & 255) + (q >>> 8 & 65280) + ((q & 65280) << 8) + ((q & 255) << 24); + }; + function InflateState() { + this.mode = 0; + this.last = false; + this.wrap = 0; + this.havedict = false; + 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 Uint16Array(320); + this.work = new Uint16Array(288); + this.lendyn = null; + this.distdyn = null; + this.sane = 0; + this.back = 0; + this.was = 0; + } + var 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 = ""; + if (state.wrap) { + strm.adler = state.wrap & 1; + } + state.mode = HEAD; + state.last = 0; + state.havedict = 0; + state.dmax = 32768; + state.head = null; + state.hold = 0; + state.bits = 0; + state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS); + state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS); + state.sane = 1; + state.back = -1; + return Z_OK$1; + }; + var 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); + }; + var inflateReset2 = (strm, windowBits) => { + let wrap; + if (!strm || !strm.state) { + return Z_STREAM_ERROR$1; + } + const state = strm.state; + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } else { + wrap = (windowBits >> 4) + 1; + if (windowBits < 48) { + windowBits &= 15; + } + } + if (windowBits && (windowBits < 8 || windowBits > 15)) { + return Z_STREAM_ERROR$1; + } + if (state.window !== null && state.wbits !== windowBits) { + state.window = null; + } + state.wrap = wrap; + state.wbits = windowBits; + return inflateReset(strm); + }; + var inflateInit2 = (strm, windowBits) => { + if (!strm) { + return Z_STREAM_ERROR$1; + } + const state = new InflateState(); + strm.state = state; + state.window = null; + const ret = inflateReset2(strm, windowBits); + if (ret !== Z_OK$1) { + strm.state = null; + } + return ret; + }; + var inflateInit = (strm) => { + return inflateInit2(strm, DEF_WBITS); + }; + var virgin = true; + var lenfix; + var distfix; + var fixedtables = (state) => { + if (virgin) { + lenfix = new Int32Array(512); + distfix = new Int32Array(32); + 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 }); + sym = 0; + while (sym < 32) { + state.lens[sym++] = 5; + } + inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); + virgin = false; + } + state.lencode = lenfix; + state.lenbits = 9; + state.distcode = distfix; + state.distbits = 5; + }; + var updatewindow = (strm, src, end, copy) => { + let dist; + const state = strm.state; + if (state.window === null) { + state.wsize = 1 << state.wbits; + state.wnext = 0; + state.whave = 0; + state.window = new Uint8Array(state.wsize); + } + 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; + } + state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext); + copy -= dist; + if (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; + }; + var inflate$2 = (strm, flush) => { + let state; + let input, output; + let next; + let put; + let have, left; + let hold; + let bits; + let _in, _out; + let copy; + let from; + let from_source; + let here = 0; + let here_bits, here_op, here_val; + let last_bits, last_op, last_val; + let len; + let ret; + const hbuf = new Uint8Array(4); + let opts; + let n; + const order = 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; + } + 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: + for (; ; ) { + switch (state.mode) { + case HEAD: + if (state.wrap === 0) { + state.mode = TYPEDO; + break; + } + while (bits < 16) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (state.wrap & 2 && hold === 35615) { + state.check = 0; + hbuf[0] = hold & 255; + hbuf[1] = hold >>> 8 & 255; + state.check = crc32_1(state.check, hbuf, 2, 0); + hold = 0; + bits = 0; + state.mode = FLAGS; + break; + } + state.flags = 0; + if (state.head) { + state.head.done = false; + } + if (!(state.wrap & 1) || (((hold & 255) << 8) + (hold >> 8)) % 31) { + strm.msg = "incorrect header check"; + state.mode = BAD; + break; + } + if ((hold & 15) !== Z_DEFLATED) { + strm.msg = "unknown compression method"; + state.mode = BAD; + break; + } + hold >>>= 4; + bits -= 4; + len = (hold & 15) + 8; + if (state.wbits === 0) { + state.wbits = len; + } else if (len > state.wbits) { + strm.msg = "invalid window size"; + state.mode = BAD; + break; + } + state.dmax = 1 << state.wbits; + strm.adler = state.check = 1; + state.mode = hold & 512 ? DICTID : TYPE; + hold = 0; + bits = 0; + break; + case FLAGS: + while (bits < 16) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + state.flags = hold; + if ((state.flags & 255) !== Z_DEFLATED) { + strm.msg = "unknown compression method"; + state.mode = BAD; + break; + } + if (state.flags & 57344) { + strm.msg = "unknown header flags set"; + state.mode = BAD; + break; + } + if (state.head) { + state.head.text = hold >> 8 & 1; + } + if (state.flags & 512) { + hbuf[0] = hold & 255; + hbuf[1] = hold >>> 8 & 255; + state.check = crc32_1(state.check, hbuf, 2, 0); + } + hold = 0; + bits = 0; + state.mode = TIME; + case TIME: + 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 & 512) { + hbuf[0] = hold & 255; + hbuf[1] = hold >>> 8 & 255; + hbuf[2] = hold >>> 16 & 255; + hbuf[3] = hold >>> 24 & 255; + state.check = crc32_1(state.check, hbuf, 4, 0); + } + hold = 0; + bits = 0; + state.mode = OS; + case OS: + while (bits < 16) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (state.head) { + state.head.xflags = hold & 255; + state.head.os = hold >> 8; + } + if (state.flags & 512) { + hbuf[0] = hold & 255; + hbuf[1] = hold >>> 8 & 255; + state.check = crc32_1(state.check, hbuf, 2, 0); + } + hold = 0; + bits = 0; + state.mode = EXLEN; + case EXLEN: + if (state.flags & 1024) { + 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 & 512) { + hbuf[0] = hold & 255; + hbuf[1] = hold >>> 8 & 255; + state.check = crc32_1(state.check, hbuf, 2, 0); + } + hold = 0; + bits = 0; + } else if (state.head) { + state.head.extra = null; + } + state.mode = EXTRA; + case EXTRA: + if (state.flags & 1024) { + copy = state.length; + if (copy > have) { + copy = have; + } + if (copy) { + if (state.head) { + len = state.head.extra_len - state.length; + if (!state.head.extra) { + state.head.extra = new Uint8Array(state.head.extra_len); + } + state.head.extra.set(input.subarray(next, next + copy), len); + } + if (state.flags & 512) { + 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; + case NAME: + if (state.flags & 2048) { + if (have === 0) { + break inf_leave; + } + copy = 0; + do { + len = input[next + copy++]; + if (state.head && len && state.length < 65536) { + state.head.name += String.fromCharCode(len); + } + } while (len && copy < have); + if (state.flags & 512) { + 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; + case COMMENT: + if (state.flags & 4096) { + if (have === 0) { + break inf_leave; + } + copy = 0; + do { + len = input[next + copy++]; + if (state.head && len && state.length < 65536) { + state.head.comment += String.fromCharCode(len); + } + } while (len && copy < have); + if (state.flags & 512) { + 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; + case HCRC: + if (state.flags & 512) { + while (bits < 16) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (hold !== (state.check & 65535)) { + strm.msg = "header crc mismatch"; + state.mode = BAD; + break; + } + 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: + while (bits < 32) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + strm.adler = state.check = zswap32(hold); + hold = 0; + bits = 0; + state.mode = DICT; + case DICT: + if (state.havedict === 0) { + 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; + state.mode = TYPE; + case TYPE: + if (flush === Z_BLOCK || flush === Z_TREES) { + break inf_leave; + } + case TYPEDO: + if (state.last) { + hold >>>= bits & 7; + bits -= bits & 7; + state.mode = CHECK; + break; + } + while (bits < 3) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + state.last = hold & 1; + hold >>>= 1; + bits -= 1; + switch (hold & 3) { + case 0: + state.mode = STORED; + break; + case 1: + fixedtables(state); + state.mode = LEN_; + if (flush === Z_TREES) { + hold >>>= 2; + bits -= 2; + break inf_leave; + } + break; + case 2: + state.mode = TABLE; + break; + case 3: + strm.msg = "invalid block type"; + state.mode = BAD; + } + hold >>>= 2; + bits -= 2; + break; + case STORED: + hold >>>= bits & 7; + bits -= bits & 7; + while (bits < 32) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if ((hold & 65535) !== (hold >>> 16 ^ 65535)) { + strm.msg = "invalid stored block lengths"; + state.mode = BAD; + break; + } + state.length = hold & 65535; + hold = 0; + bits = 0; + state.mode = COPY_; + if (flush === Z_TREES) { + break inf_leave; + } + case COPY_: + state.mode = COPY; + case COPY: + copy = state.length; + if (copy) { + if (copy > have) { + copy = have; + } + if (copy > left) { + copy = left; + } + if (copy === 0) { + break inf_leave; + } + output.set(input.subarray(next, next + copy), put); + have -= copy; + next += copy; + left -= copy; + put += copy; + state.length -= copy; + break; + } + state.mode = TYPE; + break; + case TABLE: + while (bits < 14) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + state.nlen = (hold & 31) + 257; + hold >>>= 5; + bits -= 5; + state.ndist = (hold & 31) + 1; + hold >>>= 5; + bits -= 5; + state.ncode = (hold & 15) + 4; + hold >>>= 4; + bits -= 4; + if (state.nlen > 286 || state.ndist > 30) { + strm.msg = "too many length or distance symbols"; + state.mode = BAD; + break; + } + state.have = 0; + state.mode = LENLENS; + case LENLENS: + while (state.have < state.ncode) { + while (bits < 3) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + state.lens[order[state.have++]] = hold & 7; + hold >>>= 3; + bits -= 3; + } + while (state.have < 19) { + state.lens[order[state.have++]] = 0; + } + 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; + } + state.have = 0; + state.mode = CODELENS; + case CODELENS: + while (state.have < state.nlen + state.ndist) { + for (; ; ) { + here = state.lencode[hold & (1 << state.lenbits) - 1]; + here_bits = here >>> 24; + here_op = here >>> 16 & 255; + here_val = here & 65535; + if (here_bits <= bits) { + break; + } + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (here_val < 16) { + hold >>>= here_bits; + bits -= here_bits; + state.lens[state.have++] = here_val; + } else { + if (here_val === 16) { + n = here_bits + 2; + while (bits < n) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + 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 & 3); + hold >>>= 2; + bits -= 2; + } else if (here_val === 17) { + n = here_bits + 3; + while (bits < n) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + hold >>>= here_bits; + bits -= here_bits; + len = 0; + copy = 3 + (hold & 7); + hold >>>= 3; + bits -= 3; + } else { + n = here_bits + 7; + while (bits < n) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + hold >>>= here_bits; + bits -= here_bits; + len = 0; + copy = 11 + (hold & 127); + 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; + } + } + } + if (state.mode === BAD) { + break; + } + if (state.lens[256] === 0) { + strm.msg = "invalid code -- missing end-of-block"; + state.mode = BAD; + break; + } + state.lenbits = 9; + opts = { bits: state.lenbits }; + ret = inftrees(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); + state.lenbits = opts.bits; + if (ret) { + strm.msg = "invalid literal/lengths set"; + state.mode = BAD; + break; + } + state.distbits = 6; + state.distcode = state.distdyn; + opts = { bits: state.distbits }; + ret = inftrees(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); + state.distbits = opts.bits; + if (ret) { + strm.msg = "invalid distances set"; + state.mode = BAD; + break; + } + state.mode = LEN_; + if (flush === Z_TREES) { + break inf_leave; + } + case LEN_: + state.mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + 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); + 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]; + here_bits = here >>> 24; + here_op = here >>> 16 & 255; + here_val = here & 65535; + if (here_bits <= bits) { + break; + } + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (here_op && (here_op & 240) === 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) >> last_bits)]; + here_bits = here >>> 24; + here_op = here >>> 16 & 255; + here_val = here & 65535; + if (last_bits + here_bits <= bits) { + break; + } + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + hold >>>= last_bits; + bits -= last_bits; + state.back += last_bits; + } + hold >>>= here_bits; + bits -= here_bits; + state.back += here_bits; + state.length = here_val; + if (here_op === 0) { + state.mode = LIT; + break; + } + if (here_op & 32) { + 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; + case LENEXT: + if (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; + hold >>>= state.extra; + bits -= state.extra; + state.back += state.extra; + } + state.was = state.length; + state.mode = DIST; + case DIST: + for (; ; ) { + here = state.distcode[hold & (1 << state.distbits) - 1]; + here_bits = here >>> 24; + here_op = here >>> 16 & 255; + here_val = here & 65535; + if (here_bits <= bits) { + break; + } + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if ((here_op & 240) === 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) >> last_bits)]; + here_bits = here >>> 24; + here_op = here >>> 16 & 255; + here_val = here & 65535; + if (last_bits + here_bits <= bits) { + break; + } + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + hold >>>= last_bits; + bits -= last_bits; + state.back += last_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; + case DISTEXT: + if (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; + hold >>>= state.extra; + bits -= state.extra; + state.back += state.extra; + } + if (state.offset > state.dmax) { + strm.msg = "invalid distance too far back"; + state.mode = BAD; + break; + } + state.mode = MATCH; + case MATCH: + if (left === 0) { + break inf_leave; + } + copy = _out - left; + if (state.offset > copy) { + copy = state.offset - copy; + if (copy > state.whave) { + if (state.sane) { + strm.msg = "invalid distance too far back"; + state.mode = BAD; + break; + } + } + 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 { + 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) { + while (bits < 32) { + if (have === 0) { + break inf_leave; + } + have--; + hold |= input[next++] << bits; + bits += 8; + } + _out -= left; + strm.total_out += _out; + state.total += _out; + if (_out) { + strm.adler = state.check = state.flags ? crc32_1(state.check, output, _out, put - _out) : adler32_1(state.check, output, _out, put - _out); + } + _out = left; + if ((state.flags ? hold : zswap32(hold)) !== state.check) { + strm.msg = "incorrect data check"; + state.mode = BAD; + break; + } + hold = 0; + bits = 0; + } + state.mode = LENGTH; + case LENGTH: + if (state.wrap && state.flags) { + while (bits < 32) { + if (have === 0) { + break inf_leave; + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (hold !== (state.total & 4294967295)) { + strm.msg = "incorrect length check"; + state.mode = BAD; + break; + } + hold = 0; + bits = 0; + } + state.mode = DONE; + 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: + default: + return Z_STREAM_ERROR$1; + } + } + 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 = 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; + }; + var inflateEnd = (strm) => { + if (!strm || !strm.state) { + return Z_STREAM_ERROR$1; + } + let state = strm.state; + if (state.window) { + state.window = null; + } + strm.state = null; + return Z_OK$1; + }; + var inflateGetHeader = (strm, head) => { + if (!strm || !strm.state) { + return Z_STREAM_ERROR$1; + } + const state = strm.state; + if ((state.wrap & 2) === 0) { + return Z_STREAM_ERROR$1; + } + state.head = head; + head.done = false; + return Z_OK$1; + }; + var inflateSetDictionary = (strm, dictionary) => { + const dictLength = dictionary.length; + let state; + let dictid; + let ret; + if (!strm || !strm.state) { + return Z_STREAM_ERROR$1; + } + state = strm.state; + if (state.wrap !== 0 && state.mode !== DICT) { + return Z_STREAM_ERROR$1; + } + if (state.mode === DICT) { + dictid = 1; + dictid = adler32_1(dictid, dictionary, dictLength, 0); + if (dictid !== state.check) { + return Z_DATA_ERROR$1; + } + } + ret = updatewindow(strm, dictionary, dictLength, dictLength); + if (ret) { + state.mode = MEM; + return Z_MEM_ERROR$1; + } + state.havedict = 1; + 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)"; + 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 + }; + function GZheader() { + this.text = 0; + this.time = 0; + this.xflags = 0; + this.os = 0; + this.extra = null; + this.extra_len = 0; + this.name = ""; + this.comment = ""; + this.hcrc = 0; + this.done = false; + } + var gzheader = GZheader; + var toString = Object.prototype.toString; + var { + Z_NO_FLUSH, + Z_FINISH, + Z_OK, + Z_STREAM_END, + Z_NEED_DICT, + Z_STREAM_ERROR, + Z_DATA_ERROR, + Z_MEM_ERROR + } = constants$2; + function Inflate$1(options) { + this.options = common.assign({ + chunkSize: 1024 * 64, + windowBits: 15, + to: "" + }, options || {}); + const opt = this.options; + if (opt.raw && opt.windowBits >= 0 && opt.windowBits < 16) { + opt.windowBits = -opt.windowBits; + if (opt.windowBits === 0) { + opt.windowBits = -15; + } + } + if (opt.windowBits >= 0 && opt.windowBits < 16 && !(options && options.windowBits)) { + opt.windowBits += 32; + } + if (opt.windowBits > 15 && opt.windowBits < 48) { + if ((opt.windowBits & 15) === 0) { + opt.windowBits |= 15; + } + } + this.err = 0; + this.msg = ""; + this.ended = false; + this.chunks = []; + 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); + if (opt.dictionary) { + if (typeof opt.dictionary === "string") { + opt.dictionary = strings.string2buf(opt.dictionary); + } else if (toString.call(opt.dictionary) === "[object ArrayBuffer]") { + opt.dictionary = new Uint8Array(opt.dictionary); + } + if (opt.raw) { + status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary); + if (status !== Z_OK) { + throw new Error(messages[status]); + } + } + } + } + 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; + if (toString.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) { + status = Z_NEED_DICT; + } + } + 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; + } + 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); + 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)); + } + } + } + if (status === Z_OK && last_avail_out === 0) + continue; + 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$1.prototype.onData = function(chunk) { + this.chunks.push(chunk); + }; + Inflate$1.prototype.onEnd = function(status) { + 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; + }; + function inflate$1(input, options) { + const inflator = new Inflate$1(options); + inflator.push(input); + if (inflator.err) + throw inflator.msg || messages[inflator.err]; + return inflator.result; + } + function inflateRaw$1(input, options) { + options = options || {}; + options.raw = true; + return inflate$1(input, options); + } + 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 + }; + var { Inflate, inflate, inflateRaw, ungzip } = inflate_1$1; + var inflate_1 = inflate; + var ungzip_1 = ungzip; + + // esm/filters.js + var zlib_decompress = function(buf, itemsize) { + let input_array = new Uint8Array(buf); + return inflate_1(input_array).buffer; + }; + var unshuffle = function(buf, itemsize) { + let buffer_size = buf.byteLength; + let unshuffled_view = new Uint8Array(buffer_size); + let step = Math.floor(buffer_size / itemsize); + let shuffled_view = new DataView(buf); + for (var j = 0; j < itemsize; j++) { + for (var i = 0; i < step; i++) { + unshuffled_view[j + i * itemsize] = shuffled_view.getUint8(j * step + i); + } + } + return unshuffled_view.buffer; + }; + var fletch32 = function(buf, itemsize) { + _verify_fletcher32(buf); + return buf.slice(0, -4); + }; + function _verify_fletcher32(chunk_buffer) { + var odd_chunk_buffer = chunk_buffer.byteLength % 2 != 0; + var data_length = chunk_buffer.byteLength - 4; + var view = new DataView(chunk_buffer); + var sum1 = 0; + var sum2 = 0; + for (var offset = 0; offset < data_length - 1; offset += 2) { + let datum = view.getUint16(offset, true); + sum1 = (sum1 + datum) % 65535; + sum2 = (sum2 + sum1) % 65535; + } + if (odd_chunk_buffer) { + let datum = view.getUint8(data_length - 1); + sum1 = (sum1 + datum) % 65535; + sum2 = (sum2 + sum1) % 65535; + } + var [ref_sum1, ref_sum2] = struct.unpack_from(">HH", chunk_buffer, data_length); + ref_sum1 = ref_sum1 % 65535; + ref_sum2 = ref_sum2 % 65535; + if (sum1 != ref_sum1 || sum2 != ref_sum2) { + throw 'ValueError("fletcher32 checksum invalid")'; + } + return true; + } + var GZIP_DEFLATE_FILTER = 1; + var SHUFFLE_FILTER = 2; + var FLETCH32_FILTER = 3; + var Filters = /* @__PURE__ */ new Map([ + [GZIP_DEFLATE_FILTER, zlib_decompress], + [SHUFFLE_FILTER, unshuffle], + [FLETCH32_FILTER, fletch32] + ]); + + // esm/btree.js + var AbstractBTree = class { + constructor(fh, offset) { + this.fh = fh; + this.offset = offset; + this.depth = null; + } + async init() { + this.all_nodes = /* @__PURE__ */ new Map(); + await this._read_root_node(); + await this._read_children(); + } + async _read_children() { + let node_level = this.depth; + while (node_level > 0) { + for (var parent_node of this.all_nodes.get(node_level)) { + for (var child_addr of parent_node.get("addresses")) { + this._add_node(await this._read_node(child_addr, node_level - 1)); + } + } + node_level--; + } + } + async _read_root_node() { + let root_node = await this._read_node(this.offset, null); + this._add_node(root_node); + this.depth = root_node.get("node_level"); + } + _add_node(node) { + let node_level = node.get("node_level"); + if (this.all_nodes.has(node_level)) { + this.all_nodes.get(node_level).push(node); + } else { + this.all_nodes.set(node_level, [node]); + } + } + async _read_node(offset, node_level) { + let node = await this._read_node_header(offset, node_level); + node.set("keys", []); + node.set("addresses", []); + return node; + } + async _read_node_header(offset) { + throw "NotImplementedError: must define _read_node_header in implementation class"; + } + }; + var BTreeV1 = class extends AbstractBTree { + B_LINK_NODE = /* @__PURE__ */ new Map([ + ["signature", "4s"], + ["node_type", "B"], + ["node_level", "B"], + ["entries_used", "H"], + ["left_sibling", "Q"], + ["right_sibling", "Q"] + ]); + async _read_node_header(offset, node_level) { + let node = await _unpack_struct_from_async(this.B_LINK_NODE, this.fh, offset); + if (node_level != null) { + if (node.get("node_level") != node_level) { + throw "node level does not match"; + } + } + return node; + } + }; + var BTreeV1Groups = class extends BTreeV1 { + NODE_TYPE = 0; + constructor(fh, offset) { + super(fh, offset); + this.ready = this.init(); + } + async _read_node(offset, node_level) { + let node = await this._read_node_header(offset, node_level); + offset += _structure_size(this.B_LINK_NODE); + let keys = []; + let addresses = []; + let entries_used = node.get("entries_used"); + for (var i = 0; i < entries_used; i++) { + let key = (await struct.unpack_from_async("= 0; d--) { + if (cpos[d] >= chunk_shape[d]) { + cpos[d] = 0; + apos[d] = chunk_offset[d]; + if (d > 0) { + cpos[d - 1] += 1; + apos[d - 1] += 1; + } + } else { + break; + } + } + let inbounds = apos.slice(0, -1).every(function(p, d2) { + return p < data_shape[d2]; + }); + if (inbounds) { + let cb_offset = ci * item_size; + let datum = cview[item_getter](cb_offset, !item_big_endian, item_size); + let ai = apos.slice(0, -1).reduce(function(prev, curr, index) { + return curr * data_strides[index] + prev; + }, 0); + data[ai] = datum; + } + cpos[dims - 1] += 1; + apos[dims - 1] += 1; + } + } + } + return data; + } + _filter_chunk(chunk_buffer, filter_mask, filter_pipeline, itemsize) { + let num_filters = filter_pipeline.length; + let buf = chunk_buffer.slice(); + for (var filter_index = num_filters - 1; filter_index >= 0; filter_index--) { + if (filter_mask & 1 << filter_index) { + continue; + } + let pipeline_entry = filter_pipeline[filter_index]; + let filter_id = pipeline_entry.get("filter_id"); + let client_data = pipeline_entry.get("client_data"); + if (Filters.has(filter_id)) { + buf = Filters.get(filter_id)(buf, itemsize, client_data); + } else { + throw 'NotImplementedError("Filter with id:' + filter_id.toFixed() + ' not supported")'; + } + } + return buf; + } + }; + var BTreeV2 = class extends AbstractBTree { + B_TREE_HEADER = /* @__PURE__ */ new Map([ + ["signature", "4s"], + ["version", "B"], + ["node_type", "B"], + ["node_size", "I"], + ["record_size", "H"], + ["depth", "H"], + ["split_percent", "B"], + ["merge_percent", "B"], + ["root_address", "Q"], + ["root_nrecords", "H"], + ["total_nrecords", "Q"] + ]); + B_LINK_NODE = /* @__PURE__ */ new Map([ + ["signature", "4s"], + ["version", "B"], + ["node_type", "B"] + ]); + constructor(fh, offset) { + super(fh, offset); + this.ready = this.init(); + } + async _read_root_node() { + let h = await this._read_tree_header(this.offset); + this.address_formats = this._calculate_address_formats(h); + this.header = h; + this.depth = h.get("depth"); + let address = [h.get("root_address"), h.get("root_nrecords"), h.get("total_nrecords")]; + let root_node = await this._read_node(address, this.depth); + this._add_node(root_node); + } + async _read_tree_header(offset) { + let header = await _unpack_struct_from_async(this.B_TREE_HEADER, this.fh, this.offset); + return header; + } + _calculate_address_formats(header) { + let node_size = header.get("node_size"); + let record_size = header.get("record_size"); + let nrecords_max = 0; + let ntotalrecords_max = 0; + let address_formats = /* @__PURE__ */ new Map(); + let max_depth = header.get("depth"); + for (var node_level = 0; node_level <= max_depth; node_level++) { + let offset_fmt = ""; + let num1_fmt = ""; + let num2_fmt = ""; + let offset_size, num1_size, num2_size; + if (node_level == 0) { + offset_size = 0; + num1_size = 0; + num2_size = 0; + } else if (node_level == 1) { + offset_size = 8; + offset_fmt = " 0) { + ntotalrecords_max *= nrecords_max; + } else { + ntotalrecords_max = nrecords_max; + } + } + } + return address_formats; + } + _nrecords_max(node_size, record_size, addr_size) { + return Math.floor((node_size - 10 - addr_size) / (record_size + addr_size)); + } + _required_bytes(integer) { + return Math.ceil(bitSize(integer) / 8); + } + _int_format(bytelength) { + return [" 0) { + num2 = (await struct.unpack_from_async(num2_fmt, this.fh, offset))[0]; + offset += num2_size; + } + addresses.push([address_offset, num1, num2]); + } + } + node.set("keys", keys); + node.set("addresses", addresses); + return node; + } + async _read_node_header(offset, node_level) { + let node = await _unpack_struct_from_async(this.B_LINK_NODE, this.fh, offset); + node.set("node_level", node_level); + return node; + } + *iter_records() { + for (let nodelist of this.all_nodes.values()) { + for (let node of nodelist) { + for (let key of node.get("keys")) { + yield key; + } + } + } + } + _parse_record(record) { + throw "NotImplementedError"; + } + }; + var BTreeV2GroupNames = class extends BTreeV2 { + NODE_TYPE = 5; + async _parse_record(buf, offset, size) { + let namehash = (await struct.unpack_from_async(" 0) { + throw "Filter info size not supported on FractalHeap"; + } + if (header.get("btree_address_huge_objects") == UNDEFINED_ADDRESS) { + header.set("btree_address_huge_objects", null); + } else { + throw "Huge objects not implemented in FractalHeap"; + } + if (header.get("root_block_address") == UNDEFINED_ADDRESS) { + header.set("root_block_address", null); + } + let nbits = header.get("log2_maximum_heap_size"); + let block_offset_size = this._min_size_nbits(nbits); + let h = /* @__PURE__ */ new Map([ + ["signature", "4s"], + ["version", "B"], + ["heap_header_adddress", "Q"], + ["block_offset", `${block_offset_size}B`] + ]); + this.indirect_block_header = new Map(h); + this.indirect_block_header_size = _structure_size(h); + if ((header.get("flags") & 2) == 2) { + h.set("checksum", "I"); + } + this.direct_block_header = h; + this.direct_block_header_size = _structure_size(h); + let maximum_dblock_size = header.get("maximum_direct_block_size"); + this._managed_object_offset_size = this._min_size_nbits(nbits); + let value = Math.min(maximum_dblock_size, header.get("max_managed_object_size")); + this._managed_object_length_size = this._min_size_integer(value); + let start_block_size = header.get("starting_block_size"); + let table_width = header.get("table_width"); + if (!(start_block_size > 0)) { + throw "Starting block size == 0 not implemented"; + } + let log2_maximum_dblock_size = Number(Math.floor(Math.log2(maximum_dblock_size))); + assert(1n << BigInt(log2_maximum_dblock_size) == maximum_dblock_size); + let log2_start_block_size = Number(Math.floor(Math.log2(start_block_size))); + assert(1n << BigInt(log2_start_block_size) == start_block_size); + this._max_direct_nrows = log2_maximum_dblock_size - log2_start_block_size + 2; + let log2_table_width = Math.floor(Math.log2(table_width)); + assert(1 << log2_table_width == table_width); + this._indirect_nrows_sub = log2_table_width + log2_start_block_size - 1; + this.header = header; + this.nobjects = header.get("managed_object_count") + header.get("huge_object_count") + header.get("tiny_object_count"); + let managed = []; + let root_address = header.get("root_block_address"); + let nrows = 0; + if (root_address != null) { + nrows = header.get("indirect_current_rows_count"); + } + if (nrows > 0) { + for await (let data of this._iter_indirect_block(this.fh, root_address, nrows)) { + managed.push(data); + } + } else { + let data = await this._read_direct_block(this.fh, root_address, start_block_size); + managed.push(data); + } + let data_size = managed.reduce((p, c) => p + c.byteLength, 0); + let combined = new Uint8Array(data_size); + let moffset = 0; + managed.forEach((m) => { + combined.set(new Uint8Array(m), moffset); + moffset += m.byteLength; + }); + this.managed = combined.buffer; + } + async _read_direct_block(fh, offset, block_size) { + let data = await fh.slice(offset, offset + block_size); + let header = _unpack_struct_from(this.direct_block_header, data); + assert(header.get("signature") == "FHDB"); + return data; + } + get_data(heapid) { + let firstbyte = struct.unpack_from("> 4 & 3; + let version = firstbyte >> 6; + let data_offset = 1; + if (idtype == 0) { + assert(version == 0); + let nbytes = this._managed_object_offset_size; + let offset = _unpack_integer(nbytes, heapid, data_offset); + data_offset += nbytes; + nbytes = this._managed_object_length_size; + let size = _unpack_integer(nbytes, heapid, data_offset); + return this.managed.slice(offset, offset + size); + } else if (idtype == 1) { + throw "tiny objectID not supported in FractalHeap"; + } else if (idtype == 2) { + throw "huge objectID not supported in FractalHeap"; + } else { + throw "unknown objectID type in FractalHeap"; + } + } + _min_size_integer(integer) { + return this._min_size_nbits(bitSize(integer)); + } + _min_size_nbits(nbits) { + return Math.ceil(nbits / 8); + } + async *_iter_indirect_block(fh, offset, nrows) { + let header = await _unpack_struct_from_async(this.indirect_block_header, fh, offset); + offset += this.indirect_block_header_size; + assert(header.get("signature") == "FHIB"); + let block_offset_bytes = header.get("block_offset"); + let block_offset = block_offset_bytes.reduce((p, c, i) => p + (c << i * 8), 0); + header.set("block_offset", block_offset); + let [ndirect, nindirect] = this._indirect_info(nrows); + let direct_blocks = []; + for (let i = 0; i < ndirect; i++) { + let address = (await struct.unpack_from_async(" { + return this._chunks; + }); + } + get shape() { + let msg = this.find_msg_type(DATASPACE_MSG_TYPE)[0]; + let msg_offset = msg.get("offset_to_message"); + return determine_data_shape(this.fh, msg_offset); + } + async get_filter_pipeline() { + if (this._filter_pipeline != null) { + return this._filter_pipeline; + } + let filter_msgs = this.find_msg_type(DATA_STORAGE_FILTER_PIPELINE_MSG_TYPE); + if (!filter_msgs.length) { + this._filter_pipeline = null; + return this._filter_pipeline; + } + var offset = filter_msgs[0].get("offset_to_message"); + let [version, nfilters] = await struct.unpack_from_async(" 255) { + name_length = (await struct.unpack_from_async(" 0; + filter_info.set("optional", optional); + let num_client_values = (await struct.unpack_from_async(" 0) { + name = (await struct.unpack_from_async(`${name_length}s`, buf, offset))[0]; + offset += name_length; + } + filter_info.set("name", name); + let client_values = await struct.unpack_from_async(`<${num_client_values}i`, buf, offset); + offset += 4 * num_client_values; + filter_info.set("client_data_values", client_values); + filters.push(filter_info); + } + } else { + throw `version ${version} is not supported`; + } + this._filter_pipeline = filters; + return this._filter_pipeline; + } + find_msg_type(msg_type) { + return this.msgs.filter(function(m) { + return m.get("type") == msg_type; + }); + } + async get_attributes() { + let attrs = {}; + let attr_msgs = this.find_msg_type(ATTRIBUTE_MSG_TYPE); + for (let msg of attr_msgs) { + let offset = msg.get("offset_to_message"); + let [name, value] = await this.unpack_attribute(offset); + attrs[name] = value; + } + return attrs; + } + async get_fillvalue() { + let msg = this.find_msg_type(FILLVALUE_MSG_TYPE)[0]; + var offset = msg.get("offset_to_message"); + var is_defined; + let version = (await struct.unpack_from_async("= block_size) { + [block_offset, block_size] = object_header_blocks[++current_block]; + local_offset = 0; + } + let msg = await _unpack_struct_from_async(HEADER_MSG_INFO_V1, fh, block_offset + local_offset); + let offset_to_message = block_offset + local_offset + HEADER_MSG_INFO_V1_SIZE; + msg.set("offset_to_message", offset_to_message); + if (msg.get("type") == OBJECT_CONTINUATION_MSG_TYPE) { + var [fh_off, size] = await struct.unpack_from_async("= block_size - HEADER_MSG_INFO_V2_SIZE) { + let next_block = object_header_blocks[++current_block]; + if (next_block == null) { + break; + } + [block_offset, block_size] = next_block; + local_offset = 0; + } + let msg = await _unpack_struct_from_async(HEADER_MSG_INFO_V2, buf, block_offset + local_offset); + let offset_to_message = block_offset + local_offset + HEADER_MSG_INFO_V2_SIZE + creation_order_size; + msg.set("offset_to_message", offset_to_message); + if (msg.get("type") == OBJECT_CONTINUATION_MSG_TYPE) { + var [fh_off, size] = await struct.unpack_from_async(" 0; + let link_name_character_set_field_present = (flags & 2 ** 4) > 0; + let ordered = (flags & 2 ** 2) > 0; + let link_type; + if (link_type_field_present) { + link_type = (await struct.unpack_from_async(" 0) { + offset += 8; + } + let fmt = (flags & 2) > 0 ? LINK_INFO_MSG2 : LINK_INFO_MSG1; + let link_info = await _unpack_struct_from_async(fmt, data, offset); + let output = /* @__PURE__ */ new Map(); + for (let [k, v] of link_info.entries()) { + output.set(k, v == UNDEFINED_ADDRESS2 ? null : v); + } + return output; + } + get is_dataset() { + return this.find_msg_type(DATASPACE_MSG_TYPE).length > 0; + } + async get_data() { + let msg = this.find_msg_type(DATA_STORAGE_MSG_TYPE)[0]; + let msg_offset = msg.get("offset_to_message"); + var [version, dims, layout_class, property_offset] = await this._get_data_message_properties(msg_offset); + if (layout_class == 0) { + throw "Compact storage of DataObject not implemented"; + } else if (layout_class == 1) { + return this._get_contiguous_data(property_offset); + } else if (layout_class == 2) { + return this._get_chunked_data(msg_offset); + } + } + async _get_data_message_properties(msg_offset) { + let dims, layout_class, property_offset; + let [version, arg1, arg2] = await struct.unpack_from_async("= 1 && version <= 4); + return [version, dims, layout_class, property_offset]; + } + async _get_contiguous_data(property_offset) { + let [data_offset] = await struct.unpack_from_async("=!@\|]?(i|u|f|S)(\d*)/.test(dtype)) { + let [item_getter, item_is_big_endian, item_size] = dtype_getter(dtype); + let output = new Array(fullsize); + const local_buffer = await this.fh.slice(data_offset, data_offset + item_size * fullsize); + let view = new DataView64(local_buffer); + for (var i = 0; i < fullsize; i++) { + output[i] = view[item_getter](i * item_size, !item_is_big_endian, item_size); + } + return output; + } else if (dtype.datatype_class === 5) { + return this.fh.slice(data_offset, data_offset + dtype.size); + } else { + throw "not Implemented - no proper dtype defined"; + } + } else { + let dtype_class = dtype[0]; + if (dtype_class == "REFERENCE") { + let size = dtype[1]; + if (size != 8) { + throw "NotImplementedError('Unsupported Reference type')"; + } + let ref_addresses = await this.fh.slice(data_offset, data_offset + fullsize); + return ref_addresses; + } else if (dtype_class == "VLEN_STRING") { + let character_set = dtype[2]; + const encoding = character_set == 0 ? "ascii" : "utf-8"; + const decoder = new TextDecoder(encoding); + var value = []; + for (var i = 0; i < fullsize; i++) { + const [vlen, vlen_data] = await this._vlen_size_and_data(this.fh, data_offset); + value[i] = decoder.decode(vlen_data); + data_offset += 16; + } + return value; + } else { + throw "NotImplementedError('datatype not implemented')"; + } + } + } + async _get_chunked_data(offset) { + await this._get_chunk_params(); + if (this._chunk_address == UNDEFINED_ADDRESS2) { + return []; + } + var chunk_btree = new BTreeV1RawDataChunks(this.fh, this._chunk_address, this._chunk_dims); + await chunk_btree.ready; + const dtype = await this.dtype; + const shape = await this.shape; + const chunks = await this.chunks; + const filter_pipeline = await this.get_filter_pipeline(); + let data = await chunk_btree.construct_data_from_chunks(chunks, shape, dtype, filter_pipeline); + if (dtype instanceof Array && /^VLEN/.test(dtype[0])) { + let dtype_class = dtype[0]; + for (var i = 0; i < data.length; i++) { + let [item_size, gheap_address, object_index] = data[i]; + var gheap; + if (!(gheap_address in this._global_heaps)) { + gheap = new GlobalHeap(this.fh, gheap_address); + await gheap.ready; + this._global_heaps[gheap_address] = gheap; + } else { + gheap = this._global_heaps[gheap_address]; + } + let vlen_data = gheap.objects.get(object_index); + if (dtype_class == "VLEN_STRING") { + let character_set = dtype[2]; + const encoding = character_set == 0 ? "ascii" : "utf-8"; + const decoder = new TextDecoder(encoding); + data[i] = decoder.decode(vlen_data); + } + } + } + return data; + } + async _get_chunk_params() { + if (this._chunk_params_set) { + return; + } + this._chunk_params_set = true; + var msg = this.find_msg_type(DATA_STORAGE_MSG_TYPE)[0]; + var offset = msg.get("offset_to_message"); + var [version, dims, layout_class, property_offset] = await this._get_data_message_properties(offset); + if (layout_class != 2) { + return; + } + var data_offset; + if (version == 1 || version == 2) { + var address = (await struct.unpack_from_async("= 1 && version <= 3); + var fmt = "<" + (dims - 1).toFixed() + "I"; + var chunk_shape = await struct.unpack_from_async(fmt, this.fh, data_offset); + this._chunks = chunk_shape; + this._chunk_dims = dims; + this._chunk_address = address; + return; + } + }; + async function determine_data_shape(buf, offset) { + let version = (await struct.unpack_from_async(" this.get(k)); + } + length() { + return this.keys.length; + } + _dereference(ref) { + if (!ref) { + throw "cannot deference null reference"; + } + let obj = this.file._get_object_by_address(ref); + if (obj == null) { + throw "reference not found in file"; + } + return obj; + } + async get(y) { + if (typeof y == "number") { + return this._dereference(y); + } + var path = normpath(y); + if (path == "/") { + return this.file; + } + if (path == ".") { + return this; + } + if (/^\//.test(path)) { + return this.file.get(path.slice(1)); + } + if (posix_dirname(path) != "") { + var [next_obj, additional_obj] = path.split(/\/(.*)/); + } else { + var next_obj = path; + var additional_obj = "."; + } + if (!(next_obj in this._links)) { + throw next_obj + " not found in group"; + } + var obj_name = normpath(this.name + "/" + next_obj); + let link_target = this._links[next_obj]; + if (typeof link_target == "string") { + try { + return this.get(link_target); + } catch (error) { + return null; + } + } + var dataobjs = new DataObjects(this.file._fh, link_target); + await dataobjs.ready; + if (dataobjs.is_dataset) { + if (additional_obj != ".") { + throw obj_name + " is a dataset, not a group"; + } + return new Dataset(obj_name, dataobjs, this); + } else { + var new_group = new Group(obj_name, this); + await new_group.init(dataobjs); + return new_group.get(additional_obj); + } + } + visit(func) { + return this.visititems((name, obj) => func(name)); + } + visititems(func) { + var root_name_length = this.name.length; + if (!/\/$/.test(this.name)) { + root_name_length += 1; + } + var queue = this.values.slice(); + while (queue) { + let obj = queue.shift(); + if (queue.length == 1) + console.log(obj); + let name = obj.name.slice(root_name_length); + let ret = func(name, obj); + if (ret != null) { + return ret; + } + if (obj instanceof Group) { + queue = queue.concat(obj.values); + } + } + return null; + } + get attrs() { + if (this._attrs == null) { + this._attrs = this._dataobjects.get_attributes(); + } + return this._attrs; + } + }; + var File$1 = class extends Group { + constructor(fh, filename, options) { + super("/", null); + this.ready = this.init(fh, filename, options); + } + async init(fh, filename, options) { + var superblock = new SuperBlock(fh, 0); + await superblock.ready; + var offset = await superblock.get_offset_to_dataobjects(); + var dataobjects = new DataObjects(fh, offset); + await dataobjects.ready; + this.parent = this; + this.file = this; + this.name = "/"; + this._dataobjects = dataobjects; + this._attrs = null; + this._keys = null; + this._fh = fh; + this.filename = filename || ""; + this.mode = "r"; + this.userblock_size = 0; + if (options && options.index) { + this.index = options.index; + } else { + let index_offset; + if (options && options.indexOffset) { + index_offset = options.indexOffset; + } else { + const attrs = await this.attrs; + if (attrs.hasOwnProperty("_index_offset")) { + index_offset = attrs["_index_offset"]; + } else { + const indexName = this.indexName || "_index"; + const index_link = await dataobjects.find_link(indexName); + if (index_link) { + index_offset = index_link[1]; + } + } + } + if (index_offset) { + try { + const dataobject = new DataObjects(fh, index_offset); + await dataobject.ready; + const comp_index_data = await dataobject.get_data(); + const inflated = ungzip_1(comp_index_data); + const json = new TextDecoder().decode(inflated); + this.index = JSON.parse(json); + } catch (e) { + console.error(`Error loading index by offset ${e}`); + } + } + } + if (this.index && this.name in this.index) { + this._links = this.index[this.name]; + } else { + this._links = await dataobjects.get_links(); + } + } + _get_object_by_address(obj_addr) { + if (this._dataobjects.offset == obj_addr) { + return this; + } + return this.visititems((y) => { + y._dataobjects.offset == obj_addr ? y : null; + }); + } + }; + var Dataset = class extends Array { + constructor(name, dataobjects, parent) { + super(); + this.parent = parent; + this.file = parent.file; + this.name = name; + this._dataobjects = dataobjects; + this._attrs = null; + this._astype = null; + } + get value() { + var data = this._dataobjects.get_data(); + if (this._astype == null) { + return this.getValue(data); + } + return data.astype(this._astype); + } + get shape() { + return this._dataobjects.shape; + } + get attrs() { + return this._dataobjects.get_attributes(); + } + get dtype() { + return this._dataobjects.dtype; + } + get fillvalue() { + return this._dataobjects.get_fillvalue(); + } + async to_array() { + const value = await this.value; + const shape = await this.shape; + return create_nested_array(value, shape); + } + async getValue(data) { + const dtype = await this.dtype; + if (dtype.startsWith("S")) { + return (await data).map((s) => s.substr(0, s.indexOf("\0"))); + } else { + return data; + } + } + }; + function posix_dirname(p) { + let sep = "/"; + let i = p.lastIndexOf(sep) + 1; + let head = p.slice(0, i); + let all_sep = new RegExp("^" + sep + "+$"); + let end_sep = new RegExp(sep + "$"); + if (head && !all_sep.test(head)) { + head = head.replace(end_sep, ""); + } + return head; + } + function normpath(path) { + return path.replace(/\/(\/)+/g, "/"); + } + function create_nested_array(value, shape) { + const total_length = value.length; + const dims_product = shape.reduce((previous, current) => previous * current, 1); + if (total_length !== dims_product) { + console.warn(`shape product: ${dims_product} does not match length of flattened array: ${total_length}`); + } + let output = value; + const subdims = shape.slice(1).reverse(); + for (let dim of subdims) { + const new_output = []; + const { length } = output; + let cursor = 0; + while (cursor < length) { + new_output.push(output.slice(cursor, cursor += dim)); + } + output = new_output; + } + return output; + } + /*! pako 2.0.4 https://github.com/nodeca/pako @license (MIT AND Zlib) */ + + async function openH5File(options) { + + // Some clients (notably igv-webapp) pass a File reference in the url field. Fix this + if(options.url && isBlobLike(options.url)) { + options.file = options.url; + options.url = undefined; + } + + const isRemote = options.url !== undefined; + let fileReader = options.reader ? options.reader : getReaderFor(options); + + // Set default options appropriate for spacewalk + const fetchSize = options.fetchSize || 2000; + const maxSize = options.maxSize || 200000; + + if (isRemote) { + fileReader = new BufferedFile({file: fileReader, fetchSize, maxSize}); + } + const asyncBuffer = new AsyncBuffer(fileReader); + + // Optional external index -- this is not common + const index = await readExternalIndex(options); + const indexOffset = options.indexOffset; + + // Create HDF5 file + const filename = getFilenameFor(options); + const hdfFile = new File$1(asyncBuffer, filename, {index, indexOffset}); + await hdfFile.ready; + return hdfFile + } + + async function readExternalIndex(options) { + + let indexReader; + if(options.indexReader) { + indexReader = options.indexReader; + } + else if(options.index) { + return options.index + } else if (options.indexURL) { + indexReader = new RemoteFile({url: options.indexURL}); + } else if (options.indexPath) { + indexReader = new NodeLocalFile({path: options.indexPath}); + } else if (options.indexFile) { + indexReader = new BlobFile({file: options.indexFile}); + } + if (indexReader) { + const indexFileContents = await indexReader.read(); + const indexFileJson = new TextDecoder().decode(indexFileContents); + return JSON.parse(indexFileJson) + } else { + return undefined + } + } + + + function getReaderFor(options) { + if (options.url) { // An absolute or relative URL + return new RemoteFile(options) + } else if (options.path) { // A file path + return new NodeLocalFile(options) + } else if (options.file) { // A Browser file blob + return new BlobFile(options.file) + } else { + throw Error("One of 'url', 'path (node only)', or 'file (browser only)' must be specified") + } + } + + function getFilenameFor(options) { + if (options.url) { + return getFilename(options.url) + } else if (options.path) { + return getFilename(options.path) + } else if (options.file) { + return options.file.name + } + } + + /** + * Wraps an io.Reader in an interface jsfive-async expects* + */ + class AsyncBuffer { + constructor(fileReader) { + this.fileReader = fileReader; + } + + async slice(start, end) { + return this.fileReader.read(start, end - start) + } + } + + function getFilename(pathOrURL) { + const idx = pathOrURL.lastIndexOf("/"); + return idx > 0 ? pathOrURL.substring(idx + 1) : pathOrURL + } + + function isBlobLike(obj) { + return typeof obj.slice === 'function' && typeof obj.arrayBuffer === 'function' + } + + class HDF5Reader { + + constructor(h5_file, bin_size=100000){ + + this.h5_file = h5_file; + this.bin_size = bin_size; + this.h5_obj = undefined; + } + + async fetch(){ + + if(!this.h5_obj) { + this.h5_obj = await openH5File({ + url: this.h5_file, + fetchSize: 1000000, + maxSize: 200000000 + }); + } + return this.h5_obj + } + + async get_keys(){ + /* returns a list of keys of the pytor file*/ + let h5_obj = await this.fetch(); + + return h5_obj.keys + } + + async get_rd_signal(bin_size=this.bin_size){ + + let h5_obj = await this.fetch(); + let h5_obj_keys = h5_obj.keys; + + // console.log(h5_obj_keys) + let signal_bin = new ParseSignals(h5_obj_keys); + let rd_bins = signal_bin.get_rd_bins(); + let snp_bins = signal_bin.get_snp_bins(); + this.available_bins = [...new Set(rd_bins, snp_bins)]; + + // let bin_size = this.bin_size + if(! this.available_bins.includes(bin_size)){ + bin_size = this.available_bins.at(-1); + } + + const chr_ds = await h5_obj.get("rd_chromosomes"); + await chr_ds.dtype; + let rd_chromosomes = await chr_ds.value; + //console.log(`get_rd_signal rd_chromosomes ${Date.now() - t0}`) + //rd_chromosomes = fixString(rd_chromosomes) + let rd_flag = ""; + + // get rd stat + let rd_stat = await this.rd_stat(h5_obj, h5_obj_keys, bin_size); + + var wigFeatures = []; + var wigFeatures_gc = []; + var wigFeatures_rd_call_meanshift = []; + var wigFeatures_rd_call_combined = []; + var wigFeatures_baf1 = []; + var wigFeatures_baf2 = []; + + for (let chrom of rd_chromosomes) { + // for normal rd signal + var signal_rd = `his_rd_p_${chrom}_${bin_size}${rd_flag}`; + let chr_wig = await this.get_chr_signal(h5_obj, h5_obj_keys, chrom, bin_size, signal_rd, rd_stat); + wigFeatures = wigFeatures.concat(chr_wig); + + // rd gc corrected + var signal_rd_gc = `his_rd_p_${chrom}_${bin_size}_GC`; + let chr_wig_gc = await this.get_chr_signal(h5_obj, h5_obj_keys, chrom, bin_size, signal_rd_gc, rd_stat); + wigFeatures_gc = wigFeatures_gc.concat(chr_wig_gc); + + // rd call MeanShift + + let signal_rd_call = `his_rd_p_${chrom}_${bin_size}_partition_GC_merge`; + let chr_wig_rd_call_meanshift = await this.get_chr_signal(h5_obj, h5_obj_keys, chrom, bin_size, signal_rd_call, rd_stat); + wigFeatures_rd_call_meanshift = wigFeatures_rd_call_meanshift.concat(chr_wig_rd_call_meanshift); + + let chr_wig_rd_call = await this.rd_call_combined(h5_obj, h5_obj_keys, chrom, bin_size, rd_stat); + wigFeatures_rd_call_combined = wigFeatures_rd_call_combined.concat(chr_wig_rd_call); + + // baf likelihood + let signal_baf_1 = `snp_likelihood_${chrom}_${bin_size}_mask`; + let chr_wig_bafs = await this.get_baf_signals(h5_obj, h5_obj_keys, chrom, bin_size, signal_baf_1); + + // let signal_baf_1 = `snp_i1_${chrom}_${bin_size}_mask` + // let chr_wig_bafs = await this.get_baf_signals_v2(h5_obj, h5_obj_keys, chrom, bin_size, signal_baf_1) + + wigFeatures_baf1 = wigFeatures_baf1.concat(chr_wig_bafs[0]); + wigFeatures_baf2 = wigFeatures_baf2.concat(chr_wig_bafs[1]); + // this.rd_call_combined(h5_obj, h5_obj_keys, chrom, bin_size, rd_stat) + } + this.callers = []; + if (wigFeatures_rd_call_combined.length != 0){ + this.callers.push('ReadDepth'); + } + if (wigFeatures_rd_call_combined.length != 0){ + this.callers.push('2D'); + } + + var obj = {}; + var signal_obj = { + "RD_Raw": wigFeatures, + "RD_Raw_gc_coor" : wigFeatures_gc, + "ReadDepth": wigFeatures_rd_call_meanshift, + "2D": wigFeatures_rd_call_combined, + "BAF1": wigFeatures_baf1, + "BAF2": wigFeatures_baf2 + }; + obj[bin_size] = signal_obj; + return obj + } + + decode_segments(segments_arr){ + let max = 2 ** 32 - 1; + let segments = []; + let l = []; + for (let x of segments_arr){ + if(x == max){ + segments.push(l); + l = []; + } else { + l.push(x); + } + } + return segments + } + + async rd_call_combined(h5_obj, h5_obj_keys, chrom, bin_size, rd_stat){ + let chr_wig = []; + + let segments; + let mosaic_call_segments = `his_rd_p_${chrom}_${bin_size}_partition_GC_mosaic_segments_2d`; + if (h5_obj_keys.includes(mosaic_call_segments)){ + const chrom_dataset = await h5_obj.get(mosaic_call_segments); + let chrom_data = await chrom_dataset.value; + //console.log(`rd_call_combined ${mosaic_call_segments} ${Date.now() - t0}`) + segments = this.decode_segments(chrom_data); + + } + + let mosaic_calls = `his_rd_p_${chrom}_${bin_size}_partition_GC_mosaic_call_2d`; + if (h5_obj_keys.includes(mosaic_calls)){ + const segments_call_dataset = await h5_obj.get(mosaic_calls); + let segments_call = await segments_call_dataset.to_array(); //create_nested_array(value, shape) + segments.forEach((ind_segment, segment_idx) => { + ind_segment.forEach((bin_value, bin_idx) =>{ + chr_wig.push({chr:chrom, start: bin_value*bin_size, end: (bin_value+1) * bin_size, value: (segments_call[0][segment_idx]/rd_stat[4]) *2}); + }); + }); + } + + return chr_wig + + } + + async rd_stat(h5_obj, h5_obj_keys, bin_size){ + /* + returns a list for rd statistics information + paramter + --------- + h5_obj: cnvpytor read object + h5_obj_keys: a list of available signal names + bin_size: bin size + + */ + + let rd_stat_signal = `rd_stat_${bin_size}_auto`; + let rd_stat; + if (h5_obj_keys.includes(rd_stat_signal)){ + const rd_stat_dataset = await h5_obj.get(rd_stat_signal); + rd_stat = await rd_stat_dataset.value; + //console.log(`rd_stat_signal ${rd_stat_signal} ${Date.now() - t0}`) + } + return rd_stat + } + + + async get_chr_signal(h5_obj, h5_obj_keys, chrom, bin_size, signal_name, rd_stat){ + /* return a list of dictionary for a chromosome */ + let chr_wig = []; + + if (h5_obj_keys.includes(signal_name)){ + const chrom_dataset = await h5_obj.get(signal_name); + let chrom_data = await chrom_dataset.value; + //console.log(`chr_signal ${signal_name} ${Date.now() - t0}`) + chrom_data.forEach((bin_value, bin_idx) => { + chr_wig.push({chr:chrom, start: bin_idx*bin_size, end: (bin_idx+1) * bin_size, value: (bin_value/rd_stat[4]) *2}); + }); + } + return chr_wig + } + + async get_baf_signals(h5_obj, h5_obj_keys, chrom, bin_size, signal_name){ + /* return two list of dictionary*/ + let chr_wig_1 = []; + let chr_wig_2 = []; + if (h5_obj_keys.includes(signal_name)){ + let chrom_dataset = await h5_obj.get(signal_name); + let chrom_data = await chrom_dataset.to_array(); //create_nested_array(value, shape) + chrom_data.forEach((bin_value, bin_idx) => { + let max_value = Math.max(...bin_value); + const res = bin_value.indexOf(max_value); + let lh = Math.max(res / 200, 1 - res / 200); + chr_wig_1.push({chr:chrom, start: bin_idx*bin_size, end: (bin_idx+1) * bin_size, value: -2 * lh}); + if(lh != 0.5){ + chr_wig_2.push({chr:chrom, start: bin_idx*bin_size, end: (bin_idx+1) * bin_size, value: -2 *(1-lh)}); + } + }); + } + return [chr_wig_1, chr_wig_2] + } + + async get_baf_signals_v2(h5_obj, h5_obj_keys, chrom, bin_size, signal_name){ + + /* return two list of dictionary*/ + let chr_wig_1 = []; + let chr_wig_2 = []; + if (h5_obj_keys.includes(signal_name)){ + let chrom_dataset = await h5_obj.get(signal_name); + let chrom_data = await chrom_dataset.to_array(); //create_nested_array(value, shape) + chrom_data.forEach((lh, bin_idx) => { + if (!isNaN(lh)){ + chr_wig_1.push({chr:chrom, start: bin_idx*bin_size, end: (bin_idx+1) * bin_size, value: -2 * ( 0.5 - lh )}); + if(lh != 0.5){ + chr_wig_2.push({chr:chrom, start: bin_idx*bin_size, end: (bin_idx+1) * bin_size, value: -2 * ( 0.5 + lh )}); + } + } + }); + } + console.log(chrom, chr_wig_1, chr_wig_2); + return [chr_wig_1, chr_wig_2] + + } + } + + class ParseSignals{ + + constructor(signals){ + /* + Parse a signal names + + parameter + --------- + signals: List of keys in pytor files + */ + this.signals = signals; + } + + get_rd_bins(){ + /* return list of rd bins */ + let rd_keys = []; + this.signals.forEach( val => { + let match = val.match(/^his_rd_p_(.*)_(\d+)$/); + if(match){ + rd_keys.push({chr:match[1], bin_size:match[2]}); + }}); + const rd_bins = [...new Set(rd_keys.map(item => Number(item.bin_size)))]; + return rd_bins + } + + get_snp_bins(){ + /* return list of snp bins */ + let slected_signals = []; + this.signals.forEach( val => { + let match = val.match(/^snp_likelihood_(.*)_(\d+)_mask$/); + if(match){ + slected_signals.push({chr:match[1], bin_size:match[2]}); + }}); + const bins = [...new Set(slected_signals.map(item => Number(item.bin_size)))]; + return bins + } + } + + class GetFit { + constructor(allBins) { + this.allBins = allBins; + } + getValues() { + const bins = Object.values(this.allBins).reduce( + (binResult, bin) => { return binResult.concat(bin.filter(a => a.binScore > 0).map(a => a.binScore)) }, []); + return bins + } + getMean(data) { + return (data.reduce(function (a, b) { return a + b; }) / data.length); + } + fit_data() { + let rd_list = this.getValues(); + let distParmas = getDistParams(rd_list); + return distParmas + } + + histogram(data, bins) { + const step = bins[1] - bins[0]; + const hist_bins = []; + + data.forEach((value, index) => { + bins.forEach((bin_value, bin_index) => { + if (!hist_bins[bin_value]) { + hist_bins[bin_value] = { count: 0 }; + } + if (bin_value <= value && value < bin_value + step) { + hist_bins[bin_value].count++; + return false; + } + }); + }); + const dist_p = []; + hist_bins.forEach((bin, index) => { dist_p.push(bin.count); }); + return dist_p + } + + } + + function range_function(start, stop, step) { + const data_array = Array(Math.ceil((stop - start) / step)) + .fill(start) + .map((x, y) => x + y * step); + return data_array; + } + + function filterOutliers(someArray) { + + if (someArray.length < 4) + return someArray; + + let values, q1, q3, iqr, maxValue, minValue; + + values = someArray.slice().sort((a, b) => a - b); //copy array fast and sort + + if ((values.length / 4) % 1 === 0) { //find quartiles + q1 = 1 / 2 * (values[(values.length / 4)] + values[(values.length / 4) + 1]); + q3 = 1 / 2 * (values[(values.length * (3 / 4))] + values[(values.length * (3 / 4)) + 1]); + } else { + q1 = values[Math.floor(values.length / 4 + 1)]; + q3 = values[Math.ceil(values.length * (3 / 4) + 1)]; + } + + iqr = q3 - q1; + maxValue = q3 + iqr * 1.5; + minValue = q1 - iqr * 1.5; + + return values.filter((x) => (x >= minValue) && (x <= maxValue)); + } + + function getDistParams(bins) { + let filteredBins = filterOutliers(bins); + const n = filteredBins.length; + const mean = filteredBins.reduce((a, b) => a + b) / n; + const std = Math.sqrt(filteredBins.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n); + return [mean, std] + } + + function linspace(a, b, n) { + if (typeof n === "undefined") n = Math.max(Math.round(b - a) + 1, 1); + if (n < 2) { + return n === 1 ? [a] : []; + } + var ret = Array(n); + n--; + for (let i = n; i >= 0; i--) { + ret[i] = (i * b + (n - i) * a) / n; + } + return ret; + } + + var g_utils = { range_function, getDistParams, linspace, GetFit}; + + /** + * Evaluates the cumulative distribution function (CDF) for a Student's t distribution with degrees of freedom `v` at a value `t`. + * + * @params {number} t - value for the t test + * @params {PositiveNumber} v - degree of freedom + * @returns {Probability} evaluated CDF + */ + function TdistributionCDF(t, v){ + if (isNaN( t ) || isNaN( v ) || v <= 0.0) { + return NaN; + } + if ( t === 0.0 ) { + return 0.5; + } + return 1/2 + (1/2 * (incompbeta(1/2*v, 1/2, 1) - incompbeta(1/2*v, 1/2, v/(v+t*t)))) * Math.sign( t) + + } + + + /** + * incompbeta(a,b,x) evaluates incomplete beta function, here a, b > 0 and 0 <= x <= 1. This function requires contfractbeta(a,b,x, ITMAX = 200) + * code translated from https://malishoaib.wordpress.com/2014/04/15/the-beautiful-beta-functions-in-raw-python/ + * + * @params + * @params + * @params + * @returns + */ + function incompbeta(a, b, x){ + + if(x == 0){ + return 0; + } + else if (x == 1){ + return 1; + }else + { + let lbeta = lgamma(a+b) - lgamma(a) - lgamma(b) + a * Math.log(x) + b * Math.log(1-x); + if (x < (a+1) / (a+b+2)){ + return Math.exp(lbeta) * contfractbeta(a, b, x) / a; + } + else { + return 1 - Math.exp(lbeta) * contfractbeta(b, a, 1-x) / b; + } + } + } + + /** + * contfractbeta() evaluates the continued fraction form of the incomplete Beta function; incompbeta(). + * (Code translated from: Numerical Recipes in C.) + * + * @param {*} a + * @param {*} b + * @param {*} x + * @param {*} ITMAX + * @returns + */ + function contfractbeta(a,b,x, ITMAX = 1000){ + + let EPS = 3.0e-7; + let az = 1.0; + let am = 1.0; + let bm = 1.0; + let qab = a + b; + let qap = a + 1.0; + let qam = a-1.0; + let bz = 1.0 - qab*x/qap; + + //for i in range(ITMAX): + for(let i =0; i<= ITMAX; i++){ + let em = parseFloat(i+1); + let tem = em + em; + let d = em*(b-em)*x/((qam+tem)*(a+tem)); + let ap = az + d*am; + let bp = bz+d*bm; + d = -(a+em)*(qab+em)*x/((qap+tem)*(a+tem)); + let app = ap+d*az; + let bpp = bp+d*bz; + let aold = az; + am = ap/bpp; + bm = bp/bpp; + az = app/bpp; + bz = 1.0; + if (Math.abs(az-aold)<(EPS * Math.abs(az))){ + return az + } + } + } + + /** + * Evaluates factorial of a number + * + * @param {Number} xf - Integer number + * @returns factorial of the number + */ + function factorial(xf) { + if ((xf == 0) || (xf == 1)) return 1; + else { + let result = (xf * factorial(xf - 1)); + return result + } + } + + /** + * Evalues factorial for an integer or fraction using either either factorial or gamma function + * + * @param {Number} a - integar or fraction number + * @returns value of a gamma function + */ + function gamma(a){ + let gamma; + + var qc = [75122.6331530, 80916.6278952, 36308.2951477, 8687.24529705, 1168.92649479, 83.8676043424, 2.50662827511]; + + var sum1 = 0; + var prod1 = 1; + if (a == 0) { + gamma = 1e99; + }else { + if ((a % 1) == 0) {//if integer + gamma = factorial(a - 1); + } + else { //not integer + for (let j = 0; j < qc.length; j++) { + sum1 = sum1 + qc[j] * Math.pow(a, j); + prod1 = prod1 * (a + j); + } + gamma = (sum1 * Math.pow((a + 5.5), (a + 0.5))) * Math.exp(-(a + 5.5)) / prod1; + } + } + + return gamma + } + + /** + * + * @param {Number} xg - integar or fraction number + * @returns natural log of gamma function + */ + + function lgamma(xg){ + return Math.log(gamma(xg)) + } + + function t_test_1_sample$1(mean, m, s, n) { + if (s == 0) s = 1; + var t = ((mean - m) / s) * Math.sqrt(n); + var p = 1.0 - TdistributionCDF(Math.abs(t), (n - 1)); + return p + } + + function t_test_2_samples$1(m1, s1, n1, m2, s2, n2) { + if (s1 == 0) s1 = 1; + if (s2 == 0) s2 = 1; + var t = (m1 - m2) / Math.sqrt(s1 ** 2 / n1 + s2 ** 2 / n2); + var df = ((s1 ** 2 / n1 + s2 ** 2 / n2) ** 2 * (n1 - 1) * (n2 - 1)) / + ((s1 ** 4 * (n2 - 1)) / n1 ** 2 + (s2 ** 4 * (n1 - 1)) / n2 ** 2); + + var p = 1.0 - TdistributionCDF(Math.abs(t), parseInt(df + 0.5)); + + return p + } + + var t_dist = {TdistributionCDF, gamma, t_test_1_sample: t_test_1_sample$1, t_test_2_samples: t_test_2_samples$1}; + + class CombinedCaller{ + constructor(wigFeatures, binSize) { + this.wigFeatures = wigFeatures; + this.binSize = binSize; + // let fit_obj = this.get_fit() + // this.globalMean = fit_obj.globalMean + // this.globalStd = fit_obj.globalStd + } + get_fit(){ + var fit_info = new g_utils.GetFit(this.wigFeatures); + var [globalMean, globalStd] = fit_info.fit_data(); + + return {globalMean:globalMean, globalStd:globalStd} + + } + async call_2d(omin=null, mcount=null, event_type="both", max_distance=0.1, baf_threshold=0, max_copy_number=10, min_cell_fraction=0.0){ + + let fit_obj = this.get_fit(); + this.globalMean = fit_obj.globalMean; + this.globalStd = fit_obj.globalStd; + + let overlap_min = omin==null ? 0.05 * this.binSize / 3e9: omin ; + let min_count = mcount == null ? parseInt(this.binSize / 10000) : mcount ; + + let gstat_rd0 = []; + let gstat_rd_all = []; + let gstat_rd = []; + let gstat_error = []; + let gstat_lh = []; + let gstat_n = []; + + for (const [chr, wig] of Object.entries(this.wigFeatures)) { + let segments = []; + let levels = []; + let likelihoods = []; + + wig.forEach((bin, bin_idx) => { + if (bin.hets_count > 4 ){ + + if( bin.dp_count > min_count ){ + segments.push([bin_idx]); + levels.push(bin.binScore); + likelihoods.push(bin.likelihood_score); + delete bin.likelihood_score; + + } + } + }); + + let diff_level = []; + for(let i=1; i {return Math.sqrt(Math.sqrt(x) ** 2 + this.globalStd ** 2 + Math.pow(min_flank[x_idx]/2, 2));}); + + let overlaps = []; + + for(let i=0; i< segments.length-1; i++){ + + let lh_overlap = 0; + try{ + lh_overlap = likelihood_overlap(likelihoods[i], likelihoods[i+1]); + }catch{ + console.log("Overlap failed: ", i, likelihoods[i], segments[i+1], likelihoods[i+1]); + } + + let rd_overlap = normal_overlap_approx(levels[i], error[i], levels[i+1], error[i+1]); + overlaps.push(rd_overlap * lh_overlap); + + } + + while(overlaps.length >0) { + overlaps = overlaps.filter(num => typeof num === "number"); + + let max_overlap = arrayMax(overlaps); + if(isNaN(max_overlap)){ + console.log('NaN value', overlaps); + } + if(max_overlap < overlap_min){ + // console.log("maxoverlap ",max_overlap, "is smaller than overlap min") + break + } + let i = overlaps.indexOf(max_overlap); + + let merge_level = normal_merge(levels[i], error[i], levels[i + 1], error[i + 1]); + + let nlh; + let nlh_sum; + try{ + nlh = likelihoods[i].map((l_value, l_idx) => { return l_value * likelihoods[i+1][l_idx]}); + + nlh_sum = nlh.reduce((a, c_value) => {return a + c_value}); + + }catch{ + console.log(likelihoods); + console.log('max_overlap:', max_overlap, i, overlaps.length); + console.log('likelihood: ', i ,likelihoods[i], likelihoods[i+1]); + console.log('nlh: ', nlh_sum); + } + // nlh_sum = nlh.reduce((a, c_value) => {return a + c_value}); + + levels[i] = merge_level.nl; + error[i] = merge_level.ne; + + likelihoods[i] = nlh.map(function(item) { return item/nlh_sum } ); + + segments[i].push(...segments[i+1]); + + levels.splice(i + 1, 1); + error.splice(i + 1, 1); + segments.splice(i + 1, 1); + likelihoods.splice(i + 1, 1); + overlaps.splice(i, 1); + + if(i < overlaps.length){ + + let rd_overlap = normal_overlap_approx(levels[i], error[i], levels[i+1], error[i+1]); + let new_overlap = rd_overlap * likelihood_overlap(likelihoods[i], likelihoods[i + 1]); + + overlaps[i] = new_overlap; + } + if(i > 0){ + let new_overlap = normal_overlap_approx(levels[i - 1], error[i - 1], levels[i], error[i]) + * likelihood_overlap(likelihoods[i - 1], likelihoods[i]); + overlaps[i - 1] = new_overlap; + } + + } + let ons = -1; + while(true){ + overlaps = []; + for(let i=0; i< levels.length; i++){ + for(let j=i; j { return l_value * likelihoods[i+1][l_idx]}); + let nlh_sum = nlh.reduce((a, c_value) => {return a + c_value}); + likelihoods[i] = nlh.map(function(item) { return item/nlh_sum } ); + + + segments[i].push(...segments[i+1]); + segments[i] = segments[i].sort((a,b) => a-b); + + levels.splice(j, 1); + error.splice(j, 1); + segments.splice(j, 1); + likelihoods.splice(j, 1); + + if(j >= segments.length){ + i += 1; + j = i + 1; + } + + }else { + j += 1; + if(j >= segments.length){ + i += 1; + j = i + 1; + } + } + } + if(ons == segments.length){ + break + } + ons = segments.length; + } + // console.log('final segments', segments) + + segments.forEach((seg_value, seg_idx) => { + let baf_info = likelihood_baf_pval(likelihoods[seg_idx]); + if(seg_value.length > 1){ + + seg_value.forEach((bin, bin_idx) =>{ + gstat_rd_all.push(wig[bin]); + if(baf_info.mean <= baf_threshold){ + gstat_rd0.push(wig[bin]); + } + wig[bin].segment_score = levels[seg_idx]; + }); + gstat_rd.push(levels[seg_idx]); + gstat_error.push(error[seg_idx]); + gstat_lh.push(likelihoods[seg_idx]); + + } + + }); + + continue + } + + // Third stage for call + + // let data = gstat_rd0.lengthn == 0 ? gstat_rd_all: gstat_rd0 ; + + let points = parseInt(1000 * (1 - min_cell_fraction)); + if(points == 0){ + points = 1; + } + let x = g_utils.linspace(min_cell_fraction, 1, points); + let master_lh = {}; + let germline_lh = {}; + for(let cn=10; cn > -1; cn--){ + for(let h1=0; h1 < (cn/2+1); h1++){ + let h2 = cn - h1; + let mrd = x.map((v, idx) => {return 1-v +v*cn/2}); + let g_mrd = cn / 2; + let g_mbaf; + let mbaf; + if(cn > 0){ + g_mbaf = 0.5 - (h1 / (h1 + h2)); + mbaf = x.map((v, idx) => {return 0.5 - (1 - v + v * h1) / (2 - 2 * v + (h1 + h2) * v)}); + + }else { + g_mbaf = 0; + mbaf = x.map((v, idx) => {return 0*v}); + } + + for( let ei=0; ei < gstat_rd.length; ei++){ + + let g_lh = normal(g_mrd * this.globalMean, 1, gstat_rd[ei], gstat_error[ei]) * likelihood_of_baf(gstat_lh[ei], 0.5 + g_mbaf); + if(ei in germline_lh){ + germline_lh[ei].push([cn, h1, h2, g_lh, 1.0]); + }else { + germline_lh[ei] = [cn, h1, h2, g_lh, 1.0]; + } + let slh = 0; + let max_lh = 0; + let max_x = 0; + mrd.forEach((mi, idx) => { + if(!isNaN(mbaf[idx])){ + let tmpl = normal(mi * this.globalMean, 1, gstat_rd[ei], gstat_error[ei]) * likelihood_of_baf(gstat_lh[ei], 0.5 + mbaf[idx]); + slh += tmpl; + if(tmpl > max_lh){ + max_lh = tmpl; + max_x = x[idx]; + } + } + }); + if(ei in master_lh){ + master_lh[ei].push([cn, h1, h2, slh / x.length, max_x]); + }else { + master_lh[ei] = [cn, h1, h2, slh / x.length, max_x]; + } + } + + for( let ei=0; ei < gstat_rd.length; ei++){ + if(event_type == "germline"){ + master_lh[ei].sort((a, b) => a[3] - b[3]); + } + else { + master_lh[ei].sort((a, b) => a[3] - b[3]); + if(event_type == "both"){ + + germline_lh[ei].sort((a, b) => a[3] - b[3]); + if(germline_lh[ei][0][3] > master_lh[ei][0][3]){ + //let tmp_list = list(filter( lambda x: x[0] != germline_lh[ei][0][0] and x[1] != germline_lh[ei][0][1], master_lh[ei])) + let tmp_list = master_lh[ei].filter((x) => (x[0] != germline_lh[ei][0][0]) && (x[1] <= germline_lh[ei][0][1])); + // console.log('tmp_list', tmp_list) + // master_lh[ei] = [germline_lh[ei][0]] + tmp_list + master_lh[ei] = [germline_lh[ei][0]].push(...tmp_list); + } + } + } + } + for( let ei=0; ei < gstat_rd.length; ei++){ + if(master_lh[ei][0][0] > 2); + if(master_lh[ei][0][0] < 2); + gstat_rd[ei] / this.globalMean; + t_dist.t_test_1_sample(this.globalMean, gstat_rd[ei], gstat_error[ei], gstat_n[ei]); + // console.log(etype) + + } + + + // break + } + + } + + var rawbinScore = this.formatDataStructure(this.wigFeatures, 'binScore', this.globalMean); + var callScore = this.formatDataStructure(this.wigFeatures, 'segment_score', this.globalMean); + + return {binScore: rawbinScore, segment_score: callScore} + + } + + formatDataStructure(wigFeatures, feature_column, scaling_factor = 1) { + const results = []; + for (const [chr, wig] of Object.entries(wigFeatures)) { + + wig.forEach(sample => { + var new_sample = { ...sample }; + if (scaling_factor != 1) { + new_sample.value = sample[feature_column] / scaling_factor * 2; + } + results.push(new_sample); + }); + } + + return results + } + + formatDataStructure_BAF(feature_column, scaling_factor = 2) { + const baf1 = []; + const baf2 = []; + for (const [chr, wig] of Object.entries(this.wigFeatures)) { + + wig.forEach(sample => { + + var baf1_value = { ...sample }; + var baf2_value = { ...sample }; + + let value = sample[feature_column]; + if (value != 0.5){ + baf2_value.value = -2 * (1 - value); + baf2.push(baf2_value); + } + baf1_value.value = -2 * value; + baf1.push(baf1_value); + + }); + } + + + return [baf1, baf2] + } + } + + function arrayMax(arr) { + return arr.reduce(function (p, v) { + return ( p > v ? p : v ); + }); + } + + /** + * Normal distribution. + * + * @param {float} x - Variable. + * @param {float} a - area + * @param {float} x0 - Mean value + * @param {float} sigma - Sigma + * @returns {float} - Value of distribution in x. + */ + function normal(x, a, x0, sigma){ + + return a * Math.exp(-1* (x - x0) ** 2 / (2 * sigma ** 2)) / Math.sqrt(2 * Math.PI) / sigma + + } + + /** + * Calculates two normal distributions overlap area. + * + * @param {float} m1 - Mean value of the first distribution + * @param {float} s1 - Sigma of the first distribution + * @param {float} m2 - Mean value for second distribution + * @param {float} s2 - Sigma of the second distribution + * + * @returns {float} area - Area of overlap + */ + function normal_overlap_approx(m1, s1, m2, s2){ + + return Math.exp(-1* (m1-m2)**2/ (s1**2+s2**2)) + } + + + /** + * Returns overlap area of two likelihood functions. + * + * @param {*} lk1 - First likelihood function. + * @param {*} lk2 - Second likelihood function. + * + * @returns {float} - Overlap area. + */ + function likelihood_overlap(likelihood_1, likelihood_2){ + let sum; + try{ + sum = likelihood_1.reduce((accumulator, currentValue, currentIndex) => {return accumulator + Math.min(currentValue, likelihood_2[currentIndex])}); + }catch{ + console.log("Failed to find likelihood overlap: ", likelihood_1, likelihood_2); + return 0 + } + + return sum + } + + /** + * Calculates normal distribution that is product of two given normal distributions. + * + * @param {float} m1 - Mean value of the first distribution + * @param {float} s1 - Sigma of the first distribution + * @param {float} m2 - Mean value for second distribution + * @param {float} s2 - Sigma of the second distribution + * @returns {Object} An object representing the first distribution + * @property {float} nl - Mean value of the first distribution + * @property {float} ne - Sigma of the first distribution + */ + function normal_merge(m1, s1, m2, s2){ + + if((s1 == 0) && (s2 == 0)){ + return {nl: 0.5 * (m1 + m2), ne: 0} + } + else { + return {nl: (m1 * s2 * s2 + m2 * s1 * s1) / (s1 * s1 + s2 * s2), ne: Math.sqrt(s1 * s1 * s2 * s2 / (s1 * s1 + s2 * s2))} + } + } + + /** + * Calculates likelihood for given baf + * @param {*} likelihood + * @param {*} baf + * @returns {float} likelihood value + */ + function likelihood_of_baf(likelihood, baf){ + + let bin = parseInt(baf * (likelihood.length - 1)); + let fr = baf * (likelihood.length - 1) - bin; + if(bin < likelihood.length - 1){ + return likelihood[bin] * (1 - fr) + likelihood[bin + 1] * fr + } + else { + return likelihood[bin] + } + } + + /** + * + * Calculates baf level and p-value for given likelihood function. + * + * @param {*} likelihood + * @returns {Object} An object representing BAF + * @property {float} mean BAF level (difference from 1/2) + * @property {float} p p-value for event different than 1/2 + */ + function likelihood_baf_pval(likelihood) { + const res = likelihood.length; + const max_lh = Math.max(...likelihood); + let ix = likelihood.indexOf(max_lh); + if (ix > Math.floor(res / 2)) { + ix = res - 1 - ix; + } + const b = (res / 2 - ix) / (res + 1); + + const ix1 = Math.floor((res / 2 + ix) / 2); + const ix2 = res - 1 - ix1; + let p = likelihood.slice(ix1, ix2 + 1).reduce((acc, val) => acc + val, 0) / likelihood.reduce((acc, val) => acc + val, 0); + if (ix === Math.floor(res / 2)) { + p = 1.0; + } + return {mean:b, p:p}; + } + + var combined_caller = {CombinedCaller}; + + function erf(x) { + var m = 1.0, s = 1.0, sum = x * 1.0; + for (var i = 1; i < 50; i++) { + m *= i; + s *= -1; + sum += (s * Math.pow(x, 2.0 * i + 1.0)) / (m * (2.0 * i + 1.0)); + } + return (2 * sum) / Math.sqrt(3.14159265358979) + } + + function getEValue(mean, sigma, rd, start, end) { + var arr = new DataStat(rd.slice(start, end)); + if (arr.std == 0) { + if (sigma > 0) { arr.std = (sigma * arr.mean) / mean; } + else { arr.std = 1; } + } + var p_val = t_test_1_sample(mean, arr.mean, arr.std, end - start) / (end - start); + return p_val + } + + function gaussianEValue(mean, sigma, rd, start, end) { + var arr = new DataStat(rd.slice(start, end)); + + if (arr.mean < mean) { + var x = (arr.max - arr.mean) / (sigma * Math.sqrt(2)); + return Math.pow(0.5 * (1 + erf(x)), end - start) + } + var x = (arr.min - arr.mean) / (sigma * Math.sqrt(2)); + return Math.pow(0.5 * (1 - erf(x)), end - start) + } + + function adjustToEvalue(mean, sigma, rd, start, end, pval, max_steps = 1000) { + var val = getEValue(mean, sigma, rd, start, end); + var step = 0, done = false; + while ((val > pval) & !done & (step < max_steps)) { + done = true; + step += 1; + var [v1, v2, v3, v4] = [1e10, 1e10, 1e10, 1e10]; + if (start > 0) v1 = getEValue(mean, sigma, rd, start - 1, end); + if (end - start > 2) { + var v2 = getEValue(mean, sigma, rd, start + 1, end); + var v3 = getEValue(mean, sigma, rd, start, end - 1); + } + if (end < rd.length) { var v4 = getEValue(mean, sigma, rd, start, end + 1); } + if (Math.min[(v4)] < val) { + done = false; + if (v1 == Math.min[(v4)]) { + start -= 1; + val = v1; + } + elif(v2 == Math.min[(v4)]); { + start += 1; + val = v2; + } + elif(v3 == Math.min[(v4)]); { + end -= 1; + val = v3; + } + elif(v4 == Math.min[(v4)]); { + end += 1; + val = v4; + } + } + } + if (val <= pval) { return end } + return 0; + } + + class DataStat { + constructor(data_array) { + this.data = data_array; + this.mean = data_array.reduce((acc, n) => acc + n) / data_array.length; + this.std = Math.sqrt(data_array.reduce((acc, n) => (n - this.mean) ** 2) / data_array.length); + } + } + + function t_test_1_sample(mean, m, s, n) { + if (s == 0) s = 1; + var t = ((mean - m) / s) * Math.sqrt(n); + var p = 1.0 - t_dist.TdistributionCDF(Math.abs(t), (n - 1)); + return p + } + + function t_test_2_samples(m1, s1, n1, m2, s2, n2) { + if (s1 == 0) s1 = 1; + if (s2 == 0) s2 = 1; + var t = (m1 - m2) / Math.sqrt(s1 ** 2 / n1 + s2 ** 2 / n2); + var df = ((s1 ** 2 / n1 + s2 ** 2 / n2) ** 2 * (n1 - 1) * (n2 - 1)) / + ((s1 ** 4 * (n2 - 1)) / n1 ** 2 + (s2 ** 4 * (n1 - 1)) / n2 ** 2); + + var p = 1.0 - t_dist.TdistributionCDF(Math.abs(t), parseInt(df + 0.5)); + + return p + } + + class Partition { + + constructor(rd, mean, std) { + this.rd = rd; + this.mean = mean; + this.std = std; + this.bin_bands = [2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128]; + } + + get_rd_signal_bandwidth(data_array) { + var new_array = []; + + data_array.forEach((value, index) => { + var tmp_value = 0; + if (value > this.mean / 4) { + tmp_value = this.mean / (this.std ** 2 * value); + } else { + tmp_value = 4 / this.std ** 2; + } + new_array.push(tmp_value); + }); + return new_array + } + + meanShiftCaller(repeats = 3) { + var ChrLevels = {}; + + Object.entries(this.rd).forEach(([chr, chr_rd]) => { + var masked = new Array(chr_rd.length).fill(false); + + // set the level + var levels = new Array(chr_rd.length); + + for (let b = 0; b < chr_rd.length; b++) { + if (!masked[b]) { levels[b] = chr_rd[b]; } + } + + this.bin_bands.forEach((bin_band, bin_band_index) => { + // not masked levels at current bin + // get boolean values + var not_masked = masked.map((value, index) => { return !value; }); + + // not masked level at current bin + var nm_levels = []; + + Object.entries(chr_rd).forEach(([k, v]) => { nm_levels.push(v.binScore); }); + + // set the mask border + var mask_borders = [0]; + var count = 0; + + for (let i = 0; i < masked.length; i++) { + if (masked[i]) { + if (count > 0) { + mask_borders.push(mask_borders[mask_borders.length - 1] + count - 1); + count = 0; + } + } else { count += 1; } + } + + mask_borders.shift(); + + // repeating steps + for (let step = 0; step < repeats; step++) { + var isig = this.get_rd_signal_bandwidth(nm_levels); + var grad = new Array(nm_levels.length).fill(0); + + for (let i = 0; i < nm_levels.length; i++) { + for (let j = i - 3 * bin_band; j <= i + 3 * bin_band + 1; j++) { + if (j < 0 || j >= nm_levels.length) continue; + if (Math.abs(i - j) >= nm_levels.length) continue; + + var g_value = (j - i) * Math.exp((-0.5 * (j - i) ** 2) / bin_band ** 2) * + Math.exp(-0.5 * (nm_levels[i] - nm_levels[j]) ** 2 * isig[i]); + + grad[i] += g_value; + } + } + + // get the border + var border = new Array(); + for (var i = 0; i < grad.length - 1; i++) { + if ((grad[i] < 0) & (grad[i + 1] >= 0)) border.push(i); + } + + border.push(grad.length - 1); + border = border.concat(mask_borders).sort((a, b) => a - b); + border = Array.from(new Set(border)); + + var pb = 0; + for (var i = 0; i < border.length; i++) { + var range_array = nm_levels.slice(pb, border[i] + 1); + var range_mean = range_array.reduce((acc, n) => acc + n) / range_array.length; + + nm_levels.fill(range_mean, pb, border[i] + 1); + pb = border[i] + 1; + } + } + + for (var i = 0, j = 0; i < levels.length; i++) { + if (not_masked[i]) { + levels[i] = nm_levels[j]; + j++; + } + } + + //get the border + var border = new Array(); + for (var i = 0; i < levels.length - 1; i++) { + //if(i == levels.length -1) continue; + var diff = Math.abs(levels[i + 1] - levels[i]); + + if (diff > 0.01) border.push(i + 1); + } + + border.unshift(0); + border.push(levels.length); + + // reset the mask + masked = new Array(this.rd.length).fill(false); + + // check the borders + for (var i = 1; i < border.length; i++) { + var seg = [border[i - 1], border[i]]; + var seg_left = [border[i - 1], border[i - 1]]; + if (i > 1) { seg_left[0] = border[i - 2]; } else continue; + + var seg_right = [border[i], border[i]]; + if (i < border.length - 1) { seg_right[1] = border[i + 1]; } else continue; + + var n = seg[1] - seg[0]; + var n_left = seg_left[1] - seg_left[0]; + var n_right = seg_right[1] - seg_right[0]; + if (n <= 1) continue; + var seg_array = new DataStat(levels.slice(seg[0], seg[1])); + + if (n_right <= 15 || n_left <= 15 || n <= 15) { + var ns = 1.8 * Math.sqrt(levels[seg_left[0]] / this.mean) * this.std; + if (Math.abs(levels[seg_left[0]] - levels[seg[0]]) < ns) { continue } + + ns = 1.8 * Math.sqrt(levels[seg_right[0]] / this.mean) * this.std; + if (Math.abs(levels[seg_right[0]] - levels[seg[0]]) < ns) { continue } + } else { + var seg_left_array = levels.slice(seg_left[0], seg_left[1]); + var seg_left_1 = new DataStat(seg_left_array); + + var seg_right_array = levels.slice(seg_right[0], seg_right[1]); + var seg_right_1 = new DataStat(seg_right_array); + + var ttest_2sample_1 = t_test_2_samples(seg_array.mean, seg_array.std, seg_array.data.length, + seg_left_1.mean, seg_left_1.std, seg_left_1.data.length); + if (ttest_2sample_1 > (0.01 / genome_size) * bin_size * (n + n_left)) { continue } + + var ttest_2sample_2 = t_test_2_samples(seg_array.mean, seg_array.std, seg_array.data.length, + seg_right_1.mean, seg_right_1.std, seg_right_1.data.length); + if (ttest_2sample_2 > (0.01 / genome_size) * bin_size * (n + n_right)) { continue } + } + + var ttest_1sample_1 = t_test_1_sample(this.mean, seg_array.mean, seg_array.std, seg_array.data.length); + if (ttest_1sample_1 > 0.05) { continue } + let segments_rd = nm_levels.slice(seg[0], seg[1]); + var raw_seg_data = new DataStat(segments_rd); + + masked.fill(true, seg[0], seg[1]); + levels.fill(raw_seg_data.mean, seg[0], seg[1]); + } + }); + + ChrLevels[chr] = levels; + + }); + return ChrLevels + } + call_mean_shift(repeats = 3) { + const bin_size = 1000; + // const genome_size = bin_size * this.rd.length; + const genome_size = 2871000000; + var masked = new Array(this.rd.length).fill(false); + + // set the level + var levels = new Array(this.rd.length); + for (var b = 0; b < this.rd.length; b++) { + if (!masked[b]) { + levels[b] = this.rd[b]; + } + } + this.bin_bands.forEach((bin_band, bin_band_index) => { + // not masked levels at current bin + // get boolean values + var not_masked = masked.map((value, index) => { + return !value; + }); + + // not masked level at current bin + // var nm_levels = not_masked.map((value, index) => {if(value) return this.rd[index]}); + var nm_levels = []; + not_masked.forEach((value, index) => { + if (value) nm_levels.push(this.rd[index]); + }); + + // console.log(bin_band, nm_levels); + + // set the mask border + var mask_borders = [0]; + var count = 0; + + for (var i = 0; i < masked.length; i++) { + if (masked[i]) { + if (count > 0) { + mask_borders.push( + mask_borders[mask_borders.length - 1] + count - 1, + ); + count = 0; + } + } else { + count += 1; + } + } + + mask_borders.shift(); + // repeating steps + for (var step = 0; step < repeats; step++) { + var isig = this.get_rd_signal_bandwidth(nm_levels); + // console.log(isig); + var grad = new Array(nm_levels.length).fill(0); + + for (var i = 0; i < nm_levels.length; i++) { + for (var j = i - 3 * bin_band; j <= i + 3 * bin_band + 1; j++) { + if (j < 0 || j >= nm_levels.length) continue; + if (Math.abs(i - j) >= nm_levels.length) continue; + + var g_value = + (j - i) * + Math.exp((-0.5 * (j - i) ** 2) / bin_band ** 2) * + Math.exp(-0.5 * (nm_levels[i] - nm_levels[j]) ** 2 * isig[i]); + // console.log(g_value); + grad[i] += g_value; + } + } + // console.log(grad); + + // get the border + var border = new Array(); + for (var i = 0; i < grad.length - 1; i++) { + if ((grad[i] < 0) & (grad[i + 1] >= 0)) border.push(i); + } + + border.push(grad.length - 1); + border = border.concat(mask_borders).sort((a, b) => a - b); + border = Array.from(new Set(border)); + + var pb = 0; + for (var i = 0; i < border.length; i++) { + var range_array = nm_levels.slice(pb, border[i] + 1); + var range_mean = + range_array.reduce((acc, n) => acc + n) / range_array.length; + + nm_levels.fill(range_mean, pb, border[i] + 1); + pb = border[i] + 1; + } + } + + for (var i = 0, j = 0; i < levels.length; i++) { + if (not_masked[i]) { + levels[i] = nm_levels[j]; + j++; + } + } + + //get the border + var border = new Array(); + for (var i = 0; i < levels.length - 1; i++) { + //if(i == levels.length -1) continue; + var diff = Math.abs(levels[i + 1] - levels[i]); + + if (diff > 0.01) border.push(i + 1); + } + + border.unshift(0); + border.push(levels.length); + + // reset the mask + masked = new Array(this.rd.length).fill(false); + + // check the borders + for (var i = 1; i < border.length; i++) { + var seg = [border[i - 1], border[i]]; + var seg_left = [border[i - 1], border[i - 1]]; + if (i > 1) { + seg_left[0] = border[i - 2]; + } else continue; + + var seg_right = [border[i], border[i]]; + if (i < border.length - 1) { + seg_right[1] = border[i + 1]; + } else continue; + + var n = seg[1] - seg[0]; + var n_left = seg_left[1] - seg_left[0]; + var n_right = seg_right[1] - seg_right[0]; + if (n <= 1) continue; + var seg_array = new DataStat(levels.slice(seg[0], seg[1])); + + if (n_right <= 15 || n_left <= 15 || n <= 15) { + var ns = 1.8 * Math.sqrt(levels[seg_left[0]] / this.mean) * this.std; + if (Math.abs(levels[seg_left[0]] - levels[seg[0]]) < ns) { + continue; + } + + ns = 1.8 * Math.sqrt(levels[seg_right[0]] / this.mean) * this.std; + if (Math.abs(levels[seg_right[0]] - levels[seg[0]]) < ns) { + continue; + } + } else { + var seg_left_array = levels.slice(seg_left[0], seg_left[1]); + var seg_left_1 = new DataStat(seg_left_array); + + var seg_right_array = levels.slice(seg_right[0], seg_right[1]); + var seg_right_1 = new DataStat(seg_right_array); + + var ttest_2sample_1 = t_test_2_samples( + seg_array.mean, + seg_array.std, + seg_array.data.length, + seg_left_1.mean, + seg_left_1.std, + seg_left_1.data.length, + ); + if ( + ttest_2sample_1 > + (0.01 / genome_size) * bin_size * (n + n_left) + ) { + continue; + } + + var ttest_2sample_2 = t_test_2_samples( + seg_array.mean, + seg_array.std, + seg_array.data.length, + seg_right_1.mean, + seg_right_1.std, + seg_right_1.data.length, + ); + if ( + ttest_2sample_2 > + (0.01 / genome_size) * bin_size * (n + n_right) + ) { + continue; + } + } + + var ttest_1sample_1 = t_test_1_sample( + this.mean, + seg_array.mean, + seg_array.std, + seg_array.data.length, + ); + if (ttest_1sample_1 > 0.05) { + continue; + } + var raw_seg_data = new DataStat(this.rd.slice(seg[0], seg[1])); + + masked.fill(true, seg[0], seg[1]); + levels.fill(raw_seg_data.mean, seg[0], seg[1]); + } + }); + return levels; + } + + cnv_calling(bin_size = 100000) { + var delta = 0.25; + var delta = delta * this.mean; + + var min = this.mean - delta, max = this.mean + delta; + // console.log('min: ', min, ', Max: ', max) + + // console.log('delta', delta) + var normal_genome_size = 2971000000; + + var levels = this.meanShiftCaller(); + + + var merged_level = {}; + var cnv_levels = []; + // var t_value = cdf(Math.abs(10), (5)) + // console.log('Testing student t test:', t_value) + + Object.entries(levels).forEach(([chr, chr_levels]) => { + + var done = false; + while (!done) { + done = true; + + // + // get the borders + // + var borders = new Array(1).fill(0); + for (let i = 0; i < chr_levels.length - 1; i++) { + var diff = Math.abs(chr_levels[i + 1] - chr_levels[i]); + if (diff > 0.01) borders.push(i + 1); + } + borders.push(chr_levels.length); + + for (let ix = 0; ix < borders.length - 2; ix++) { + var v1 = Math.abs(chr_levels[borders[ix]] - chr_levels[borders[ix + 1]]); + // console.log(ix, v1); + if (v1 < delta) { + var v2 = v1 + 1, v3 = v1 + 1; + + if (ix > 0) { v2 = Math.abs(chr_levels[borders[ix]] - chr_levels[borders[ix - 1]]); } + if (ix < borders.length - 3) { v3 = Math.abs(levels[borders[ix + 1]] - chr_levels[borders[ix + 2]]); } + + if (v1 < v2 && v1 < v3) { + done = false; + + var tmp_array = new DataStat(chr_levels.slice(borders[ix], borders[ix + 2])); + chr_levels.fill(tmp_array.mean, borders[ix], borders[ix + 2]); + borders.splice(ix + 1, ix + 1); + } + } + } + } + // console.log('updated levels', chr_levels) + + // var chr_rd = this.rd[chr] + var chr_rd = []; + Object.entries(this.rd[chr]).forEach(([bin, binDict]) => { chr_rd.push(binDict.binScore); }); + // console.log('cnv_calling', chr_rd) + + // + // Calling Segments + // + + //var flags = [""] * levels.length; + var flags = new Array(chr_levels.length).fill(""); + + // console.log('default levels', chr, chr_levels) + var b = 0; + var pval = (0.05 * bin_size) / normal_genome_size; + while (b < chr_levels.length) { + var b0 = b, border_start = b; + while ((b < chr_levels.length) & (chr_levels[b] < min)) { + b += 1; + } + var border_end = b; + + if (border_end > border_start + 1) { + // console.log('border min', border_start, border_end) + var adj = adjustToEvalue(this.mean, this.std, chr_rd, border_start, border_end, pval); + // console.log(adj) + if (adj) { + var border_start, border_end = adj; + flags.fill("D", border_start, border_end); + } + } + border_start = b; + while ((b < chr_levels.length) & (chr_levels[b] > max)) { b += 1; } + border_end = b; + + if (border_end > border_start + 1) { + adj = adjustToEvalue(this.mean, this.std, chr_rd, border_start, border_end, pval); + // console.log(adj) + if (adj) { + (border_end = adj); + // flags[bs:be] = ["A"] * (be - bs) + flags.fill("A", border_start, border_end); + } + } + if (b == b0) b += 1; + } + + //console.log(chr, 'segments', segments) + // + // Calling additional deletions + // + b = 0; + while (b < chr_levels.length) { + while ((b < chr_levels.length) & (flags[b] != "")) b += 1; + border_start = b; + while ((b < chr_levels.length) & (chr_levels[b] < min)) b += 1; + border_end = b; + if (border_end > border_start + 1) { + if (gaussianEValue(this.mean, this.std, chr_rd, border_start, border_end) < 0.05 / normal_genome_size) { + flags.fill(["d"] * (border_end - border_start), border_start, border_end); + } + b -= 1; + } + b += 1; + } + + b = 0; + var cf; + if (b < chr_levels.length) cf = flags[b]; + + border_start = 0; + + //var merge = [...this.rd] + var merge = [...chr_rd]; + // console.log('initial merge', merge) + while (b < chr_levels.length) { + while (flags[b] == cf) { + b += 1; + if (b >= flags.length) break; + } + if (b > border_start) { + var merge_arr = new DataStat(merge.slice(border_start, b)); + merge.fill(merge_arr.mean, border_start, b); + } + if (b < chr_levels.length) cf = flags[b]; + border_start = b; + } + + merged_level[chr] = merge; + + + // + // calculate calls + // + b = 0; + while (b < chr_levels.length) { + cf = flags[b]; + if (cf == "") { + b += 1; + continue + } + border_start = b; + while (b < chr_levels.length & cf == flags[b]) { b += 1; } + var border_arr = new DataStat(merge.slice(border_start, b)); + let cnv = border_arr.mean / this.mean; + let event_type; + if (cf == "D") { + event_type = "deletion"; + } else { + event_type = "duplication"; + } + let cnv_dict = { + chr: chr, + start: bin_size * border_start + 1, + end: bin_size * b, + size: bin_size * (b - border_start + 1), + value: cnv * 2, + event_type: event_type + }; + cnv_levels.push(cnv_dict); + + } + }); + + return [merged_level, cnv_levels] + } + } + + + + var read_depth_caller = { Partition }; + + class CNVpytorVCF { + constructor(allVariants, binSize) { + this.allVariants = allVariants; + this.rowBinSize = 10000; + this.binSize = binSize; + this.binFactor = binSize / this.rowBinSize; + + } + + async computeDepthFeatures() { + + const chromosomes = Object.keys(this.allVariants); + const wigFeatures = []; + + for (let chr of chromosomes) { + const variants = this.allVariants[chr]; + if (variants.length === 0) continue + + const firstSnp = variants[0]; + + let sum = 0; + let count = 0; + let binStart = firstSnp.start = firstSnp.start % this.binSize; // Place start at integer multiple of binSize + let binEnd = binStart + this.binSize; + let featureBin = 0; + + for (let snp of variants) { + const position = snp.start + 1; // Convert from 0-base to 1-base coords + featureBin = Math.max(Math.floor((snp.start - 1) / this.binSize), 0); + if (position > binEnd) { + + if (count > 0) { + wigFeatures.push({ chr, start: binStart, end: binEnd, value: sum, bin: featureBin, count: count }); + } + + // Start new bin + sum = 0; + //count = 0 + binStart = snp.start - snp.start % this.binSize; + binEnd = binStart + this.binSize; + } + + const dpValue = snp.calls[9].info["DP"]; + // const dpValue = snp.getInfo("DP") + + if (dpValue) { + sum += Number.parseInt(dpValue); + count++; + } + } + + // final bin + if (count > 0) { + wigFeatures.push({ chr, start: binStart, end: binEnd, value: sum, bin: featureBin, count: count }); + } + } + + return wigFeatures + } + + // for for read depth calculation + async computeReadDepth() { + const chromosomes = Object.keys(this.allVariants); + var wigFeatures = {}; + + for (let chr of chromosomes) { + const variants = this.allVariants[chr]; + var featureBin; + if (variants.length === 0) continue + for (let snp of variants) { + featureBin = Math.max(Math.floor(snp.start / this.rowBinSize), 0); + if (!wigFeatures[chr]) { wigFeatures[chr] = []; } + if (!wigFeatures[chr][featureBin]) { + + wigFeatures[chr][featureBin] = { + chr, + start: featureBin * this.rowBinSize, + end: (featureBin + 1) * this.rowBinSize, + value: 0, + sum_score: 0, + count: 0, + }; + } + const dpValue = snp.calls[9].info["DP"]; + if (dpValue) { + // sum += Number.parseInt(dpValue) + wigFeatures[chr][featureBin].sum_score += Number.parseInt(dpValue); + wigFeatures[chr][featureBin].count++; + } + + } + // for last bin + //let last_bin = wigFeatures[chr][featureBin] + //last_bin.value = parseInt(last_bin.sum_score / last_bin.count) * 100 + } + + // adjust the bin values to actual bin size + var avgbin = {}; + for (let chr of chromosomes) { + if (!avgbin[chr]) { avgbin[chr] = []; } + for (let k = 0; k < wigFeatures[chr].length / this.binFactor; k++) { + const featureBin = k; + if (!avgbin[chr][k]) { + avgbin[chr][k] = { + chr, + binScore: 0, + start: featureBin * this.binSize, + end: (featureBin + 1) * this.binSize, + }; + } + + for (var j = k * 10; j < 10 * k + 10; j++) { + if (wigFeatures[chr][j]) { + var tmp_score = parseInt(wigFeatures[chr][j].sum_score / wigFeatures[chr][j].count) * 100; + avgbin[chr][k].binScore += tmp_score; + } + } + } + } + + var finalFeatureSet = this.readDepthMeanshift(avgbin); + return finalFeatureSet + + } + readDepthMeanshift(wigFeatures) { + + // Get global mean and standrad deviation + var fit_info = new g_utils.GetFit(wigFeatures); + var [globamMean, globalStd] = fit_info.fit_data(); + + // Apply partition method + var partition = new read_depth_caller.Partition(wigFeatures, globamMean, globalStd); + var partition_array = partition.meanShiftCaller(); + var caller_array = partition.cnv_calling(); + + // Assign the partition values to each bin + Object.entries(wigFeatures).forEach(([chr, chr_rd]) => { + chr_rd.forEach((bin, index) => { + bin.partition_level = parseInt(partition_array[chr][index]); + bin.partition_call = parseInt(caller_array[0][chr][index]); + }); + }); + + var rawbinScore = this.formatDataStructure(wigFeatures, 'binScore', globamMean); + var partition_levels = this.formatDataStructure(wigFeatures, 'partition_level', globamMean); + var partition_call = this.formatDataStructure(wigFeatures, 'partition_call', globamMean); + + return [rawbinScore, partition_levels, partition_call, caller_array[1]] + + } + formatDataStructure(wigFeatures, feature_column, scaling_factor = 1) { + const results = []; + for (const [chr, wig] of Object.entries(wigFeatures)) { + + wig.forEach(sample => { + var new_sample = { ...sample }; + if (scaling_factor != 1) { + new_sample.value = sample[feature_column] / scaling_factor * 2; + } + results.push(new_sample); + }); + } + + return results + } + + async computeBAF_v2() { + + const chromosomes = Object.keys(this.allVariants); + const wigFeatures = {}; + const baf1 = [], baf2 = []; + + for (let chr of chromosomes) { + const variants = this.allVariants[chr]; + if (variants.length === 0) continue + var featureBin; + for (let snp of variants) { + featureBin = Math.max(Math.floor(snp.start / this.binSize), 0); + + if (!wigFeatures[chr]) { + wigFeatures[chr] = []; + } + if (!wigFeatures[chr][featureBin]) { + if (featureBin > 0) { + // calculating the BAF likelihood for previous bin + let previous_featureBin = featureBin - 1; + if (wigFeatures[chr][previous_featureBin]) { + + const updated_bin = this.get_max_min_score(wigFeatures[chr][previous_featureBin]); + + if (updated_bin.value != 0.5) { + let baf_wig = Object.assign({}, updated_bin); + baf_wig.value = -2 * (1 - updated_bin.value); + baf2.push(baf_wig); + } + updated_bin.value = - 2 * updated_bin.value; + wigFeatures[chr][previous_featureBin] = updated_bin; + + baf1.push(wigFeatures[chr][previous_featureBin]); + + } + } + wigFeatures[chr][featureBin] = { + chr, + start: featureBin * this.binSize, + end: (featureBin + 1) * this.binSize, + value: 0, + count: 0, + likelihood_score: [], + min_score: 0, + }; + } + const calls = snp.calls[9]; + let genotype = calls.genotype; + let ad_score = calls.info["AD"].split(','); + let ad_a = ad_score[0], ad_b = ad_score[1]; + + if ((genotype[0] == 0 && genotype[1] == 1) || (genotype[0] == 1 && genotype[1] == 0)) { + //apply the beta function + if (wigFeatures[chr][featureBin].likelihood_score.length == 0) { + wigFeatures[chr][featureBin].likelihood_score = g_utils.linspace(0, 1, 100).map((value, index) => { + return beta(ad_a, ad_b, value); + }); + } else { + var sum = 0; + + wigFeatures[chr][featureBin].likelihood_score = g_utils.linspace(0, 1, 100).map((value, index) => { + var likelihood_value = wigFeatures[chr][featureBin].likelihood_score[index] * beta(ad_a, ad_b, value); + sum = sum + likelihood_value; + return likelihood_value; + }); + + wigFeatures[chr][featureBin].likelihood_score = g_utils.linspace(0, 1, 100).map((value, index) => { + return wigFeatures[chr][featureBin].likelihood_score[index] / sum; + }); + } + wigFeatures[chr][featureBin].count++; + } + } + + // last feature bin + const updated_bin = this.get_max_min_score(wigFeatures[chr][featureBin]); + + if (updated_bin.value != 0.5) { + let baf_wig = Object.assign({}, updated_bin); + baf_wig.value = -2 * (1 - updated_bin.value); + baf2.push(baf_wig); + } + updated_bin.value = -2 * updated_bin.value; + wigFeatures[chr][featureBin] = updated_bin; + baf1.push(wigFeatures[chr][featureBin]); + } + + return [baf1, baf2] + + } + + + format_BAF_likelihood(wigFeatures) { + const results = []; + + for (const [chr, wig] of Object.entries(wigFeatures)) { + wig.forEach(sample => { + var new_sample = { ...sample }; + if (sample.value != 0.5) { + new_sample.value = 1 - sample.value; + results.push(new_sample); + } + }); + } + return results + } + + get_max_min_score(sample) { + + if (sample.likelihood_score.length > 0) { + const max = Math.max(...sample.likelihood_score); + const res = sample.likelihood_score.indexOf(max); + sample.value = Math.max(res / 100, 1 - res / 100); + sample.min_score = Math.min(res / 100, 1 - res / 100); + + } else { + sample.score = 0; + } + + return sample + } + + async getAllbins() { + const bins = await this.computeDepthFeatures(); + + //console.log('getAllbins', bins["value"]) + + const fitter = new g_utils.GetFit(bins); + + fitter.fit_data(); + // dconsole.log('rd list', distParams) + + return bins + } + + async read_rd_baf(caller='ReadDepth'){ + + const chromosomes = Object.keys(this.allVariants); + var wigFeatures = {}; + + for (let chr of chromosomes) { + const variants = this.allVariants[chr]; + var featureBin; + if (variants.length === 0) continue + for (let snp of variants) { + featureBin = Math.max(Math.floor(snp.start / this.rowBinSize), 0); + if (!wigFeatures[chr]) { wigFeatures[chr] = []; } + if (!wigFeatures[chr][featureBin]) { + + wigFeatures[chr][featureBin] = { + chr, + start: featureBin * this.rowBinSize, + end: (featureBin + 1) * this.rowBinSize, + // value: 0, + dp_sum_score: 0, + dp_count: 0, + hets_count:0, + hets: [], + //likelihood_score: [], + }; + } + const calls = snp.calls[9]; + const dpValue = calls.info["DP"]; + if (dpValue) { + + wigFeatures[chr][featureBin].dp_sum_score += Number.parseInt(dpValue); + wigFeatures[chr][featureBin].dp_count++; + } + + let ad_score = calls.info["AD"].split(','); + let genotype = calls.genotype; + if ((genotype[0] == 0 && genotype[1] == 1) || (genotype[0] == 1 && genotype[1] == 0)) { + //apply the beta function + wigFeatures[chr][featureBin].hets_count++; + let ad_a = parseInt(ad_score[0]), ad_b = parseInt(ad_score[1]); + wigFeatures[chr][featureBin].hets.push({ref:ad_a, alt:ad_b}); + } + + } + + } + + var avgbin = this.adjust_bin_size(wigFeatures); + var finalFeatureSet; + if(caller == 'ReadDepth'){ + finalFeatureSet = this.readDepthMeanshift(avgbin); + var baf = this.formatDataStructure_BAF(avgbin, 'max_likelihood'); + }else if(caller=='2D'){ + + let caller_obj = new combined_caller.CombinedCaller(avgbin, this.binSize); + let processed_bins = await caller_obj.call_2d(); + + finalFeatureSet = [processed_bins.binScore, [], processed_bins.segment_score]; + + var baf = caller_obj.formatDataStructure_BAF('max_likelihood'); + } + + + return [finalFeatureSet, baf] + } + + formatDataStructure_BAF(wigFeatures, feature_column, scaling_factor = 2) { + const baf1 = []; + const baf2 = []; + for (const [chr, wig] of Object.entries(wigFeatures)) { + + wig.forEach(sample => { + delete sample.likelihood_score; + var baf1_value = { ...sample }; + var baf2_value = { ...sample }; + + let value = sample[feature_column]; + if (value != 0.5){ + baf2_value.value = -2 * (1 - value); + baf2.push(baf2_value); + } + baf1_value.value = -2 * value; + baf1.push(baf1_value); + + }); + } + + + return [baf1, baf2] + } + + adjust_bin_size(wigFeatures){ + const chromosomes = Object.keys(this.allVariants); + // adjust the bin values to actual bin size + var avgbin = {}; + for (let chr of chromosomes) { + if (!avgbin[chr]) { avgbin[chr] = []; } + for (let k = 0; k < wigFeatures[chr].length / this.binFactor; k++) { + const featureBin = k; + if (!avgbin[chr][k]) { + avgbin[chr][k] = { + chr, + start: featureBin * this.binSize, + end: (featureBin + 1) * this.binSize, + dp_count: 0, + hets_count: 0, + binScore: 0, + likelihood_score: [], + }; + } + + for (var j = k * 10; j < 10 * k + 10; j++) { + + if (wigFeatures[chr][j]) { + var tmp_score = parseInt(wigFeatures[chr][j].dp_sum_score / wigFeatures[chr][j].dp_count) * 100; + avgbin[chr][k].binScore += tmp_score; + avgbin[chr][k].dp_count += wigFeatures[chr][j].dp_count; + avgbin[chr][k].hets_count += wigFeatures[chr][j].hets_count; + + if (wigFeatures[chr][j].hets.length != 0){ + + + wigFeatures[chr][j].hets.forEach((hets, hets_idx) => { + if(avgbin[chr][k].likelihood_score.length == 0){ + avgbin[chr][k].likelihood_score = g_utils.linspace(0, 1, 100).map((value, index) => { + return beta(hets.ref, hets.alt, value); + }); + } + else { + var likelihood_sum = 0; + avgbin[chr][k].likelihood_score = g_utils.linspace(0, 1, 100).map((value, index) => { + var likelihood_value = avgbin[chr][k].likelihood_score[index] * beta(hets.ref, hets.alt, value); + likelihood_sum += likelihood_value; + return likelihood_value; + }); + + avgbin[chr][k].likelihood_score = g_utils.linspace(0, 1, 100).map((value, index) => { + return avgbin[chr][k].likelihood_score[index] / likelihood_sum; + }); + + } + }); + + // avgbin[chr][k].likelihood_score *= wigFeatures[chr][j].likelihood_score + } + } + } + + const updated_bin = this.get_max_min_score(avgbin[chr][k]); + avgbin[chr][k].max_likelihood = updated_bin.value; + } + } + return avgbin + } + + } + + function beta(a, b, p, phased = true) { + return p ** a * (1 - p) ** b + p ** b * (1 - p) ** a; + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 The Regents of the University of California + * Author: Arijit Panda + * + * 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. + */ + + const DEFAULT_TRACK_HEIGHT = 250; + + class CNVPytorTrack extends TrackBase { + + constructor(config, browser) { + super(config, browser); + this.featureType = 'numeric'; + this.paintAxis = paintAxis; + + if (!config.max) { + this.defaultScale = true; + this.autoscale = false; + } + + // Invoke height setter last to allocated to coverage and alignment tracks + this.height = (config.height !== undefined ? config.height : DEFAULT_TRACK_HEIGHT); + } + + async init(config) { + + this.type = "cnvpytor"; + this.graphType = config.graphType || "points"; + this.bin_size = config.bin_size || 100000; + this.signal_name = config.signal_name || "rd_snp"; + this.cnv_caller = config.cnv_caller || '2D'; + this.colors = config.colors || ['gray', 'black', 'green', 'blue']; + super.init(config); + + } + + get supportsWholeGenome() { + return true + } + + get_signals() { + let signals = []; + + if (this.signal_name == 'rd_snp') { + signals = ["RD_Raw", "RD_Raw_gc_coor", this.cnv_caller, "BAF1", "BAF2"]; + + } else if (this.signal_name == 'rd') { + signals = ["RD_Raw", "RD_Raw_gc_coor", this.cnv_caller]; + + } else if (this.signal_name == 'snp') { + signals = ["BAF1", "BAF2"]; + + } else if (this.signal_name == 'cnh') { + signals = [this.cnv_caller]; + } + return signals + } + + get_signal_colors() { + + let signal_colors = [ + { singal_name: 'RD_Raw', color: this.colors[0] }, + { singal_name: 'RD_Raw_gc_coor', color: this.colors[1] }, + { singal_name: 'ReadDepth', color: this.colors[2] }, + { singal_name: '2D', color: this.colors[2] }, + { singal_name: 'BAF1', color: this.colors[3] }, + { singal_name: 'BAF2', color: this.colors[3] }, + ]; + return signal_colors + } + + async postInit() { + + if (this.config.format == 'vcf') { + this.featureSource = FeatureSource(this.config, this.browser.genome); + this.header = await this.getHeader(); + + + var allVariants = this.featureSource.reader.features.reduce(function (r, a) { + r[a.chr] = r[a.chr] || []; + r[a.chr].push(a); + return r + }, Object.create(null)); + + const cnvpytor_obj = new CNVpytorVCF(allVariants, this.bin_size); + + let wigFeatures; + let bafFeatures; + this.wigFeatures_obj = {}; + this.wigFeatures_obj[this.bin_size] = {}; + + let dataWigs; + if(this.config.cnv_caller == '2D'){ + + dataWigs = await cnvpytor_obj.read_rd_baf('2D'); + + wigFeatures = dataWigs[0]; + bafFeatures = dataWigs[1]; + this.wigFeatures_obj[this.bin_size]['2D'] = wigFeatures[2]; + + this.available_callers = ['2D']; + }else { + dataWigs = await cnvpytor_obj.read_rd_baf(); + wigFeatures = dataWigs[0]; + bafFeatures = dataWigs[1]; + this.wigFeatures_obj[this.bin_size]['ReadDepth'] = wigFeatures[2]; + this.available_callers = ['ReadDepth']; + } + + this.wigFeatures_obj[this.bin_size]['RD_Raw'] = wigFeatures[0]; + this.wigFeatures_obj[this.bin_size]['RD_Raw_gc_coor'] = wigFeatures[1]; + this.wigFeatures_obj[this.bin_size]['BAF1'] = bafFeatures[0]; + this.wigFeatures_obj[this.bin_size]['BAF2'] = bafFeatures[1]; + + this.available_bins = [this.bin_size]; + + this.set_available_callers(); + + } else { + this.cnvpytor_obj = new HDF5Reader(this.config.url, this.bin_size); + this.wigFeatures_obj = await this.cnvpytor_obj.get_rd_signal(this.bin_size); + this.available_bins = this.cnvpytor_obj.available_bins; + this.available_callers = this.cnvpytor_obj.callers; + this.set_available_callers(); + } + + this.tracks = []; + const p = []; + + this.signals = this.get_signals(); + this.signal_colors = this.get_signal_colors(); + + for (let bin_size in this.wigFeatures_obj) { + + for (const [signal_name, wig] of Object.entries(this.wigFeatures_obj[bin_size])) { + + if (this.signals.includes(signal_name)) { + let tconf = {}; + tconf.type = "wig"; + tconf.isMergedTrack = true; + tconf.features = wig; + tconf.name = signal_name; + tconf.color = this.signal_colors.filter(x => x.singal_name === signal_name).map(x => x.color); + 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; + 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; + } + + return Promise.all(p) + } + + set_available_callers() { + if (!this.available_callers.includes(this.cnv_caller)) { + if (this.available_callers.length > 0) { + this.cnv_caller = this.available_callers[0]; + } else { + this.cnv_caller = null; + } + } + } + + 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 + } + + 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)); + + items.push('
'); + items.push("Bin Sizes"); + for (let rd_bin of this.available_bins) { + const checkBox = createCheckbox(rd_bin, rd_bin === this.bin_size); + items.push({ + object: $$1(checkBox), + click: async () => { + this.bin_size = rd_bin; + + await this.recreate_tracks(rd_bin); + this.clearCachedFeatures(); + this.trackView.updateViews(); + this.trackView.repaintViews(); + } + }); + } + items.push('
'); + items.push("Signal Type"); + + let signal_dct = {"rd_snp": "RD and BAF Likelihood", "rd": "RD Signal", "snp": "BAF Likelihood"}; + for (let signal_name in signal_dct) { + const checkBox = createCheckbox(signal_dct[signal_name], signal_name === this.signal_name); + items.push({ + object: $$1(checkBox), + click: async () => { + this.signal_name = signal_name; + await this.recreate_tracks(this.bin_size); + this.clearCachedFeatures(); + this.trackView.updateViews(); + this.trackView.repaintViews(); + + } + }); + } + + // cnv caller setting + items.push('
'); + items.push("CNV caller"); + for (let cnv_caller of this.available_callers) { + const checkBox = createCheckbox(cnv_caller, cnv_caller === this.cnv_caller); + items.push({ + object: $$1(checkBox), + click: async () => { + this.cnv_caller = cnv_caller; + await this.recreate_tracks(this.bin_size); + this.clearCachedFeatures(); + this.trackView.updateViews(); + this.trackView.repaintViews(); + } + }); + } + + return items + } + + async recreate_tracks(bin_size) { + this.tracks = []; + const p = []; + + if (!(bin_size in this.wigFeatures_obj)) { + this.wigFeatures_obj = {...this.wigFeatures_obj, ...await this.cnvpytor_obj.get_rd_signal(bin_size)}; + } + + this.signals = this.get_signals(); + this.signal_colors = this.get_signal_colors(); + + for (const [signal_name, wig] of Object.entries(this.wigFeatures_obj[bin_size])) { + if (this.signals.includes(signal_name)) { + let tconf = {}; + tconf.type = "wig"; + tconf.isMergedTrack = true; + tconf.features = wig; + tconf.name = signal_name; + tconf.color = this.signal_colors.filter(x => x.singal_name === signal_name).map(x => x.color); + 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; + 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; + } + return Promise.all(p) + } + + async getFeatures(chr, bpStart, bpEnd, bpPerPixel) { + + if(this.tracks) { + const promises = this.tracks.map((t) => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel)); + return Promise.all(promises) + } else { + return undefined // This can happen if a redraw is triggered before the track has initialized. + } + } + + // 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 mergedFeatures = options.features // Array of feature arrays, 1 for each track + const mergedFeatures = options.features; + if(!mergedFeatures) return + + if (this.defaultScale) { + if (this.signal_name == 'rd_snp') { + this.dataRange = { + min: this.config.min || this.dataRange.min || -2, + max: this.config.max || this.dataRange.max || 6 + }; + } else if (this.signal_name == 'rd') { + this.dataRange = { + min: this.config.min || this.dataRange.min || 0, + max: this.config.max || this.dataRange.max || 6 + }; + } else if (this.signal_name == 'snp') { + this.dataRange = { + min: this.config.min || this.dataRange.min || -2, + max: this.config.max || this.dataRange.max || 0 + }; + } + } + + if (this.autoscale) { + this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures); + } + + if(this.tracks) { + 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; + if (this.graphType) { + this.tracks[i].graphType = this.graphType; + } + this.tracks[i].draw(trackOptions); + } + } + + // guides lines + 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); + + // 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': 1 + }; + 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) { + + 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('
'); + popupData.push(`
${this.tracks[i].name}
`); + const trackPopupData = this.tracks[i].popupData(clickState, featuresArray[i]); + popupData.push(...trackPopupData); + + } + return popupData + } + } + + } + + 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} + } + + //import CNVPytorTrack from "./CNVpytor/cnvpytorTrack.js" + + + 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)], + ['blat', (config, browser) => new BlatTrack(config, browser)], + ['cnvpytor', (config, browser) => new CNVPytorTrack(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, + trackFunctions, + 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. + */ + + + /** + * 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. + */ + + // Reference frame classes. Converts domain coordinates (usually genomic) to pixel coordinates + + 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 = domUtils.guid(); + } + + /** + * Extend this frame to accomodate the given locus. Used th CircularView methods to merge 2 frames. + * @param locus + */ + 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 clamp -- if true "clamp" shift to prevent panning off edge of chromosome. This is disabled if "show soft clipping" is on + * @param viewportWidth + */ + shiftPixels(pixels, viewportWidth, clamp) { + + const currentStart = this.start; + const deltaBP = pixels * this.bpPerPixel; + + this.start += deltaBP; + + if(clamp) { + this.clampStart(viewportWidth); + } + + this.end = this.start + viewportWidth * this.bpPerPixel; + + 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) + } + + getMultiLocusLabelBPLengthOnly(pixels) { + const margin = ' '; + const ss = Math.floor(this.start) + 1; + const ee = Math.round(this.start + this.bpPerPixel * pixels); + return `${margin}${this.chr}${margin}${prettyBasePairNumber(ee - ss)}${margin}` + } + + getMultiLocusLabelLocusOnly(pixels) { + const margin = ' '; + const {chr, start, end } = this.getPresentationLocusComponents(pixels); + return `${margin}${chr}:${start}-${end}${margin}` + } + + getMultiLocusLabel(pixels) { + const margin = ' '; + const {chr, start, end } = this.getPresentationLocusComponents(pixels); + const ss = Math.floor(this.start) + 1; + const ee = Math.round(this.start + this.bpPerPixel * pixels); + return `${margin}${chr}:${start}-${end}${margin}${margin}(${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, isSoftclipped) { + + 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. + if(!isSoftclipped) { + const chromosome = genome.getChromosome(locus.chr); + validateGenomicExtent(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 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.get(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) { + + // Check for tab delimited locus string + const tabTokens = locus.split('\t'); + if (tabTokens.length >= 3) { + // Possibly a tab-delimited locus + try { + const chr = browser.genome.getChromosomeName(tabTokens[0]); + const start = parseInt(tabTokens[1].replace(/,/g, ''), 10) - 1; + const end = parseInt(tabTokens[2].replace(/,/g, ''), 10); + if (!isNaN(start) && !isNaN(end)) { + return {chr, start, end} + } + } catch (e) { + // Not a tab delimited locus, apparently, but not really an error as that was a guess + } + + } + + 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) { + + let b = a[1].split('-'); + if (b.length > 2) { + // Allow for negative coordinates, which is possible if showing alignment soft clips + if (a[1].startsWith('-')) { + const i = a[1].indexOf('-', 1); + if (i > 0) { + const t1 = a[1].substring(0, i); + const t2 = a[1].substring(i + 1); + b = [t1, t2]; + } + } else { + return undefined + } + + } + + 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) { + // Don't clamp coordinates if single coordinate is supplied. + 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); + } + + // Allow negative coordinates only if browser is softclipped, i.e. there is at least alignment track with softclipping on + if (extent.start < 0 && !browser.isSoftclipped()) { + const delta = -extent.start; + extent.start += delta; + extent.end += delta; + } + } + } + + 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 + + } + + /* + * 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) { + this.updateNavbar(this.createResponsiveClassSchedule(width)); + } + + 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) { + + let candidates = {}; + + const isWGV = this.browser.isMultiLocusWholeGenomeView() || + (this.browser.referenceFrameList && + GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)); + + + if (isWGV) { + 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 (isWGV) { + 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 = domUtils.div({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 = domUtils.div({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)) : ''; + } + } + + class CursorGuide { + + constructor(columnContainer, browser) { + this.browser = browser; + this.columnContainer = columnContainer; + + this.horizontalGuide = domUtils.div({class: 'igv-cursor-guide-horizontal'}); + columnContainer.appendChild(this.horizontalGuide); + + this.verticalGuide = domUtils.div({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} = domUtils.translateMouseCoordinates(event, this.columnContainer); + this.horizontalGuide.style.top = `${y}px`; + + const target = document.elementFromPoint(event.clientX, event.clientY); + + const viewport = findAncestorOfClass(target, 'igv-viewport'); + + 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(); + } + } + + } + + } + + /** + * Walk up the tree until a parent is found with the given classname. If no ancestor is found return undefined. + * @param target + * @param classname + * @returns {*} + */ + function findAncestorOfClass(target, classname) { + + while (target.parentElement) { + if (target.parentElement.classList.contains(classname)) { + return target.parentElement + } else { + target = target.parentElement; + } + } + 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 CursorGuideButton { + + constructor(browser, parent) { + + this.browser = browser; + + this.button = domUtils.div({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 = domUtils.div({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 = domUtils.div({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 = domUtils.div({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'; + } + + } + + const sliderMin = 0; + let sliderMax = 23; + let sliderValueRaw = 0; + + const ZoomWidget = function (browser, parent) { + + this.browser = browser; + + this.zoomContainer = domUtils.div({class: 'igv-zoom-widget'}); + parent.appendChild(this.zoomContainer); + + // zoom out + this.zoomOutButton = domUtils.div(); + this.zoomContainer.appendChild(this.zoomOutButton); + this.zoomOutButton.appendChild(icons$1.createIcon('minus-circle')); + this.zoomOutButton.addEventListener('click', () => { + // browser.zoomWithScaleFactor(2.0) + browser.zoomOut(); + }); + + // Range slider + const el = domUtils.div(); + 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 = domUtils.div(); + this.zoomContainer.appendChild(this.zoomInButton); + this.zoomInButton.appendChild(icons$1.createIcon('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)}`; + + // referenceFrame.description('zoom.update') + + // 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 = domUtils.div({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(domUtils.div({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 = domUtils.div({class: 'igv-column-shim'}); + insertElementAfter(shim, referenceElement); + + const column = domUtils.div({class: 'igv-column'}); + insertElementAfter(column, shim); + + return column + }, + + insertBefore: (referenceElement, count) => { + + for (let i = 0; i < count; i++) { + + const column = domUtils.div({class: 'igv-column'}); + insertElementBefore(column, referenceElement); + + if (count > 1 && i > 0) { + const columnShim = domUtils.div({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 = domUtils.div({class: 'igv-center-line'}); + column.appendChild(this.container); + + if (browser.isCenterLineVisible) { + this.show(); + } else { + this.hide(); + } + } + + repaint() { + + if (this.referenceFrame) { + + const ppb = 1.0 / this.referenceFrame.bpPerPixel; + if (ppb > 1) { + const width = Math.floor(this.referenceFrame.toPixels(1)); + this.container.style.width = `${width}px`; + this.container.classList.remove('igv-center-line-thin'); + this.container.classList.add('igv-center-line-wide'); + } else { + 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({context, referenceFrame, pixelWidth, pixelHeight, bpPerPixel, bpStart}) { + + if (GenomeUtils.isWholeGenomeView(referenceFrame.chr)) { + this.drawWholeGenome({context, pixelWidth, pixelHeight, bpPerPixel}); + } else { + this.doDraw({context, referenceFrame, pixelWidth, pixelHeight, bpStart}); + } + } + + drawWholeGenome({context, pixelWidth, pixelHeight, bpPerPixel}) { + + 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({context, referenceFrame, pixelWidth, pixelHeight, bpStart}) { + + 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 = domUtils.div({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 = domUtils.div({class: 'igv-navbar-button'}); + parent.append(button); + button.textContent = b.label; + button.addEventListener('click', () => b.callback(browser)); + }; + + class ROIManager { + + constructor(browser, roiMenu, roiTable, top, roiSets) { + + this.browser = browser; + this.roiMenu = roiMenu; + this.roiTable = roiTable; + this.top = top; + this.roiSets = roiSets || []; + this.boundLocusChangeHandler = locusChangeHandler.bind(this); + browser.on('locuschange', this.boundLocusChangeHandler); + + } + + async initialize() { + + if (this.roiSets.length > 0) { + this.browser.showROITableButton = true; + this.browser.roiTableControl.setVisibility(this.browser.showROITableButton); + } + + const promises = this.roiSets.map(roiSet => this.renderROISet({ + browser: this.browser, + pixelTop: this.top, + roiSet + })); + + if (promises.length > 0) { + await Promise.all(promises); + } + + const records = await this.getTableRecords(); + this.roiTable.renderTable(records); + + } + + async loadROI(config, genome) { + + const configs = Array.isArray(config) ? config : [config]; + + for (let c of configs) { + this.roiSets.push(new ROISet(c, genome)); + } + + await this.initialize(); + + } + + clearROIs() { + + this.roiTable.clearTable(); + + const elements = this.browser.columnContainer.querySelectorAll('.igv-roi-region'); + for (let el of elements) { + el.remove(); + } + + for (let roiSet of this.roiSets) { + roiSet.dispose(); + } + + this.roiSets = []; + + } + + async getTableRecords() { + + const records = []; + + for (let roiSet of this.roiSets) { + const setName = roiSet.isUserDefined ? '' : (roiSet.name || ''); + const allFeatures = await roiSet.getAllFeatures(); + for (let chr of Object.keys(allFeatures)) { + for (let feature of allFeatures[chr]) { + records.push({setName, feature}); + } + } + } + + return records + } + + presentTable() { + this.roiTable.present(); + } + + async repaintTable() { + const records = await this.getTableRecords(); + this.roiTable.renderTable(records); + } + + dismissTable() { + this.roiTable.dismiss(); + } + + async updateUserDefinedROISet(feature) { + + let userDefinedROISet = await this.getUserDefinedROISet(); + + if (undefined === userDefinedROISet) { + userDefinedROISet = this.initializeUserDefinedROISet(); + } + + userDefinedROISet.addFeature(feature); + + if (false === this.browser.showROITableButton) { + this.setROITableButtonVisibility(true); + } + + await this.renderROISet({browser: this.browser, pixelTop: this.top, roiSet: userDefinedROISet}); + + const records = await this.getTableRecords(); + this.roiTable.renderTable(records); + } + + setROITableButtonVisibility(isVisible) { + this.browser.showROITableButton = isVisible; + this.browser.roiTableControl.setVisibility(this.browser.showROITableButton); + } + + async renderAllROISets() { + + for (let roiSet of this.roiSets) { + await this.renderROISet({browser: this.browser, pixelTop: this.top, roiSet}); + } + } + + async renderROISet({browser, pixelTop, roiSet}) { + + const columns = browser.columnContainer.querySelectorAll('.igv-column'); + + for (let i = 0; i < columns.length; i++) { + + let {chr, start: viewStart, end: viewEnd, bpPerPixel} = browser.referenceFrameList[i]; + + const elements = columns[i].querySelectorAll('.igv-roi-region'); + for (let el of elements) { + const regionKey = el.dataset.region; + const {chr: regionChr, start: regionStart, end: regionEnd} = parseRegionKey(regionKey); + if (regionChr !== chr || regionEnd < viewStart || regionStart > viewEnd) { + el.remove(); + } + } + + const features = await roiSet.getFeatures(chr, viewStart, viewEnd); + + if (features) { + + for (let feature of features) { + + const regionKey = createRegionKey(chr, feature.start, feature.end); + + const { + x: pixelX, + width: pixelWidth + } = screenCoordinates(Math.max(viewStart, feature.start), Math.min(viewEnd, feature.end), viewStart, bpPerPixel); + + + const el = columns[i].querySelector(createSelector(regionKey)); + + if (el) { + el.style.left = `${pixelX}px`; + el.style.width = `${pixelWidth}px`; + + } else { + const element = this.createRegionElement(browser.columnContainer, pixelTop, pixelX, pixelWidth, roiSet, regionKey, feature.name); + columns[i].appendChild(element); + } + } + } + } + } + + createRegionElement(columnContainer, pixelTop, pixelX, pixelWidth, roiSet, regionKey, name) { + + const regionElement = domUtils.div({class: 'igv-roi-region'}); + + regionElement.style.top = `${pixelTop}px`; + regionElement.style.left = `${pixelX}px`; + regionElement.style.width = `${pixelWidth}px`; + regionElement.style.backgroundColor = roiSet.color; + regionElement.dataset.region = regionKey; + + const header = domUtils.div(); + regionElement.appendChild(header); + + header.style.backgroundColor = roiSet.headerColor; + + if (true === roiSet.isUserDefined) { + header.addEventListener('click', event => { + event.preventDefault(); + event.stopPropagation(); + + const {x, y} = domUtils.translateMouseCoordinates(event, columnContainer); + this.roiMenu.present(x, y, this, columnContainer, regionElement); + }); + } else if (name) { + header.addEventListener('click', event => { + event.preventDefault(); + event.stopPropagation(); + if (this.popover) { + this.popover.dispose(); + } + this.popover = new Popover(columnContainer, roiSet.name); + this.popover.presentContentWithEvent(event, name); + }); + } else { + header.style.pointerEvents = 'none'; + } + + return regionElement + } + + renderSVGContext(context, {deltaX, deltaY}) { + + for (const regionElement of document.querySelectorAll('.igv-roi-region')) { + + // body + const { x, y, width, height } = regionElement.getBoundingClientRect(); + context.fillStyle = regionElement.style.backgroundColor; + context.fillRect(x+deltaX, y+deltaY, width, height); + + // header + const header = regionElement.querySelector('div'); + const { x:xx, y:yy, width:ww, height:hh } = header.getBoundingClientRect(); + context.fillStyle = header.style.backgroundColor; + context.fillRect(xx+deltaX, yy+deltaY, ww, hh); + } + } + + async getUserDefinedROISet() { + return this.roiSets.find(roiSet => true === roiSet.isUserDefined) + } + + initializeUserDefinedROISet() { + + const config = + { + isUserDefined: true, + features: [] + }; + const userDefinedROISet = new ROISet(config, this.browser.genome); + this.roiSets.push(userDefinedROISet); + + return userDefinedROISet + } + + async deleteUserDefinedRegionWithKey(regionKey, columnContainer) { + + columnContainer.querySelectorAll(createSelector(regionKey)).forEach(node => node.remove()); + + const feature = await this.findUserDefinedRegionWithKey(regionKey); + + const set = await this.getUserDefinedROISet(); + + if (set) { + set.removeFeature(feature); + } + + const records = await this.getTableRecords(); + + if (0 === records.length) { + this.browser.roiTableControl.buttonHandler(false); + this.setROITableButtonVisibility(false); + } + + } + + async findUserDefinedRegionWithKey(regionKey) { + + const {chr, start, end} = parseRegionKey(regionKey); + const set = await this.getUserDefinedROISet(); + + if (set) { + const features = await set.getFeatures(chr, start, end); + + for (let feature of features) { + if (feature.chr === chr && feature.start >= start && feature.end <= end) { + return feature + } + } + } + + return undefined + } + + toJSON() { + return this.roiSets.map(roiSet => roiSet.toJSON()) + } + + dispose() { + + this.browser.off('locuschange', this.boundLocusChangeHandler); + + const removable = this.browser.columnContainer.querySelectorAll('.igv-roi-region'); + + for (let el of removable) { + el.remove(); + } + + if (this.roiMenu) { + this.roiMenu.dispose(); + } + + if (this.roiTable) { + this.roiTable.dispose(); + } + + for (let roiSet of this.roiSets) { + roiSet.dispose(); + } + + for (let key of Object.keys(this)) { + this[key] = undefined; + } + + } + } + + function locusChangeHandler() { + this.renderAllROISets(); + } + + function createRegionKey(chr, start, end) { + return `${chr}-${start}-${end}` + } + + function createSelector(regionKey) { + return `[data-region="${regionKey}"]` + } + + function parseRegionKey(regionKey) { + let [chr, ss, ee] = regionKey.split('-'); + ss = parseInt(ss); + ee = parseInt(ee); + + return {chr, start: ss, end: ee, locus: `${chr}:${ss}-${ee}`, bedRecord: `${chr}\t${ss}\t${ee}`} + } + + class ROITable extends RegionTableBase { + constructor(config) { + + const cooked = Object.assign({ 'width':'512px' }, config); + super(cooked); + } + + tableRowDOM(record) { + + const dom = domUtils.div({ class: 'igv-roi-table-row' }); + + const { setName, feature } = record; + dom.dataset.region = createRegionKey(feature.chr, feature.start, feature.end); + + let strings = + [ + feature.chr, + numberFormatter$1(feature.start), + numberFormatter$1(feature.end), + feature.name || '', + setName + ]; + + if (4 === this.columnFormat.length) { + strings = strings.slice(0, 4); + } + + for (let i = 0; i < strings.length; i++) { + const el = domUtils.div(); + dom.appendChild(el); + el.style.width = this.columnFormat[ i ].width; + el.innerText = strings[ i ]; + } + + this.tableRowDOMHelper(dom); + + return dom + } + + renderTable(records) { + + Array.from(this.tableRowContainer.querySelectorAll('.igv-roi-table-row')).forEach(el => el.remove()); + + if (records.length > 0) { + + const sortedRecords = records.sort((a, b) => (a.feature.chr.localeCompare(b.feature.chr) || a.feature.start - b.feature.start || a.feature.end - b.feature.end)); + + for (let record of sortedRecords) { + const row = this.tableRowDOM(record); + this.tableRowContainer.appendChild(row); + } + + } + } + + dispose() { + + document.removeEventListener('click', this.boundGotoButtonHandler); + + this.browser.roiTableControl.buttonHandler(false); + super.dispose(); + } + + static getColumnFormatConfiguration(doIncludeROISetNames) { + + if (true === doIncludeROISetNames) { + + return [ + { label: 'Chr', width: '20%' }, + { label: 'Start', width: '15%' }, + { label: 'End', width: '15%' }, + { label: 'Description', width: '30%' }, + { label: 'ROI Set', width: '20%' } + ] + } else { + return [ + { label: 'Chr', width: '25%' }, + { label: 'Start', width: '20%' }, + { label: 'End', width: '20%' }, + { label: 'Description', width: '35%' } + ] + } + + } + + static gotoButtonHandler (event) { + + event.stopPropagation(); + + const selected = this.tableDOM.querySelectorAll('.igv-roi-table-row-selected'); + const loci = []; + for (let el of selected) { + const { locus } = parseRegionKey(el.dataset.region); + loci.push(locus); + } + + for (let el of this.tableDOM.querySelectorAll('.igv-roi-table-row')) { + el.classList.remove('igv-roi-table-row-selected'); + } + + this.setTableRowSelectionState(false); + + if (loci.length > 0) { + this.browser.search(loci.join(' ')); + } + + } + + } + + class ROIMenu { + constructor(browser, parent) { + + this.browser = browser; + + // container + this.container = domUtils.div({ class: 'igv-roi-menu-next-gen' }); + parent.appendChild(this.container); + + // header + const header = domUtils.div(); + this.container.appendChild(header); + + uiUtils.attachDialogCloseHandlerWithParent(header, () => this.container.style.display = 'none'); + + // body + this.body = domUtils.div(); + this.container.appendChild(this.body); + + this.container.style.display = 'none'; + + } + + async present(x, y, roiManager, columnContainer, regionElement) { + + removeAllChildNodes(this.body); + + const feature = await this.browser.roiManager.findUserDefinedRegionWithKey(regionElement.dataset.region); + + // Description Copy + const _description_copy_ = domUtils.div(); + this.body.appendChild(_description_copy_); + + const placeholder = 'Description'; + const str = (feature.name || placeholder); + + _description_copy_.innerText = str; + _description_copy_.setAttribute('title', str); + placeholder === str ? _description_copy_.classList.add('igv-roi-placeholder') : _description_copy_.classList.remove('igv-roi-placeholder'); + + + // Set Description + const description = domUtils.div(); + this.body.appendChild(description); + description.innerText = 'Set Description'; + + description.addEventListener('click', event => { + + event.stopPropagation(); + + this.container.style.display = 'none'; + + const callback = () => { + + const value = this.browser.inputDialog.input.value || ''; + feature.name = value.trim(); + + this.container.style.display = 'none'; + + this.browser.roiManager.repaintTable(); + }; + + const config = + { + label: 'Description', + value: (feature.name || ''), + callback + }; + + this.browser.inputDialog.present(config, event); + + }); + + + // Delete Region + const _delete_ = domUtils.div(); + this.body.appendChild(_delete_); + _delete_.innerText = 'Delete Region'; + + _delete_.addEventListener('click', event => { + event.stopPropagation(); + this.container.style.display = 'none'; + this.browser.roiManager.deleteUserDefinedRegionWithKey(regionElement.dataset.region, this.browser.columnContainer); + }); + + + + + + + + // columnContainer.addEventListener('click', event => { + // event.stopPropagation() + // this.container.style.display = 'none' + // }) + + this.container.style.left = `${ x }px`; + this.container.style.top = `${ y }px`; + this.container.style.display = 'flex'; + + } + + async __present(x, y, roiManager, columnContainer, regionElement) { + + removeAllChildNodes(this.container); + + const feature = await this.browser.roiManager.findUserDefinedRegionWithKey(regionElement.dataset.region); + + let row; + + // Go To + // row = DOMUtils.div({ class: 'igv-roi-menu-row' }) + // row.innerText = 'Go To' + // this.container.appendChild(row) + // + // row.addEventListener('click', event => { + // event.stopPropagation() + // this.container.style.display = 'none' + // + // const { locus } = parseRegionKey(regionElement.dataset.region) + // this.browser.search(locus) + // }) + + // Description: + row = domUtils.div({ class: 'igv-roi-menu-row-edit-description' }); + this.container.appendChild(row); + + row.addEventListener('click', e => { + e.stopPropagation(); + }); + + const str = 'description-input'; + + const label = document.createElement('label'); + row.appendChild(label); + + label.setAttribute('for', str); + label.innerText = 'Description:'; + + const input = document.createElement('input'); + row.appendChild(input); + + input.setAttribute('type', 'text'); + input.setAttribute('name', str); + // input.setAttribute('placeholder', feature.name || 'Edit Description') + input.setAttribute('placeholder', ''); + input.value = feature.name || ''; + + input.addEventListener('change', async e => { + + e.stopPropagation(); + + const feature = await this.browser.roiManager.findUserDefinedRegionWithKey(regionElement.dataset.region); + feature.name = input.value; + + input.blur(); + this.container.style.display = 'none'; + + await this.browser.roiManager.repaintTable(); + }); + + + // Delete + row = domUtils.div({ class: 'igv-roi-menu-row' }); + row.innerText = 'Delete region'; + this.container.appendChild(row); + + row.addEventListener('click', event => { + event.stopPropagation(); + this.container.style.display = 'none'; + this.browser.roiManager.deleteUserDefinedRegionWithKey(regionElement.dataset.region, this.browser.columnContainer); + }); + + this.container.style.left = `${ x }px`; + this.container.style.top = `${ y }px`; + this.container.style.display = 'flex'; + + columnContainer.addEventListener('click', event => { + event.stopPropagation(); + this.container.style.display = 'none'; + }); + + } + + dispose() { + this.container.innerHTML = ''; + } + + } + + function removeAllChildNodes(parent) { + while (parent.firstChild) { + parent.removeChild(parent.firstChild); + } + } + + class TrackROISet { + + constructor(config, genome) { + this.name = config.name; + this.featureSource = config.featureSource || FeatureSource(config, genome); + this.color = config.color || ROI_DEFAULT_COLOR; + } + + async getFeatures(chr, start, end) { + return this.featureSource.getFeatures({chr, start, end}) + } + + draw(drawConfiguration) { + + const { context, bpPerPixel, bpStart, pixelTop, pixelHeight, pixelWidth, features, } = drawConfiguration; + + if (!features) { + return + } + + const bpEnd = bpStart + (pixelWidth * bpPerPixel) + 1; + for (let { start:regionStartBP, end:regionEndBP } of features) { + + if (regionEndBP < bpStart) { + continue + } + + if (regionStartBP > bpEnd) { + break + } + + const { x, width } = screenCoordinates(regionStartBP, regionEndBP, bpStart, bpPerPixel); + IGVGraphics.fillRect(context, x, pixelTop, width, pixelHeight, { fillStyle: this.color }); + } + } + } + + /* + * 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 ROITableControl { + + constructor(parent, browser) { + this.browser = browser; + this.button = domUtils.div({class: 'igv-navbar-button'}); + parent.appendChild(this.button); + this.button.textContent = 'ROI Table'; + + this.button.addEventListener('click', () => { + this.buttonHandler(!browser.roiTableVisible); + }); + + this.browser = browser; + + this.setVisibility(browser.showROITableButton); + + this.setState(browser.roiTableVisible); + } + + buttonHandler(status) { + this.browser.roiTableVisible = status; + this.setState(this.browser.roiTableVisible); + this.browser.setROITableVisibility(this.browser.roiTableVisible); + } + + setVisibility(doShowROITablelButton) { + if (true === doShowROITablelButton) { + this.show(); + } else { + this.hide(); + } + } + + setState(roiTableVisible) { + if (true === roiTableVisible) { + 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.roiTableVisible); + } + + hide() { + this.button.style.display = 'none'; + } + } + + // css - $igv-scrollbar-outer-width: 14px; + 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 = domUtils.guid(); + this.namespace = '.browser_' + this.guid; + + this.parent = parentDiv; + + this.root = domUtils.div({class: 'igv-container'}); + parentDiv.appendChild(this.root); + + this.alert = new Alert(this.root); + + this.columnContainer = domUtils.div({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.roiTableVisible = config.showROITable; + this.showROITableButton = config.showROITableButton; + + 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('
', {class: 'igv-navbar'}); + this.$navigation = $navBar; + + const $navbarLeftContainer = $$1('
', {class: 'igv-navbar-left-container'}); + $navBar.append($navbarLeftContainer); + + // IGV logo + const $logo = $$1('
', {class: 'igv-logo'}); + $navbarLeftContainer.append($logo); + + const logoSvg = logo(); + logoSvg.css("width", "34px"); + logoSvg.css("height", "32px"); + $logo.append(logoSvg); + + this.$current_genome = $$1('
', {class: 'igv-current-genome'}); + $navbarLeftContainer.append(this.$current_genome); + this.$current_genome.text(''); + + const $genomicLocation = $$1('
', {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('
', {class: 'igv-locus-size-group'}); + $genomicLocation.append($locusSizeGroup); + + const $searchContainer = $$1('
', {class: 'igv-search-container'}); + $locusSizeGroup.append($searchContainer); + + // browser.$searchInput = $(''); + this.$searchInput = $$1('', {class: 'igv-search-input', type: 'text', placeholder: 'Locus Search'}); + $searchContainer.append(this.$searchInput); + + this.$searchInput.change(() => this.doSearch(this.$searchInput.val())); + + const searchIconContainer = domUtils.div({class: 'igv-search-icon-container'}); + $searchContainer.append($$1(searchIconContainer)); + + searchIconContainer.appendChild(icons$1.createIcon("search")); + + searchIconContainer.addEventListener('click', () => this.doSearch(this.$searchInput.val())); + + this.windowSizePanel = new WindowSizePanel($locusSizeGroup.get(0), this); + + const $navbarRightContainer = $$1('
', {class: 'igv-navbar-right-container'}); + $navBar.append($navbarRightContainer); + + const $toggle_button_container = $$1('
'); + $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); + + // ROI Control + this.roiTableControl = new ROITableControl($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-${domUtils.guid()}`; + + this.dataRangeDialog = new DataRangeDialog(this, $$1(this.root)); + this.dataRangeDialog.$container.get(0).id = `igv-data-range-dialog-${domUtils.guid()}`; + + this.genericColorPicker = new GenericColorPicker({parent: this.columnContainer, width: 432}); + this.genericColorPicker.container.id = `igv-track-color-picker-${domUtils.guid()}`; + + 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 + }; + + /** + * PUBLIC API FUNCTION + * + * Return the current genomic region as a locus string, or array of locus strings if in multi-locus view + * @returns {string|*[]|*} + */ + currentLoci() { + const noCommaLocusString = (rf) => `${rf.chr}:${rf.start + 1}-${rf.end}`; + if (undefined === this.referenceFrameList || 0 === this.referenceFrameList.length) { + return "" + } else if (1 === this.referenceFrameList.length) { + return noCommaLocusString(this.referenceFrameList[0]) + } else { + return this.referenceFrameList.map(rf => noCommaLocusString(rf)) + } + } + + /** + * Render browse display as SVG + * @returns {string} + */ + toSVG() { + + const {y, width, height} = this.columnContainer.getBoundingClientRect(); + + const h_render = 8000; + + const config = + { + + width, + height: h_render, + + backdropColor: 'white', + + multiLocusGap: 0, + + viewbox: + { + x: 0, + y: 0, + width, + height: h_render + } + + }; + + const context = new ctx(config); + + // tracks -> SVG + const delta = {deltaX: 0, deltaY: -y}; + for (let trackView of this.trackViews) { + trackView.renderSVGContext(context, delta); + } + + this.roiManager.renderSVGContext(context, delta); + + // 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) { + + // TODO: depricated + this.roiSets = []; + + 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$1(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} + */ + 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 genomeOrReference = session.reference || session.genome; + if(!genomeOrReference) { + console.warn("No genome or reference object specified"); + return; + } + const genomeConfig = await GenomeUtils.expandReference(this.alert, genomeOrReference); + + + 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 + let ideogramHeight = 0; + if (false !== session.showIdeogram) { + + const track = new IdeogramTrack(this); + track.id = 'ideogram'; + + const trackView = new TrackView(this, this.columnContainer, track); + const {$viewport} = trackView.viewports[0]; + ideogramHeight = getElementAbsoluteHeight($viewport.get(0)); + + this.trackViews.push(trackView); + } + + 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 (this.roiManager) { + this.roiManager.dispose(); + } + + const roiMenu = new ROIMenu(this, this.columnContainer); + const roiTableConfig = + { + browser: this, + parent: this.columnContainer, + headerTitle: 'Regions of Interest', + dismissHandler: () => this.roiTableControl.buttonHandler(false), + gotoButtonHandler: ROITable.gotoButtonHandler + }; + if (session.roi) { + + const roiSetList = session.roi.map(c => new ROISet(c, this.genome)); + + const named = roiSetList.filter(({name}) => name !== undefined && name.length > 0); + + roiTableConfig.columnFormat = ROITable.getColumnFormatConfiguration(named.length > 0); + + const roiTable = new ROITable(roiTableConfig); + + this.roiManager = new ROIManager(this, roiMenu, roiTable, ideogramHeight, roiSetList); + } else { + + roiTableConfig.columnFormat = ROITable.getColumnFormatConfiguration(false); + + const roiTable = new ROITable(roiTableConfig); + + this.roiManager = new ROIManager(this, roiMenu, roiTable, ideogramHeight, undefined); + } + + await this.roiManager.initialize(); + + // Tracks. Start with genome tracks, if any, then append session tracks + const genomeTracks = genomeConfig.tracks || []; + const trackConfigurations = session.tracks ? genomeTracks.concat(session.tracks) : genomeTracks; + + // Ensure that we always have a sequence track with no explicit URL (=> the reference genome sequence track) + const pushSequenceTrack = trackConfigurations.filter(track => 'sequence' === track.type && !track.url && !track.fastaURL).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) { + + // If the genome is defined directly from a fasta file or data url the "id" will be the full url. Don't display + // this. + let genomeLabel = (genome.id && genome.id.length < 10 ? genome.id : ''); + this.$current_genome.text(genomeLabel); + 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(this.alert, 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); + } + + setROITableVisibility(isVisible) { + true === isVisible ? this.roiManager.presentTable() : this.roiManager.dismissTable(); + } + + // 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'; + + } + + /** + * Public API function. Load a list of tracks. + * + * @param configList Array of track configurations + * @returns {Promise<*>} Promise for track objects + */ + 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 + } + + /** + * Public API function + * + * Load an individual track. If part of an autoscale group force a general update + * + * @param config A track configuration + * @returns {Promise<*>} Promise for track object + */ + async loadTrack(config) { + + const newTrack = this._loadTrack(config); + + if (config.autoscaleGroup) { + // Await newTrack load and update all views + await newTrack; + this.updateViews(); + } + + return newTrack + } + + /** + * Return a promise to load a track. Private function used by loadTrack() and loadTrackList() + * + * @param config + * @returns {*} + */ + + async _loadTrack(config) { + + // config might be json + if (isString$2(config)) { + config = JSON.parse(config); + } + + try { + + const newTrack = await this.createTrack(config); + + if (undefined === newTrack) { + return + } + + // 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); + this.alert.present(new Error(msg), undefined); + } + } + + /** + * Public API function - load a region of interest + * + * @param config A "track" configuration object, or array of objects, of type == "annotation" (bed, gff, etc) + * + * @returns {Promise} + */ + async loadROI(config) { + await this.roiManager.loadROI(config, this.genome); + } + + /** + * Public API function - clear all regions of interest (ROI), including preloaded and user-defined ROIs + */ + clearROIs() { + this.roiManager.clearROIs(); + } + + /** + * Public API function. Return a promise for the list of user-defined regions-of-interest + */ + async getUserDefinedROIs() { + + if (this.roiManager) { + + const set = await this.roiManager.getUserDefinedROISet(); + if (undefined === set) { + return [] + } + + const featureHash = await set.getAllFeatures(); + const featureList = []; + for (let value of Object.values(featureHash)) { + featureList.push(...value); + } + + return featureList + + } else { + return [] + } + } + + getRulerTrackView() { + const list = this.trackViews.filter(({track}) => 'ruler' === track.id); + return list.length > 0 ? list[0] : undefined + } + + /** + * Create a Track object. + * @param config + * @returns {Promise<*>} + */ + async createTrack(config) { + + // Resolve function and promise urls + let url = await resolveURL(config.url || config.fastaURL); + if (isString$2(url)) { + url = url.trim(); + } + + if (url) { + if (config.format) { + config.format = config.format.toLowerCase(); + } else if (config.fastaURL) { + config.format = "fasta"; // by definition + } else { + let filename = config.filename; + if (!filename) { + filename = await getFilename$1(url); + } + + const format = inferFileFormat(filename); + + if ("tsv" === format) { + config.format = await inferFileFormatFromHeader(config); + } else if (format) { + config.format = format; + } else { + if (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 (undefined === track) { + this.alert.present(new Error(`Error creating track. Could not determine track type for file: ${config.url || config}`), undefined); + } else { + + if (config.roi && config.roi.length > 0) { + track.roiSets = config.roi.map(r => new TrackROISet(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) + } + + /** + * NOTE: Public API function + * + * Remove all tracks matching the given name. Usually this will be a single track, but there is no + * guarantee names are unique + * + * @param name + */ + removeTrackByName(name) { + const copy = this.trackViews.slice(); + for (let trackView of copy) { + if (name === trackView.track.name) { + this.removeTrack(trackView.track); + } + } + } + + /** + * NOTE: Public API function + * + * Remove the given track. If it has already been removed this is a no-op. + * + * @param track + */ + removeTrack(track) { + for (let trackView of this.trackViews) { + if (track === trackView.track) { + this._removeTrack(trackView.track); + break + } + } + } + + _removeTrack(track) { + if (track.disposed) return + this.trackViews.splice(this.trackViews.indexOf(track.trackView), 1); + this.fireEvent('trackremoved', [track]); + this.fireEvent('trackorderchanged', [this.getTrackOrder()]); + if (track.trackView) { + 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} + */ + 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() { + + if(!this.referenceFrameList) return + 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) { + + if(!this.referenceFrameList) return + + 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) { + + if(!this.referenceFrameList) return + + // 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} + */ + 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(({viewports}) => 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} + */ + async doSearch(string, init) { + const success = await this.search(string, init); + if (!success) { + this.alert.present(new Error(`Unrecognized locus: ${string} `)); + } + 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} 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), this.isSoftclipped()); + + // 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(); + } + } + + /** + * Return a json-like object (note not a json string) representing the current state. + * + */ + 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 (json.reference.fastaURL instanceof File) { // Test specifically for File. Other types of File-like objects might be savable) { + throw new Error(`Error. Sessions cannot include local file references ${json.reference.fastaURL.name}.`) + } else if (json.reference.indexURL instanceof File) { // Test specifically for File. Other types of File-like objects might be savable) { + 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; + } + + json["roi"] = this.roiManager.toJSON(); + + 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:
'; + for (let e of errors) { + message += ` (${n++}) ${e.toString()}
`; + } + 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 = domUtils.pageCoordinates(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'); + } + } + + isTrackPanning() { + return this.dragObject + } + + isSoftclipped() { + const result = this.trackViews.find(tv => tv.track.showSoftClips === true); + return result !== undefined + } + + + /** + * 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(); + } + } + + /** + * End vertical dragging of tracks (i.e. track re-order, not horizontal panning of data) + */ + 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); + } + + 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} + */ + async function resize() { + + if(!this.referenceFrameList) return + + 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} = domUtils.pageCoordinates(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) { + // Scrolling => dragging track vertically + this.isScrolling = true; + const viewportHeight = viewport.$viewport.height(); + const contentHeight = viewport.trackView.maxViewportContentHeight(); + this.vpMouseDown.r = viewportHeight / contentHeight; + } + } + } + + if (this.dragObject) { + const clampDrag = !this.isSoftclipped(); + let deltaX = this.vpMouseDown.lastMouseX - x; + const viewChanged = referenceFrame.shiftPixels(deltaX, viewport.$viewport.width(), clampDrag); + 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( + '' + + 'IGV' + + '' + + '' + + '' + + ';' + + '' + + ' ' + ) + } + + 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) { + igvxhr.setOauthToken(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); + } + + browser.navbarManager.navbarDidResize(browser.$navigation.width()); + + 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.showROITableButton) { + config.showROITableButton = false; + } + + if (undefined === config.showROITable) { + config.showROITable = false; + } + + 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}\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}\n.igv-navbar .igv-navbar-left-container .igv-logo {\n width: 34px;\n height: 32px;\n margin-right: 8px;\n}\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}\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}\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}\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}\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}\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}\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}\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}\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}\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}\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}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div {\n margin-left: 0;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div:last-child {\n margin-left: 0;\n margin-right: 0;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-750 {\n display: none;\n}\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}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\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}\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}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n display: block;\n height: fit-content;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n display: block;\n width: 125px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n display: block;\n}\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}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\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}\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}\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}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n display: none;\n}\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\n.igv-navbar-button-clicked {\n color: white;\n background-color: #737373;\n}\n\n.igv-navbar-button:hover {\n cursor: pointer;\n}\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}\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\n.igv-zoom-in-notice {\n position: absolute;\n top: 10px;\n left: 50%;\n}\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\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\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: 12px;\n height: 12px;\n color: #666666;\n background-color: white;\n z-index: 1000;\n}\n.igv-multi-locus-close-button > svg {\n vertical-align: top;\n}\n\n.igv-multi-locus-close-button:hover {\n cursor: pointer;\n color: #434343;\n}\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: 12px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-multi-locus-ruler-label > div {\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n color: rgb(16, 16, 16);\n background-color: white;\n}\n.igv-multi-locus-ruler-label > div {\n cursor: pointer;\n}\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}\n.igv-multi-locus-ruler-label-square-dot > div:first-child {\n width: 14px;\n height: 14px;\n}\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: rgb(16, 16, 16);\n}\n\n.igv-ruler-sweeper {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 26px;\n bottom: 0;\n left: 0;\n width: 0;\n z-index: 99999;\n background-color: rgba(68, 134, 247, 0.25);\n}\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}\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\n.igv-track-label {\n position: absolute;\n left: 8px;\n top: 8px;\n width: auto;\n height: auto;\n max-width: 50%;\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\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n background-color: #e8e8e8;\n}\n\n.igv-track-label-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-top: 4px;\n}\n\n.igv-center-line {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\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\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\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\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\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\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}\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}\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}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\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}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\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}\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}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n font-size: 16px;\n}\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}\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}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\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}\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}\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\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}\n.igv-menu-popup > div:not(:first-child) {\n width: 100%;\n}\n.igv-menu-popup > div:not(:first-child) > div {\n background: white;\n}\n.igv-menu-popup > div:not(:first-child) > div.context-menu {\n padding-left: 4px;\n padding-right: 4px;\n}\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}\n.igv-menu-popup > div:not(:first-child) > div:hover {\n background: #efefef;\n}\n\n.igv-menu-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 1px;\n padding-top: 1px;\n}\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}\n.igv-menu-popup-header div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-menu-popup-header div:hover {\n cursor: pointer;\n color: #444;\n}\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}\n.igv-menu-popup-check-container div {\n padding-top: 2px;\n padding-left: 8px;\n}\n.igv-menu-popup-check-container div:first-child {\n position: relative;\n width: 12px;\n height: 12px;\n}\n.igv-menu-popup-check-container div:first-child svg {\n position: absolute;\n width: 12px;\n height: 12px;\n}\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}\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}\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}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\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}\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: rgb(255, 255, 255);\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n}\n\n@keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n@-webkit-keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n.igv-roi-menu-next-gen {\n position: absolute;\n z-index: 512;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background-color: white;\n width: 192px;\n border-radius: 4px;\n border-color: #7F7F7F;\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: stretch;\n}\n.igv-roi-menu-next-gen > div:first-child {\n height: 24px;\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}\n.igv-roi-menu-next-gen > div:first-child > div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-roi-menu-next-gen > div:first-child > div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-menu-next-gen > div:last-child {\n background-color: white;\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: 0;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n text-align: start;\n vertical-align: middle;\n}\n.igv-roi-menu-next-gen > div:last-child > div {\n height: 24px;\n padding-left: 4px;\n border-bottom-style: solid;\n border-bottom-width: thin;\n border-bottom-color: #7f7f7f;\n}\n.igv-roi-menu-next-gen > div:last-child > div:not(:first-child):hover {\n background-color: rgba(127, 127, 127, 0.1);\n}\n.igv-roi-menu-next-gen > div:last-child div:first-child {\n font-style: italic;\n text-align: center;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.igv-roi-menu-next-gen > div:last-child > div:last-child {\n border-bottom-width: 0;\n border-bottom-color: transparent;\n}\n\n.igv-roi-placeholder {\n font-style: normal;\n color: rgba(75, 75, 75, 0.6);\n}\n\n.igv-roi-table {\n position: absolute;\n z-index: 1024;\n width: min-content;\n max-width: 1600px;\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: 12px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n cursor: default;\n}\n.igv-roi-table > div {\n height: 24px;\n font-size: 14px;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n}\n.igv-roi-table > div:first-child {\n border-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-top-width: 0;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-roi-table > div:first-child > div:first-child {\n text-align: center;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n margin-left: 4px;\n margin-right: 4px;\n width: calc(100% - 4px - 12px);\n}\n.igv-roi-table > div:first-child > div:last-child {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7f7f7f;\n}\n.igv-roi-table > div:first-child > div:last-child > svg {\n display: block;\n}\n.igv-roi-table > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-table > .igv-roi-table-description {\n padding: 4px;\n margin-left: 4px;\n word-break: break-all;\n overflow-y: auto;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-goto-explainer {\n margin-top: 5px;\n margin-left: 4px;\n color: #7F7F7F;\n font-style: italic;\n height: 24px;\n border-top: solid lightgray;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-column-titles {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n padding-right: 16px;\n background-color: white;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: #7f7f7f;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container {\n overflow: auto;\n resize: both;\n max-width: 1600px;\n height: 360px;\n background-color: transparent;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: transparent;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row-hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n.igv-roi-table > div:last-child {\n height: 32px;\n line-height: 32px;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: transparent;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-width: 0;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n\n.igv-roi-table-row-selected {\n background-color: rgba(0, 0, 0, 0.125);\n}\n\n.igv-roi-table-button {\n cursor: pointer;\n height: 20px;\n user-select: none;\n line-height: 20px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 13px;\n font-weight: 400;\n color: black;\n padding-left: 6px;\n padding-right: 6px;\n background-color: rgb(239, 239, 239);\n border-color: black;\n border-style: solid;\n border-width: thin;\n border-radius: 3px;\n}\n\n.igv-roi-table-button:hover {\n font-weight: 400;\n background-color: rgba(0, 0, 0, 0.13);\n}\n\n.igv-roi-region {\n z-index: 64;\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n overflow: visible;\n margin-top: 44px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-region > div {\n position: relative;\n width: 100%;\n height: 8px;\n pointer-events: auto;\n}\n\n.igv-roi-menu {\n position: absolute;\n z-index: 1024;\n width: 144px;\n border-color: #7f7f7f;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-menu > div:not(:last-child) {\n border-bottom-color: rgba(128, 128, 128, 0.5);\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-roi-menu > div:first-child {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-top-color: transparent;\n border-top-style: solid;\n border-top-width: 0;\n}\n.igv-roi-menu > 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: 0;\n}\n\n.igv-roi-menu-row {\n height: 24px;\n padding-left: 8px;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n background-color: white;\n}\n\n.igv-roi-menu-row-edit-description {\n width: -webkit-fill-available;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n background-color: white;\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-menu-row-edit-description > label {\n margin-left: 2px;\n margin-bottom: 0;\n display: block;\n width: -webkit-fill-available;\n}\n.igv-roi-menu-row-edit-description > input {\n display: block;\n margin-left: 2px;\n margin-right: 2px;\n margin-bottom: 1px;\n width: -webkit-fill-available;\n}\n\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\n.igv-viewport {\n position: relative;\n margin-top: 5px;\n line-height: 1;\n overflow-x: hidden;\n overflow-y: hidden;\n}\n\n.igv-viewport-content {\n position: relative;\n width: 100%;\n}\n.igv-viewport-content > canvas {\n position: relative;\n display: block;\n}\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\n.igv-column-shim {\n width: 1px;\n margin-left: 2px;\n margin-right: 2px;\n background-color: #545453;\n}\n\n.igv-column {\n position: relative;\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\n.igv-axis-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 width: 50px;\n}\n.igv-axis-column > div {\n margin-top: 5px;\n width: 100%;\n}\n\n.igv-sample-name-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\n.igv-scrollbar-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 width: 14px;\n}\n.igv-scrollbar-column > div {\n position: relative;\n margin-top: 5px;\n width: 14px;\n}\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}\n.igv-scrollbar-column > div > div:hover {\n background-color: #c4c4c4;\n}\n\n.igv-track-drag-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 width: 12px;\n background-color: white;\n}\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}\n.igv-track-drag-column .igv-track-drag-handle-hover {\n background-color: #787878;\n}\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\n.igv-gear-menu-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 width: 28px;\n}\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}\n.igv-gear-menu-column > div > div {\n position: relative;\n margin-top: 4px;\n width: 16px;\n height: 16px;\n color: #7F7F7F;\n}\n.igv-gear-menu-column > div > div:hover {\n cursor: pointer;\n color: #444;\n}\n\n/*# sourceMappingURL=dom.css.map */\n'; + + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.innerHTML = css; + + document.head.append(style); + + } + + // Defines the top-level API for the igv module + + const setApiKey = igvxhr.setApiKey; + + embedCSS(); + + function setGoogleOauthToken(accessToken) { + return igvxhr.setOauthToken(accessToken) + } + + function setOauthToken(accessToken, host) { + return igvxhr.setOauthToken(accessToken, host) + } + + // Backward compatibility + const oauth = igvxhr.oauth; + + var index = { + TrackUtils, + IGVGraphics, + MenuUtils, + DataRangeDialog, + createTrack, + createBrowser, + removeBrowser, + removeAllBrowsers, + visibilityChange, + setGoogleOauthToken, + setOauthToken, + oauth, + version, + setApiKey, + registerFileFormats + }; + + return index; + +})); diff --git a/igv.min.js b/igv.min.js new file mode 100644 index 0000000..e64e92e --- /dev/null +++ b/igv.min.js @@ -0,0 +1,48 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).igv=t()}(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 e=[],t=window.document,i=Object.getPrototypeOf,n=e.slice,r=e.concat,s=e.push,o=e.indexOf,a={},l=a.toString,h=a.hasOwnProperty,c=h.toString,d=c.call(Object),u={},f=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},p=function(e){return null!=e&&e===e.window},g={type:!0,src:!0,noModule:!0};function m(e,i,n){var r,s=(i=i||t).createElement("script");if(s.text=e,n)for(r in g)n[r]&&(s[r]=n[r]);i.head.appendChild(s).parentNode.removeChild(s)}function b(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?a[l.call(e)]||"object":typeof e}var w="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",v=function(e,t){return new v.fn.init(e,t)},y=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function _(e){var t=!!e&&"length"in e&&e.length,i=b(e);return!f(e)&&!p(e)&&("array"===i||0===t||"number"==typeof t&&t>0&&t-1 in e)}v.fn=v.prototype={jquery:w,constructor:v,length:0,toArray:function(){return n.call(this)},get:function(e){return null==e?n.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=v.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return v.each(this,e)},map:function(e){return this.pushStack(v.map(this,(function(t,i){return e.call(t,i,t)})))},slice:function(){return this.pushStack(n.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,i=+e+(e<0?t:0);return this.pushStack(i>=0&&i+~]|"+O+")"+O+"*"),j=new RegExp("="+O+"*([^\\]'\"]*?)"+O+"*\\]","g"),$=new RegExp(z),W=new RegExp("^"+P+"$"),G={ID:new RegExp("^#("+P+")"),CLASS:new RegExp("^\\.("+P+")"),TAG:new RegExp("^("+P+"|[*])"),ATTR:new RegExp("^"+D),PSEUDO:new RegExp("^"+z),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),bool:new RegExp("^(?:"+N+")$","i"),needsContext:new RegExp("^"+O+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)","i")},Z=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,X=/^[^{]+\{\s*\[native \w/,Y=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,J=new RegExp("\\\\([\\da-f]{1,6}"+O+"?|("+O+")|.)","ig"),ee=function(e,t,i){var n="0x"+t-65536;return n!=n||i?t:n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ne=function(){u()},re=be((function(e){return!0===e.disabled&&("form"in e||"label"in e)}),{dir:"parentNode",next:"legend"});try{I.apply(T=B.call(_.childNodes),_.childNodes),T[_.childNodes.length].nodeType}catch(e){I={apply:T.length?function(e,t){L.apply(e,B.call(t))}:function(e,t){for(var i=e.length,n=0;e[i++]=t[n++];);e.length=i-1}}}function se(e,t,n,r){var s,a,h,c,d,p,b,w=t&&t.ownerDocument,x=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==x&&9!==x&&11!==x)return n;if(!r&&((t?t.ownerDocument||t:_)!==f&&u(t),t=t||f,g)){if(11!==x&&(d=Y.exec(e)))if(s=d[1]){if(9===x){if(!(h=t.getElementById(s)))return n;if(h.id===s)return n.push(h),n}else if(w&&(h=w.getElementById(s))&&v(t,h)&&h.id===s)return n.push(h),n}else{if(d[2])return I.apply(n,t.getElementsByTagName(e)),n;if((s=d[3])&&i.getElementsByClassName&&t.getElementsByClassName)return I.apply(n,t.getElementsByClassName(s)),n}if(i.qsa&&!A[e+" "]&&(!m||!m.test(e))){if(1!==x)w=t,b=e;else if("object"!==t.nodeName.toLowerCase()){for((c=t.getAttribute("id"))?c=c.replace(te,ie):t.setAttribute("id",c=y),a=(p=o(e)).length;a--;)p[a]="#"+c+" "+me(p[a]);b=p.join(","),w=K.test(e)&&pe(t.parentNode)||t}if(b)try{return I.apply(n,w.querySelectorAll(b)),n}catch(e){}finally{c===y&&t.removeAttribute("id")}}}return l(e.replace(V,"$1"),t,n,r)}function oe(){var e=[];return function t(i,r){return e.push(i+" ")>n.cacheLength&&delete t[e.shift()],t[i+" "]=r}}function ae(e){return e[y]=!0,e}function le(e){var t=f.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function he(e,t){var i=t&&e,n=i&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(n)return n;if(i)for(;i=i.nextSibling;)if(i===t)return-1;return e?1:-1}function ce(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function de(e){return function(t){var i=t.nodeName.toLowerCase();return("input"===i||"button"===i)&&t.type===e}}function ue(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&re(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function fe(e){return ae((function(t){return t=+t,ae((function(i,n){for(var r,s=e([],i.length,t),o=s.length;o--;)i[r=s[o]]&&(i[r]=!(n[r]=i[r]))}))}))}function pe(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in i=se.support={},s=se.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},u=se.setDocument=function(e){var t,r,o=e?e.ownerDocument||e:_;return o!==f&&9===o.nodeType&&o.documentElement?(p=(f=o).documentElement,g=!s(f),_!==f&&(r=f.defaultView)&&r.top!==r&&(r.addEventListener?r.addEventListener("unload",ne,!1):r.attachEvent&&r.attachEvent("onunload",ne)),i.attributes=le((function(e){return e.className="i",!e.getAttribute("className")})),i.getElementsByTagName=le((function(e){return e.appendChild(f.createComment("")),!e.getElementsByTagName("*").length})),i.getElementsByClassName=X.test(f.getElementsByClassName),i.getById=le((function(e){return p.appendChild(e).id=y,!f.getElementsByName||!f.getElementsByName(y).length})),i.getById?(n.filter.ID=function(e){var t=e.replace(J,ee);return function(e){return e.getAttribute("id")===t}},n.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var i=t.getElementById(e);return i?[i]:[]}}):(n.filter.ID=function(e){var t=e.replace(J,ee);return function(e){var i=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return i&&i.value===t}},n.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var i,n,r,s=t.getElementById(e);if(s){if((i=s.getAttributeNode("id"))&&i.value===e)return[s];for(r=t.getElementsByName(e),n=0;s=r[n++];)if((i=s.getAttributeNode("id"))&&i.value===e)return[s]}return[]}}),n.find.TAG=i.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):i.qsa?t.querySelectorAll(e):void 0}:function(e,t){var i,n=[],r=0,s=t.getElementsByTagName(e);if("*"===e){for(;i=s[r++];)1===i.nodeType&&n.push(i);return n}return s},n.find.CLASS=i.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&g)return t.getElementsByClassName(e)},b=[],m=[],(i.qsa=X.test(f.querySelectorAll))&&(le((function(e){p.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&m.push("[*^$]="+O+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||m.push("\\["+O+"*(?:value|"+N+")"),e.querySelectorAll("[id~="+y+"-]").length||m.push("~="),e.querySelectorAll(":checked").length||m.push(":checked"),e.querySelectorAll("a#"+y+"+*").length||m.push(".#.+[+~]")})),le((function(e){e.innerHTML="";var t=f.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&m.push("name"+O+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&m.push(":enabled",":disabled"),p.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&m.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),m.push(",.*:")}))),(i.matchesSelector=X.test(w=p.matches||p.webkitMatchesSelector||p.mozMatchesSelector||p.oMatchesSelector||p.msMatchesSelector))&&le((function(e){i.disconnectedMatch=w.call(e,"*"),w.call(e,"[s!='']:x"),b.push("!=",z)})),m=m.length&&new RegExp(m.join("|")),b=b.length&&new RegExp(b.join("|")),t=X.test(p.compareDocumentPosition),v=t||X.test(p.contains)?function(e,t){var i=9===e.nodeType?e.documentElement:e,n=t&&t.parentNode;return e===n||!(!n||1!==n.nodeType||!(i.contains?i.contains(n):e.compareDocumentPosition&&16&e.compareDocumentPosition(n)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},E=t?function(e,t){if(e===t)return d=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!i.sortDetached&&t.compareDocumentPosition(e)===n?e===f||e.ownerDocument===_&&v(_,e)?-1:t===f||t.ownerDocument===_&&v(_,t)?1:c?F(c,e)-F(c,t):0:4&n?-1:1)}:function(e,t){if(e===t)return d=!0,0;var i,n=0,r=e.parentNode,s=t.parentNode,o=[e],a=[t];if(!r||!s)return e===f?-1:t===f?1:r?-1:s?1:c?F(c,e)-F(c,t):0;if(r===s)return he(e,t);for(i=e;i=i.parentNode;)o.unshift(i);for(i=t;i=i.parentNode;)a.unshift(i);for(;o[n]===a[n];)n++;return n?he(o[n],a[n]):o[n]===_?-1:a[n]===_?1:0},f):f},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&u(e),t=t.replace(j,"='$1']"),i.matchesSelector&&g&&!A[t+" "]&&(!b||!b.test(t))&&(!m||!m.test(t)))try{var n=w.call(e,t);if(n||i.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){}return se(t,f,null,[e]).length>0},se.contains=function(e,t){return(e.ownerDocument||e)!==f&&u(e),v(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!==f&&u(e);var r=n.attrHandle[t.toLowerCase()],s=r&&M.call(n.attrHandle,t.toLowerCase())?r(e,t,!g):void 0;return void 0!==s?s:i.attributes||!g?e.getAttribute(t):(s=e.getAttributeNode(t))&&s.specified?s.value:null},se.escape=function(e){return(e+"").replace(te,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,s=0;if(d=!i.detectDuplicates,c=!i.sortStable&&e.slice(0),e.sort(E),d){for(;t=e[s++];)t===e[s]&&(r=n.push(s));for(;r--;)e.splice(n[r],1)}return c=null,e},r=se.getText=function(e){var t,i="",n=0,s=e.nodeType;if(s){if(1===s||9===s||11===s){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)i+=r(e)}else if(3===s||4===s)return e.nodeValue}else for(;t=e[n++];)i+=r(t);return i},n=se.selectors={cacheLength:50,createPseudo:ae,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(J,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(J,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,i=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":i&&$.test(i)&&(t=o(i,!0))&&(t=i.indexOf(")",i.length-t)-i.length)&&(e[0]=e[0].slice(0,t),e[2]=i.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(J,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&C(e,(function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")}))},ATTR:function(e,t,i){return function(n){var r=se.attr(n,e);return null==r?"!="===t:!t||(r+="","="===t?r===i:"!="===t?r!==i:"^="===t?i&&0===r.indexOf(i):"*="===t?i&&r.indexOf(i)>-1:"$="===t?i&&r.slice(-i.length)===i:"~="===t?(" "+r.replace(H," ")+" ").indexOf(i)>-1:"|="===t&&(r===i||r.slice(0,i.length+1)===i+"-"))}},CHILD:function(e,t,i,n,r){var s="nth"!==e.slice(0,3),o="last"!==e.slice(-4),a="of-type"===t;return 1===n&&0===r?function(e){return!!e.parentNode}:function(t,i,l){var h,c,d,u,f,p,g=s!==o?"nextSibling":"previousSibling",m=t.parentNode,b=a&&t.nodeName.toLowerCase(),w=!l&&!a,v=!1;if(m){if(s){for(;g;){for(u=t;u=u[g];)if(a?u.nodeName.toLowerCase()===b:1===u.nodeType)return!1;p=g="only"===e&&!p&&"nextSibling"}return!0}if(p=[o?m.firstChild:m.lastChild],o&&w){for(v=(f=(h=(c=(d=(u=m)[y]||(u[y]={}))[u.uniqueID]||(d[u.uniqueID]={}))[e]||[])[0]===x&&h[1])&&h[2],u=f&&m.childNodes[f];u=++f&&u&&u[g]||(v=f=0)||p.pop();)if(1===u.nodeType&&++v&&u===t){c[e]=[x,f,v];break}}else if(w&&(v=f=(h=(c=(d=(u=t)[y]||(u[y]={}))[u.uniqueID]||(d[u.uniqueID]={}))[e]||[])[0]===x&&h[1]),!1===v)for(;(u=++f&&u&&u[g]||(v=f=0)||p.pop())&&((a?u.nodeName.toLowerCase()!==b:1!==u.nodeType)||!++v||(w&&((c=(d=u[y]||(u[y]={}))[u.uniqueID]||(d[u.uniqueID]={}))[e]=[x,v]),u!==t)););return(v-=r)===n||v%n==0&&v/n>=0}}},PSEUDO:function(e,t){var i,r=n.pseudos[e]||n.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return r[y]?r(t):r.length>1?(i=[e,e,"",t],n.setFilters.hasOwnProperty(e.toLowerCase())?ae((function(e,i){for(var n,s=r(e,t),o=s.length;o--;)e[n=F(e,s[o])]=!(i[n]=s[o])})):function(e){return r(e,0,i)}):r}},pseudos:{not:ae((function(e){var t=[],i=[],n=a(e.replace(V,"$1"));return n[y]?ae((function(e,t,i,r){for(var s,o=n(e,null,r,[]),a=e.length;a--;)(s=o[a])&&(e[a]=!(t[a]=s))})):function(e,r,s){return t[0]=e,n(t,null,s,i),t[0]=null,!i.pop()}})),has:ae((function(e){return function(t){return se(e,t).length>0}})),contains:ae((function(e){return e=e.replace(J,ee),function(t){return(t.textContent||t.innerText||r(t)).indexOf(e)>-1}})),lang:ae((function(e){return W.test(e||"")||se.error("unsupported lang: "+e),e=e.replace(J,ee).toLowerCase(),function(t){var i;do{if(i=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(i=i.toLowerCase())===e||0===i.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}})),target:function(t){var i=e.location&&e.location.hash;return i&&i.slice(1)===t.id},root:function(e){return e===p},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ue(!1),disabled:ue(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!n.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return Z.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:fe((function(){return[0]})),last:fe((function(e,t){return[t-1]})),eq:fe((function(e,t,i){return[i<0?i+t:i]})),even:fe((function(e,t){for(var i=0;i=0;)e.push(n);return e})),gt:fe((function(e,t,i){for(var n=i<0?i+t:i;++n1?function(t,i,n){for(var r=e.length;r--;)if(!e[r](t,i,n))return!1;return!0}:e[0]}function ve(e,t,i,n,r){for(var s,o=[],a=0,l=e.length,h=null!=t;a-1&&(s[h]=!(o[h]=d))}}else b=ve(b===o?b.splice(p,b.length):b),r?r(null,o,b,l):I.apply(o,b)}))}function _e(e){for(var t,i,r,s=e.length,o=n.relative[e[0].type],a=o||n.relative[" "],l=o?1:0,c=be((function(e){return e===t}),a,!0),d=be((function(e){return F(t,e)>-1}),a,!0),u=[function(e,i,n){var r=!o&&(n||i!==h)||((t=i).nodeType?c(e,i,n):d(e,i,n));return t=null,r}];l1&&we(u),l>1&&me(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(V,"$1"),i,l0,r=e.length>0,s=function(s,o,a,l,c){var d,p,m,b=0,w="0",v=s&&[],y=[],_=h,k=s||r&&n.find.TAG("*",c),C=x+=null==_?1:Math.random()||.1,S=k.length;for(c&&(h=o===f||o||c);w!==S&&null!=(d=k[w]);w++){if(r&&d){for(p=0,o||d.ownerDocument===f||(u(d),a=!g);m=e[p++];)if(m(d,o||f,a)){l.push(d);break}c&&(x=C)}i&&((d=!m&&d)&&b--,s&&v.push(d))}if(b+=w,i&&w!==b){for(p=0;m=t[p++];)m(v,y,o,a);if(s){if(b>0)for(;w--;)v[w]||y[w]||(y[w]=R.call(l));y=ve(y)}I.apply(l,y),c&&!s&&y.length>0&&b+t.length>1&&se.uniqueSort(l)}return c&&(x=C,h=_),v};return i?ae(s):s}(s,r)),a.selector=e}return a},l=se.select=function(e,t,i,r){var s,l,h,c,d,u="function"==typeof e&&e,f=!r&&o(e=u.selector||e);if(i=i||[],1===f.length){if((l=f[0]=f[0].slice(0)).length>2&&"ID"===(h=l[0]).type&&9===t.nodeType&&g&&n.relative[l[1].type]){if(!(t=(n.find.ID(h.matches[0].replace(J,ee),t)||[])[0]))return i;u&&(t=t.parentNode),e=e.slice(l.shift().value.length)}for(s=G.needsContext.test(e)?0:l.length;s--&&(h=l[s],!n.relative[c=h.type]);)if((d=n.find[c])&&(r=d(h.matches[0].replace(J,ee),K.test(l[0].type)&&pe(t.parentNode)||t))){if(l.splice(s,1),!(e=r.length&&me(l)))return I.apply(i,r),i;break}}return(u||a(e,f))(r,t,!g,i,!t||K.test(e)&&pe(t.parentNode)||t),i},i.sortStable=y.split("").sort(E).join("")===y,i.detectDuplicates=!!d,u(),se}(window);v.find=x,v.expr=x.selectors,v.expr[":"]=v.expr.pseudos,v.uniqueSort=v.unique=x.uniqueSort,v.text=x.getText,v.isXMLDoc=x.isXML,v.contains=x.contains,v.escapeSelector=x.escape;var k=function(e,t,i){for(var n=[],r=void 0!==i;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(r&&v(e).is(i))break;n.push(e)}return n},C=function(e,t){for(var i=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&i.push(e);return i},S=v.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var E=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function M(e,t,i){return f(t)?v.grep(e,(function(e,n){return!!t.call(e,n,e)!==i})):t.nodeType?v.grep(e,(function(e){return e===t!==i})):"string"!=typeof t?v.grep(e,(function(e){return o.call(t,e)>-1!==i})):v.filter(t,e,i)}v.filter=function(e,t,i){var n=t[0];return i&&(e=":not("+e+")"),1===t.length&&1===n.nodeType?v.find.matchesSelector(n,e)?[n]:[]:v.find.matches(e,v.grep(t,(function(e){return 1===e.nodeType})))},v.fn.extend({find:function(e){var t,i,n=this.length,r=this;if("string"!=typeof e)return this.pushStack(v(e).filter((function(){for(t=0;t1?v.uniqueSort(i):i},filter:function(e){return this.pushStack(M(this,e||[],!1))},not:function(e){return this.pushStack(M(this,e||[],!0))},is:function(e){return!!M(this,"string"==typeof e&&S.test(e)?v(e):e||[],!1).length}});var T,R=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(v.fn.init=function(e,i,n){var r,s;if(!e)return this;if(n=n||T,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:R.exec(e))||!r[1]&&i)return!i||i.jquery?(i||n).find(e):this.constructor(i).find(e);if(r[1]){if(i=i instanceof v?i[0]:i,v.merge(this,v.parseHTML(r[1],i&&i.nodeType?i.ownerDocument||i:t,!0)),E.test(r[1])&&v.isPlainObject(i))for(r in i)f(this[r])?this[r](i[r]):this.attr(r,i[r]);return this}return(s=t.getElementById(r[2]))&&(this[0]=s,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):f(e)?void 0!==n.ready?n.ready(e):e(v):v.makeArray(e,this)}).prototype=v.fn,T=v(t);var L=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};function B(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}v.fn.extend({has:function(e){var t=v(e,this),i=t.length;return this.filter((function(){for(var e=0;e-1:1===i.nodeType&&v.find.matchesSelector(i,e))){s.push(i);break}return this.pushStack(s.length>1?v.uniqueSort(s):s)},index:function(e){return e?"string"==typeof e?o.call(v(e),this[0]):o.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(v.uniqueSort(v.merge(this.get(),v(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),v.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,i){return k(e,"parentNode",i)},next:function(e){return B(e,"nextSibling")},prev:function(e){return B(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,i){return k(e,"nextSibling",i)},prevUntil:function(e,t,i){return k(e,"previousSibling",i)},siblings:function(e){return C((e.parentNode||{}).firstChild,e)},children:function(e){return C(e.firstChild)},contents:function(e){return A(e,"iframe")?e.contentDocument:(A(e,"template")&&(e=e.content||e),v.merge([],e.childNodes))}},(function(e,t){v.fn[e]=function(i,n){var r=v.map(this,t,i);return"Until"!==e.slice(-5)&&(n=i),n&&"string"==typeof n&&(r=v.filter(n,r)),this.length>1&&(I[e]||v.uniqueSort(r),L.test(e)&&r.reverse()),this.pushStack(r)}}));var F=/[^\x20\t\r\n\f]+/g;function N(e){return e}function O(e){throw e}function P(e,t,i,n){var r;try{e&&f(r=e.promise)?r.call(e).done(t).fail(i):e&&f(r=e.then)?r.call(e,t,i):t.apply(void 0,[e].slice(n))}catch(e){i.apply(void 0,[e])}}v.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return v.each(e.match(F)||[],(function(e,i){t[i]=!0})),t}(e):v.extend({},e);var t,i,n,r,s=[],o=[],a=-1,l=function(){for(r=r||e.once,n=t=!0;o.length;a=-1)for(i=o.shift();++a-1;)s.splice(i,1),i<=a&&a--})),this},has:function(e){return e?v.inArray(e,s)>-1:s.length>0},empty:function(){return s&&(s=[]),this},disable:function(){return r=o=[],s=i="",this},disabled:function(){return!s},lock:function(){return r=o=[],i||t||(s=i=""),this},locked:function(){return!!r},fireWith:function(e,i){return r||(i=[e,(i=i||[]).slice?i.slice():i],o.push(i),t||l()),this},fire:function(){return h.fireWith(this,arguments),this},fired:function(){return!!n}};return h},v.extend({Deferred:function(e){var t=[["notify","progress",v.Callbacks("memory"),v.Callbacks("memory"),2],["resolve","done",v.Callbacks("once memory"),v.Callbacks("once memory"),0,"resolved"],["reject","fail",v.Callbacks("once memory"),v.Callbacks("once memory"),1,"rejected"]],i="pending",n={state:function(){return i},always:function(){return r.done(arguments).fail(arguments),this},catch:function(e){return n.then(null,e)},pipe:function(){var e=arguments;return v.Deferred((function(i){v.each(t,(function(t,n){var s=f(e[n[4]])&&e[n[4]];r[n[1]]((function(){var e=s&&s.apply(this,arguments);e&&f(e.promise)?e.promise().progress(i.notify).done(i.resolve).fail(i.reject):i[n[0]+"With"](this,s?[e]:arguments)}))})),e=null})).promise()},then:function(e,i,n){var r=0;function s(e,t,i,n){return function(){var o=this,a=arguments,l=function(){var l,h;if(!(e=r&&(i!==O&&(o=void 0,a=[n]),t.rejectWith(o,a))}};e?h():(v.Deferred.getStackHook&&(h.stackTrace=v.Deferred.getStackHook()),window.setTimeout(h))}}return v.Deferred((function(r){t[0][3].add(s(0,r,f(n)?n:N,r.notifyWith)),t[1][3].add(s(0,r,f(e)?e:N)),t[2][3].add(s(0,r,f(i)?i:O))})).promise()},promise:function(e){return null!=e?v.extend(e,n):n}},r={};return v.each(t,(function(e,s){var o=s[2],a=s[5];n[s[1]]=o.add,a&&o.add((function(){i=a}),t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),o.add(s[3].fire),r[s[0]]=function(){return r[s[0]+"With"](this===r?void 0:this,arguments),this},r[s[0]+"With"]=o.fireWith})),n.promise(r),e&&e.call(r,r),r},when:function(e){var t=arguments.length,i=t,r=Array(i),s=n.call(arguments),o=v.Deferred(),a=function(e){return function(i){r[e]=this,s[e]=arguments.length>1?n.call(arguments):i,--t||o.resolveWith(r,s)}};if(t<=1&&(P(e,o.done(a(i)).resolve,o.reject,!t),"pending"===o.state()||f(s[i]&&s[i].then)))return o.then();for(;i--;)P(s[i],a(i),o.reject);return o.promise()}});var D=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;v.Deferred.exceptionHook=function(e,t){window.console&&window.console.warn&&e&&D.test(e.name)&&window.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},v.readyException=function(e){window.setTimeout((function(){throw e}))};var z=v.Deferred();function H(){t.removeEventListener("DOMContentLoaded",H),window.removeEventListener("load",H),v.ready()}v.fn.ready=function(e){return z.then(e).catch((function(e){v.readyException(e)})),this},v.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--v.readyWait:v.isReady)||(v.isReady=!0,!0!==e&&--v.readyWait>0||z.resolveWith(t,[v]))}}),v.ready.then=z.then,"complete"===t.readyState||"loading"!==t.readyState&&!t.documentElement.doScroll?window.setTimeout(v.ready):(t.addEventListener("DOMContentLoaded",H),window.addEventListener("load",H));var V=function(e,t,i,n,r,s,o){var a=0,l=e.length,h=null==i;if("object"===b(i))for(a in r=!0,i)V(e,t,a,i[a],!0,s,o);else if(void 0!==n&&(r=!0,f(n)||(o=!0),h&&(o?(t.call(e,n),t=null):(h=t,t=function(e,t,i){return h.call(v(e),i)})),t))for(;a1,null,!0)},removeData:function(e){return this.each((function(){Q.remove(this,e)}))}}),v.extend({queue:function(e,t,i){var n;if(e)return t=(t||"fx")+"queue",n=Z.get(e,t),i&&(!n||Array.isArray(i)?n=Z.access(e,t,v.makeArray(i)):n.push(i)),n||[]},dequeue:function(e,t){t=t||"fx";var i=v.queue(e,t),n=i.length,r=i.shift(),s=v._queueHooks(e,t);"inprogress"===r&&(r=i.shift(),n--),r&&("fx"===t&&i.unshift("inprogress"),delete s.stop,r.call(e,(function(){v.dequeue(e,t)}),s)),!n&&s&&s.empty.fire()},_queueHooks:function(e,t){var i=t+"queueHooks";return Z.get(e,i)||Z.access(e,i,{empty:v.Callbacks("once memory").add((function(){Z.remove(e,[t+"queue",i])}))})}}),v.fn.extend({queue:function(e,t){var i=2;return"string"!=typeof e&&(t=e,e="fx",i--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ce={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function de(e,t){var i;return i=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?v.merge([e],i):i}function ue(e,t){for(var i=0,n=e.length;i-1)r&&r.push(s);else if(h=v.contains(s.ownerDocument,s),o=de(d.appendChild(s),"script"),h&&ue(o),i)for(c=0;s=o[c++];)he.test(s.type||"")&&i.push(s);return d}!function(){var e=t.createDocumentFragment().appendChild(t.createElement("div")),i=t.createElement("input");i.setAttribute("type","radio"),i.setAttribute("checked","checked"),i.setAttribute("name","t"),e.appendChild(i),u.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",u.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var ge=t.documentElement,me=/^key/,be=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,we=/^([^.]*)(?:\.(.+)|)/;function ve(){return!0}function ye(){return!1}function _e(){try{return t.activeElement}catch(e){}}function xe(e,t,i,n,r,s){var o,a;if("object"==typeof t){for(a in"string"!=typeof i&&(n=n||i,i=void 0),t)xe(e,a,i,n,t[a],s);return e}if(null==n&&null==r?(r=i,n=i=void 0):null==r&&("string"==typeof i?(r=n,n=void 0):(r=n,n=i,i=void 0)),!1===r)r=ye;else if(!r)return e;return 1===s&&(o=r,r=function(e){return v().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=v.guid++)),e.each((function(){v.event.add(this,t,r,n,i)}))}v.event={global:{},add:function(e,t,i,n,r){var s,o,a,l,h,c,d,u,f,p,g,m=Z.get(e);if(m)for(i.handler&&(i=(s=i).handler,r=s.selector),r&&v.find.matchesSelector(ge,r),i.guid||(i.guid=v.guid++),(l=m.events)||(l=m.events={}),(o=m.handle)||(o=m.handle=function(t){return void 0!==v&&v.event.triggered!==t.type?v.event.dispatch.apply(e,arguments):void 0}),h=(t=(t||"").match(F)||[""]).length;h--;)f=g=(a=we.exec(t[h])||[])[1],p=(a[2]||"").split(".").sort(),f&&(d=v.event.special[f]||{},f=(r?d.delegateType:d.bindType)||f,d=v.event.special[f]||{},c=v.extend({type:f,origType:g,data:n,handler:i,guid:i.guid,selector:r,needsContext:r&&v.expr.match.needsContext.test(r),namespace:p.join(".")},s),(u=l[f])||((u=l[f]=[]).delegateCount=0,d.setup&&!1!==d.setup.call(e,n,p,o)||e.addEventListener&&e.addEventListener(f,o)),d.add&&(d.add.call(e,c),c.handler.guid||(c.handler.guid=i.guid)),r?u.splice(u.delegateCount++,0,c):u.push(c),v.event.global[f]=!0)},remove:function(e,t,i,n,r){var s,o,a,l,h,c,d,u,f,p,g,m=Z.hasData(e)&&Z.get(e);if(m&&(l=m.events)){for(h=(t=(t||"").match(F)||[""]).length;h--;)if(f=g=(a=we.exec(t[h])||[])[1],p=(a[2]||"").split(".").sort(),f){for(d=v.event.special[f]||{},u=l[f=(n?d.delegateType:d.bindType)||f]||[],a=a[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),o=s=u.length;s--;)c=u[s],!r&&g!==c.origType||i&&i.guid!==c.guid||a&&!a.test(c.namespace)||n&&n!==c.selector&&("**"!==n||!c.selector)||(u.splice(s,1),c.selector&&u.delegateCount--,d.remove&&d.remove.call(e,c));o&&!u.length&&(d.teardown&&!1!==d.teardown.call(e,p,m.handle)||v.removeEvent(e,f,m.handle),delete l[f])}else for(f in l)v.event.remove(e,f+t[h],i,n,!0);v.isEmptyObject(l)&&Z.remove(e,"handle events")}},dispatch:function(e){var t,i,n,r,s,o,a=v.event.fix(e),l=new Array(arguments.length),h=(Z.get(this,"events")||{})[a.type]||[],c=v.event.special[a.type]||{};for(l[0]=a,t=1;t=1))for(;h!==this;h=h.parentNode||this)if(1===h.nodeType&&("click"!==e.type||!0!==h.disabled)){for(s=[],o={},i=0;i-1:v.find(r,this,null,[h]).length),o[r]&&s.push(n);s.length&&a.push({elem:h,handlers:s})}return h=this,l\x20\t\r\n\f]*)[^>]*)\/>/gi,Ce=/\s*$/g;function Ee(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&v(e).children("tbody")[0]||e}function Me(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Te(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Re(e,t){var i,n,r,s,o,a,l,h;if(1===t.nodeType){if(Z.hasData(e)&&(s=Z.access(e),o=Z.set(t,s),h=s.events))for(r in delete o.handle,o.events={},h)for(i=0,n=h[r].length;i1&&"string"==typeof b&&!u.checkClone&&Se.test(b))return e.each((function(r){var s=e.eq(r);w&&(t[0]=b.call(this,r,s.html())),Ie(s,t,i,n)}));if(p&&(o=(s=pe(t,e[0].ownerDocument,!1,e,n)).firstChild,1===s.childNodes.length&&(s=o),o||n)){for(l=(a=v.map(de(s,"script"),Me)).length;d")},clone:function(e,t,i){var n,r,s,o,a=e.cloneNode(!0),l=v.contains(e.ownerDocument,e);if(!(u.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||v.isXMLDoc(e)))for(o=de(a),n=0,r=(s=de(e)).length;n0&&ue(o,!l&&de(e,"script")),a},cleanData:function(e){for(var t,i,n,r=v.event.special,s=0;void 0!==(i=e[s]);s++)if(W(i)){if(t=i[Z.expando]){if(t.events)for(n in t.events)r[n]?v.event.remove(i,n):v.removeEvent(i,n,t.handle);i[Z.expando]=void 0}i[Q.expando]&&(i[Q.expando]=void 0)}}}),v.fn.extend({detach:function(e){return Be(this,e,!0)},remove:function(e){return Be(this,e)},text:function(e){return V(this,(function(e){return void 0===e?v.text(this):this.empty().each((function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)}))}),null,e,arguments.length)},append:function(){return Ie(this,arguments,(function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Ee(this,e).appendChild(e)}))},prepend:function(){return Ie(this,arguments,(function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Ee(this,e);t.insertBefore(e,t.firstChild)}}))},before:function(){return Ie(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this)}))},after:function(){return Ie(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)}))},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(v.cleanData(de(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map((function(){return v.clone(this,e,t)}))},html:function(e){return V(this,(function(e){var t=this[0]||{},i=0,n=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ce.test(e)&&!ce[(le.exec(e)||["",""])[1].toLowerCase()]){e=v.htmlPrefilter(e);try{for(;i=0&&(l+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-s-l-a-.5))),l}function Xe(e,t,i){var n=Ne(e),r=Pe(e,t,n),s="border-box"===v.css(e,"boxSizing",!1,n),o=s;if(Fe.test(r)){if(!i)return r;r="auto"}return o=o&&(u.boxSizingReliable()||r===e.style[t]),("auto"===r||!parseFloat(r)&&"inline"===v.css(e,"display",!1,n))&&(r=e["offset"+t[0].toUpperCase()+t.slice(1)],o=!0),(r=parseFloat(r)||0)+Qe(e,t,i||(s?"border":"content"),o,n,r)+"px"}v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var i=Pe(e,"opacity");return""===i?"1":i}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,i,n){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var r,s,o,a=$(t),l=Ue.test(t),h=e.style;if(l||(t=Ge(a)),o=v.cssHooks[t]||v.cssHooks[a],void 0===i)return o&&"get"in o&&void 0!==(r=o.get(e,!1,n))?r:h[t];"string"===(s=typeof i)&&(r=ee.exec(i))&&r[1]&&(i=function(e,t,i,n){var r,s,o=20,a=n?function(){return n.cur()}:function(){return v.css(e,t,"")},l=a(),h=i&&i[3]||(v.cssNumber[t]?"":"px"),c=(v.cssNumber[t]||"px"!==h&&+l)&&ee.exec(v.css(e,t));if(c&&c[3]!==h){for(l/=2,h=h||c[3],c=+l||1;o--;)v.style(e,t,c+h),(1-s)*(1-(s=a()/l||.5))<=0&&(o=0),c/=s;c*=2,v.style(e,t,c+h),i=i||[]}return i&&(c=+c||+l||0,r=i[1]?c+(i[1]+1)*i[2]:+i[2],n&&(n.unit=h,n.start=c,n.end=r)),r}(e,t,r),s="number"),null!=i&&i==i&&("number"===s&&(i+=r&&r[3]||(v.cssNumber[a]?"":"px")),u.clearCloneStyle||""!==i||0!==t.indexOf("background")||(h[t]="inherit"),o&&"set"in o&&void 0===(i=o.set(e,i,n))||(l?h.setProperty(t,i):h[t]=i))}},css:function(e,t,i,n){var r,s,o,a=$(t);return Ue.test(t)||(t=Ge(a)),(o=v.cssHooks[t]||v.cssHooks[a])&&"get"in o&&(r=o.get(e,!0,i)),void 0===r&&(r=Pe(e,t,n)),"normal"===r&&t in je&&(r=je[t]),""===i||i?(s=parseFloat(r),!0===i||isFinite(s)?s||0:r):r}}),v.each(["height","width"],(function(e,t){v.cssHooks[t]={get:function(e,i,n){if(i)return!Ve.test(v.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Xe(e,t,n):ne(e,qe,(function(){return Xe(e,t,n)}))},set:function(e,i,n){var r,s=Ne(e),o="border-box"===v.css(e,"boxSizing",!1,s),a=n&&Qe(e,t,n,o,s);return o&&u.scrollboxSize()===s.position&&(a-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(s[t])-Qe(e,t,"border",!1,s)-.5)),a&&(r=ee.exec(i))&&"px"!==(r[3]||"px")&&(e.style[t]=i,i=v.css(e,t)),Ze(0,i,a)}}})),v.cssHooks.marginLeft=De(u.reliableMarginLeft,(function(e,t){if(t)return(parseFloat(Pe(e,"marginLeft"))||e.getBoundingClientRect().left-ne(e,{marginLeft:0},(function(){return e.getBoundingClientRect().left})))+"px"})),v.each({margin:"",padding:"",border:"Width"},(function(e,t){v.cssHooks[e+t]={expand:function(i){for(var n=0,r={},s="string"==typeof i?i.split(" "):[i];n<4;n++)r[e+te[n]+t]=s[n]||s[n-2]||s[0];return r}},"margin"!==e&&(v.cssHooks[e+t].set=Ze)})),v.fn.extend({css:function(e,t){return V(this,(function(e,t,i){var n,r,s={},o=0;if(Array.isArray(t)){for(n=Ne(e),r=t.length;o1)}}),v.fn.delay=function(e,t){return e=v.fx&&v.fx.speeds[e]||e,t=t||"fx",this.queue(t,(function(t,i){var n=window.setTimeout(t,e);i.stop=function(){window.clearTimeout(n)}}))},ze=t.createElement("input"),He=t.createElement("select").appendChild(t.createElement("option")),ze.type="checkbox",u.checkOn=""!==ze.value,u.optSelected=He.selected,(ze=t.createElement("input")).value="t",ze.type="radio",u.radioValue="t"===ze.value;var Ye,Ke=v.expr.attrHandle;v.fn.extend({attr:function(e,t){return V(this,v.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each((function(){v.removeAttr(this,e)}))}}),v.extend({attr:function(e,t,i){var n,r,s=e.nodeType;if(3!==s&&8!==s&&2!==s)return void 0===e.getAttribute?v.prop(e,t,i):(1===s&&v.isXMLDoc(e)||(r=v.attrHooks[t.toLowerCase()]||(v.expr.match.bool.test(t)?Ye:void 0)),void 0!==i?null===i?void v.removeAttr(e,t):r&&"set"in r&&void 0!==(n=r.set(e,i,t))?n:(e.setAttribute(t,i+""),i):r&&"get"in r&&null!==(n=r.get(e,t))?n:null==(n=v.find.attr(e,t))?void 0:n)},attrHooks:{type:{set:function(e,t){if(!u.radioValue&&"radio"===t&&A(e,"input")){var i=e.value;return e.setAttribute("type",t),i&&(e.value=i),t}}}},removeAttr:function(e,t){var i,n=0,r=t&&t.match(F);if(r&&1===e.nodeType)for(;i=r[n++];)e.removeAttribute(i)}}),Ye={set:function(e,t,i){return!1===t?v.removeAttr(e,i):e.setAttribute(i,i),i}},v.each(v.expr.match.bool.source.match(/\w+/g),(function(e,t){var i=Ke[t]||v.find.attr;Ke[t]=function(e,t,n){var r,s,o=t.toLowerCase();return n||(s=Ke[o],Ke[o]=r,r=null!=i(e,t,n)?o:null,Ke[o]=s),r}}));var Je=/^(?:input|select|textarea|button)$/i,et=/^(?:a|area)$/i;function tt(e){return(e.match(F)||[]).join(" ")}function it(e){return e.getAttribute&&e.getAttribute("class")||""}function nt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(F)||[]}v.fn.extend({prop:function(e,t){return V(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each((function(){delete this[v.propFix[e]||e]}))}}),v.extend({prop:function(e,t,i){var n,r,s=e.nodeType;if(3!==s&&8!==s&&2!==s)return 1===s&&v.isXMLDoc(e)||(t=v.propFix[t]||t,r=v.propHooks[t]),void 0!==i?r&&"set"in r&&void 0!==(n=r.set(e,i,t))?n:e[t]=i:r&&"get"in r&&null!==(n=r.get(e,t))?n:e[t]},propHooks:{tabIndex:{get:function(e){var t=v.find.attr(e,"tabindex");return t?parseInt(t,10):Je.test(e.nodeName)||et.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),u.optSelected||(v.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),v.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],(function(){v.propFix[this.toLowerCase()]=this})),v.fn.extend({addClass:function(e){var t,i,n,r,s,o,a,l=0;if(f(e))return this.each((function(t){v(this).addClass(e.call(this,t,it(this)))}));if((t=nt(e)).length)for(;i=this[l++];)if(r=it(i),n=1===i.nodeType&&" "+tt(r)+" "){for(o=0;s=t[o++];)n.indexOf(" "+s+" ")<0&&(n+=s+" ");r!==(a=tt(n))&&i.setAttribute("class",a)}return this},removeClass:function(e){var t,i,n,r,s,o,a,l=0;if(f(e))return this.each((function(t){v(this).removeClass(e.call(this,t,it(this)))}));if(!arguments.length)return this.attr("class","");if((t=nt(e)).length)for(;i=this[l++];)if(r=it(i),n=1===i.nodeType&&" "+tt(r)+" "){for(o=0;s=t[o++];)for(;n.indexOf(" "+s+" ")>-1;)n=n.replace(" "+s+" "," ");r!==(a=tt(n))&&i.setAttribute("class",a)}return this},toggleClass:function(e,t){var i=typeof e,n="string"===i||Array.isArray(e);return"boolean"==typeof t&&n?t?this.addClass(e):this.removeClass(e):f(e)?this.each((function(i){v(this).toggleClass(e.call(this,i,it(this),t),t)})):this.each((function(){var t,r,s,o;if(n)for(r=0,s=v(this),o=nt(e);t=o[r++];)s.hasClass(t)?s.removeClass(t):s.addClass(t);else void 0!==e&&"boolean"!==i||((t=it(this))&&Z.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":Z.get(this,"__className__")||""))}))},hasClass:function(e){var t,i,n=0;for(t=" "+e+" ";i=this[n++];)if(1===i.nodeType&&(" "+tt(it(i))+" ").indexOf(t)>-1)return!0;return!1}});var rt=/\r/g;v.fn.extend({val:function(e){var t,i,n,r=this[0];return arguments.length?(n=f(e),this.each((function(i){var r;1===this.nodeType&&(null==(r=n?e.call(this,i,v(this).val()):e)?r="":"number"==typeof r?r+="":Array.isArray(r)&&(r=v.map(r,(function(e){return null==e?"":e+""}))),(t=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,r,"value")||(this.value=r))}))):r?(t=v.valHooks[r.type]||v.valHooks[r.nodeName.toLowerCase()])&&"get"in t&&void 0!==(i=t.get(r,"value"))?i:"string"==typeof(i=r.value)?i.replace(rt,""):null==i?"":i:void 0}}),v.extend({valHooks:{option:{get:function(e){var t=v.find.attr(e,"value");return null!=t?t:tt(v.text(e))}},select:{get:function(e){var t,i,n,r=e.options,s=e.selectedIndex,o="select-one"===e.type,a=o?null:[],l=o?s+1:r.length;for(n=s<0?l:o?s:0;n-1)&&(i=!0);return i||(e.selectedIndex=-1),s}}}}),v.each(["radio","checkbox"],(function(){v.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=v.inArray(v(e).val(),t)>-1}},u.checkOn||(v.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})})),u.focusin="onfocusin"in window;var st=/^(?:focusinfocus|focusoutblur)$/,ot=function(e){e.stopPropagation()};v.extend(v.event,{trigger:function(e,i,n,r){var s,o,a,l,c,d,u,g,m=[n||t],b=h.call(e,"type")?e.type:e,w=h.call(e,"namespace")?e.namespace.split("."):[];if(o=g=a=n=n||t,3!==n.nodeType&&8!==n.nodeType&&!st.test(b+v.event.triggered)&&(b.indexOf(".")>-1&&(w=b.split("."),b=w.shift(),w.sort()),c=b.indexOf(":")<0&&"on"+b,(e=e[v.expando]?e:new v.Event(b,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=w.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+w.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),i=null==i?[e]:v.makeArray(i,[e]),u=v.event.special[b]||{},r||!u.trigger||!1!==u.trigger.apply(n,i))){if(!r&&!u.noBubble&&!p(n)){for(l=u.delegateType||b,st.test(l+b)||(o=o.parentNode);o;o=o.parentNode)m.push(o),a=o;a===(n.ownerDocument||t)&&m.push(a.defaultView||a.parentWindow||window)}for(s=0;(o=m[s++])&&!e.isPropagationStopped();)g=o,e.type=s>1?l:u.bindType||b,(d=(Z.get(o,"events")||{})[e.type]&&Z.get(o,"handle"))&&d.apply(o,i),(d=c&&o[c])&&d.apply&&W(o)&&(e.result=d.apply(o,i),!1===e.result&&e.preventDefault());return e.type=b,r||e.isDefaultPrevented()||u._default&&!1!==u._default.apply(m.pop(),i)||!W(n)||c&&f(n[b])&&!p(n)&&((a=n[c])&&(n[c]=null),v.event.triggered=b,e.isPropagationStopped()&&g.addEventListener(b,ot),n[b](),e.isPropagationStopped()&&g.removeEventListener(b,ot),v.event.triggered=void 0,a&&(n[c]=a)),e.result}},simulate:function(e,t,i){var n=v.extend(new v.Event,i,{type:e,isSimulated:!0});v.event.trigger(n,null,t)}}),v.fn.extend({trigger:function(e,t){return this.each((function(){v.event.trigger(e,t,this)}))},triggerHandler:function(e,t){var i=this[0];if(i)return v.event.trigger(e,t,i,!0)}}),u.focusin||v.each({focus:"focusin",blur:"focusout"},(function(e,t){var i=function(e){v.event.simulate(t,e.target,v.event.fix(e))};v.event.special[t]={setup:function(){var n=this.ownerDocument||this,r=Z.access(n,t);r||n.addEventListener(e,i,!0),Z.access(n,t,(r||0)+1)},teardown:function(){var n=this.ownerDocument||this,r=Z.access(n,t)-1;r?Z.access(n,t,r):(n.removeEventListener(e,i,!0),Z.remove(n,t))}}}));var at,lt=/\[\]$/,ht=/\r?\n/g,ct=/^(?:submit|button|image|reset|file)$/i,dt=/^(?:input|select|textarea|keygen)/i;function ut(e,t,i,n){var r;if(Array.isArray(t))v.each(t,(function(t,r){i||lt.test(e)?n(e,r):ut(e+"["+("object"==typeof r&&null!=r?t:"")+"]",r,i,n)}));else if(i||"object"!==b(t))n(e,t);else for(r in t)ut(e+"["+r+"]",t[r],i,n)}v.param=function(e,t){var i,n=[],r=function(e,t){var i=f(t)?t():t;n[n.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==i?"":i)};if(Array.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,(function(){r(this.name,this.value)}));else for(i in e)ut(i,e[i],t,r);return n.join("&")},v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var e=v.prop(this,"elements");return e?v.makeArray(e):this})).filter((function(){var e=this.type;return this.name&&!v(this).is(":disabled")&&dt.test(this.nodeName)&&!ct.test(e)&&(this.checked||!ae.test(e))})).map((function(e,t){var i=v(this).val();return null==i?null:Array.isArray(i)?v.map(i,(function(e){return{name:t.name,value:e.replace(ht,"\r\n")}})):{name:t.name,value:i.replace(ht,"\r\n")}})).get()}}),v.fn.extend({wrapAll:function(e){var t;return this[0]&&(f(e)&&(e=e.call(this[0])),t=v(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map((function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e})).append(this)),this},wrapInner:function(e){return f(e)?this.each((function(t){v(this).wrapInner(e.call(this,t))})):this.each((function(){var t=v(this),i=t.contents();i.length?i.wrapAll(e):t.append(e)}))},wrap:function(e){var t=f(e);return this.each((function(i){v(this).wrapAll(t?e.call(this,i):e)}))},unwrap:function(e){return this.parent(e).not("body").each((function(){v(this).replaceWith(this.childNodes)})),this}}),v.expr.pseudos.hidden=function(e){return!v.expr.pseudos.visible(e)},v.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},u.createHTMLDocument=((at=t.implementation.createHTMLDocument("").body).innerHTML="
",2===at.childNodes.length),v.parseHTML=function(e,i,n){return"string"!=typeof e?[]:("boolean"==typeof i&&(n=i,i=!1),i||(u.createHTMLDocument?((r=(i=t.implementation.createHTMLDocument("")).createElement("base")).href=t.location.href,i.head.appendChild(r)):i=t),o=!n&&[],(s=E.exec(e))?[i.createElement(s[1])]:(s=pe([e],i,o),o&&o.length&&v(o).remove(),v.merge([],s.childNodes)));var r,s,o},v.offset={setOffset:function(e,t,i){var n,r,s,o,a,l,h=v.css(e,"position"),c=v(e),d={};"static"===h&&(e.style.position="relative"),a=c.offset(),s=v.css(e,"top"),l=v.css(e,"left"),("absolute"===h||"fixed"===h)&&(s+l).indexOf("auto")>-1?(o=(n=c.position()).top,r=n.left):(o=parseFloat(s)||0,r=parseFloat(l)||0),f(t)&&(t=t.call(e,i,v.extend({},a))),null!=t.top&&(d.top=t.top-a.top+o),null!=t.left&&(d.left=t.left-a.left+r),"using"in t?t.using.call(e,d):c.css(d)}},v.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each((function(t){v.offset.setOffset(this,e,t)}));var t,i,n=this[0];return n?n.getClientRects().length?(t=n.getBoundingClientRect(),i=n.ownerDocument.defaultView,{top:t.top+i.pageYOffset,left:t.left+i.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,i,n=this[0],r={top:0,left:0};if("fixed"===v.css(n,"position"))t=n.getBoundingClientRect();else{for(t=this.offset(),i=n.ownerDocument,e=n.offsetParent||i.documentElement;e&&(e===i.body||e===i.documentElement)&&"static"===v.css(e,"position");)e=e.parentNode;e&&e!==n&&1===e.nodeType&&((r=v(e).offset()).top+=v.css(e,"borderTopWidth",!0),r.left+=v.css(e,"borderLeftWidth",!0))}return{top:t.top-r.top-v.css(n,"marginTop",!0),left:t.left-r.left-v.css(n,"marginLeft",!0)}}},offsetParent:function(){return this.map((function(){for(var e=this.offsetParent;e&&"static"===v.css(e,"position");)e=e.offsetParent;return e||ge}))}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},(function(e,t){var i="pageYOffset"===t;v.fn[e]=function(n){return V(this,(function(e,n,r){var s;if(p(e)?s=e:9===e.nodeType&&(s=e.defaultView),void 0===r)return s?s[t]:e[n];s?s.scrollTo(i?s.pageXOffset:r,i?r:s.pageYOffset):e[n]=r}),e,n,arguments.length)}})),v.each(["top","left"],(function(e,t){v.cssHooks[t]=De(u.pixelPosition,(function(e,i){if(i)return i=Pe(e,t),Fe.test(i)?v(e).position()[t]+"px":i}))})),v.each({Height:"height",Width:"width"},(function(e,t){v.each({padding:"inner"+e,content:t,"":"outer"+e},(function(i,n){v.fn[n]=function(r,s){var o=arguments.length&&(i||"boolean"!=typeof r),a=i||(!0===r||!0===s?"margin":"border");return V(this,(function(t,i,r){var s;return p(t)?0===n.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(s=t.documentElement,Math.max(t.body["scroll"+e],s["scroll"+e],t.body["offset"+e],s["offset"+e],s["client"+e])):void 0===r?v.css(t,i,a):v.style(t,i,r,a)}),t,o?r:void 0,o)}}))})),v.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(e,t){v.fn[t]=function(e,i){return arguments.length>0?this.on(t,null,e,i):this.trigger(t)}})),v.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),v.fn.extend({bind:function(e,t,i){return this.on(e,null,t,i)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,i,n){return this.on(t,e,i,n)},undelegate:function(e,t,i){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",i)}}),v.proxy=function(e,t){var i,r,s;if("string"==typeof t&&(i=e[t],t=e,e=i),f(e))return r=n.call(arguments,2),s=function(){return e.apply(t||this,r.concat(n.call(arguments)))},s.guid=e.guid=e.guid||v.guid++,s},v.holdReady=function(e){e?v.readyWait++:v.ready(!0)},v.isArray=Array.isArray,v.parseJSON=JSON.parse,v.nodeName=A,v.isFunction=f,v.isWindow=p,v.camelCase=$,v.type=b,v.now=Date.now,v.isNumeric=function(e){var t=v.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))};const ft=v;function pt(e){return gt("div",e)}function gt(e,t){const i=document.createElement(e);return t&&(t.class&&i.classList.add(t.class),t.id&&(i.id=t.id),t.style&&wt(i,t.style)),i}function mt(e){const t=getComputedStyle(e);"none"!==t.display&&(e._initialDisplay=t.display),e.style.display="none"}function bt(e){if("none"===getComputedStyle(e).display){const t=e._initialDisplay||"block";e.style.display=t}}function wt(e,t){for(let i of Object.keys(t))e.style[i]=t[i]}let vt=(e,{clientX:t,clientY:i})=>{const{left:n,top:r,width:s,height:o}=e.getBoundingClientRect(),a=t-n,l=i-r;return{x:a,y:l,xNormalized:a/s,yNormalized:l/o,width:s,height:o}};function yt(e,t){const{clientX:i,clientY:n}=e;return vt(t,{clientX:i,clientY:n})}var _t=Object.freeze({__proto__:null,applyStyle:wt,create:gt,div:pt,empty:function(e){for(;e.firstChild;)e.removeChild(e.firstChild)},guid:function(){return("0000"+(Math.random()*Math.pow(36,4)<<0).toString(36)).slice(-4)},hide:mt,hideAll:function(e){document.querySelectorAll(e).forEach((e=>{mt(e)}))},offset:function(e){if(!e.getClientRects().length)return{top:0,left:0};const t=e.getBoundingClientRect(),i=e.ownerDocument.defaultView;return{top:t.top+i.pageYOffset,left:t.left+i.pageXOffset}},pageCoordinates:function(e){if(e.type.startsWith("touch")){const t=e.touches[0];return{x:t.pageX,y:t.pageY}}return{x:e.pageX,y:e.pageY}},relativeDOMBBox:(e,t)=>{const{x:i,y:n,width:r,height:s}=e.getBoundingClientRect(),{x:o,y:a,width:l,height:h}=t.getBoundingClientRect();return{x:o-i,y:a-n,width:l,height:h}},show:bt,translateMouseCoordinates:yt});function xt(e,t){const i=pt({class:"igv-ui-trackgear-popover-check-container"}),n=Ct("check",!0===t?"#444":"transparent");n.style.borderColor="gray",n.style.borderWidth="1px",n.style.borderStyle="solid",i.appendChild(n);let r=pt();return r.textContent=e,i.appendChild(r),i}function kt(e,t){return Ct(e,t)}function Ct(e,t){t=t||"currentColor";let i=St[e];i||(console.error(`No icon named: ${e}`),i=St.question);const n=document.createElementNS("http://www.w3.org/2000/svg","svg");n.setAttributeNS(null,"viewBox","0 0 "+i[0]+" "+i[1]);const r=document.createElementNS("http://www.w3.org/2000/svg","path");return r.setAttributeNS(null,"fill",t),r.setAttributeNS(null,"d",i[4]),n.appendChild(r),n}const St={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"]};var At=Object.freeze({__proto__:null,createCheckbox:xt,createIcon:kt});function Et(e,t){var i=document.createElement("div");e.appendChild(i),i.appendChild(kt("times")),i.addEventListener("click",(function(e){e.preventDefault(),e.stopPropagation(),t()}))}var Mt=Object.freeze({__proto__:null,attachDialogCloseHandlerWithParent:Et});let Tt;function Rt(e,t,i){t.addEventListener("mousedown",function(e){e.stopPropagation(),e.preventDefault();const t=Lt.bind(this),n=It.bind(this),r=getComputedStyle(this);Tt={constraint:i,dragFunction:t,dragEndFunction:n,screenX:e.screenX,screenY:e.screenY,top:parseInt(r.top.replace("px","")),left:parseInt(r.left.replace("px",""))},document.addEventListener("mousemove",t),document.addEventListener("mouseup",n),document.addEventListener("mouseleave",n),document.addEventListener("mouseexit",n)}.bind(e))}function Lt(e){if(!Tt)return void console.error("No drag data!");e.stopPropagation(),e.preventDefault();const t=e.screenX-Tt.screenX,i=e.screenY-Tt.screenY,n=Tt.left+t,r=Tt.constraint?Math.max(Tt.constraint.minY,Tt.top+i):Tt.top+i;this.style.left=`${n}px`,this.style.top=`${r}px`}function It(e){if(!Tt)return void console.error("No drag data!");e.stopPropagation(),e.preventDefault();const t=Tt.dragFunction,i=Tt.dragEndFunction;document.removeEventListener("mousemove",t),document.removeEventListener("mouseup",i),document.removeEventListener("mouseleave",i),document.removeEventListener("mouseexit",i),Tt=void 0}const Bt={401:"Access unauthorized",403:"Access forbidden",404:"Not found"};class Ft{constructor(e,t){this.alertProps=Object.assign({shouldFocus:!0,preventScroll:!1},t),this.container=pt({class:"igv-ui-alert-dialog-container"}),e.appendChild(this.container),this.container.setAttribute("tabIndex","-1");const i=pt();this.container.appendChild(i),this.errorHeadline=pt(),i.appendChild(this.errorHeadline),this.errorHeadline.textContent="";let n=pt({class:"igv-ui-alert-dialog-body"});this.container.appendChild(n),this.body=pt({class:"igv-ui-alert-dialog-body-copy"}),n.appendChild(this.body);let r=pt();this.container.appendChild(r),this.ok=pt(),r.appendChild(this.ok),this.ok.textContent="OK";const s=()=>{"function"==typeof this.callback&&(this.callback("OK"),this.callback=void 0),this.body.innerHTML="",mt(this.container)};this.ok.addEventListener("click",(e=>{e.stopPropagation(),s()})),this.container.addEventListener("keypress",(e=>{e.stopPropagation(),"Enter"===e.key&&s()})),Rt(this.container,i),mt(this.container)}present(e,t){this.errorHeadline.textContent=e.message?"ERROR":"";let i=e.message||e;Bt.hasOwnProperty(i)&&(i=Bt[i]),this.body.innerHTML=i,this.callback=t,bt(this.container),this.alertProps.shouldFocus&&this.container.focus({preventScroll:this.alertProps.preventScroll})}}class Nt{constructor(e){this.parent=e,this.container=pt({class:"igv-ui-generic-dialog-container"}),e.appendChild(this.container);const t=pt({class:"igv-ui-generic-dialog-header"});this.container.appendChild(t),this.label=pt({class:"igv-ui-generic-dialog-one-liner"}),this.container.appendChild(this.label),this.label.text="Unlabeled",this.input_container=pt({class:"igv-ui-generic-dialog-input"}),this.container.appendChild(this.input_container),this.input=document.createElement("input"),this.input_container.appendChild(this.input);const i=pt({class:"igv-ui-generic-dialog-ok-cancel"});this.container.appendChild(i),this.ok=pt(),i.appendChild(this.ok),this.ok.textContent="OK",this.cancel=pt(),i.appendChild(this.cancel),this.cancel.textContent="Cancel",mt(this.container),this.input.addEventListener("keyup",(e=>{13===e.keyCode&&("function"==typeof this.callback&&(this.callback(this.input.value),this.callback=void 0),this.input.value=void 0,mt(this.container))})),this.ok.addEventListener("click",(()=>{"function"==typeof this.callback&&(this.callback(this.input.value),this.callback=void 0),this.input.value=void 0,mt(this.container)}));const n=()=>{this.input.value="",mt(this.container)};this.cancel.addEventListener("click",n),Et(t,n),Rt(this.container,t)}present(e,t){this.label.textContent=e.label,this.input.value=e.value,this.callback=e.callback||e.click,bt(this.container),this.clampLocation(t.clientX,t.clientY)}clampLocation(e,t){const{width:i,height:n}=this.container.getBoundingClientRect(),r=window.innerHeight,s=window.innerWidth,o=Math.min(r-n,t),a=Math.min(s-i,e);this.container.style.left=`${a}px`,this.container.style.top=`${o}px`}}const Ot={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"};class Pt{constructor({parent:e,top:t,left:i,width:n,height:r,border:s,closeHandler:o}){let a=pt({class:"igv-ui-generic-container"});e.appendChild(a),mt(a),this.container=a,void 0!==t&&(this.container.style.top=`${t}px`),void 0!==i&&(this.container.style.left=`${i}px`),void 0!==n&&(this.container.style.width=`${n}px`),void 0!==r&&(this.container.style.height=`${r}px`),s&&(this.container.style.border=s);const l=pt();this.container.appendChild(l),Et(l,(e=>{mt(this.container),"function"==typeof o&&o(e)})),Rt(this.container,l)}show(){bt(this.container)}hide(){mt(this.container)}dispose(){this.container.parent&&this.container.parent.removeChild(this.container)}}class Dt extends Pt{constructor({parent:e,top:t,left:i,width:n,height:r,defaultColors:s,colorHandler:o}){super({parent:e,top:t,left:i,width:n,height:r,border:"1px solid gray"}),zt(this.container,o,s)}}const zt=(e,t,i)=>{const n=Object.values(Ot);for(let i of n){const n=pt({class:"igv-ui-color-swatch"});e.appendChild(n),Ht(n,i,t)}if(i)for(let n of i){const i=pt({class:"igv-ui-color-swatch"});e.appendChild(i),Ht(i,n,t)}},Ht=(e,t,i)=>{e.style.backgroundColor=t,e.addEventListener("mouseenter",(i=>e.style.borderColor=t)),e.addEventListener("mouseleave",(t=>e.style.borderColor="white")),e.addEventListener("click",(e=>{e.stopPropagation(),i(t)})),e.addEventListener("touchend",(e=>{e.stopPropagation(),i(t)}))};class Vt{constructor(e,t){this.parent=e,this.popover=pt({class:"igv-ui-popover"}),e.appendChild(this.popover);const i=pt();this.popover.appendChild(i);const n=pt();i.appendChild(n),t&&(n.textContent=t),Et(i,(()=>this.hide())),Rt(this.popover,i),this.popoverContent=pt(),this.popover.appendChild(this.popoverContent),this.popover.style.display="none"}presentContentWithEvent(e,t){this.popover.style.display="block",this.popoverContent.innerHTML=t,Ut(e,this.popover,this.popoverContent)}presentMenu(e,t){if(0===t.length)return;this.popover.style.display="block";const i=function(e,t){const i=e.map((function(e,i){let n;if("string"==typeof e)n=pt(),n.innerHTML=e;else if("Node"==typeof e)n=e;else{if("function"==typeof e.init&&e.init(),"checkbox"===e.type)n=xt("Show all bases",e.value);else if("color"===e.type){const r=new Dt({parent:t.parentElement,width:364,colorHandler:t=>e.click(t)});n=pt(),"string"==typeof e.label&&(n.innerHTML=e.label);const s=e=>{r.show(),mt(t),e.preventDefault(),e.stopPropagation()};n.addEventListener("click",s),n.addEventListener("touchend",s),n.addEventListener("mouseup",(function(e){e.preventDefault(),e.stopPropagation()}))}else n=pt(),"string"==typeof e.label&&(n.innerHTML=e.label);if(e.click&&"color"!==e.type){function o(i){e.click(),mt(t),i.preventDefault(),i.stopPropagation()}n.addEventListener("click",o),n.addEventListener("touchend",o),n.addEventListener("mouseup",(function(e){e.preventDefault(),e.stopPropagation()}))}}return{object:n,init:e.init}}));return i}(t,this.popover);for(let e of i)this.popoverContent.appendChild(e.object);Ut(e,this.popover,this.popoverContent)}hide(){this.popover.style.display="none",this.dispose()}dispose(){this.popover&&this.popover.parentNode.removeChild(this.popover);const e=Object.keys(this);for(let t of e)this[t]=void 0}}function Ut(e,t,i){const{x:n,y:r,width:s}=yt(e,t.parentNode);t.style.top=`${r}px`;const{width:o}=t.getBoundingClientRect(),a=n+o,l=a-s;t.style.left=`${a>s?n-l:n}px`,i.style.maxWidth=`${Math.min(o,s)}px`}class qt extends Pt{constructor({parent:e,width:t}){super({parent:e,width:t,border:"1px solid gray"})}configure(e,t){this.colorHandlers=t,this.setActiveColorHandler("color"),this.createSwatches(e)}setActiveColorHandler(e){this.activeColorHandler=this.colorHandlers[e]}createSwatches(e){this.container.querySelectorAll(".igv-ui-color-swatch").forEach((e=>e.remove()));const t=Object.values(Ot);for(let e of t){const t=pt({class:"igv-ui-color-swatch"});this.container.appendChild(t),this.decorateSwatch(t,e)}if(e)for(let t of e){const e=pt({class:"igv-ui-color-swatch"});this.container.appendChild(e),this.decorateSwatch(e,t)}}decorateSwatch(e,t){e.style.backgroundColor=t,e.addEventListener("mouseenter",(()=>e.style.borderColor=t)),e.addEventListener("mouseleave",(()=>e.style.borderColor="white")),e.addEventListener("click",(e=>{e.stopPropagation(),this.activeColorHandler(t)})),e.addEventListener("touchend",(e=>{e.stopPropagation(),this.activeColorHandler(t)}))}}if("undefined"!=typeof document){function Ub(e){for(let t of document.styleSheets)if(t=t.href?t.href.replace(/^.*[\\\/]/,""):"",t===e)return!0;return!1}Ub("igv-ui.css")||function(){const e=document.createElement("style");e.setAttribute("type","text/css"),e.setAttribute("title","igv-ui.css"),e.innerHTML='.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}\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}\n.igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px;\n}\n.igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\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}\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}\n.igv-ui-popover > div:last-child > div > span {\n font-weight: bolder;\n}\n.igv-ui-popover > div:last-child hr {\n width: 100%;\n}\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}\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}\n.igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px;\n}\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}\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}\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}\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}\n.igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f;\n}\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\n.igv-ui-color-swatch:hover {\n border-color: dimgray;\n}\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}\n.igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f;\n}\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\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}\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}\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}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\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}\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}\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}\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}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\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}\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}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px;\n}\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}\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}\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}\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}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\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}\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}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\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}\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}\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\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}\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}\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}\n.igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\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}\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}\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}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\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}\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}\n.igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-ui-panel, .igv-ui-panel-row, .igv-ui-panel-column {\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\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column;\n}\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row;\n}\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\n.igv-ui-table {\n background-color: white;\n}\n\n.igv-ui-table thead {\n position: sticky;\n top: 0;\n}\n\n.igv-ui-table th {\n text-align: left;\n}\n\n.igv-ui-table td {\n padding-right: 20px;\n}\n\n.igv-ui-table tr:hover {\n background-color: lightblue;\n}\n\n/*# sourceMappingURL=igv-ui.css.map */\n',document.head.append(e)}()}function jt(e,t){const i=_t.div({class:"igv-menu-popup-check-container"}),n=_t.div();i.appendChild(n);const r=At.createIcon("check",!0===t?"#444":"transparent");n.appendChild(r);const s=_t.div();return s.innerText=e,i.appendChild(s),i}const $t={trackMenuItemList:function(e){const t=new Set(["alignment","annotation","variant","eqtl","snp"]),i=e.track.config&&void 0!==e.track.config.visibilityWindow;let n=[];return"sequence"!==e.track.config.type&&(n.push(function(e){const t=t=>{const i=function(){let t=e.browser.inputDialog.input.value;t=""===t||void 0===t?"untitled":t.trim(),e.track.name=t},n={label:"Track Name",value:Gt(e.track)||"unnamed",callback:i};e.browser.inputDialog.present(n,t)},i=ft("
");return i.text("Set track name"),{object:i,click:t}}(e)),n.push(function(e){const t=t=>{const i=()=>{const t=Number(e.browser.inputDialog.input.value,10);void 0!==t&&(void 0!==e.track.minHeight&&e.track.minHeight>t&&(e.track.minHeight=t),void 0!==e.track.maxHeight&&e.track.maxHeight");return i.text("Set track height"),{object:i,click:t}}(e))),this.showColorPicker(e.track)&&(n.push("
"),n.push(Wt({trackView:e,label:"Set track color",option:"color"})),n.push(function({trackView:e,label:t}){const i=ft("
");return i.text(t),{object:i,click:()=>{e.track.color=void 0,e.repaintViews()}}}({trackView:e,label:"Unset track color"})),"wig"!==e.track.config.type&&"annotation"!==e.track.config.type||(n.push(Wt({trackView:e,label:"Set alt color",option:"altColor"})),n.push(function({trackView:e,label:t}){const i=ft("
");return i.text(t),{object:i,click:()=>{e.track.altColor=void 0,e.repaintViews()}}}({trackView:e,label:"Unset alt color"})))),e.track.menuItemList&&(n=n.concat(e.track.menuItemList())),(i||t.has(e.track.type))&&(n.push("
"),n.push(function(e){const t=t=>{const i=()=>{let t=e.browser.inputDialog.input.value;t=""===t||void 0===t?-1:t.trim(),e.track.visibilityWindow=Number.parseInt(t),e.track.config.visibilityWindow=Number.parseInt(t),e.updateViews()},n={label:"Visibility Window",value:e.track.visibilityWindow,callback:i};e.browser.inputDialog.present(n,t)},i=ft("
");return i.text("Set visibility window"),{object:i,click:t}}(e))),!1!==e.track.removable&&(n.push("
"),n.push(function(e){const t=ft("
");return t.text("Remove track"),{object:t,click:()=>e.browser.removeTrack(e.track)}}(e))),n},numericDataMenuItems:function(e){const t=[];t.push("
");const i=ft("
");i.text("Set data range");return t.push({object:i,click:()=>{e.browser.dataRangeDialog.configure(e),e.browser.dataRangeDialog.present(ft(e.browser.columnContainer))}}),void 0!==e.track.logScale&&t.push({object:ft(jt("Log scale",e.track.logScale)),click:()=>{e.track.logScale=!e.track.logScale,e.repaintViews()}}),t.push({object:ft(jt("Autoscale",e.track.autoscale)),click:()=>{e.track.autoscale=!e.track.autoscale,e.updateViews()}}),t},trackMenuItemListHelper:function(e,t){var i=[];return e.length>0&&(i=e.map((function(e,i){var n;if(e.name?(n=ft("
")).text(e.name):e.object?n=e.object:"string"==typeof e.label?(n=ft("
")).html(e.label):"string"==typeof e&&(n=e.startsWith("<")?ft(e):ft("
"+e+"
")),0===i&&n.addClass("igv-track-menu-border-top"),e.click){function r(i){e.click(i),t.hide(),i.preventDefault(),i.stopPropagation()}n.on("click",r),n.on("touchend",(function(e){r(e)})),n.on("mouseup",(function(e){e.preventDefault(),e.stopPropagation()}))}return{object:n,init:e.init||void 0}}))),i},showColorPicker:e=>void 0===e.type||"bedtype"===e.type||"alignment"===e.type||"annotation"===e.type||"variant"===e.type||"wig"===e.type||"interact"===e.type,createMenuItem(e,t){const i=ft("
");return i.text(e),{object:i,click:t}}};function Wt({trackView:e,label:t,option:i}){const n=ft("
");return n.text(t),{object:n,click:()=>e.presentColorPicker(i)}}function Gt(e){return e.trackView.viewports[0].$trackLabel.text()}class Zt{constructor(e,t,i){this.browser=e,this.$container=ft("
",{class:"igv-generic-dialog-container"}),t.append(this.$container),this.$container.offset({left:0,top:0});const n=ft("
",{class:"igv-generic-dialog-header"});this.$container.append(n),Mt.attachDialogCloseHandlerWithParent(n[0],(()=>{this.$minimum_input.val(void 0),this.$maximum_input.val(void 0),this.$container.offset({left:0,top:0}),this.$container.hide()})),this.$minimum=ft("
",{class:"igv-generic-dialog-label-input"}),this.$container.append(this.$minimum);const r=ft("
");r.text("Minimum"),this.$minimum.append(r),this.$minimum_input=ft(""),this.$minimum.append(this.$minimum_input),this.$maximum=ft("
",{class:"igv-generic-dialog-label-input"}),this.$container.append(this.$maximum);const s=ft("
");s.text("Maximum"),this.$maximum.append(s),this.$maximum_input=ft(""),this.$maximum.append(this.$maximum_input);const o=ft("
",{class:"igv-generic-dialog-ok-cancel"});this.$container.append(o),this.$ok=ft("
"),o.append(this.$ok),this.$ok.text("OK"),this.$cancel=ft("
"),o.append(this.$cancel),this.$cancel.text("Cancel"),this.$cancel.on("click",(()=>{this.$minimum_input.val(void 0),this.$maximum_input.val(void 0),this.$container.offset({left:0,top:0}),this.$container.hide()})),Rt(this.$container.get(0),n.get(0)),this.$container.hide()}configure(e){const t=e.dataRange();let i,n;t?(i=t.min,n=t.max):(i=0,n=100),this.$minimum_input.val(i),this.$maximum_input.val(n),this.$minimum_input.unbind(),this.$minimum_input.on("keyup",(t=>{13===t.keyCode&&this.processResults(e)})),this.$maximum_input.unbind(),this.$maximum_input.on("keyup",(t=>{13===t.keyCode&&this.processResults(e)})),this.$ok.unbind(),this.$ok.on("click",(t=>{this.processResults(e)}))}processResults(e){const t=Number(this.$minimum_input.val()),i=Number(this.$maximum_input.val());isNaN(t)||isNaN(i)?this.browser.alert.present(new Error("Must input numeric values"),void 0):e.setDataRange(t,i),this.$minimum_input.val(void 0),this.$maximum_input.val(void 0),this.$container.offset({left:0,top:0}),this.$container.hide()}present(e){const t=e.offset().top,i=ft("body").scrollTop();this.$container.offset({left:e.width()-this.$container.width(),top:t+i}),this.$container.show()}}function Qt(e){return"string"==typeof e||e instanceof String}function Xt(e){var t=String(e).split(/[.,]/);return t[0].split("").reverse().reduce((function(e,t,i){return i%3==0?e+","+t:e+t})).split("").reverse().join("")+(t[1]?"."+t[1]:"")}const Yt=function(e){return e.split(/\n|\r\n|\r/g)};function Kt(e,t){var i,n,r=[],s=e.length,o=0,a=!1;if(s>0)for(r[o]=e.charAt(0),i=1;i0?e.charAt(0).toUpperCase()+e.slice(1):e}function ei(e){if(void 0!==e.name)return e.name;if(Qt(e)){let t=e.lastIndexOf("/"),i=t<0?e:e.substr(t+1);return t=i.indexOf("?"),t>0&&(i=i.substr(0,t)),i}throw Error("Expected File or string, got "+typeof e)}function ti(e){return!!e&&("function"!=typeof e&&(e instanceof File||e.hasOwnProperty("name")&&"function"==typeof e.slice&&"function"==typeof e.arrayBuffer))}function ii(e,t){const i=document.createElement("a");i.setAttribute("href",t),i.setAttribute("download",e),i.style.display="none",document.body.appendChild(i),i.click(),document.body.removeChild(i)}function ni(e){for(var t=ri,i=t.parser.loose.exec(e),n={},r=14;r--;)n[t.key[r]]=i[r]||"";return n[t.q.name]={},n[t.key[12]].replace(t.q.parser,(function(e,i,r){i&&(n[t.q.name][i]=r)})),n}"object"==typeof process&&"undefined"==typeof window&&(global.atob=function(e){return Buffer.from(e,"base64").toString("binary")});const ri={strictMode:!1,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*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};function si(e){let t=e.length;for(;--t>=0;)e[t]=0}const oi=256,ai=286,li=30,hi=15,ci=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]),di=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]),ui=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]),fi=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),pi=new Array(576);si(pi);const gi=new Array(60);si(gi);const mi=new Array(512);si(mi);const bi=new Array(256);si(bi);const wi=new Array(29);si(wi);const vi=new Array(li);function yi(e,t,i,n,r){this.static_tree=e,this.extra_bits=t,this.extra_base=i,this.elems=n,this.max_length=r,this.has_stree=e&&e.length}let _i,xi,ki;function Ci(e,t){this.dyn_tree=e,this.max_code=0,this.stat_desc=t}si(vi);const Si=e=>e<256?mi[e]:mi[256+(e>>>7)],Ai=(e,t)=>{e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255},Ei=(e,t,i)=>{e.bi_valid>16-i?(e.bi_buf|=t<>16-e.bi_valid,e.bi_valid+=i-16):(e.bi_buf|=t<{Ei(e,i[2*t],i[2*t+1])},Ti=(e,t)=>{let i=0;do{i|=1&e,e>>>=1,i<<=1}while(--t>0);return i>>>1},Ri=(e,t,i)=>{const n=new Array(16);let r,s,o=0;for(r=1;r<=hi;r++)o=o+i[r-1]<<1,n[r]=o;for(s=0;s<=t;s++){let t=e[2*s+1];0!==t&&(e[2*s]=Ti(n[t]++,t))}},Li=e=>{let t;for(t=0;t{e.bi_valid>8?Ai(e,e.bi_buf):e.bi_valid>0&&(e.pending_buf[e.pending++]=e.bi_buf),e.bi_buf=0,e.bi_valid=0},Bi=(e,t,i,n)=>{const r=2*t,s=2*i;return e[r]{const n=e.heap[i];let r=i<<1;for(;r<=e.heap_len&&(r{let n,r,s,o,a=0;if(0!==e.sym_next)do{n=255&e.pending_buf[e.sym_buf+a++],n+=(255&e.pending_buf[e.sym_buf+a++])<<8,r=e.pending_buf[e.sym_buf+a++],0===n?Mi(e,r,t):(s=bi[r],Mi(e,s+oi+1,t),o=ci[s],0!==o&&(r-=wi[s],Ei(e,r,o)),n--,s=Si(n),Mi(e,s,i),o=di[s],0!==o&&(n-=vi[s],Ei(e,n,o)))}while(a{const i=t.dyn_tree,n=t.stat_desc.static_tree,r=t.stat_desc.has_stree,s=t.stat_desc.elems;let o,a,l,h=-1;for(e.heap_len=0,e.heap_max=573,o=0;o>1;o>=1;o--)Fi(e,i,o);l=s;do{o=e.heap[1],e.heap[1]=e.heap[e.heap_len--],Fi(e,i,1),a=e.heap[1],e.heap[--e.heap_max]=o,e.heap[--e.heap_max]=a,i[2*l]=i[2*o]+i[2*a],e.depth[l]=(e.depth[o]>=e.depth[a]?e.depth[o]:e.depth[a])+1,i[2*o+1]=i[2*a+1]=l,e.heap[1]=l++,Fi(e,i,1)}while(e.heap_len>=2);e.heap[--e.heap_max]=e.heap[1],((e,t)=>{const i=t.dyn_tree,n=t.max_code,r=t.stat_desc.static_tree,s=t.stat_desc.has_stree,o=t.stat_desc.extra_bits,a=t.stat_desc.extra_base,l=t.stat_desc.max_length;let h,c,d,u,f,p,g=0;for(u=0;u<=hi;u++)e.bl_count[u]=0;for(i[2*e.heap[e.heap_max]+1]=0,h=e.heap_max+1;h<573;h++)c=e.heap[h],u=i[2*i[2*c+1]+1]+1,u>l&&(u=l,g++),i[2*c+1]=u,c>n||(e.bl_count[u]++,f=0,c>=a&&(f=o[c-a]),p=i[2*c],e.opt_len+=p*(u+f),s&&(e.static_len+=p*(r[2*c+1]+f)));if(0!==g){do{for(u=l-1;0===e.bl_count[u];)u--;e.bl_count[u]--,e.bl_count[u+1]+=2,e.bl_count[l]--,g-=2}while(g>0);for(u=l;0!==u;u--)for(c=e.bl_count[u];0!==c;)d=e.heap[--h],d>n||(i[2*d+1]!==u&&(e.opt_len+=(u-i[2*d+1])*i[2*d],i[2*d+1]=u),c--)}})(e,t),Ri(i,h,e.bl_count)},Pi=(e,t,i)=>{let n,r,s=-1,o=t[1],a=0,l=7,h=4;for(0===o&&(l=138,h=3),t[2*(i+1)+1]=65535,n=0;n<=i;n++)r=o,o=t[2*(n+1)+1],++a{let n,r,s=-1,o=t[1],a=0,l=7,h=4;for(0===o&&(l=138,h=3),n=0;n<=i;n++)if(r=o,o=t[2*(n+1)+1],!(++a{Ei(e,0+(n?1:0),3),Ii(e),Ai(e,i),Ai(e,~i),i&&e.pending_buf.set(e.window.subarray(t,t+i),e.pending),e.pending+=i};var Vi=(e,t,i,n)=>{let r,s,o=0;e.level>0?(2===e.strm.data_type&&(e.strm.data_type=(e=>{let t,i=4093624447;for(t=0;t<=31;t++,i>>>=1)if(1&i&&0!==e.dyn_ltree[2*t])return 0;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return 1;for(t=32;t{let t;for(Pi(e,e.dyn_ltree,e.l_desc.max_code),Pi(e,e.dyn_dtree,e.d_desc.max_code),Oi(e,e.bl_desc),t=18;t>=3&&0===e.bl_tree[2*fi[t]+1];t--);return e.opt_len+=3*(t+1)+5+5+4,t})(e),r=e.opt_len+3+7>>>3,s=e.static_len+3+7>>>3,s<=r&&(r=s)):r=s=i+5,i+4<=r&&-1!==t?Hi(e,t,i,n):4===e.strategy||s===r?(Ei(e,2+(n?1:0),3),Ni(e,pi,gi)):(Ei(e,4+(n?1:0),3),((e,t,i,n)=>{let r;for(Ei(e,t-257,5),Ei(e,i-1,5),Ei(e,n-4,4),r=0;r{zi||((()=>{let e,t,i,n,r;const s=new Array(16);for(i=0,n=0;n<28;n++)for(wi[n]=i,e=0;e<1<>=7;n(e.pending_buf[e.sym_buf+e.sym_next++]=t,e.pending_buf[e.sym_buf+e.sym_next++]=t>>8,e.pending_buf[e.sym_buf+e.sym_next++]=i,0===t?e.dyn_ltree[2*i]++:(e.matches++,t--,e.dyn_ltree[2*(bi[i]+oi+1)]++,e.dyn_dtree[2*Si(t)]++),e.sym_next===e.sym_end),_tr_align:e=>{Ei(e,2,3),Mi(e,256,pi),(e=>{16===e.bi_valid?(Ai(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):e.bi_valid>=8&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)})(e)}};var qi=(e,t,i,n)=>{let r=65535&e|0,s=e>>>16&65535|0,o=0;for(;0!==i;){o=i>2e3?2e3:i,i-=o;do{r=r+t[n++]|0,s=s+r|0}while(--o);r%=65521,s%=65521}return r|s<<16|0};const ji=new Uint32Array((()=>{let e,t=[];for(var i=0;i<256;i++){e=i;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[i]=e}return t})());var $i=(e,t,i,n)=>{const r=ji,s=n+i;e^=-1;for(let i=n;i>>8^r[255&(e^t[i])];return-1^e},Wi={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"},Gi={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_MEM_ERROR:-4,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};const{_tr_init:Zi,_tr_stored_block:Qi,_tr_flush_block:Xi,_tr_tally:Yi,_tr_align:Ki}=Ui,{Z_NO_FLUSH:Ji,Z_PARTIAL_FLUSH:en,Z_FULL_FLUSH:tn,Z_FINISH:nn,Z_BLOCK:rn,Z_OK:sn,Z_STREAM_END:on,Z_STREAM_ERROR:an,Z_DATA_ERROR:ln,Z_BUF_ERROR:hn,Z_DEFAULT_COMPRESSION:cn,Z_FILTERED:dn,Z_HUFFMAN_ONLY:un,Z_RLE:fn,Z_FIXED:pn,Z_DEFAULT_STRATEGY:gn,Z_UNKNOWN:mn,Z_DEFLATED:bn}=Gi,wn=258,vn=262,yn=42,_n=113,xn=666,kn=(e,t)=>(e.msg=Wi[t],t),Cn=e=>2*e-(e>4?9:0),Sn=e=>{let t=e.length;for(;--t>=0;)e[t]=0},An=e=>{let t,i,n,r=e.w_size;t=e.hash_size,n=t;do{i=e.head[--n],e.head[n]=i>=r?i-r:0}while(--t);t=r,n=t;do{i=e.prev[--n],e.prev[n]=i>=r?i-r:0}while(--t)};let En=(e,t,i)=>(t<{const t=e.state;let i=t.pending;i>e.avail_out&&(i=e.avail_out),0!==i&&(e.output.set(t.pending_buf.subarray(t.pending_out,t.pending_out+i),e.next_out),e.next_out+=i,t.pending_out+=i,e.total_out+=i,e.avail_out-=i,t.pending-=i,0===t.pending&&(t.pending_out=0))},Tn=(e,t)=>{Xi(e,e.block_start>=0?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,Mn(e.strm)},Rn=(e,t)=>{e.pending_buf[e.pending++]=t},Ln=(e,t)=>{e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t},In=(e,t,i,n)=>{let r=e.avail_in;return r>n&&(r=n),0===r?0:(e.avail_in-=r,t.set(e.input.subarray(e.next_in,e.next_in+r),i),1===e.state.wrap?e.adler=qi(e.adler,t,r,i):2===e.state.wrap&&(e.adler=$i(e.adler,t,r,i)),e.next_in+=r,e.total_in+=r,r)},Bn=(e,t)=>{let i,n,r=e.max_chain_length,s=e.strstart,o=e.prev_length,a=e.nice_match;const l=e.strstart>e.w_size-vn?e.strstart-(e.w_size-vn):0,h=e.window,c=e.w_mask,d=e.prev,u=e.strstart+wn;let f=h[s+o-1],p=h[s+o];e.prev_length>=e.good_match&&(r>>=2),a>e.lookahead&&(a=e.lookahead);do{if(i=t,h[i+o]===p&&h[i+o-1]===f&&h[i]===h[s]&&h[++i]===h[s+1]){s+=2,i++;do{}while(h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&so){if(e.match_start=t,o=n,n>=a)break;f=h[s+o-1],p=h[s+o]}}}while((t=d[t&c])>l&&0!=--r);return o<=e.lookahead?o:e.lookahead},Fn=e=>{const t=e.w_size;let i,n,r;do{if(n=e.window_size-e.lookahead-e.strstart,e.strstart>=t+(t-vn)&&(e.window.set(e.window.subarray(t,t+t-n),0),e.match_start-=t,e.strstart-=t,e.block_start-=t,e.insert>e.strstart&&(e.insert=e.strstart),An(e),n+=t),0===e.strm.avail_in)break;if(i=In(e.strm,e.window,e.strstart+e.lookahead,n),e.lookahead+=i,e.lookahead+e.insert>=3)for(r=e.strstart-e.insert,e.ins_h=e.window[r],e.ins_h=En(e,e.ins_h,e.window[r+1]);e.insert&&(e.ins_h=En(e,e.ins_h,e.window[r+3-1]),e.prev[r&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=r,r++,e.insert--,!(e.lookahead+e.insert<3)););}while(e.lookahead{let i,n,r,s=e.pending_buf_size-5>e.w_size?e.w_size:e.pending_buf_size-5,o=0,a=e.strm.avail_in;do{if(i=65535,r=e.bi_valid+42>>3,e.strm.avail_outn+e.strm.avail_in&&(i=n+e.strm.avail_in),i>r&&(i=r),i>8,e.pending_buf[e.pending-2]=~i,e.pending_buf[e.pending-1]=~i>>8,Mn(e.strm),n&&(n>i&&(n=i),e.strm.output.set(e.window.subarray(e.block_start,e.block_start+n),e.strm.next_out),e.strm.next_out+=n,e.strm.avail_out-=n,e.strm.total_out+=n,e.block_start+=n,i-=n),i&&(In(e.strm,e.strm.output,e.strm.next_out,i),e.strm.next_out+=i,e.strm.avail_out-=i,e.strm.total_out+=i)}while(0===o);return a-=e.strm.avail_in,a&&(a>=e.w_size?(e.matches=2,e.window.set(e.strm.input.subarray(e.strm.next_in-e.w_size,e.strm.next_in),0),e.strstart=e.w_size,e.insert=e.strstart):(e.window_size-e.strstart<=a&&(e.strstart-=e.w_size,e.window.set(e.window.subarray(e.w_size,e.w_size+e.strstart),0),e.matches<2&&e.matches++,e.insert>e.strstart&&(e.insert=e.strstart)),e.window.set(e.strm.input.subarray(e.strm.next_in-a,e.strm.next_in),e.strstart),e.strstart+=a,e.insert+=a>e.w_size-e.insert?e.w_size-e.insert:a),e.block_start=e.strstart),e.high_waterr&&e.block_start>=e.w_size&&(e.block_start-=e.w_size,e.strstart-=e.w_size,e.window.set(e.window.subarray(e.w_size,e.w_size+e.strstart),0),e.matches<2&&e.matches++,r+=e.w_size,e.insert>e.strstart&&(e.insert=e.strstart)),r>e.strm.avail_in&&(r=e.strm.avail_in),r&&(In(e.strm,e.window,e.strstart,r),e.strstart+=r,e.insert+=r>e.w_size-e.insert?e.w_size-e.insert:r),e.high_water>3,r=e.pending_buf_size-r>65535?65535:e.pending_buf_size-r,s=r>e.w_size?e.w_size:r,n=e.strstart-e.block_start,(n>=s||(n||t===nn)&&t!==Ji&&0===e.strm.avail_in&&n<=r)&&(i=n>r?r:n,o=t===nn&&0===e.strm.avail_in&&i===n?1:0,Qi(e,e.block_start,i,o),e.block_start+=i,Mn(e.strm)),o?3:1)},On=(e,t)=>{let i,n;for(;;){if(e.lookahead=3&&(e.ins_h=En(e,e.ins_h,e.window[e.strstart+3-1]),i=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart),0!==i&&e.strstart-i<=e.w_size-vn&&(e.match_length=Bn(e,i)),e.match_length>=3)if(n=Yi(e,e.strstart-e.match_start,e.match_length-3),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=3){e.match_length--;do{e.strstart++,e.ins_h=En(e,e.ins_h,e.window[e.strstart+3-1]),i=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart}while(0!=--e.match_length);e.strstart++}else e.strstart+=e.match_length,e.match_length=0,e.ins_h=e.window[e.strstart],e.ins_h=En(e,e.ins_h,e.window[e.strstart+1]);else n=Yi(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++;if(n&&(Tn(e,!1),0===e.strm.avail_out))return 1}return e.insert=e.strstart<2?e.strstart:2,t===nn?(Tn(e,!0),0===e.strm.avail_out?3:4):e.sym_next&&(Tn(e,!1),0===e.strm.avail_out)?1:2},Pn=(e,t)=>{let i,n,r;for(;;){if(e.lookahead=3&&(e.ins_h=En(e,e.ins_h,e.window[e.strstart+3-1]),i=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart),e.prev_length=e.match_length,e.prev_match=e.match_start,e.match_length=2,0!==i&&e.prev_length4096)&&(e.match_length=2)),e.prev_length>=3&&e.match_length<=e.prev_length){r=e.strstart+e.lookahead-3,n=Yi(e,e.strstart-1-e.prev_match,e.prev_length-3),e.lookahead-=e.prev_length-1,e.prev_length-=2;do{++e.strstart<=r&&(e.ins_h=En(e,e.ins_h,e.window[e.strstart+3-1]),i=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart)}while(0!=--e.prev_length);if(e.match_available=0,e.match_length=2,e.strstart++,n&&(Tn(e,!1),0===e.strm.avail_out))return 1}else if(e.match_available){if(n=Yi(e,0,e.window[e.strstart-1]),n&&Tn(e,!1),e.strstart++,e.lookahead--,0===e.strm.avail_out)return 1}else e.match_available=1,e.strstart++,e.lookahead--}return e.match_available&&(n=Yi(e,0,e.window[e.strstart-1]),e.match_available=0),e.insert=e.strstart<2?e.strstart:2,t===nn?(Tn(e,!0),0===e.strm.avail_out?3:4):e.sym_next&&(Tn(e,!1),0===e.strm.avail_out)?1:2};function Dn(e,t,i,n,r){this.good_length=e,this.max_lazy=t,this.nice_length=i,this.max_chain=n,this.func=r}const zn=[new Dn(0,0,0,0,Nn),new Dn(4,4,8,4,On),new Dn(4,5,16,8,On),new Dn(4,6,32,32,On),new Dn(4,4,16,16,Pn),new Dn(8,16,32,32,Pn),new Dn(8,16,128,128,Pn),new Dn(8,32,128,256,Pn),new Dn(32,128,258,1024,Pn),new Dn(32,258,258,4096,Pn)];function Hn(){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=bn,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 Uint16Array(1146),this.dyn_dtree=new Uint16Array(122),this.bl_tree=new Uint16Array(78),Sn(this.dyn_ltree),Sn(this.dyn_dtree),Sn(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new Uint16Array(16),this.heap=new Uint16Array(573),Sn(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new Uint16Array(573),Sn(this.depth),this.sym_buf=0,this.lit_bufsize=0,this.sym_next=0,this.sym_end=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}const Vn=e=>{if(!e)return 1;const t=e.state;return!t||t.strm!==e||t.status!==yn&&57!==t.status&&69!==t.status&&73!==t.status&&91!==t.status&&103!==t.status&&t.status!==_n&&t.status!==xn?1:0},Un=e=>{if(Vn(e))return kn(e,an);e.total_in=e.total_out=0,e.data_type=mn;const t=e.state;return t.pending=0,t.pending_out=0,t.wrap<0&&(t.wrap=-t.wrap),t.status=2===t.wrap?57:t.wrap?yn:_n,e.adler=2===t.wrap?0:1,t.last_flush=-2,Zi(t),sn},qn=e=>{const t=Un(e);var i;return t===sn&&((i=e.state).window_size=2*i.w_size,Sn(i.head),i.max_lazy_match=zn[i.level].max_lazy,i.good_match=zn[i.level].good_length,i.nice_match=zn[i.level].nice_length,i.max_chain_length=zn[i.level].max_chain,i.strstart=0,i.block_start=0,i.lookahead=0,i.insert=0,i.match_length=i.prev_length=2,i.match_available=0,i.ins_h=0),t},jn=(e,t,i,n,r,s)=>{if(!e)return an;let o=1;if(t===cn&&(t=6),n<0?(o=0,n=-n):n>15&&(o=2,n-=16),r<1||r>9||i!==bn||n<8||n>15||t<0||t>9||s<0||s>pn||8===n&&1!==o)return kn(e,an);8===n&&(n=9);const a=new Hn;return e.state=a,a.strm=e,a.status=yn,a.wrap=o,a.gzhead=null,a.w_bits=n,a.w_size=1<{let i=t.length;if(Vn(e))return an;const n=e.state,r=n.wrap;if(2===r||1===r&&n.status!==yn||n.lookahead)return an;if(1===r&&(e.adler=qi(e.adler,t,i,0)),n.wrap=0,i>=n.w_size){0===r&&(Sn(n.head),n.strstart=0,n.block_start=0,n.insert=0);let e=new Uint8Array(n.w_size);e.set(t.subarray(i-n.w_size,i),0),t=e,i=n.w_size}const s=e.avail_in,o=e.next_in,a=e.input;for(e.avail_in=i,e.next_in=0,e.input=t,Fn(n);n.lookahead>=3;){let e=n.strstart,t=n.lookahead-2;do{n.ins_h=En(n,n.ins_h,n.window[e+3-1]),n.prev[e&n.w_mask]=n.head[n.ins_h],n.head[n.ins_h]=e,e++}while(--t);n.strstart=e,n.lookahead=2,Fn(n)}return n.strstart+=n.lookahead,n.block_start=n.strstart,n.insert=n.lookahead,n.lookahead=0,n.match_length=n.prev_length=2,n.match_available=0,e.next_in=o,e.input=a,e.avail_in=s,n.wrap=r,sn},Wn={deflateInit:(e,t)=>jn(e,t,bn,15,8,gn),deflateInit2:jn,deflateReset:qn,deflateResetKeep:Un,deflateSetHeader:(e,t)=>Vn(e)||2!==e.state.wrap?an:(e.state.gzhead=t,sn),deflate:(e,t)=>{if(Vn(e)||t>rn||t<0)return e?kn(e,an):an;const i=e.state;if(!e.output||0!==e.avail_in&&!e.input||i.status===xn&&t!==nn)return kn(e,0===e.avail_out?hn:an);const n=i.last_flush;if(i.last_flush=t,0!==i.pending){if(Mn(e),0===e.avail_out)return i.last_flush=-1,sn}else if(0===e.avail_in&&Cn(t)<=Cn(n)&&t!==nn)return kn(e,hn);if(i.status===xn&&0!==e.avail_in)return kn(e,hn);if(i.status===yn&&0===i.wrap&&(i.status=_n),i.status===yn){let t=bn+(i.w_bits-8<<4)<<8,n=-1;if(n=i.strategy>=un||i.level<2?0:i.level<6?1:6===i.level?2:3,t|=n<<6,0!==i.strstart&&(t|=32),t+=31-t%31,Ln(i,t),0!==i.strstart&&(Ln(i,e.adler>>>16),Ln(i,65535&e.adler)),e.adler=1,i.status=_n,Mn(e),0!==i.pending)return i.last_flush=-1,sn}if(57===i.status)if(e.adler=0,Rn(i,31),Rn(i,139),Rn(i,8),i.gzhead)Rn(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)),Rn(i,255&i.gzhead.time),Rn(i,i.gzhead.time>>8&255),Rn(i,i.gzhead.time>>16&255),Rn(i,i.gzhead.time>>24&255),Rn(i,9===i.level?2:i.strategy>=un||i.level<2?4:0),Rn(i,255&i.gzhead.os),i.gzhead.extra&&i.gzhead.extra.length&&(Rn(i,255&i.gzhead.extra.length),Rn(i,i.gzhead.extra.length>>8&255)),i.gzhead.hcrc&&(e.adler=$i(e.adler,i.pending_buf,i.pending,0)),i.gzindex=0,i.status=69;else if(Rn(i,0),Rn(i,0),Rn(i,0),Rn(i,0),Rn(i,0),Rn(i,9===i.level?2:i.strategy>=un||i.level<2?4:0),Rn(i,3),i.status=_n,Mn(e),0!==i.pending)return i.last_flush=-1,sn;if(69===i.status){if(i.gzhead.extra){let t=i.pending,n=(65535&i.gzhead.extra.length)-i.gzindex;for(;i.pending+n>i.pending_buf_size;){let r=i.pending_buf_size-i.pending;if(i.pending_buf.set(i.gzhead.extra.subarray(i.gzindex,i.gzindex+r),i.pending),i.pending=i.pending_buf_size,i.gzhead.hcrc&&i.pending>t&&(e.adler=$i(e.adler,i.pending_buf,i.pending-t,t)),i.gzindex+=r,Mn(e),0!==i.pending)return i.last_flush=-1,sn;t=0,n-=r}let r=new Uint8Array(i.gzhead.extra);i.pending_buf.set(r.subarray(i.gzindex,i.gzindex+n),i.pending),i.pending+=n,i.gzhead.hcrc&&i.pending>t&&(e.adler=$i(e.adler,i.pending_buf,i.pending-t,t)),i.gzindex=0}i.status=73}if(73===i.status){if(i.gzhead.name){let t,n=i.pending;do{if(i.pending===i.pending_buf_size){if(i.gzhead.hcrc&&i.pending>n&&(e.adler=$i(e.adler,i.pending_buf,i.pending-n,n)),Mn(e),0!==i.pending)return i.last_flush=-1,sn;n=0}t=i.gzindexn&&(e.adler=$i(e.adler,i.pending_buf,i.pending-n,n)),i.gzindex=0}i.status=91}if(91===i.status){if(i.gzhead.comment){let t,n=i.pending;do{if(i.pending===i.pending_buf_size){if(i.gzhead.hcrc&&i.pending>n&&(e.adler=$i(e.adler,i.pending_buf,i.pending-n,n)),Mn(e),0!==i.pending)return i.last_flush=-1,sn;n=0}t=i.gzindexn&&(e.adler=$i(e.adler,i.pending_buf,i.pending-n,n))}i.status=103}if(103===i.status){if(i.gzhead.hcrc){if(i.pending+2>i.pending_buf_size&&(Mn(e),0!==i.pending))return i.last_flush=-1,sn;Rn(i,255&e.adler),Rn(i,e.adler>>8&255),e.adler=0}if(i.status=_n,Mn(e),0!==i.pending)return i.last_flush=-1,sn}if(0!==e.avail_in||0!==i.lookahead||t!==Ji&&i.status!==xn){let n=0===i.level?Nn(i,t):i.strategy===un?((e,t)=>{let i;for(;;){if(0===e.lookahead&&(Fn(e),0===e.lookahead)){if(t===Ji)return 1;break}if(e.match_length=0,i=Yi(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,i&&(Tn(e,!1),0===e.strm.avail_out))return 1}return e.insert=0,t===nn?(Tn(e,!0),0===e.strm.avail_out?3:4):e.sym_next&&(Tn(e,!1),0===e.strm.avail_out)?1:2})(i,t):i.strategy===fn?((e,t)=>{let i,n,r,s;const o=e.window;for(;;){if(e.lookahead<=wn){if(Fn(e),e.lookahead<=wn&&t===Ji)return 1;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=3&&e.strstart>0&&(r=e.strstart-1,n=o[r],n===o[++r]&&n===o[++r]&&n===o[++r])){s=e.strstart+wn;do{}while(n===o[++r]&&n===o[++r]&&n===o[++r]&&n===o[++r]&&n===o[++r]&&n===o[++r]&&n===o[++r]&&n===o[++r]&&re.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=3?(i=Yi(e,1,e.match_length-3),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(i=Yi(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),i&&(Tn(e,!1),0===e.strm.avail_out))return 1}return e.insert=0,t===nn?(Tn(e,!0),0===e.strm.avail_out?3:4):e.sym_next&&(Tn(e,!1),0===e.strm.avail_out)?1:2})(i,t):zn[i.level].func(i,t);if(3!==n&&4!==n||(i.status=xn),1===n||3===n)return 0===e.avail_out&&(i.last_flush=-1),sn;if(2===n&&(t===en?Ki(i):t!==rn&&(Qi(i,0,0,!1),t===tn&&(Sn(i.head),0===i.lookahead&&(i.strstart=0,i.block_start=0,i.insert=0))),Mn(e),0===e.avail_out))return i.last_flush=-1,sn}return t!==nn?sn:i.wrap<=0?on:(2===i.wrap?(Rn(i,255&e.adler),Rn(i,e.adler>>8&255),Rn(i,e.adler>>16&255),Rn(i,e.adler>>24&255),Rn(i,255&e.total_in),Rn(i,e.total_in>>8&255),Rn(i,e.total_in>>16&255),Rn(i,e.total_in>>24&255)):(Ln(i,e.adler>>>16),Ln(i,65535&e.adler)),Mn(e),i.wrap>0&&(i.wrap=-i.wrap),0!==i.pending?sn:on)},deflateEnd:e=>{if(Vn(e))return an;const t=e.state.status;return e.state=null,t===_n?kn(e,ln):sn},deflateSetDictionary:$n,deflateInfo:"pako deflate (from Nodeca project)"};const Gn=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var Zn={assign:function(e){const t=Array.prototype.slice.call(arguments,1);for(;t.length;){const i=t.shift();if(i){if("object"!=typeof i)throw new TypeError(i+"must be non-object");for(const t in i)Gn(i,t)&&(e[t]=i[t])}}return e},flattenChunks:e=>{let t=0;for(let i=0,n=e.length;i=252?6:jb>=248?5:jb>=240?4:jb>=224?3:jb>=192?2:1;Xn[254]=Xn[254]=1;var Yn={string2buf:e=>{if("function"==typeof TextEncoder&&TextEncoder.prototype.encode)return(new TextEncoder).encode(e);let t,i,n,r,s,o=e.length,a=0;for(r=0;r>>6,t[s++]=128|63&i):i<65536?(t[s++]=224|i>>>12,t[s++]=128|i>>>6&63,t[s++]=128|63&i):(t[s++]=240|i>>>18,t[s++]=128|i>>>12&63,t[s++]=128|i>>>6&63,t[s++]=128|63&i);return t},buf2string:(e,t)=>{const i=t||e.length;if("function"==typeof TextDecoder&&TextDecoder.prototype.decode)return(new TextDecoder).decode(e.subarray(0,t));let n,r;const s=new Array(2*i);for(r=0,n=0;n4)s[r++]=65533,n+=o-1;else{for(t&=2===o?31:3===o?15:7;o>1&&n1?s[r++]=65533:t<65536?s[r++]=t:(t-=65536,s[r++]=55296|t>>10&1023,s[r++]=56320|1023&t)}}return((e,t)=>{if(t<65534&&e.subarray&&Qn)return String.fromCharCode.apply(null,e.length===t?e:e.subarray(0,t));let i="";for(let n=0;n{(t=t||e.length)>e.length&&(t=e.length);let i=t-1;for(;i>=0&&128==(192&e[i]);)i--;return i<0||0===i?t:i+Xn[e[i]]>t?i:t}};var Kn=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};const Jn=Object.prototype.toString,{Z_NO_FLUSH:er,Z_SYNC_FLUSH:tr,Z_FULL_FLUSH:ir,Z_FINISH:nr,Z_OK:rr,Z_STREAM_END:sr,Z_DEFAULT_COMPRESSION:or,Z_DEFAULT_STRATEGY:ar,Z_DEFLATED:lr}=Gi;function hr(e){this.options=Zn.assign({level:or,method:lr,chunkSize:16384,windowBits:15,memLevel:8,strategy:ar},e||{});let t=this.options;t.raw&&t.windowBits>0?t.windowBits=-t.windowBits:t.gzip&&t.windowBits>0&&t.windowBits<16&&(t.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new Kn,this.strm.avail_out=0;let i=Wn.deflateInit2(this.strm,t.level,t.method,t.windowBits,t.memLevel,t.strategy);if(i!==rr)throw new Error(Wi[i]);if(t.header&&Wn.deflateSetHeader(this.strm,t.header),t.dictionary){let e;if(e="string"==typeof t.dictionary?Yn.string2buf(t.dictionary):"[object ArrayBuffer]"===Jn.call(t.dictionary)?new Uint8Array(t.dictionary):t.dictionary,i=Wn.deflateSetDictionary(this.strm,e),i!==rr)throw new Error(Wi[i]);this._dict_set=!0}}function cr(e,t){const i=new hr(t);if(i.push(e,!0),i.err)throw i.msg||Wi[i.err];return i.result}hr.prototype.push=function(e,t){const i=this.strm,n=this.options.chunkSize;let r,s;if(this.ended)return!1;for(s=t===~~t?t:!0===t?nr:er,"string"==typeof e?i.input=Yn.string2buf(e):"[object ArrayBuffer]"===Jn.call(e)?i.input=new Uint8Array(e):i.input=e,i.next_in=0,i.avail_in=i.input.length;;)if(0===i.avail_out&&(i.output=new Uint8Array(n),i.next_out=0,i.avail_out=n),(s===tr||s===ir)&&i.avail_out<=6)this.onData(i.output.subarray(0,i.next_out)),i.avail_out=0;else{if(r=Wn.deflate(i,s),r===sr)return i.next_out>0&&this.onData(i.output.subarray(0,i.next_out)),r=Wn.deflateEnd(this.strm),this.onEnd(r),this.ended=!0,r===rr;if(0!==i.avail_out){if(s>0&&i.next_out>0)this.onData(i.output.subarray(0,i.next_out)),i.avail_out=0;else if(0===i.avail_in)break}else this.onData(i.output)}return!0},hr.prototype.onData=function(e){this.chunks.push(e)},hr.prototype.onEnd=function(e){e===rr&&(this.result=Zn.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg};var dr=function(e,t){return(t=t||{}).raw=!0,cr(e,t)},ur=function(e,t){return(t=t||{}).gzip=!0,cr(e,t)},fr={Deflate:hr,deflate:cr,deflateRaw:dr,gzip:ur,constants:Gi};const pr=16209;var gr=function(e,t){let i,n,r,s,o,a,l,h,c,d,u,f,p,g,m,b,w,v,y,_,x,k,C,S;const A=e.state;i=e.next_in,C=e.input,n=i+(e.avail_in-5),r=e.next_out,S=e.output,s=r-(t-e.avail_out),o=r+(e.avail_out-257),a=A.dmax,l=A.wsize,h=A.whave,c=A.wnext,d=A.window,u=A.hold,f=A.bits,p=A.lencode,g=A.distcode,m=(1<>>24,u>>>=v,f-=v,v=w>>>16&255,0===v)S[r++]=65535&w;else{if(!(16&v)){if(0==(64&v)){w=p[(65535&w)+(u&(1<>>=v,f-=v),f<15&&(u+=C[i++]<>>24,u>>>=v,f-=v,v=w>>>16&255,!(16&v)){if(0==(64&v)){w=g[(65535&w)+(u&(1<a){e.msg="invalid distance too far back",A.mode=pr;break e}if(u>>>=v,f-=v,v=r-s,_>v){if(v=_-v,v>h&&A.sane){e.msg="invalid distance too far back",A.mode=pr;break e}if(x=0,k=d,0===c){if(x+=l-v,v2;)S[r++]=k[x++],S[r++]=k[x++],S[r++]=k[x++],y-=3;y&&(S[r++]=k[x++],y>1&&(S[r++]=k[x++]))}else{x=r-_;do{S[r++]=S[x++],S[r++]=S[x++],S[r++]=S[x++],y-=3}while(y>2);y&&(S[r++]=S[x++],y>1&&(S[r++]=S[x++]))}break}}break}}while(i>3,i-=y,f-=y<<3,u&=(1<{const l=a.bits;let h,c,d,u,f,p,g=0,m=0,b=0,w=0,v=0,y=0,_=0,x=0,k=0,C=0,S=null;const A=new Uint16Array(16),E=new Uint16Array(16);let M,T,R,L=null;for(g=0;g<=mr;g++)A[g]=0;for(m=0;m=1&&0===A[w];w--);if(v>w&&(v=w),0===w)return r[s++]=20971520,r[s++]=20971520,a.bits=1,0;for(b=1;b0&&(0===e||1!==w))return-1;for(E[1]=0,g=1;g852||2===e&&k>592)return 1;for(;;){M=g-_,o[m]+1=p?(T=L[o[m]-p],R=S[o[m]-p]):(T=96,R=0),h=1<>_)+c]=M<<24|T<<16|R|0}while(0!==c);for(h=1<>=1;if(0!==h?(C&=h-1,C+=h):C=0,m++,0==--A[g]){if(g===w)break;g=t[i+o[m]]}if(g>v&&(C&u)!==d){for(0===_&&(_=v),f+=b,y=g-_,x=1<852||2===e&&k>592)return 1;d=C&u,r[d]=v<<24|y<<16|f-s|0}}return 0!==C&&(r[f+C]=g-_<<24|64<<16|0),a.bits=v,0};const{Z_FINISH:xr,Z_BLOCK:kr,Z_TREES:Cr,Z_OK:Sr,Z_STREAM_END:Ar,Z_NEED_DICT:Er,Z_STREAM_ERROR:Mr,Z_DATA_ERROR:Tr,Z_MEM_ERROR:Rr,Z_BUF_ERROR:Lr,Z_DEFLATED:Ir}=Gi,Br=16180,Fr=16190,Nr=16191,Or=16192,Pr=16194,Dr=16199,zr=16200,Hr=16206,Vr=16209,Ur=e=>(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24);function qr(){this.strm=null,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 Uint16Array(320),this.work=new Uint16Array(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}const jr=e=>{if(!e)return 1;const t=e.state;return!t||t.strm!==e||t.mode16211?1:0},$r=e=>{if(jr(e))return Mr;const t=e.state;return e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=Br,t.last=0,t.havedict=0,t.flags=-1,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new Int32Array(852),t.distcode=t.distdyn=new Int32Array(592),t.sane=1,t.back=-1,Sr},Wr=e=>{if(jr(e))return Mr;const t=e.state;return t.wsize=0,t.whave=0,t.wnext=0,$r(e)},Gr=(e,t)=>{let i;if(jr(e))return Mr;const n=e.state;return t<0?(i=0,t=-t):(i=5+(t>>4),t<48&&(t&=15)),t&&(t<8||t>15)?Mr:(null!==n.window&&n.wbits!==t&&(n.window=null),n.wrap=i,n.wbits=t,Wr(e))},Zr=(e,t)=>{if(!e)return Mr;const i=new qr;e.state=i,i.strm=e,i.window=null,i.mode=Br;const n=Gr(e,t);return n!==Sr&&(e.state=null),n};let Qr,Xr,Yr=!0;const Kr=e=>{if(Yr){Qr=new Int32Array(512),Xr=new Int32Array(32);let t=0;for(;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(_r(1,e.lens,0,288,Qr,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;_r(2,e.lens,0,32,Xr,0,e.work,{bits:5}),Yr=!1}e.lencode=Qr,e.lenbits=9,e.distcode=Xr,e.distbits=5},Jr=(e,t,i,n)=>{let r;const s=e.state;return null===s.window&&(s.wsize=1<=s.wsize?(s.window.set(t.subarray(i-s.wsize,i),0),s.wnext=0,s.whave=s.wsize):(r=s.wsize-s.wnext,r>n&&(r=n),s.window.set(t.subarray(i-n,i-n+r),s.wnext),(n-=r)?(s.window.set(t.subarray(i-n,i),0),s.wnext=n,s.whave=s.wsize):(s.wnext+=r,s.wnext===s.wsize&&(s.wnext=0),s.whave{let i,n,r,s,o,a,l,h,c,d,u,f,p,g,m,b,w,v,y,_,x,k,C=0;const S=new Uint8Array(4);let A,E;const M=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);if(jr(e)||!e.output||!e.input&&0!==e.avail_in)return Mr;i=e.state,i.mode===Nr&&(i.mode=Or),o=e.next_out,r=e.output,l=e.avail_out,s=e.next_in,n=e.input,a=e.avail_in,h=i.hold,c=i.bits,d=a,u=l,k=Sr;e:for(;;)switch(i.mode){case Br:if(0===i.wrap){i.mode=Or;break}for(;c<16;){if(0===a)break e;a--,h+=n[s++]<>>8&255,i.check=$i(i.check,S,2,0),h=0,c=0,i.mode=16181;break}if(i.head&&(i.head.done=!1),!(1&i.wrap)||(((255&h)<<8)+(h>>8))%31){e.msg="incorrect header check",i.mode=Vr;break}if((15&h)!==Ir){e.msg="unknown compression method",i.mode=Vr;break}if(h>>>=4,c-=4,x=8+(15&h),0===i.wbits&&(i.wbits=x),x>15||x>i.wbits){e.msg="invalid window size",i.mode=Vr;break}i.dmax=1<>8&1),512&i.flags&&4&i.wrap&&(S[0]=255&h,S[1]=h>>>8&255,i.check=$i(i.check,S,2,0)),h=0,c=0,i.mode=16182;case 16182:for(;c<32;){if(0===a)break e;a--,h+=n[s++]<>>8&255,S[2]=h>>>16&255,S[3]=h>>>24&255,i.check=$i(i.check,S,4,0)),h=0,c=0,i.mode=16183;case 16183:for(;c<16;){if(0===a)break e;a--,h+=n[s++]<>8),512&i.flags&&4&i.wrap&&(S[0]=255&h,S[1]=h>>>8&255,i.check=$i(i.check,S,2,0)),h=0,c=0,i.mode=16184;case 16184:if(1024&i.flags){for(;c<16;){if(0===a)break e;a--,h+=n[s++]<>>8&255,i.check=$i(i.check,S,2,0)),h=0,c=0}else i.head&&(i.head.extra=null);i.mode=16185;case 16185:if(1024&i.flags&&(f=i.length,f>a&&(f=a),f&&(i.head&&(x=i.head.extra_len-i.length,i.head.extra||(i.head.extra=new Uint8Array(i.head.extra_len)),i.head.extra.set(n.subarray(s,s+f),x)),512&i.flags&&4&i.wrap&&(i.check=$i(i.check,n,f,s)),a-=f,s+=f,i.length-=f),i.length))break e;i.length=0,i.mode=16186;case 16186:if(2048&i.flags){if(0===a)break e;f=0;do{x=n[s+f++],i.head&&x&&i.length<65536&&(i.head.name+=String.fromCharCode(x))}while(x&&f>9&1,i.head.done=!0),e.adler=i.check=0,i.mode=Nr;break;case 16189:for(;c<32;){if(0===a)break e;a--,h+=n[s++]<>>=7&c,c-=7&c,i.mode=Hr;break}for(;c<3;){if(0===a)break e;a--,h+=n[s++]<>>=1,c-=1,3&h){case 0:i.mode=16193;break;case 1:if(Kr(i),i.mode=Dr,t===Cr){h>>>=2,c-=2;break e}break;case 2:i.mode=16196;break;case 3:e.msg="invalid block type",i.mode=Vr}h>>>=2,c-=2;break;case 16193:for(h>>>=7&c,c-=7&c;c<32;){if(0===a)break e;a--,h+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",i.mode=Vr;break}if(i.length=65535&h,h=0,c=0,i.mode=Pr,t===Cr)break e;case Pr:i.mode=16195;case 16195:if(f=i.length,f){if(f>a&&(f=a),f>l&&(f=l),0===f)break e;r.set(n.subarray(s,s+f),o),a-=f,s+=f,l-=f,o+=f,i.length-=f;break}i.mode=Nr;break;case 16196:for(;c<14;){if(0===a)break e;a--,h+=n[s++]<>>=5,c-=5,i.ndist=1+(31&h),h>>>=5,c-=5,i.ncode=4+(15&h),h>>>=4,c-=4,i.nlen>286||i.ndist>30){e.msg="too many length or distance symbols",i.mode=Vr;break}i.have=0,i.mode=16197;case 16197:for(;i.have>>=3,c-=3}for(;i.have<19;)i.lens[M[i.have++]]=0;if(i.lencode=i.lendyn,i.lenbits=7,A={bits:i.lenbits},k=_r(0,i.lens,0,19,i.lencode,0,i.work,A),i.lenbits=A.bits,k){e.msg="invalid code lengths set",i.mode=Vr;break}i.have=0,i.mode=16198;case 16198:for(;i.have>>24,b=C>>>16&255,w=65535&C,!(m<=c);){if(0===a)break e;a--,h+=n[s++]<>>=m,c-=m,i.lens[i.have++]=w;else{if(16===w){for(E=m+2;c>>=m,c-=m,0===i.have){e.msg="invalid bit length repeat",i.mode=Vr;break}x=i.lens[i.have-1],f=3+(3&h),h>>>=2,c-=2}else if(17===w){for(E=m+3;c>>=m,c-=m,x=0,f=3+(7&h),h>>>=3,c-=3}else{for(E=m+7;c>>=m,c-=m,x=0,f=11+(127&h),h>>>=7,c-=7}if(i.have+f>i.nlen+i.ndist){e.msg="invalid bit length repeat",i.mode=Vr;break}for(;f--;)i.lens[i.have++]=x}}if(i.mode===Vr)break;if(0===i.lens[256]){e.msg="invalid code -- missing end-of-block",i.mode=Vr;break}if(i.lenbits=9,A={bits:i.lenbits},k=_r(1,i.lens,0,i.nlen,i.lencode,0,i.work,A),i.lenbits=A.bits,k){e.msg="invalid literal/lengths set",i.mode=Vr;break}if(i.distbits=6,i.distcode=i.distdyn,A={bits:i.distbits},k=_r(2,i.lens,i.nlen,i.ndist,i.distcode,0,i.work,A),i.distbits=A.bits,k){e.msg="invalid distances set",i.mode=Vr;break}if(i.mode=Dr,t===Cr)break e;case Dr:i.mode=zr;case zr:if(a>=6&&l>=258){e.next_out=o,e.avail_out=l,e.next_in=s,e.avail_in=a,i.hold=h,i.bits=c,gr(e,u),o=e.next_out,r=e.output,l=e.avail_out,s=e.next_in,n=e.input,a=e.avail_in,h=i.hold,c=i.bits,i.mode===Nr&&(i.back=-1);break}for(i.back=0;C=i.lencode[h&(1<>>24,b=C>>>16&255,w=65535&C,!(m<=c);){if(0===a)break e;a--,h+=n[s++]<>v)],m=C>>>24,b=C>>>16&255,w=65535&C,!(v+m<=c);){if(0===a)break e;a--,h+=n[s++]<>>=v,c-=v,i.back+=v}if(h>>>=m,c-=m,i.back+=m,i.length=w,0===b){i.mode=16205;break}if(32&b){i.back=-1,i.mode=Nr;break}if(64&b){e.msg="invalid literal/length code",i.mode=Vr;break}i.extra=15&b,i.mode=16201;case 16201:if(i.extra){for(E=i.extra;c>>=i.extra,c-=i.extra,i.back+=i.extra}i.was=i.length,i.mode=16202;case 16202:for(;C=i.distcode[h&(1<>>24,b=C>>>16&255,w=65535&C,!(m<=c);){if(0===a)break e;a--,h+=n[s++]<>v)],m=C>>>24,b=C>>>16&255,w=65535&C,!(v+m<=c);){if(0===a)break e;a--,h+=n[s++]<>>=v,c-=v,i.back+=v}if(h>>>=m,c-=m,i.back+=m,64&b){e.msg="invalid distance code",i.mode=Vr;break}i.offset=w,i.extra=15&b,i.mode=16203;case 16203:if(i.extra){for(E=i.extra;c>>=i.extra,c-=i.extra,i.back+=i.extra}if(i.offset>i.dmax){e.msg="invalid distance too far back",i.mode=Vr;break}i.mode=16204;case 16204:if(0===l)break e;if(f=u-l,i.offset>f){if(f=i.offset-f,f>i.whave&&i.sane){e.msg="invalid distance too far back",i.mode=Vr;break}f>i.wnext?(f-=i.wnext,p=i.wsize-f):p=i.wnext-f,f>i.length&&(f=i.length),g=i.window}else g=r,p=o-i.offset,f=i.length;f>l&&(f=l),l-=f,i.length-=f;do{r[o++]=g[p++]}while(--f);0===i.length&&(i.mode=zr);break;case 16205:if(0===l)break e;r[o++]=i.length,l--,i.mode=zr;break;case Hr:if(i.wrap){for(;c<32;){if(0===a)break e;a--,h|=n[s++]<Zr(e,15),inflateInit2:Zr,inflate:es,inflateEnd:e=>{if(jr(e))return Mr;let t=e.state;return t.window&&(t.window=null),e.state=null,Sr},inflateGetHeader:(e,t)=>{if(jr(e))return Mr;const i=e.state;return 0==(2&i.wrap)?Mr:(i.head=t,t.done=!1,Sr)},inflateSetDictionary:(e,t)=>{const i=t.length;let n,r,s;return jr(e)?Mr:(n=e.state,0!==n.wrap&&n.mode!==Fr?Mr:n.mode===Fr&&(r=1,r=qi(r,t,i,0),r!==n.check)?Tr:(s=Jr(e,t,i,i),s?(n.mode=16210,Rr):(n.havedict=1,Sr)))},inflateInfo:"pako inflate (from Nodeca project)"};var is=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1};const ns=Object.prototype.toString,{Z_NO_FLUSH:rs,Z_FINISH:ss,Z_OK:os,Z_STREAM_END:as,Z_NEED_DICT:ls,Z_STREAM_ERROR:hs,Z_DATA_ERROR:cs,Z_MEM_ERROR:ds}=Gi;function us(e){this.options=Zn.assign({chunkSize:65536,windowBits:15,to:""},e||{});const t=this.options;t.raw&&t.windowBits>=0&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(t.windowBits>=0&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),t.windowBits>15&&t.windowBits<48&&0==(15&t.windowBits)&&(t.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new Kn,this.strm.avail_out=0;let i=ts.inflateInit2(this.strm,t.windowBits);if(i!==os)throw new Error(Wi[i]);if(this.header=new is,ts.inflateGetHeader(this.strm,this.header),t.dictionary&&("string"==typeof t.dictionary?t.dictionary=Yn.string2buf(t.dictionary):"[object ArrayBuffer]"===ns.call(t.dictionary)&&(t.dictionary=new Uint8Array(t.dictionary)),t.raw&&(i=ts.inflateSetDictionary(this.strm,t.dictionary),i!==os)))throw new Error(Wi[i])}function fs(e,t){const i=new us(t);if(i.push(e),i.err)throw i.msg||Wi[i.err];return i.result}us.prototype.push=function(e,t){const i=this.strm,n=this.options.chunkSize,r=this.options.dictionary;let s,o,a;if(this.ended)return!1;for(o=t===~~t?t:!0===t?ss:rs,"[object ArrayBuffer]"===ns.call(e)?i.input=new Uint8Array(e):i.input=e,i.next_in=0,i.avail_in=i.input.length;;){for(0===i.avail_out&&(i.output=new Uint8Array(n),i.next_out=0,i.avail_out=n),s=ts.inflate(i,o),s===ls&&r&&(s=ts.inflateSetDictionary(i,r),s===os?s=ts.inflate(i,o):s===cs&&(s=ls));i.avail_in>0&&s===as&&i.state.wrap>0&&0!==e[i.next_in];)ts.inflateReset(i),s=ts.inflate(i,o);switch(s){case hs:case cs:case ls:case ds:return this.onEnd(s),this.ended=!0,!1}if(a=i.avail_out,i.next_out&&(0===i.avail_out||s===as))if("string"===this.options.to){let e=Yn.utf8border(i.output,i.next_out),t=i.next_out-e,r=Yn.buf2string(i.output,e);i.next_out=t,i.avail_out=n-t,t&&i.output.set(i.output.subarray(e,e+t),0),this.onData(r)}else this.onData(i.output.length===i.next_out?i.output:i.output.subarray(0,i.next_out));if(s!==os||0!==a){if(s===as)return s=ts.inflateEnd(this.strm),this.onEnd(s),this.ended=!0,!0;if(0===i.avail_in)break}}return!0},us.prototype.onData=function(e){this.chunks.push(e)},us.prototype.onEnd=function(e){e===os&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=Zn.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg};var ps=function(e,t){return(t=t||{}).raw=!0,fs(e,t)},gs={Inflate:us,inflate:fs,inflateRaw:ps,ungzip:fs,constants:Gi};const{Deflate:ms,deflate:bs,deflateRaw:ws,gzip:vs}=fr,{Inflate:ys,inflate:_s,inflateRaw:xs,ungzip:ks}=gs;var Cs=ws,Ss=_s,As=xs,Es=ks;const Ms=4;function Ts(e,t){const i=[];let n=0,r=0;for(t=t||e.byteLength-18;n100?Bs(new Uint8Array(e.buffer,e.byteOffset+t,r),i,n):function(e,t,i,n,r){for(let s=0;s=0){r=atob(r);const e=new Uint8Array(r.length);for(let t=0;t0?Es(e):e,i}return decodeURIComponent(r)}const Ns=function(e,t){if(0!==e.length){var i=Math.floor(e.length*((100-t)/100));return 0===i?(e.sort((function(e,t){return t-e})),e[i]):function(e,t){var i,n=new Ds;for(i=0;in.content[0])&&(n.content.length===t&&n.pop(),n.push(r))}return n.content[0]}(e,i)}},Os=function(e,t,i){return Math.min(Math.max(e,t),i)},Ps=function(e){return Math.log(e)/Math.LN2};function Ds(){this.content=[]}function zs(e,t){return Math.random()*(t-e)+e}Ds.prototype={push:function(e){this.content.push(e),this.bubbleUp(this.content.length-1)},pop:function(){var e=this.content[0],t=this.content.pop();return this.content.length>0&&(this.content[0]=t,this.sinkDown(0)),e},remove:function(e){for(var t=this.content.length,i=0;i0;){var n=Math.floor((e+1)/2)-1,r=this.content[n];if(i>=r)break;this.content[n]=t,this.content[e]=r,e=n}},sinkDown:function(e){for(var t=this.content.length,i=this.content[e],n=i;;){var r=2*(e+1),s=r-1,o=null;if(s{let e=[];for(let t=1;t>=.5;t-=.1)for(let i=0;i<1;i+=1/28){const n="rgb("+Hs.hsvToRgb(i,1,t).join(",")+")";e.push(n)}return e.pop(),e.push(Hs.rgbColor(16,16,16)),e},rgbToHex:function(e){return(e=e.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i))&&4===e.length?"#"+("0"+parseInt(e[1],10).toString(16)).slice(-2)+("0"+parseInt(e[2],10).toString(16)).slice(-2)+("0"+parseInt(e[3],10).toString(16)).slice(-2):""},hexToRgb:function(e){var t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);if(null!==t)return"rgb("+parseInt(t[1],16)+","+parseInt(t[2],16)+","+parseInt(t[3],16)+")"},hsvToRgb:function(e,t,i){var n,r,s,o=Math.floor(6*e),a=6*e-o,l=i*(1-t),h=i*(1-a*t),c=i*(1-(1-a)*t);switch(o%6){case 0:n=i,r=c,s=l;break;case 1:n=h,r=i,s=l;break;case 2:n=l,r=i,s=c;break;case 3:n=l,r=h,s=i;break;case 4:n=c,r=l,s=i;break;case 5:n=i,r=l,s=h}return[Math.floor(255*n),Math.floor(255*r),Math.floor(255*s)]},hslToRgb:function(e,t,i){var n,r,s;if(0===t)n=r=s=i;else{var o=i<.5?i*(1+t):i+t-i*t,a=2*i-o;n=Hs.hue2rgb(a,o,e+1/3),r=Hs.hue2rgb(a,o,e),s=Hs.hue2rgb(a,o,e-1/3)}return[255*n,255*r,255*s]},hue2rgb:(e,t,i)=>(i<0&&(i+=1),i>1&&(i-=1),i<1/6?e+6*(t-e)*i:i<.5?t:i<2/3?e+(t-e)*(2/3-i)*6:e),rgbaColor:function(e,t,i,n){return"rgba("+(e=Os(e,0,255))+","+(t=Os(t,0,255))+","+(i=Os(i,0,255))+","+(n=Os(n,0,1))+")"},rgbColor:function(e,t,i){return"rgb("+(e=Os(e,0,255))+","+(t=Os(t,0,255))+","+(i=Os(i,0,255))+")"},greyScale:function(e){var t=Os(e,0,255);return"rgb("+t+","+t+","+t+")"},randomGrey:function(e,t){e=Os(e,0,255),t=Os(t,0,255);var i=Math.round(zs(e,t)).toString(10);return"rgb("+i+","+i+","+i+")"},randomRGB:function(e,t){return e=Os(e,0,255),t=Os(t,0,255),"rgb("+Math.round(zs(e,t)).toString(10)+","+Math.round(zs(e,t)).toString(10)+","+Math.round(zs(e,t)).toString(10)+")"},randomRGBConstantAlpha:function(e,t,i){return e=Os(e,0,255),t=Os(t,0,255),"rgba("+Math.round(zs(e,t)).toString(10)+","+Math.round(zs(e,t)).toString(10)+","+Math.round(zs(e,t)).toString(10)+","+i+")"},addAlpha:function(e,t){if("0"===e||"."===e)e="rgb(0,0,0)";else{const t=this.colorNameToHex(e);t&&(e=t)}var i=/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(e);if(e.startsWith("rgba")){const i=e.lastIndexOf(",");return e.substring(0,i+1)+t.toString()+")"}return i&&(e=Hs.hexToRgb(e)),e.startsWith("rgb")?e.replace("rgb","rgba").replace(")",", "+t+")"):(console.log(e+" is not an rgb style string"),e)},rgbComponents:function(e){if("0"===e||"."===e)return[0,0,0];if(/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(e))e=Hs.hexToRgb(e);else if(!e.startsWith("rgb")){const t=this.colorNameToHex(e);e=this.hexToRgb(t)}if(e.startsWith("rgb("))return e.substring(4,e.length-1).split(",").map((e=>Number.parseInt(e.trim())));if(e.startsWith("rgba("))return e.substring(5,e.length-1).split(",").map(((e,t)=>(e=e.trim(),3===t?Number.parseFloat(e):Number.parseInt(e))));throw Error("Unrecognized color string: color")},getCompositeColor:function(e,t,i){return"rgb("+Math.floor(i*t[0]+(1-i)*e[0])+","+Math.floor(i*t[1]+(1-i)*e[1])+","+Math.floor(i*t[2]+(1-i)*e[2])+")"},createColorString:function(e){return(e=function(e){return void 0===e||((e.startsWith("'")||e.startsWith('"'))&&(e=e.substring(1)),(e.endsWith("'")||e.endsWith('"'))&&(e=e.substring(0,e.length-1))),e}(e)).includes(",")?e.startsWith("rgb")?e:"rgb("+e+")":e},darkenLighten:function(e,t){let i,n=this.colorNameToHex(e);i=n?Hs.hexToRgb(n):e.startsWith("rgb(")?e:Hs.hexToRgb(e);const r=i.replace(")","").substring(4).split(","),s=Math.max(0,Math.min(255,Number.parseInt(r[0].trim())+t)),o=Math.max(0,Math.min(255,Number.parseInt(r[1].trim())+t)),a=Math.max(0,Math.min(255,Number.parseInt(r[2].trim())+t));return"rgb("+s.toString()+","+o.toString()+","+a.toString()+")"},colorNameToHex:function(e){return{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"}[e]}},Vs="googleapis";class Us{constructor(){this.oauthTokens={}}setToken(e,t){t=t||Vs,this.oauthTokens[t]=e}getToken(e){let t;e=e||Vs;for(let i of Object.keys(this.oauthTokens)){if(qs(i).test(e)){t=this.oauthTokens[i];break}}return t}removeToken(e){e=e||Vs;for(let t of Object.keys(this.oauthTokens)){qs(t).test(e)&&(this.oauthTokens[t]=void 0)}}}function qs(e){return new RegExp("^"+e.split(/\*+/).map(js).join(".*")+"$")}function js(e){return e.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&")}function $s(e){return e.includes("googleapis")&&!e.includes("urlshortener")||Ws(e)||Gs(e)}function Ws(e){return e.startsWith("gs://")||e.startsWith("https://www.googleapis.com/storage")||e.startsWith("https://storage.cloud.google.com")||e.startsWith("https://storage.googleapis.com")}function Gs(e){return e.indexOf("drive.google.com")>=0||e.indexOf("www.googleapis.com/drive")>0}function Zs(e){let{bucket:t,object:i}=function(e){let t,i;if(e.startsWith("gs://")){const n=e.indexOf("/",5);if(n>=0){t=e.substring(5,n);const r=e.indexOf("?");i=r<0?e.substring(n+1):e.substring(n+1,r)}}else if(e.startsWith("https://storage.googleapis.com")||e.startsWith("https://storage.cloud.google.com")){const n=e.indexOf("/v1/b/",8);if(n>0){const r=e.indexOf("/o/",n);if(r>0){const s=e.indexOf("?",r);t=e.substring(n+6,r),i=s>0?e.substring(r+3,s):e.substring(r+3)}}else{const n=e.indexOf("/",8),r=e.indexOf("/",n+1),s=e.indexOf("?",r);r>0&&(t=e.substring(n+1,r),i=s<0?e.substring(r+1):e.substring(r+1,s))}}else if(e.startsWith("https://www.googleapis.com/storage/v1/b")){const n=e.indexOf("/v1/b/",8),r=e.indexOf("/o/",n);if(r>0){const s=e.indexOf("?",r);t=e.substring(n+6,r),i=s>0?e.substring(r+3,s):e.substring(r+3)}}if(t&&i)return{bucket:t,object:i};throw Error(`Unrecognized Google Storage URI: ${e}`)}(e);i=function(e){let t="";return e.split("").forEach((function(e){Qs.has(e)?t+=Qs.get(e):t+=e})),t}(i);const n=e.indexOf("?");return`https://storage.googleapis.com/storage/v1/b/${t}/o/${i}${n>0?e.substring(n)+"&alt=media":"?alt=media"}`}const Qs=new Map;function Xs(){return window.google&&window.google.igv}async function Ys(e){if(!Xs())throw Error("Google oAuth has not been initialized");if(google.igv.tokenResponse&&Date.now(){try{t.callback=e=>{void 0!==e.error&&n(e),google.igv.tokenResponse=e,google.igv.tokenExpiresAt=Date.now()+1e3*e.expires_in,i(e.access_token)},t.requestAccessToken({scope:e})}catch(e){console.log(e)}}))}}function Ks(){return google.igv.apiKey}function Js(e){var t=eo(e);return t?"https://www.googleapis.com/drive/v3/files/"+t+"?alt=media&supportsTeamDrives=true":e}function eo(e){if(e.includes("/open?id=")){const t=e.indexOf("/open?id=")+9,i=e.indexOf("&");if(t>0&&i>t)return e.substring(t,i);if(t>0)return e.substring(t)}else{if(e.includes("/file/d/")){const t=e.indexOf("/file/d/")+8,i=e.lastIndexOf("/");return e.substring(t,i)}if(e.startsWith("https://www.googleapis.com/drive")){let t=e.indexOf("/files/");const i=e.indexOf("?");if(t>0)return t+=7,i>0?e.substring(t,i):e.substring(t)}}throw Error("Unknown Google Drive url format: "+e)}Qs.set("!","%21"),Qs.set("#","%23"),Qs.set("$","%24"),Qs.set("%","%25"),Qs.set("&","%26"),Qs.set("'","%27"),Qs.set("(","%28"),Qs.set(")","%29"),Qs.set("*","%2A"),Qs.set("+","%2B"),Qs.set(",","%2C"),Qs.set("/","%2F"),Qs.set(":","%3A"),Qs.set(";","%3B"),Qs.set("=","%3D"),Qs.set("?","%3F"),Qs.set("@","%40"),Qs.set("[","%5B"),Qs.set("]","%5D"),Qs.set(" ","%20");class to{constructor(e){this.requestsPerSecond=e.requestsPerSecond||10,this.lastStartTime=0,this.queued=[]}add(e,t){var i=this;return new Promise((function(t,n){i.queued.push({resolve:t,reject:n,asyncFunction:e}),i.dequeue()}))}addAll(e,t){var i=e.map(function(e){return this.add(e,t)}.bind(this));return Promise.all(i)}dequeue(){if(this.queued.length>0){var e=new Date,t=1e3/this.requestsPerSecond+1,i=e-this.lastStartTime;i>=t?this._execute():setTimeout(function(){this.dequeue()}.bind(this),t-i)}}async _execute(){this.lastStartTime=new Date;var e=this.queued.shift();const t=e.asyncFunction;try{const i=await t();e.resolve(i)}catch(t){e.reject(t)}}}function io(){if(Xs()){return Xs()&&google.igv.tokenResponse&&Date.now()0)for(var o=0;othis._loadURL(e,t))):this._loadURL(e,t)}throw Error(`url must be either a 'File', 'string', 'function', or 'Promise'. Actual type: ${i}`)}async _loadURL(e,t){const i=this;e=function(e){return e.startsWith("https://www.dropbox.com")?e.replace("//www.dropbox.com","//dl.dropboxusercontent.com"):e.startsWith("https://drive.google.com")?Js(e):e.includes("//www.broadinstitute.org/igvdata")?e.replace("//www.broadinstitute.org/igvdata","//data.broadinstitute.org/igvdata"):e.includes("//igvdata.broadinstitute.org")?e.replace("//igvdata.broadinstitute.org","//s3.amazonaws.com/igv.broadinstitute.org"):e.includes("//igv.genepattern.org")?e.replace("//igv.genepattern.org","//igv-genepattern-org.s3.amazonaws.com"):e.startsWith("ftp://ftp.ncbi.nlm.nih.gov/geo")?e.replace("ftp://","https://"):e}(e);let n=(t=t||{}).oauthToken||this.getOauthToken(e);return n&&(n=await("function"==typeof n?n():n)),new Promise((function(r,s){$s(e)&&!function(e){return e.indexOf("X-Goog-Signature")>-1}(e)&&(Ws(e)&&(e=Zs(e)),e=function(e){let t=ro.apiKey;t||"undefined"==typeof gapi||(t=gapi.apiKey);if(void 0!==t&&!e.includes("key=")){const i=e.includes("?")?"&":"?";e=e+i+"key="+t}return e}(e),Gs(e)&&function(e){if(e.includes("supportsTeamDrive"))return e;{const t=e.includes("?")?"&":"?";e=e+t+"supportsTeamDrive=true"}}(e),n||(n=io()));const o=t.headers||{};n&&function(e,t){t&&(e["Cache-Control"]="no-cache",e.Authorization="Bearer "+t)}(o,n);const a=t.range,l=new XMLHttpRequest,h=t.sendData||t.body,c=t.method||(h?"POST":"GET"),d=t.responseType,u=t.contentType,f=t.mimeType;if(l.open(c,e),t.timeout&&(l.timeout=t.timeout),a){let e="";a.size&&(e=a.start+a.size-1),l.setRequestHeader("Range","bytes="+a.start+"-"+e)}if(u&&l.setRequestHeader("Content-Type",u),f&&l.overrideMimeType(f),d&&(l.responseType=d),o)for(let e of Object.keys(o)){const t=o[e];l.setRequestHeader(e,t)}!0===t.withCredentials&&(l.withCredentials=!0),l.onload=async function(n){if(0===l.status||l.status>=200&&l.status<=300)if("HEAD"===t.method){const e=t.requestedHeaders||["content-length"],i={};for(let t of e)i[t]=l.getResponseHeader(t);r(i)}else a&&206!==l.status&&0!==a.start?(l.response.length>1e5&&!i.RANGE_WARNING_GIVEN&&alert(`Warning: Range header ignored for URL: ${e}. This can have severe performance impacts.`),r(l.response.slice(a.start,a.start+a.size))):r(l.response);else 416===l.status?p(Error("416 Unsatisfiable Range")):"undefined"==typeof gapi||404!==l.status&&401!==l.status&&403!==l.status||!$s(e)||t.retries?403===l.status?p("Access forbidden: "+e):p(l.status):g()},l.onerror=function(i){$s(e)&&!t.retries?g():p("Error accessing resource: "+e+" Status: "+l.status)},l.ontimeout=function(e){p("Timed out")},l.onabort=function(e){console.log("Aborted"),s(e)};try{l.send(h)}catch(i){$s(e)&&!t.retries?g():p(i)}function p(e){if(!s)throw e;s(e)}async function g(){try{const n=await async function(e){if(Xs()){const t=function(e){return Gs(e)?"https://www.googleapis.com/auth/drive.readonly":Ws(e)?"https://www.googleapis.com/auth/devstorage.read_only":"https://www.googleapis.com/auth/userinfo.profile"}(e);return await Ys(t)}throw Error("Authorization is required, but Google oAuth has not been initalized. Contact your site administrator for assistance.")}(e);t.retries=1,t.oauthToken=n;const s=await i.load(e,t);r(s)}catch(e){if(e.error){const t=e.error.startsWith("popup_blocked")?"Google login popup blocked by browser.":e.error;alert(t)}else p(e)}}}))}async _loadFileSlice(e,t){let i=t&&t.range?e.slice(t.range.start,t.range.start+t.range.size):e;const n=await i.arrayBuffer();return"arraybuffer"===t.responseType?n:no(n)}async _loadStringFromFile(e,t){const i=t.range?e.slice(t.range.start,t.range.start+t.range.size):e;return no(await i.arrayBuffer())}async _loadStringFromUrl(e,t){(t=t||{}).responseType="arraybuffer";return no(await this.load(e,t))}setOauthToken(e,t){this.oauth.setToken(e,t)}getOauthToken(e){const t=$s(e)?void 0:ni(e).host;let i=this.oauth.getToken(t);if(i)return i;if(void 0===t){const e=io();if(e&&e.expires_at>Date.now())return e.access_token}}async getContentLength(e,t){(t=t||{}).method="HEAD",t.requestedHeaders=["content-length"];const i=(await this._loadURL(e,t))["content-length"];return i?Number.parseInt(i):0}};var so={color:1};so.parent=so,so.left=so,so.right=so;class oo{constructor(){this.root=so}insert(e,t,i){var n=new fo(new uo(e,t,i));for(this.treeInsert(n),n.color=2;n!==this.root&&2===n.parent.color;)if(n.parent===n.parent.parent.left){let e=n.parent.parent.right;2===e.color?(n.parent.color=1,e.color=1,n.parent.parent.color=2,n=n.parent.parent):(n===n.parent.right&&(n=n.parent,lo.call(this,n)),n.parent.color=1,n.parent.parent.color=2,ho.call(this,n.parent.parent))}else{let e=n.parent.parent.left;2===e.color?(n.parent.color=1,e.color=1,n.parent.parent.color=2,n=n.parent.parent):(n===n.parent.left&&(n=n.parent,ho.call(this,n)),n.parent.color=1,n.parent.parent.color=2,lo.call(this,n.parent.parent))}this.root.color=1}findOverlapping(e,t){var i=new uo(e,t,0);if(this.root===so)return[];var n=ao.call(this,i,this.root,[]);return n.length>1&&n.sort((function(e,t){return e.low-t.low})),n}logIntervals(){!function e(t,i){for(var n="",r=0;r=e.low&&ao.call(this,e,t.left,i),t.right!==so&&t.right.min<=e.high&&ao.call(this,e,t.right,i),i}function lo(e){var t=e.right;e.right=t.left,t.left!==so&&(t.left.parent=e),t.parent=e.parent,e.parent===so?this.root=t:e.parent.left===e?e.parent.left=t:e.parent.right=t,t.left=e,e.parent=t,co.call(this,e)}function ho(e){var t=e.left;e.left=t.right,t.right!==so&&(t.right.parent=e),t.parent=e.parent,e.parent===so?this.root=t:e.parent.right===e?e.parent.right=t:e.parent.left=t,t.right=e,e.parent=t,co.call(this,e)}function co(e){for(;e!==so;){var t=e.left.max>e.right.max?e.left.max:e.right.max,i=e.interval.high;e.max=t>i?t:i;var n=e.left.mine.low?1:this.highe.high?1:0}overlaps(e){return this.low<=e.high&&e.low<=this.high}}function fo(e){this.parent=so,this.left=so,this.right=so,this.interval=e,this.color=2}class po{constructor(e,t,i){e=e||[],this.treeMap=this.buildTreeMap(e,t),this.range=i,this.count=e.length}containsRange(e){return void 0===this.range||this.range.contains(e.chr,e.start,e.end)}queryFeatures(e,t,i){const n=this.treeMap[e];if(!n)return[];const r=n.findOverlapping(t,i);if(0===r.length)return[];{const n=[],s=this.allFeatures[e];if(s){for(let e of r){const r=e.value;for(let e=r.start;ei)break;r.end>=t&&n.push(r)}}n.sort((function(e,t){return e.start-t.start}))}return n}}getAllFeatures(){return this.allFeatures}buildTreeMap(e,t){const i={},n=[];if(this.allFeatures={},e){for(let i of e){let e=i.chr;t&&(e=t.getChromosomeName(e));let r=this.allFeatures[e];r||(n.push(e),r=[],this.allFeatures[e]=r),r.push(i)}for(let e of n){const t=this.allFeatures[e];t.sort((function(e,t){return e.start===t.start?0:e.start>t.start?1:-1})),i[e]=go(t)}}return i}}function go(e){const t=new oo,i=e.length,n=Math.max(10,Math.round(i/10));for(let r=0;rt.start?1:-1}));for(let r=0;ri)break;s.end>t&&e.push(s)}})),e.sort((function(e,t){return e.start-t.start})),e)}return[]};const wo={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}};const vo={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 yo{constructor(e){this.colors=vo[e],Array.isArray(this.colors)||(this.colors=[]),this.colorTable={},this.nextIdx=0,this.colorGenerator=new xo}getColor(e){return this.colorTable.hasOwnProperty(e)||(this.nextIdxthis.hexwidth?e:new Array(this.hexwidth-e.length+1).join("0")+e},xo.prototype.get=function(e,t){this.hue+=this.goldenRatio,this.hue%=1,"number"!=typeof e&&(e=.5),"number"!=typeof t&&(t=.95);var i=this.hsvToRgb(this.hue,e,t);return"#"+this.padHex(i[0].toString(16))+this.padHex(i[1].toString(16))+this.padHex(i[2].toString(16))},new xo;const Co={configureHighDPICanvas:function(e,t,i){const n=window.devicePixelRatio;e.canvas.style.width=`${t}px`,e.canvas.width=Math.floor(n*t),e.canvas.style.height=`${i}px`,e.canvas.height=Math.floor(n*i),e.scale(n,n)},setProperties:function(e,t){for(var i in t)if(t.hasOwnProperty(i)){var n=t[i];e[i]=n}},strokeLine:function(e,t,i,n,r,s){t=Math.floor(t)+.5,i=Math.floor(i)+.5,n=Math.floor(n)+.5,r=Math.floor(r)+.5,s&&(e.save(),Co.setProperties(e,s)),e.beginPath(),e.moveTo(t,i),e.lineTo(n,r),e.stroke(),s&&e.restore()},fillRect:function(e,t,i,n,r,s){t=Math.round(t),i=Math.round(i),s&&(e.save(),Co.setProperties(e,s)),e.fillRect(t,i,n,r),s&&e.restore()},fillPolygon:function(e,t,i,n){n&&(e.save(),Co.setProperties(e,n)),So(e,t,i),e.fill(),n&&e.restore()},strokePolygon:function(e,t,i,n){n&&(e.save(),Co.setProperties(e,n)),So(e,t,i),e.stroke(),n&&e.restore()},fillText:function(e,t,i,n,r,s){if((r||s)&&e.save(),r&&Co.setProperties(e,r),s){for(var o in e.translate(i,n),s){var a=s[o];"translate"===o&&e.translate(a.x,a.y),"rotate"===o&&e.rotate(a.angle*Math.PI/180)}e.fillText(t,0,0)}else e.fillText(t,i,n);(r||s)&&e.restore()},strokeText:function(e,t,i,n,r,s){if((r||s)&&e.save(),r&&Co.setProperties(e,r),s){for(var o in e.translate(i,n),s){var a=s[o];"translate"===o&&e.translate(a.x,a.y),"rotate"===o&&e.rotate(a.angle*Math.PI/180)}e.strokeText(t,0,0)}else e.strokeText(t,i,n);(r||s)&&e.restore()},strokeCircle:function(e,t,i,n,r){r&&(e.save(),Co.setProperties(e,r)),e.beginPath(),e.arc(t,i,n,0,2*Math.PI),e.stroke(),r&&e.restore()},fillCircle:function(e,t,i,n,r){r&&(e.save(),Co.setProperties(e,r)),e.beginPath(),e.arc(t,i,n,0,2*Math.PI),e.fill(),r&&e.restore()},drawArrowhead:function(e,t,i,n,r){e.save(),n||(n=5),r&&(e.lineWidth=r),e.beginPath(),e.moveTo(t,i-n/2),e.lineTo(t,i+n/2),e.lineTo(t+n,i),e.lineTo(t,i-n/2),e.closePath(),e.fill(),e.restore()},dashedLine:function(e,t,i,n,r,s,o={}){void 0===s&&(s=2),e.setLineDash([s,s]),Co.strokeLine(e,t,i,n,r,o),e.setLineDash([])},roundRect:function(e,t,i,n,r,s,o,a){void 0===a&&(a=!0),void 0===s&&(s=5),e.beginPath(),e.moveTo(t+s,i),e.lineTo(t+n-s,i),e.quadraticCurveTo(t+n,i,t+n,i+s),e.lineTo(t+n,i+r-s),e.quadraticCurveTo(t+n,i+r,t+n-s,i+r),e.lineTo(t+s,i+r),e.quadraticCurveTo(t,i+r,t,i+r-s),e.lineTo(t,i+s),e.quadraticCurveTo(t,i,t+s,i),e.closePath(),a&&e.stroke(),o&&e.fill()},polygon:function(e,t,i,n,r){void 0===r&&(r=!0),e.beginPath();var s=t.length;e.moveTo(t[0],i[0]);for(var o=1;o{for(let t=0;t{e.translate(t,0),e.scale(-1,1),e.translate(-t,0)}};function So(e,t,i){var n,r=t.length;for(n=0;n0){t=Number.MAX_VALUE,i=-Number.MAX_VALUE;for(let n of e)Number.isNaN(n.value)||(t=Math.min(t,n.value),i=Math.max(i,n.value));i>0&&(t=Math.min(0,t)),i<0&&(i=0)}else t=0,i=100;return{min:t,max:i}},Lo=function(e,t,i){let n=t.start,r=t.end;if(void 0===r)n-=i/2,r=n+i,r>e?(r=e,n=r-i):n<0&&(n=0,r=i);else if(r-ne?(r=e,n=r-i):(n=t-i/2,r=n+i)}t.start=Math.ceil(n),t.end=Math.floor(r)};async function Io(e){if(Qt(e)&&e.startsWith("https://drive.google.com")){if(void 0===Ks())throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access");const t=await async function(e){let t="https://www.googleapis.com/drive/v3/files/"+eo(e)+"?supportsTeamDrives=true";const i=Ks();i&&(t+="&key="+i);const n=await fetch(t);let r=await n.json();if(r.error&&404===r.error.code){let e="https://www.googleapis.com/auth/drive.readonly";const i=await Ys(e);if(!i)throw Error(r.error);{const e=await fetch(t,{headers:{Authorization:`Bearer ${i}`}});if(r=await e.json(),r.error)throw Error(r.error)}}return r}(e);return t.originalFileName||t.name}return ei(e)}function Bo(e){var t,i;return e>1e7?(t=" mb",i=e/1e6,Math.floor(i).toString()+t):e>1e4?(t=" kb",i=e/1e3,Xt(Math.floor(i))+t):Xt(e)+" bp"}function Fo(e){return Qt(e)&&e.startsWith("data:")}function No(e,t){const i=_t.div({class:t});e.appendChild(i)}function Oo(e,t){t.parentNode.insertBefore(e,t)}function Po(e,t){t.parentNode.insertBefore(e,t.nextSibling)}function Do(){return"https:"===window.location.protocol||"localhost"===window.location.hostname}function zo(e,t){if(e.length<6)return void console.log("Skipping line: "+e.join(" "));var i={chr1:e[0],start1:Number.parseInt(e[1]),end1:Number.parseInt(e[2]),chr2:e[3],start2:Number.parseInt(e[4]),end2:Number.parseInt(e[5])};if(isNaN(i.start1)||isNaN(i.end1)||isNaN(i.start2)||isNaN(i.end2))return;t&&void 0===t.hiccups&&(t.hiccups=!!t.columnNames&&Vo(t.columnNames));const n=t&&t.hiccups,r=n?6:10;if(n||(e.length>6&&"."!==e[6]&&(i.name=e[6]),e.length>7&&"."!==e[7]&&(i.score=Number(e[7])),e.length>8&&"."!==e[8]&&(i.strand1=e[8]),e.length>9&&"."!==e[9]&&(i.strand2=e[9])),t){const n=t.colorColumn;n&&nr&&t.columnNames&&t.columnNames.length===e.length&&(i.extras=e.slice(r))}return i.chr1===i.chr2&&(i.chr=i.chr1,i.start=Math.min(i.start1,i.start2),i.end=Math.max(i.end1,i.end2)),i}function Ho(e,t){if(!(e.length<8))return{chr:e[0],start:Number.parseInt(e[1]),end:Number.parseInt(e[2]),color:Hs.createColorString(e[6]),value:Number(e[7])}}function Vo(e){return e&&(e.includes("fdrDonut")||e.includes("fdr_donut"))}const Uo=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","tsv","hiccups","fasta","fa","fna","pytor"]);function qo(e){return Eo&&Eo[e]?function(e){const t=e.fields,i=["chr","start","end"];for(let n=0;n0&&(e=e.substr(0,t)),e.endsWith(".gz")&&(e=e.substr(0,e.length-3)),(e.endsWith(".txt")||e.endsWith(".tab")||e.endsWith(".bgz"))&&(e=e.substr(0,e.length-4)),i=(t=e.lastIndexOf("."))<0?e:e.substr(t+1)){case"bw":return"bigwig";case"bb":return"bigbed";case"fasta":case"fa":case"fna":return"fasta";default:return Uo.has(i)?i:void 0}}function $o(e,t){if(Qt(e)){if(e.includes("?")){const i=e.indexOf("?");return e.substring(0,i)+"."+t+e.substring(i)}return e+"."+t}}function Wo(e){if(function(e){e.featureType&&(e.type=e.type||e.featureType,e.featureType=void 0);"junctions"===e.type?e.type="junction":"bed"===e.type?(e.type="annotation",e.format=e.format||"bed"):"annotations"===e.type?e.type="annotation":"alignments"===e.type?e.type="alignment":"bam"===e.type?(e.type="alignment",e.format="bam"):"vcf"===e.type?(e.type="variant",e.format="vcf"):"t2d"===e.type?e.type="gwas":"FusionJuncSpan"!==e.type||e.format?"aed"===e.type&&(e.type="annotation",e.format=e.format||"aed"):e.format="fusionjuncspan"}(e),e.type)return e.type;if(e.format){switch(e.format.toLowerCase()){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"hiccups":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";case"fasta":return"sequence";case"pytor":return"cnvpytor";default:return"annotation"}}}async function Go(e){if(e.url){const t=await ro.loadString(e.url,To(e,{range:{start:0,size:1e3}}));if(t){if(Vo(t.split("\n")[0].split("\t")))return"hiccups"}}}function Zo(e,t){Eo[e]={fields:t}}var Qo=Object.freeze({__proto__:null,knownFileExtensions:Uo,getFormat:qo,inferFileFormat:jo,inferFileFormatFromHeader:Go,inferTrackType:Wo,inferIndexPath:$o,registerFileFormats:Zo});const Xo=[["A","T"],["G","C"],["Y","R"],["W","S"],["K","M"],["D","H"],["B","V"]],Yo=new Map;for(let $b of Xo){const Wb=$b[0],Gb=$b[1];Yo.set(Wb,Gb),Yo.set(Gb,Wb),Yo.set(Wb.toLowerCase(),Gb.toLowerCase()),Yo.set(Gb.toLowerCase(),Wb.toLowerCase())}function Ko(e){let t="",i=e.length;for(;i-- >0;){const n=e[i];t+=Yo.has(n)?Yo.get(n):n}return t}class Jo{constructor(e,t,i){this.name=e,this.order=t,this.bpLength=i}}const ea=Yt,ta=new Set(["fastaURL","indexURL","cytobandURL","indexed"]);class ia{constructor(e){this.fastaURL=e.fastaURL,this.withCredentials=e.withCredentials,this.chromosomeNames=[],this.chromosomes={},this.sequences=new Map;const t={};for(let i in e)e.hasOwnProperty(i)&&!ta.has(i)&&(t[i]=e[i]);this.config=t}async init(){return this.loadAll()}async getSequence(e,t,i){if(!this.sequences.has(e))return;let n=this.sequences.get(e).find((e=>e.contains(t,i)));if(!n&&(n=this.sequences.get(e).find((e=>e.overlaps(t,i))),!n))return;t-=n.offset,i-=n.offset;let r="";if(t<0)for(let e=t;e")){a&&a.seq&&l.call(this,a,o++);const e=r.substr(1).split(/\s+/),t=e[0].split(":");if(a.chr=t[0],a.seq="",a.offset=0,t.length>1&&t[1].indexOf("-")>0){const i=t[1].split("-");2===i.length&&/^[0-9]+$/.test(i[0])&&/^[0-9]+$/.test(i[1]);const n=Number.parseInt(i[0]);if(Number.parseInt(i[1])>n&&(a.offset=n-1),e.length>1&&e[1].startsWith("@len="))try{a.length=parseInt(e[1].trim().substring(5))}catch(e){a.length=void 0,console.error(`Error parsing sequence length for ${r}`)}else a.length=void 0}}else a.seq+=r;a&&a.seq&&l.call(this,a,o)}function l(e,i){const n=e.length||e.offset+e.seq.length;if(t.has(e.chr)){const t=this.chromosomes[e.chr];t.bpLength=Math.max(t.bpLength,n)}else this.chromosomeNames.push(e.chr),this.sequences.set(e.chr,[]),this.chromosomes[e.chr]=new Jo(e.chr,i,n),t.add(e.chr);this.sequences.get(e.chr).push(new na(e.offset,e.seq))}}}class na{constructor(e,t){this.offset=e,this.sequence=t}contains(e,t){return this.offset<=e&&this.end>=t}overlaps(e,t){return this.offsete}get end(){return this.offset+this.sequence.length}}const ra=function(e,t,i,n){this.chr=e,this.start=t,this.end=i,this.features=n};ra.prototype.contains=function(e,t,i){return this.chr===e&&this.start<=t&&this.end>=i},ra.prototype.containsRange=function(e){return this.chr===e.chr&&this.start<=e.start&&this.end>=e.end};const sa=Yt,oa=new Set(["fastaURL","indexURL","compressedIndexURL","cytobandURL","indexed"]);class aa{constructor(e){this.file=e.fastaURL,this.indexFile=e.indexURL||e.indexFile||this.file+".fai",this.compressedIndexFile=e.compressedIndexURL||!1,this.withCredentials=e.withCredentials,this.chromosomeNames=[],this.chromosomes={},this.sequences={},this.offsets={};const t={};for(let i in e)e.hasOwnProperty(i)&&!oa.has(i)&&(t[i]=e[i]);this.config=t}async init(){return this.getIndex()}async getSequence(e,t,i){if(!(this.interval&&this.interval.contains(e,t,i))){let n=t,r=i;if(i-t<5e4){const e=i-t,s=Math.round(t+e/2);n=Math.max(0,s-25e3),r=s+25e3}const s=await this.readSequence(e,n,r);this.interval=new ra(e,n,r,s)}const n=t-this.interval.start,r=i-t;return this.interval.features?this.interval.features.substr(n,r):null}async getIndex(){if(this.index)return this.index;{const e=await ro.load(this.indexFile,To(this.config)),t=sa(e),i=t.length;let n=0,r=0;for(this.index={};nthis.compressedIndex[n][1])return[n];let r=0,s=n,o=Math.floor(this.compressedIndex.length/2),a=this.compressedIndex.length+1,l=!1;for(let t=0;te){l=!0;break}t=t)break}const h=i[i.length-1];return h===this.compressedIndex.length-1&&this.compressedIndex[h][1]0&&(b=Math.min(s-r,a-u),w+=m.substr(v,b),v+=b+h);v{this.reversed=!this.reversed,this.trackView.repaintViews()}},{name:this.frameTranslate?"Close Translation":"Three-frame Translate",click:()=>{if(this.frameTranslate=!this.frameTranslate,this.frameTranslate){for(let e of this.trackView.viewports)e.setContentHeight(ba);this.trackView.setTrackHeight(ba)}else{for(let e of this.trackView.viewports)e.setContentHeight(25);this.trackView.setTrackHeight(25)}this.trackView.repaintViews()}}]}contextMenuItemList(e){const t=e.viewport;if(t.referenceFrame.bpPerPixel<=1){const e=t.getWidth()*t.referenceFrame.bpPerPixel,i=t.referenceFrame.chr,n=Math.floor(t.referenceFrame.start),r=Math.ceil(n+e),s=[{label:this.reversed?"View visible sequence (reversed)...":"View visible sequence...",click:async()=>{let e=await this.browser.genome.sequence.getSequence(i,n,r);e?this.reversed&&(e=Ko(e)):e="Unknown sequence",this.browser.alert.present(e)}}];return Do()&&s.push({label:"Copy visible sequence",click:async()=>{let e=await this.browser.genome.sequence.getSequence(i,n,r);e?this.reversed&&(e=Ko(e)):e="Unknown sequence";try{await navigator.clipboard.writeText(e)}catch(e){console.error(e),this.browser.alert.present(`error copying sequence to clipboard ${e}`)}}}),s.push("
"),s}}translateSequence(e){const t=[[],[],[]];for(let i of[0,1,2]){let n=i;for(;e.length-n>=3;){let r=e.slice(n,n+3);this.reversed&&(r=r.split("").reverse().join(""));const s=fa[r.toUpperCase()]||"";t[i].push({codons:r,aminoA:s}),n+=3}}return t}async getSequenceSource(){return this.config.fastaURL?(this.fasta||(this.fasta=new va(this.config,this.browser.genome),await this.fasta.init()),this.fasta):this.browser.genome.sequence}async getFeatures(e,t,i,n){if(t=Math.floor(t),i=Math.floor(i),n&&n>10)return null;{const n=await this.getSequenceSource();return{bpStart:t,sequence:await n.getSequence(e,t,i)}}}draw(e){const t=e.context;if(e.features){let i=e.features.sequence;if(!i)return;this.reversed&&(i=i.split("").map((function(e){return pa[e]})).join(""));const n=e.features.bpStart,r=1+e.bpStart+e.pixelWidth*e.bpPerPixel;for(let s=Math.floor(e.bpStart);s<=r;s++){const r=Math.floor(s-n);if(r>=0&&r.1)Co.fillRect(t,n,5,o,10,{fillStyle:l});else{const i=n+.5*(o-t.measureText(a).width);"y"===e.axis?(t.save(),Co.labelTransformWithContext(t,i),Co.strokeText(t,a,i,15,{strokeStyle:l}),t.restore()):Co.strokeText(t,a,i,15,{strokeStyle:l})}}}if(this.frameTranslate){let r=25;const s=this.translateSequence(i);for(let i=0;ie.pixelWidth)break;let f=l.aminoA;l.aminoA.indexOf("STOP")>-1?(a="rgb(255, 0, 0)",f="STOP"):"M"===l.aminoA&&(a="rgb(0, 153, 0)",f="START"),Co.fillRect(t,c,r,d-c,25,{fillStyle:a}),e.bpPerPixel<=.1&&Co.strokeText(t,f,u-t.measureText(f).width/2,r+15)}r+=30}}}}get supportsWholeGenome(){return!1}computePixelHeight(e){return this.height=this.frameTranslate?ba:25,this.height}fillColor(e){return this.color?this.color:"dna"===this.sequenceType?da[e]||"gray":"rgb(0, 0, 150)"}getState(){const e={type:"sequence"};return this.order!==ua&&(e.order=this.order),this.reversed&&(e.revealed=!0),e}}class va{constructor(e,t){this.config=e,this.genome=t}async init(){this.fasta=await ca(this.config),this.chrNameMap=new Map;for(let e of this.fasta.chromosomeNames)this.chrNameMap.set(this.genome.getChromosomeName(e),e)}async getSequence(e,t,i){const n=this.chrNameMap.has(e)?this.chrNameMap.get(e):e;return this.fasta.getSequence(n,t,i)}}class ya{constructor(e,t,i,n){this.guid=_t.guid(),this.trackView=e,this.referenceFrame=i,this.browser=e.browser,this.$viewport=ft('
'),t.appendChild(this.$viewport.get(0)),e.track.height&&(this.$viewport.get(0).style.height=`${e.track.height}px`),e.track instanceof wa&&(this.alert=new Ft(this.$viewport.get(0))),this.contentTop=0,this.contentHeight=this.$viewport.height(),this.$viewport.width(n),this.initializationHelper()}initializationHelper(){}showMessage(e){this.messageDiv||(this.messageDiv=document.createElement("div"),this.messageDiv.className="igv-viewport-message",this.$viewport.append(ft(this.messageDiv))),this.messageDiv.textContent=e,this.messageDiv.style.display="inline-block"}hideMessage(e){this.messageDiv&&(this.messageDiv.style.display="none")}setTrackLabel(e){}startSpinner(){}stopSpinner(){}checkZoomIn(){return!0}shift(){}setTop(e){this.contentTop=e,this.$viewport.height()}async loadFeatures(){}clearCache(){}async repaint(){}draw(e,t,i){console.log("Viewport - draw(drawConfiguration, features, roiFeatures)")}checkContentHeight(e){let t=this.trackView.track;if(e=e||this.cachedFeatures,"FILL"===t.displayMode)this.setContentHeight(this.$viewport.height());else if("function"==typeof t.computePixelHeight&&e&&e.length>0){let i=t.computePixelHeight(e);i!==this.contentHeight&&this.setContentHeight(i)}}getContentHeight(){return this.contentHeight}setContentHeight(e){this.contentHeight=e}isLoading(){return!1}saveSVG(){}isVisible(){return this.$viewport.width()}setWidth(e){this.$viewport.width(e)}getWidth(){return this.$viewport.width()}getContentTop(){return this.contentTop}containsPosition(e,t){console.log("Viewport - containsPosition(chr, position)")}addMouseHandlers(){}removeMouseHandlers(){}dispose(){this.popover&&this.popover.dispose(),this.$viewport.get(0).remove();for(let e of Object.keys(this))this[e]=void 0}} +/*!! + * 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 _a(e,t){var i,n=Object.keys(t);for(i=0;i0){"path"===this.__currentElement.nodeName&&(this.__currentElementsToStyle||(this.__currentElementsToStyle={element:t,children:[]}),this.__currentElementsToStyle.children.push(this.__currentElement),this.__applyCurrentDefaultPath());var i=this.__createElement("g");t.appendChild(i),this.__currentElement=i}var n=this.__currentElement.getAttribute("transform");n?n+=" ":n="",n+=e,this.__currentElement.setAttribute("transform",n)}addTrackGroupWithTranslationAndClipRect(e,t,i,n,r,s){const o=e+"_clip_rect";let a=this.__createElement("clipPath",{id:o});this.__defs.appendChild(a),a.appendChild(this.__createElement("rect",{x:"0",y:s.toString(),width:n.toString(),height:r.toString()}));let l=this.__createElement("g");this.__rootGroup.appendChild(l),l.setAttribute("transform",_a("translate({x},{y})",{x:t,y:i})),l.setAttribute("id",e+"_group"),l.setAttribute("clip-path",_a("url(#{id})",{id:o})),this.__currentElement=l}scale(e,t){void 0===t&&(t=e),this.__addTransform(_a("scale({x},{y})",{x:e,y:t}))}rotate(e){var t=180*e/Math.PI;this.__addTransform(_a("rotate({angle},{cx},{cy})",{angle:t,cx:0,cy:0}))}translate(e,t){this.__addTransform(_a("translate({x},{y})",{x:e,y:t}))}transform(e,t,i,n,r,s){this.__addTransform(_a("matrix({a},{b},{c},{d},{e},{f})",{a:e,b:t,c:i,d:n,e:r,f:s}))}beginPath(){var e;this.__currentDefaultPath="",this.__currentPosition={},e=this.__createElement("path",{},!0),this.__closestGroupOrSvg().appendChild(e),this.__currentElement=e}__applyCurrentDefaultPath(){var e=this.__currentElement;"path"===e.nodeName?e.setAttribute("d",this.__currentDefaultPath):console.error("Attempted to apply path command to node",e.nodeName)}__addPathCommand(e){this.__currentDefaultPath+=" ",this.__currentDefaultPath+=e}moveTo(e,t){"path"!==this.__currentElement.nodeName&&this.beginPath(),this.__currentPosition={x:e,y:t},this.__addPathCommand(_a("M {x} {y}",{x:e,y:t}))}closePath(){this.__currentDefaultPath&&this.__addPathCommand("Z")}lineTo(e,t){this.__currentPosition={x:e,y:t},this.__currentDefaultPath&&this.__currentDefaultPath.indexOf("M")>-1?this.__addPathCommand(_a("L {x} {y}",{x:e,y:t})):this.__addPathCommand(_a("M {x} {y}",{x:e,y:t}))}bezierCurveTo(e,t,i,n,r,s){this.__currentPosition={x:r,y:s},this.__addPathCommand(_a("C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}",{cp1x:e,cp1y:t,cp2x:i,cp2y:n,x:r,y:s}))}quadraticCurveTo(e,t,i,n){this.__currentPosition={x:i,y:n},this.__addPathCommand(_a("Q {cpx} {cpy} {x} {y}",{cpx:e,cpy:t,x:i,y:n}))}arcTo(e,t,i,n,r){var s=this.__currentPosition&&this.__currentPosition.x,o=this.__currentPosition&&this.__currentPosition.y;if(void 0!==s&&void 0!==o){if(r<0)throw new Error("IndexSizeError: The radius provided ("+r+") is negative.");if(s===e&&o===t||e===i&&t===n||0===r)this.lineTo(e,t);else{var a=Ca([s-e,o-t]),l=Ca([i-e,n-t]);if(a[0]*l[1]!=a[1]*l[0]){var h=a[0]*l[0]+a[1]*l[1],c=Math.acos(Math.abs(h)),d=Ca([a[0]+l[0],a[1]+l[1]]),u=r/Math.sin(c/2),f=e+u*d[0],p=t+u*d[1],g=[-a[1],a[0]],m=[l[1],-l[0]],b=function(e){var t=e[0];return e[1]>=0?Math.acos(t):-Math.acos(t)},w=b(g),v=b(m);this.lineTo(f+g[0]*r,p+g[1]*r),this.arc(f,p,r,w,v)}else this.lineTo(e,t)}}}stroke(){"path"===this.__currentElement.nodeName&&this.__currentElement.setAttribute("paint-order","fill stroke markers"),this.__applyCurrentDefaultPath(),this.__applyStyleToCurrentElement("stroke")}fill(){"path"===this.__currentElement.nodeName&&this.__currentElement.setAttribute("paint-order","stroke fill markers"),this.__applyCurrentDefaultPath(),this.__applyStyleToCurrentElement("fill")}rect(e,t,i,n){"path"!==this.__currentElement.nodeName&&this.beginPath(),this.moveTo(e,t),this.lineTo(e+i,t),this.lineTo(e+i,t+n),this.lineTo(e,t+n),this.lineTo(e,t),this.closePath()}fillRect(e,t,i,n){n<0&&(t+=n,n=-n),i<0&&(e+=i,i=-i);var r,s,o,a={x:e,y:t,width:i,height:n};(!this.viewbox||(r=this.viewbox,s=a,r.xs.x&&r.ys.y))&&(o=this.__createElement("rect",a,!0),this.__closestGroupOrSvg().appendChild(o),this.__currentElement=o,this.__applyStyleToCurrentElement("fill"))}strokeRect(e,t,i,n){var r;r=this.__createElement("rect",{x:e,y:t,width:i,height:n},!0),this.__closestGroupOrSvg().appendChild(r),this.__currentElement=r,this.__applyStyleToCurrentElement("stroke")}strokeEllipse(e,t,i,n,r,s,o,a){this.__ellipse(e,t,i,n,r,s,o,a,"stroke")}fillEllipse(e,t,i,n,r,s,o,a){this.__ellipse(e,t,i,n,r,s,o,a,"fill")}__ellipse(e,t,i,n,r,s,o,a,l){const h={cx:e,cy:t,rx:i,ry:n},c=this.__createElement("ellipse",h,!0);this.__closestGroupOrSvg().appendChild(c),this.__currentElement=c,this.__applyStyleToCurrentElement(l)}__clearCanvas(){for(var e=this.__closestGroupOrSvg().getAttribute("transform"),t=this.__root.childNodes[1],i=t.childNodes,n=i.length-1;n>=0;n--)i[n]&&t.removeChild(i[n]);this.__currentElement=t,this.__groupStack=[],e&&this.__addTransform(e)}clearRect(e,t,i,n){if(0!==e||0!==t||i!==this.width||n!==this.height){var r,s=this.__closestGroupOrSvg();r=this.__createElement("rect",{x:e,y:t,width:i,height:n,fill:"#FFFFFF"},!0),s.appendChild(r)}else this.__clearCanvas()}createLinearGradient(e,t,i,n){var r=this.__createElement("linearGradient",{id:xa(this.__ids),x1:e+"px",x2:i+"px",y1:t+"px",y2:n+"px",gradientUnits:"userSpaceOnUse"},!1);return this.__defs.appendChild(r),new Ea(r,this)}createRadialGradient(e,t,i,n,r,s){var o=this.__createElement("radialGradient",{id:xa(this.__ids),cx:n+"px",cy:r+"px",r:s+"px",fx:e+"px",fy:t+"px",gradientUnits:"userSpaceOnUse"},!1);return this.__defs.appendChild(o),new Ea(o,this)}__parseFont(){var e=/^\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.exec(this.font),t={style:e[1]||"normal",size:e[4]||"10px",family:e[6]||"sans-serif",weight:e[3]||"normal",decoration:e[2]||"normal",href:null};return"underline"===this.__fontUnderline&&(t.decoration="underline"),this.__fontHref&&(t.href=this.__fontHref),t}__wrapTextLink(e,t){if(e.href){var i=this.__createElement("a");return i.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",e.href),i.appendChild(t),i}return t}__applyText(e,t,i,n){var r,s,o=this.__parseFont(),a=this.__closestGroupOrSvg(),l=this.__createElement("text",{"font-family":o.family,"font-size":o.size,"font-style":o.style,"font-weight":o.weight,"text-decoration":o.decoration,x:t,y:i,"text-anchor":(r=this.textAlign,s={left:"start",right:"end",center:"middle",start:"start",end:"end"},s[r]||s.start),"dominant-baseline":ka(this.textBaseline)},!0);l.appendChild(this.__document.createTextNode(e)),this.__currentElement=l,this.__applyStyleToCurrentElement(n),a.appendChild(this.__wrapTextLink(o,l))}fillText(e,t,i){this.__applyText(e,t,i,"fill")}strokeText(e,t,i){this.__applyText(e,t,i,"stroke")}measureText(e){return this.__ctx.font=this.font,this.__ctx.measureText(e)}arc(e,t,i,n,r,s){if(n!==r){(n%=2*Math.PI)===(r%=2*Math.PI)&&(r=(r+2*Math.PI-.001*(s?-1:1))%(2*Math.PI));var o=e+i*Math.cos(r),a=t+i*Math.sin(r),l=e+i*Math.cos(n),h=t+i*Math.sin(n),c=s?0:1,d=0,u=r-n;u<0&&(u+=2*Math.PI),d=s?u>Math.PI?0:1:u>Math.PI?1:0,this.lineTo(l,h),this.__addPathCommand(_a("A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}",{rx:i,ry:i,xAxisRotation:0,largeArcFlag:d,sweepFlag:c,endX:o,endY:a})),this.__currentPosition={x:o,y:a}}}clip(){var e=this.__closestGroupOrSvg(),t=this.__createElement("clipPath"),i=xa(this.__ids),n=this.__createElement("g");this.__applyCurrentDefaultPath(),e.removeChild(this.__currentElement),t.setAttribute("id",i),t.appendChild(this.__currentElement),this.__defs.appendChild(t),e.setAttribute("clip-path",_a("url(#{id})",{id:i})),e.appendChild(n),this.__currentElement=n}drawImage(){var e,t,i,n,r,s,o,a,l,h,c,d,u,f=Array.prototype.slice.call(arguments),p=f[0],g=0,m=0;if(3===f.length)e=f[1],t=f[2],i=r=p.width,n=s=p.height;else if(5===f.length)e=f[1],t=f[2],i=f[3],n=f[4],r=p.width,s=p.height;else{if(9!==f.length)throw new Error("Invalid number of arguments passed to drawImage: "+arguments.length);g=f[1],m=f[2],r=f[3],s=f[4],e=f[5],t=f[6],i=f[7],n=f[8]}o=this.__closestGroupOrSvg(),this.__currentElement;var b="translate("+e+", "+t+")";if(p instanceof Ta){if((a=p.getSvg().cloneNode(!0)).childNodes&&a.childNodes.length>1){for(l=a.childNodes[0];l.childNodes.length;)u=l.childNodes[0].getAttribute("id"),this.__ids[u]=u,this.__defs.appendChild(l.childNodes[0]);if(h=a.childNodes[1]){var w,v=h.getAttribute("transform");w=v?v+" "+b:b,h.setAttribute("transform",w),o.appendChild(h)}}}else"CANVAS"!==p.nodeName&&"IMG"!==p.nodeName||((c=this.__createElement("image")).setAttribute("width",i),c.setAttribute("height",n),c.setAttribute("preserveAspectRatio","none"),(g||m||r!==p.width||s!==p.height)&&((d=this.__document.createElement("canvas")).width=i,d.height=n,d.getContext("2d").drawImage(p,g,m,r,s,0,0,i,n),p=d),c.setAttribute("transform",b),c.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","CANVAS"===p.nodeName?p.toDataURL():p.getAttribute("src")),o.appendChild(c))}createPattern(e,t){let i,n=this.__document.__createElement("pattern"),r=xa(this.__ids);return n.setAttribute("id",r),n.setAttribute("width",e.width),n.setAttribute("height",e.height),"CANVAS"===e.nodeName||"IMG"===e.nodeName?(i=this.__createElement("image"),i.setAttribute("width",e.width),i.setAttribute("height",e.height),i.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","CANVAS"===e.nodeName?e.toDataURL():e.getAttribute("src")),n.appendChild(i),this.__defs.appendChild(n)):e instanceof Ta&&(n.appendChild(e.__root.childNodes[1]),this.__defs.appendChild(n)),new Ma(n,this)}setLineDash(e){e&&e.length>0?this.lineDash=e.join(","):this.lineDash=null}drawFocusRing(){}createImageData(){}getImageData(){}putImageData(){}globalCompositeOperation(){}setTransform(){}}const Ra=function(e,t,i,n){this.start=e,this.end=t,this.name=i,this.stain=0,"acen"===n?this.type="c":(this.type=n.charAt(1),"p"===this.type&&(this.stain=parseInt(n.substring(4))))};function La(){return"2.15.9"}const Ia=Yt,Ba={loadGenome:async function(e){const t=e.cytobandURL,i=e.aliasURL,n=await ca(e);let r;i&&(r=await function(e,t){return ro.loadString(e,To(t)).then((function(e){var t=Ia(e),i=[];return t.forEach((function(e){!e.startsWith("#")&&e.length>0&&i.push(e.split("\t"))})),i}))}(i,n.config));const s=new Fa(e,n,r);return t&&(s.cytobands=await async function(e,t,i){let n;if(Fo(e)){const t=Fs(e);n="";const i=t.length;for(let e=0;e1?function(e,t){let i;if(t.chromosomeOrder)Array.isArray(t.chromosomeOrder)?e.wgChromosomeNames=t.chromosomeOrder:e.wgChromosomeNames=t.chromosomeOrder.split(",").map((e=>e.trim())),i=e.wgChromosomeNames.map((t=>e.chromosomes[t])).filter((e=>void 0!==e));else{const t=Object.keys(e.chromosomes).map((t=>e.chromosomes[t].bpLength)).reduce(((e,t)=>Math.max(e,t)))/50;i=Object.values(e.chromosomes).filter((e=>e.bpLength>t));const n=i.filter((e=>r(e.name.replace("chr","")))),s=i.filter((e=>!r(e.name.replace("chr",""))));n.sort(((e,t)=>Number.parseInt(e.name.replace("chr",""))-Number.parseInt(t.name.replace("chr",""))));const o=n.map((e=>e.name));for(let e of s)o.push(e.name);e.wgChromosomeNames=o}const n=i.reduce(((e,t)=>e+t.bpLength),0);function r(e){return/^\d+$/.test(e)}e.chromosomes.all={name:"all",bpLength:n}}(this,e):this.wgChromosomeNames=t.chromosomeNames;var n={},r=this;n.all="all",this.chromosomeNames.forEach((function(e){var t=e.startsWith("chr")?e.substring(3):"chr"+e;n[t.toLowerCase()]=e,"chrM"===e&&(n.mt="chrM"),"MT"===e&&(n.chrm="MT"),n[e.toLowerCase()]=e})),i&&i.forEach((function(e){var t,i;for(i=0;ie.bpLength)&&(e=i)}return e}}getChromosomes(){return this.chromosomes}getGenomeCoordinate(e,t){var i=this.getCumulativeOffset(e);if(void 0!==i)return i+t}getChromosomeCoordinate(e){let t;void 0===this.cumulativeOffsets&&(this.cumulativeOffsets=computeCumulativeOffsets.call(this));let i=0;for(let n of this.wgChromosomeNames){const r=this.cumulativeOffsets[n];if(r>e){return{chr:t,position:e-i}}t=n,i=r}return{chr:this.wgChromosomeNames[this.wgChromosomeNames.length-1],position:0}}getCumulativeOffset(e){void 0===this.cumulativeOffsets&&(this.cumulativeOffsets=function(){let e=this,t={},i=0;for(let n of e.wgChromosomeNames){t[n]=Math.floor(i);i+=e.getChromosome(n).bpLength}return t}.call(this));const t=this.getChromosomeName(e);return this.cumulativeOffsets[t]}getGenomeLength(){let e=this;if(!this.bpLength){let t=0;e.wgChromosomeNames.forEach((function(i){let n=e.chromosomes[i];t+=n.bpLength})),this.bpLength=t}return this.bpLength}async getSequence(e,t,i){return e=this.getChromosomeName(e),this.sequence.getSequence(e,t,i)}addFeaturesToDB(e,t){const i=(e,t)=>{const i=this.featureDB.get(e);i&&(t=t.end-t.start>i.end-i.start?t:i),this.featureDB.set(e,t)};for(let n of e)if(n.name&&i(n.name.toUpperCase(),n),n.gene&&n.gene.name&&i(n.gene.name.toUpperCase(),n),t.searchableFields)for(let e of t.searchableFields){const t=n.getAttributeValue(e);t&&(t.indexOf(" ")>0?i(t.replaceAll(" ","+").toUpperCase(),n):i(t.toUpperCase(),n))}}}let Na,Oa=0,Pa=0;class Da extends ya{constructor(e,t,i,n){super(e,t,i,n)}initializationHelper(){this.$spinner=ft("
",{class:"igv-loading-spinner-container"}),this.$viewport.append(this.$spinner),this.$spinner.append(ft("
"));const e=this.trackView.track;"sequence"!==e.type&&(this.$zoomInNotice=this.createZoomInNotice(this.$viewport)),e.name&&"sequence"!==e.id&&(this.$trackLabel=ft('
'),this.$viewport.append(this.$trackLabel),this.setTrackLabel(e.name),!1===this.browser.trackLabelsVisible&&this.$trackLabel.hide()),this.stopSpinner(),this.addMouseHandlers()}setContentHeight(e){super.setContentHeight(e),this.featureCache&&(this.featureCache.redraw=!0)}setTrackLabel(e){this.$trackLabel.empty(),this.$trackLabel.html(e);const t=this.$trackLabel.text();this.$trackLabel.attr("title",t)}startSpinner(){this.$spinner.show()}stopSpinner(){this.$spinner&&this.$spinner.hide()}checkZoomIn(){if(this.trackView.track&&"sequence"===this.trackView.track.type&&this.referenceFrame.bpPerPixel>10)return ft(this.canvas).remove(),this.canvas=void 0,!1;if(!this.viewIsReady())return!1;if((()=>{if("all"!==this.referenceFrame.chr.toLowerCase()||this.trackView.track.supportsWholeGenome){const e=this.trackView.track.visibilityWindow;return void 0!==e&&e>0&&this.referenceFrame.bpPerPixel*this.$viewport.width()>e}return!0})()){if(this.canvas&&(ft(this.canvas).remove(),this.canvas=void 0),this.trackView.track.autoHeight){const e=this.trackView.minHeight||0;this.setContentHeight(e)}return this.$zoomInNotice&&this.$zoomInNotice.show(),!1}return this.$zoomInNotice&&this.$zoomInNotice.hide(),!0}shift(){const e=this.referenceFrame;if(this.canvas&&this.canvas._data&&this.canvas._data.referenceFrame.chr===this.referenceFrame.chr&&this.canvas._data.bpPerPixel===e.bpPerPixel){const t=Math.round((this.canvas._data.bpStart-e.start)/e.bpPerPixel);this.canvas.style.left=t+"px"}}setTop(e){if(super.setTop(e),this.canvas){const t=this.$viewport.height(),i=e+this.canvas._data.pixelTop,n=i+this.canvas._data.pixelHeight;(i>0||n0)for(let n of i.roiSets){const i=await n.getFeatures(t,r,s,e.bpPerPixel);o.push({track:n,features:i})}const a=i&&("wig"===i.type||"merged"===i.type);return this.featureCache=new za(t,r,s,e.bpPerPixel,n,o,a),this.loading=!1,this.hideMessage(),this.stopSpinner(),this.featureCache}}catch(e){this.trackView&&!0!==this.trackView.disposed&&(this.showMessage("Error loading track data"),this.browser.alert.present(e),console.error(e))}finally{this.loading=!1,this.stopSpinner()}}}repaintDimensions(){const e=Ba.isWholeGenomeView(this.referenceFrame.chr),t=e?this.$viewport.width():3*this.$viewport.width(),i=this.referenceFrame.bpPerPixel;return{bpStart:this.referenceFrame.start-(e?0:t/3*i),bpEnd:this.referenceFrame.end+(e?0:t/3*i),pixelWidth:t}}repaint(){if(void 0===this.featureCache)return;const{features:e,roiFeatures:t}=this.featureCache,{bpStart:i,bpEnd:n,pixelWidth:r}=this.repaintDimensions(),s=this.$viewport.height(),o=this.getContentHeight(),a=t?Math.max(o,s):o,l=Math.min(a,3*s);if(0===r||0===l)return void(this.canvas&&ft(this.canvas).remove());const h=Math.max(0,-this.contentTop-Math.floor(l/3)),c=this.referenceFrame.bpPerPixel,d=Math.round((i-this.referenceFrame.start)/c),u=(this.contentTop||0)+h,f=document.createElement("canvas");f.style.position="relative",f.style.display="block",f.style.width=r+"px",f.style.height=l+"px",f.style.left=d+"px",f.style.top=u+"px";const p="FILL"===this.trackView.track.displayMode||!1!==this.trackView.track.supportHiDPI?window.devicePixelRatio:1;f.width=p*r,f.height=p*l;const g=f.getContext("2d");g.scale(p,p),g.translate(0,-h);const m={context:g,pixelXOffset:d,pixelWidth:r,pixelHeight:l,pixelTop:h,bpStart:i,bpEnd:n,bpPerPixel:c,referenceFrame:this.referenceFrame,selection:this.selection,viewport:this,viewportWidth:this.$viewport.width()};this.draw(m,e,t),this.canvas&&ft(this.canvas).remove(),f._data=m,this.canvas=f,this.$viewport.append(ft(f))}refresh(){if(!this.canvas||!this.featureCache)return;const e=this.canvas._data;e.context.clearRect(0,0,this.canvas.width,this.canvas.height);const{features:t,roiFeatures:i}=this.featureCache;this.draw(e,t,i)}draw(e,t,i){if(t&&(e.features=t,this.trackView.track.draw(e)),i&&i.length>0)for(let t of i)e.features=t.features,t.track.draw(e)}containsPosition(e,t){return this.referenceFrame.chr===e&&t>=this.referenceFrame.start&&t<=this.referenceFrame.calculateEnd(this.getWidth())}isLoading(){return this.loading}savePNG(){if(!this.canvas)return;const e=this.canvas._data,t=e?e.pixelTop:0,i=window.devicePixelRatio,n=this.$viewport.width()*i,r=this.$viewport.height()*i,s=-ft(this.canvas).position().left*i,o=(-this.contentTop-t)*i,a=this.canvas.getContext("2d").getImageData(s,o,n,r),l=document.createElement("canvas"),h=l.getContext("2d");l.width=a.width,l.height=a.height,h.putImageData(a,0,0);ii((this.$trackLabel.text()?this.$trackLabel.text():"image")+".png",l.toDataURL("image/png"))}saveSVG(){let{width:e,height:t}=this.browser.columnContainer.getBoundingClientRect();const i=new Ta({width:e,height:8e3,backdropColor:"white",multiLocusGap:0,viewbox:{x:0,y:0,width:e,height:8e3}}),n={deltaX:32,deltaY:32};this.renderViewportToSVG(i,n),i.setHeight(t);const r=(this.trackView.track.name||this.trackView.track.id).replace(/\W/g,""),s=this.browser.referenceFrameList.indexOf(this.referenceFrame),o=i.getSerializedSvg(!0),a=URL.createObjectURL(new Blob([o],{type:"application/octet-stream"}));ii(`${`${r}_referenceFrame_${s}_guid_${_t.guid()}`}.svg`,a)}renderViewportToSVG(e,{deltaX:t,deltaY:i}){if(this.$zoomInNotice&&this.$zoomInNotice.is(":visible"))return;const{width:n,height:r}=this.$viewport.get(0).getBoundingClientRect(),s=`${(this.trackView.track.name||this.trackView.track.id).replace(/\W/g,"")}_referenceFrame_${this.browser.referenceFrameList.indexOf(this.referenceFrame)}_guid_${_t.guid()}`;this.drawSVGWithContext(e,n,r,s,t,i+this.contentTop,-this.contentTop)}renderSVGContext(e,{deltaX:t,deltaY:i}){if(this.renderViewportToSVG(e,{deltaX:t,deltaY:i}),(!this.$zoomInNotice||!this.$zoomInNotice.is(":visible"))&&this.$trackLabel&&!0===this.browser.trackLabelsVisible){const{x:n,y:r,width:s,height:o}=_t.relativeDOMBBox(this.$viewport.get(0),this.$trackLabel.get(0));this.renderTrackLabelSVG(e,t+n,i+r,s,o)}}renderTrackLabelSVG(e,t,i,n,r){const s=`${(this.trackView.track.name||this.trackView.track.id).replace(/\W/g,"")}_track_label_guid_${_t.guid()}`;e.saveWithTranslationAndClipRect(s,t,i,n,r,0),e.fillStyle="white",e.fillRect(0,0,n,r),e.font="12px Arial",e.fillStyle="rgb(68, 68, 68)";const{width:o}=e.measureText(this.$trackLabel.text()),a=.25*(n-o),l=.7*(r-12);e.fillText(this.$trackLabel.text(),a,r-l),e.strokeStyle="rgb(68, 68, 68)",e.strokeRect(0,0,n,r),e.restore()}drawSVGWithContext(e,t,i,n,r,s,o){e.saveWithTranslationAndClipRect(n,r,s,t,i,o);let{start:a,bpPerPixel:l}=this.referenceFrame;const h={context:e,viewport:this,referenceFrame:this.referenceFrame,top:o,pixelTop:o,pixelWidth:t,pixelHeight:i,bpStart:a,bpEnd:a+t*l,bpPerPixel:l,viewportWidth:t,selection:this.selection},c=this.featureCache?this.featureCache.features:void 0,d=this.featureCache?this.featureCache.roiFeatures:void 0;this.draw(h,c,d),e.restore()}get cachedFeatures(){return this.featureCache?this.featureCache.features:[]}clearCache(){this.featureCache=void 0,this.canvas&&(this.canvas._data=void 0)}async getFeatures(e,t,i,n,r){if(this.featureCache&&this.featureCache.containsRange(t,i,n,r))return this.featureCache.features;if("function"==typeof e.getFeatures){const s=await e.getFeatures(t,i,n,r,this);return this.checkContentHeight(s),s}}needsRepaint(){if(!this.canvas)return!0;const e=this.canvas._data;return!e||this.referenceFrame.starte.bpEnd||this.referenceFrame.chr!==e.referenceFrame.chr||this.referenceFrame.bpPerPixel!=e.bpPerPixel}needsReload(){if(!this.featureCache)return!0;const{chr:e,bpPerPixel:t}=this.referenceFrame,{bpStart:i,bpEnd:n}=this.repaintDimensions();return!this.featureCache.containsRange(e,i,n,t)}createZoomInNotice(e){const t=ft("
",{class:"igv-zoom-in-notice-container"});e.append(t);const i=ft("
");return t.append(i),i.text("Zoom in to see features"),t.hide(),t}viewIsReady(){return this.browser&&this.browser.referenceFrameList&&this.referenceFrame}addMouseHandlers(){const e=this.$viewport.get(0);this.addViewportContextMenuHandler(e);const t=e=>{this.enableClick=!0,this.browser.mouseDownOnViewport(e,this),_t.pageCoordinates(e)};e.addEventListener("mousedown",t),e.addEventListener("touchstart",t);const i=e=>{this.browser.dragObject||this.browser.isScrolling?(this.browser.cancelTrackPan(),this.enableClick=!1):(this.browser.cancelTrackPan(),this.browser.endTrackDrag())};e.addEventListener("mouseup",i),e.addEventListener("touchend",i),"function"==typeof this.trackView.track.hoverText&&e.addEventListener("mousemove",(e=>{if(0===e.buttons&&Date.now()-Pa>100){Pa=Date.now();const t=this.createClickState(e);if(t){const e=this.trackView.track.hoverText(t);e?this.$viewport[0].setAttribute("title",e):this.$viewport[0].removeAttribute("title")}}})),this.addViewportClickHandler(this.$viewport.get(0)),this.trackView.track.name&&"sequence"!==this.trackView.track.config.type&&this.addTrackLabelClickHandler(this.$trackLabel.get(0))}addViewportContextMenuHandler(e){e.addEventListener("contextmenu",(e=>{if(this.browser.dragObject)return!1;const t=this.createClickState(e);if(void 0===t)return!1;e.preventDefault();let i=[];if("function"==typeof this.trackView.track.contextMenuItemList){const e=this.trackView.track.contextMenuItemList(t);e&&(i=e)}i.length>0&&i.push({label:ft("
")}),i.push({label:"Save Image (PNG)",click:()=>this.savePNG()}),i.push({label:"Save Image (SVG)",click:()=>this.saveSVG()}),this.browser.menuPopup.presentTrackContextMenu(e,i)}))}addViewportClickHandler(e){e.addEventListener("click",(e=>{if(this.enableClick&&this.canvas){if(3===e.which||e.ctrlKey)return;if(ft(".igv-popover").hide(),this.browser.dragObject||this.browser.isScrolling)return;e.preventDefault();const t=_t.translateMouseCoordinates(e,this.$viewport.get(0)).x,i=_t.translateMouseCoordinates(e,this.canvas).x,n=this.referenceFrame,r=Math.floor(n.start+n.toBP(i)),s=Date.now();if(s-Oae));e[this.browser.referenceFrameList.indexOf(this.referenceFrame)]=t,i=e.join(" ")}this.browser.search(i)}else this.browser.zoomWithScaleFactor(.5,e,this.referenceFrame)}else e.shiftKey&&"function"==typeof this.trackView.track.shiftClick?this.trackView.track.shiftClick(r,e):"function"==typeof this.trackView.track.popupData&&(Na=setTimeout((()=>{const t=this.getPopupContent(e);t&&(this.popover&&this.popover.dispose(),this.popover=new Vt(this.browser.columnContainer),this.popover.presentContentWithEvent(e,t)),window.clearTimeout(Na),Na=void 0}),this.browser.constants.doubleClickDelay));Oa=s}}))}addTrackLabelClickHandler(e){e.addEventListener("click",(e=>{e.stopPropagation();const{track:t}=this.trackView;let i;"function"==typeof t.description?i=t.description():t.description&&(i=`
${t.description}
`),i&&(this.popover&&this.popover.dispose(),this.popover=new Vt(this.browser.columnContainer,t.name||""),this.popover.presentContentWithEvent(e,i))}))}createClickState(e){if(!this.canvas)return;const t=this.referenceFrame,i=_t.translateMouseCoordinates(e,this.$viewport.get(0)),n=_t.translateMouseCoordinates(e,this.canvas),r=t.start+t.toBP(i.x);return{event:e,viewport:this,referenceFrame:t,genomicLocation:r,y:i.y-this.contentTop,canvasX:n.x,canvasY:n.y}}getPopupContent(e){const t=this.createClickState(e);if(void 0===t)return;let i=this.trackView.track;const n=i.popupData(t),r=this.browser.fireEvent("trackclick",[i,n]);let s;return void 0===r||!0===r?n&&n.length>0&&(s=n.map((e=>{if(e.name){const t=`${e.name}   ${e.value}`;return`
${t}
`}return"
"===e?e:e.html?e.html:`
${e}
`})).join("")):"string"==typeof r&&(s=r),s}}class za{constructor(e,t,i,n,r,s,o){this.chr=e,this.bpStart=t,this.bpEnd=i,this.bpPerPixel=n,this.features=r,this.roiFeatures=s,this.multiresolution=o}containsRange(e,t,i,n){const r=this.multiresolution?this.bpPerPixel/n:1;return t>=this.bpStart&&i<=this.bpEnd&&e===this.chr&&r>.5&&r<2}overlapsRange(e,t,i){return this.chr===e&&i>=this.bpStart&&t<=this.bpEnd}}function Ha(e,t){if(!(e.length<6))return{chr:e[0],start:e[1],end:e[2],chr1:e[8],start1:Number.parseInt(e[9]),end1:Number.parseInt(e[10]),chr2:e[13],start2:Number.parseInt(e[14]),end2:Number.parseInt(e[15]),name:e[3],score:Number(e[4]),value:Number(e[5]),color:"."===e[7]?void 0:"0"===e[7]?"rgb(0,0,0)":e[7]};console.log("Skipping line: "+e.join(" "))}const Va=new Set(["transcript","primary_transcript","processed_transcript","mRNA","mrna","lnc_RNA","miRNA","ncRNA","rRNA","scRNA","snRNA","snoRNA","tRNA"]),Ua=new Set(["CDS","cds"]),qa=new Set(["start_codon","stop_codon"]),ja=new Set(["5UTR","3UTR","UTR","five_prime_UTR","three_prime_UTR","3'-UTR","5'-UTR"]),$a=new Set(["exon","coding-exon"]),Wa=new Set;for(let Qb of[Ua,qa,ja,$a])for(let Xb of Qb)Wa.add(Xb);function Ga(e){return e.includes("intron")}function Za(e){return Ua.has(e)||qa.has(e)}function Qa(e){return ja.has(e)}function Xa(e){return Va.has(e)||e.endsWith("RNA")||e.endsWith("transcript")}function Ya(e){return Wa.has(e)||e.endsWith("RNA")||Ga(e)}const Ka=new Set(["id","parent","name"]);class Ja{constructor(e){Object.assign(this,e)}popupData(e){const t=this.geneObject?this.geneObject.popupData():[];if(this.geneObject&&t.push("
"),this.name&&t.push({name:"Name",value:this.name}),t.push({name:"Type",value:this.type}),t.push({name:"Source",value:this.source}),void 0!==this.score&&t.push({name:"Score",value:this.score}),this.attributeString){const e=rl(this.attributeString,this.delim);for(let[i,n]of e)void 0!==n&&n.length>0&&!Ka.has(i.toLowerCase())&&t.push({name:i+":",value:n})}return t.push({name:"Location",value:`${this.chr}:${Xt(this.start+1)}-${Xt(this.end)}`}),t}getAttributeValue(e){if(this.hasOwnProperty(e))return this[e];if(this._attributeCache||(this._attributeCache=new Map),this._attributeCache.has(e))return this._attributeCache.get(e);{const t=rl(this.attributeString,this.delim);let i;for(let[n,r]of t)if(n===e){i=r;break}return this._attributeCache.set(e,i),i}}}class el extends Ja{constructor(e){super(e),this.exons=[],this.parts=[]}addExon(e){this.exons.push(e),this.start=Math.min(this.start,e.start),this.end=Math.max(this.end,e.end)}addPart(e){this.parts.push(e)}assembleParts(){if(0===this.parts.length)return;this.parts.sort((function(e,t){return e.start-t.start}));let e=this.parts[0].start,t=this.parts[0].end;for(let i=1;i=t&&i.start<=e)return i}addCDS(e){let t;const i=this.exons;for(let n of i)if(n.start<=e.start&&n.end>=e.end){t=n;break}t?(t.cdStart=t.cdStart?Math.min(e.start,t.cdStart):e.start,t.cdEnd=t.cdEnd?Math.max(e.end,t.cdEnd):e.end):console.error("No exon found spanning "+e.start+"-"+e.end),this.cdStart=this.cdStart?Math.min(e.start,this.cdStart):e.start,this.cdEnd=this.cdEnd?Math.max(e.end,this.cdEnd):e.end}addUTR(e){let t;const i=this.exons;for(let n=0;n=e.end){t=i[n];break}t?e.start===t.start&&e.end===t.end?t.utr=!0:(e.endt.start&&(t.cdEnd=e.start)):console.error("No exon found spanning "+cds.start+"-"+cds.end)}finish(){this.assembleParts();var e=this.cdStart,t=this.cdEnd;this.exons.sort((function(e,t){return e.start-t.start})),e&&this.exons.forEach((function(i){(i.endt)&&(i.utr=!0)}))}popupData(e){const t=super.popupData(e);for(let i of this.exons)if(!i.pseudo&&e>=i.start&&e");const n=i.popupData(e);for(let e of n)t.push(e)}for(let i of this.parts)if(e>=i.start&&e");const n=i.popupData(e);for(let e of n)t.push(e)}return t}}function tl(e,t){const i=t.format;if(e.length<9)return;const n="gff3"===i?"=":" ";return new Ja({source:al(e[1]),type:e[2],chr:e[0],start:parseInt(e[3])-1,end:parseInt(e[4]),score:"."===e[5]?void 0:Number(e[5]),strand:e[6],phase:"."===e[7]?0:parseInt(e[7]),attributeString:e[8],delim:n})}function il(e,t){const i=tl(e,t);if(!i)return;const n=rl(i.attributeString,i.delim);for(let[e,t]of n){const n=e.toLowerCase();"color"===n||"colour"===n?i.color=Hs.createColorString(t):"ID"===e?i.id=t:"Parent"===e&&(i.parent=t)}return i}function nl(e,t){const i=tl(e,t);if(!i)return;const n=rl(i.attributeString,i.delim);let r,s;switch(i.type){case"gene":r="gene_id";break;case"transcript":r="transcript_id",s="gene_id";break;default:s="transcript_id"}for(let[e,t]of n){const n=e.toLowerCase();"color"===n||"colour"===n?i.color=Hs.createColorString(t):e===r?i.id=t:e===s&&(i.parent=t)}return i}function rl(e,t,i=!1){var n=[];for(let r of e.split(";")){r=r.trim();const e=r.indexOf(t);if(e>0&&e2?parseInt(e[2]):n+1;if(isNaN(n)||isNaN(r))return new ll("Unparsable bed record.");const s=new _l({chr:i,start:n,end:r,score:1e3});try{if(e.length>3){if(e[3].indexOf(";")>0&&e[3].indexOf("=")>0){const i=rl(e[3],"=",!0);s.attributes={};for(let e of i)s.attributes[e[0]]=e[1],null!=t.nameField&&e[0]===t.nameField&&(s.name=e[1])}s.name||(s.name="."===e[3]?"":e[3])}if(e.length>4&&(s.score="."===e[4]?0:Number(e[4]),isNaN(s.score)))return s;if(e.length>5&&(s.strand=e[5],"."!==s.strand&&"+"!==s.strand&&"-"!==s.strand))return s;if(e.length>6&&(s.cdStart=parseInt(e[6]),isNaN(s.cdStart)))return s;if(e.length>7&&(s.cdEnd=parseInt(e[7]),isNaN(s.cdEnd)))return s;if(e.length>8&&"."!==e[8]&&"0"!==e[8]&&(s.color=Hs.createColorString(e[8])),e.length>11){const t=parseInt(e[9]);if(t>1e3)return s;const i=e[10].replace(/,$/,"").split(","),r=e[11].replace(/,$/,"").split(",");if(i.length!==r.length||t!==i.length)return s;const o=[];for(let e=0;e0&&(ml(o,s.cdStart,s.cdEnd),s.exons=o)}if(t){let i=t.thicknessColumn,n=t.colorColumn;n&&ni?n.utr=!0:(t>=r&&t<=e&&(n.cdStart=t),i>=r&&i<=e&&(n.cdEnd=i))}}function bl(e,t){var i,n,r,s,o,a,l;if(!(e.length<9))return i=e[0],n=parseInt(e[1]),r=parseInt(e[2]),o=e[3],a=Number(e[4]),s=e[5].trim(),l=Number(e[6]),0===a&&(a=l),{chr:i,start:n,end:r,name:o,score:a,strand:s,signal:l,pValue:Number(e[7]),qValue:Number(e[8])}}function wl(e,t){if(e.length<=3)return;const i={chr:e[0],start:parseInt(e[1]),end:parseInt(e[2]),value:Number(e[3])};if(t){let n=t.colorColumn;n&&n0)for(var u=l.split(","),f=0;fd&&(d=m),h.spanning_frag_coords.push({left:g,right:m})}return h.start=c,h.end=d,h}}function Cl(e,t){if(e.length<7)return null;const i={chr:e[0],start:parseInt(e[1])-1,end:parseInt(e[2]),"Strongest SNP-risk allele":e[3],"Disease/Phenotype":e[4],"P-value":e[5],"Odds ratio or beta":e[6]};return e.length>6&&(i.PUBMEDID=`${e[7]}`),i}function Sl(e,t){const i=t.customFormat;if(e.length1&&(t=e)}else{this.setDecoder(n.format);const e=i.split(this.delimiter||"\t");try{const i=Object.assign({columnNames:t},n);if(this.decode(e,i))break;e.length>1&&(t=e)}catch(i){e.length>1&&(t=e)}}}if(t){n.columnNames=t;for(let e=0;e0&&console.error(`Error parsing line '${s}': ${l.message}`)):l&&t.push(l)}return i===zo&&function(e){if(0==e.length)return;const t=e[0];if(void 0===t.score&&void 0!==t.name){for(let t of e)if(!("number"==typeof(i=t.name)?i-i==0:"string"==typeof i&&""!==i.trim()&&(Number.isFinite?Number.isFinite(+i):isFinite(+i)))&&"."!==t.name)return;for(let t of e)t.score=Number(t.name),delete t.name}var i;const n=e.filter((e=>e.chr1!==e.chr2));for(let t of n){const i=Object.assign({},t);i.dup=!0,e.push(i),t.chr=t.chr1,t.start=t.start1,t.end=t.end1,i.chr=i.chr2,i.start=i.start2,i.end=i.end2}}(t),t}setDecoder(e){switch(e){case"narrowpeak":case"broadpeak":case"regionpeak":case"peaks":this.decode=bl,this.delimiter=this.config.delimiter||/\s+/;break;case"bedgraph":this.decode=wl,this.delimiter=/\s+/;break;case"wig":this.decode=vl,this.delimiter=this.config.delimiter||/\s+/;break;case"gff3":case"gff":this.decode=il,this.delimiter="\t";break;case"gtf":this.decode=nl,this.delimiter="\t";break;case"fusionjuncspan":this.decode=kl,this.delimiter=this.config.delimiter||/\s+/;break;case"gtexgwas":this.skipRows=1,this.decode=Cl,this.delimiter="\t";break;case"refflat":this.decode=fl,this.delimiter=this.config.delimiter||/\s+/;break;case"genepred":this.decode=dl,this.delimiter=this.config.delimiter||/\s+/;break;case"genepredext":this.decode=ul,this.delimiter=this.config.delimiter||/\s+/;break;case"ensgene":this.decode=dl,this.header.shift=1,this.delimiter=this.config.delimiter||/\s+/;break;case"refgene":this.decode=ul,this.delimiter=this.config.delimiter||/\s+/,this.header.shift=1;break;case"bed":this.decode=hl,this.delimiter=this.config.delimiter||/\s+/;break;case"bedpe":case"hiccups":this.decode=zo,this.delimiter=this.config.delimiter||"\t";break;case"bedpe-domain":this.decode=Ho,this.headerLine=!0,this.delimiter=this.config.delimiter||"\t";break;case"bedpe-loop":this.decode=zo,this.delimiter=this.config.delimiter||"\t",this.header={colorColumn:7};break;case"interact":this.decode=Ha,this.delimiter=this.config.delimiter||/\s+/;break;case"snp":this.decode=yl,this.delimiter="\t";break;case"rmsk":this.decode=cl,this.delimiter="\t";break;case"gcnv":this.decode=Al,this.delimiter="\t";break;default:const t=qo(e);void 0!==t?(this.decode=Sl,this.header.customFormat=t,this.delimiter=t.delimiter||"\t"):(this.decode=hl,this.delimiter=this.config.delimiter||/\s+/)}}}function Ml(e){const t={},i=e.split(/(?:")([^"]+)(?:")|([^\s"]+)(?=\s+|$)/g);let n;const r=[];for(let e of i)e&&0!==e.trim().length&&(e.endsWith("=")?n=e:n?(r.push(n+e),n=void 0):r.push(e));for(let e of r){if(!e)return;var s=e.split("=",2);if(2===s.length){const e=s[0].trim(),i=s[1].trim();if(t.hasOwnProperty(e)){let n=t[e];Array.isArray(n)?n.push(i):t[e]=[n,i]}else t[e]=i}}return"interact"==t.type?t.format="interact":"gcnv"===t.type&&(t.format="gcnv"),t}function Tl(e){let t={},i=e.split(/\s+/);if(2===i.length){i[1].split(";").forEach((function(e){let i=e.split("=");"color"===i[0]?t.colorColumn=Number.parseInt(i[1])-1:"thickness"===i[0]&&(t.thicknessColumn=Number.parseInt(i[1])-1)}))}return t}function Rl(e){const t=e.split(/\s+/);return{format:"fixedStep",chrom:t[1].split("=")[1],start:parseInt(t[2].split("=")[1],10)-1,step:parseInt(t[3].split("=")[1],10),span:t.length>4?parseInt(t[4].split("=")[1],10):1,index:0}}function Ll(e){const t=e.split(/\s+/);return{format:"variableStep",chrom:t[1].split("=")[1],span:t.length>2?parseInt(t[2].split("=")[1],10):1}}class Il{static defaults={height:50,autoHeight:!1,visibilityWindow:void 0,color:void 0,altColor:void 0,supportHiDPI:!0};constructor(e,t){this.browser=t,this.init(e)}init(e){this.config=e,e.displayMode&&(e.displayMode=e.displayMode.toUpperCase());const t=Object.assign({},Il.defaults);if(this.constructor.defaults)for(let e of Object.keys(this.constructor.defaults))t[e]=this.constructor.defaults[e];for(let n of Object.keys(t))this[n]=e.hasOwnProperty(n)?e[n]:t[n],"color"!==n&&"altColor"!==n||(this[n]=Qt(i=this[n])&&i.indexOf(",")>0&&!i.startsWith("rgb(")&&!i.startsWith("rgba(")?`rgb(${i})`:i);var i;e.name||e.label?this.name=e.name||e.label:ti(e.url)?this.name=e.url.name:Qt(e.url)&&!e.url.startsWith("data:")&&(this.name=ei(e.url)),this.url=e.url,this.config.type&&(this.type=this.config.type),this.id=void 0===this.config.id?this.name:this.config.id,this.order=e.order,this.autoscaleGroup=e.autoscaleGroup,this.removable=void 0===e.removable||e.removable,this.minHeight=e.minHeight||Math.min(25,this.height),this.maxHeight=e.maxHeight||Math.max(1e3,this.height),e.onclick&&(this.onclick=e.onclick,e.onclick=void 0),e.description&&("function"==typeof e.description?this.description=e.description:this.description=()=>e.description),e.hoverTextFields?this.hoverText=Bl.bind(this):"function"==typeof this.config.hoverText&&(this.hoverText=this.config.hoverText)}get name(){return this._name}set name(e){this._name=e,this.trackView&&this.trackView.setTrackLabelName(e)}updateConfig(e){this.init(e)}clearCachedFeatures(){this.trackView&&this.trackView.clearCachedFeatures()}updateViews(){this.trackView&&this.trackView.updateViews()}getState(){const e={};for(let i of Object.keys(this.config))i.startsWith("_")||(void 0===(t=this.config[i])||"function"==typeof t||t instanceof File||t instanceof Promise)||(e[i]=this.config[i]);var t;for(let t of Object.keys(e)){if(t.startsWith("_"))continue;const i=this[t];i&&(Mo(i)||"boolean"==typeof i||"metadata"===t)&&(e[t]=i)}const i=Il.defaults;if(this.constructor.defaults)for(let e of Object.keys(this.constructor.defaults))i[e]=this.constructor.defaults[e];for(let t of Object.keys(i))void 0!==this[t]&&i[t]!==this[t]&&(e[t]=this[t]);return!this.autoscale&&this.dataRange&&(e.min=this.dataRange.min,e.max=this.dataRange.max),e}get supportsWholeGenome(){return!0===this.config.supportsWholeGenome}hasSamples(){return!1}getGenomeId(){return this.browser.genome?this.browser.genome.id:void 0}setTrackProperties(e){if(this.disposed)return;const t={};let i;for(let n of Object.keys(e))switch(n.toLowerCase()){case"usescore":t.useScore=1===e[n]||"1"===e[n]||"on"===e[n]||!0===e[n];break;case"visibility":switch(e[n]){case"2":case"3":case"pack":case"full":t.displayMode="EXPANDED";break;case"4":case"squish":t.displayMode="SQUISHED";break;case"1":case"dense":t.displayMode="COLLAPSED"}break;case"color":case"altcolor":t[n]=e[n].startsWith("rgb(")?e[n]:"rgb("+e[n]+")";break;case"featurevisiblitywindow":case"visibilitywindow":t.visibilityWindow=Number.parseInt(e[n]);break;case"maxheightpixels":i=e[n].split(":"),3===i.length&&(t.minHeight=Number.parseInt(i[2]),t.height=Number.parseInt(i[1]),t.maxHeight=Number.parseInt(i[0]));break;case"viewlimits":if(!this.config.autoscale){i=e[n].split(":");let r,s=0;1==i.length?r=Number(i[0]):2==i.length&&(s=Number(i[0]),r=Number(i[1])),t.autoscale=!1,t.dataRange={min:s,max:r},this.viewLimitMin=s,this.viewLimitMax=r}case"name":t[n]=e[n];break;case"url":t.infoURL=e[n];break;case"type":const r=e[n];Fl.has(r)?t[n]=Fl.get(r):t[n]=r;break;case"graphtype":t.graphType=e[n];break;default:t[n]=e[n]}for(let e of Object.keys(t))if(!this.config.hasOwnProperty(e)||"name"===e&&this.config._derivedName){let i=t[e];if("true"===i&&(i=!0),"false"===i&&(i=!1),this[e]=i,"height"===e&&this.trackView)try{const e=Number.parseInt(i);this.trackView.setTrackHeight(e)}catch(e){console.error(e)}}}clickedFeatures(e){const t=e.viewport.cachedFeatures;if(!t||!Array.isArray(t)||0===t.length)return[];const i=e.genomicLocation,n=e.referenceFrame.bpPerPixel>.2?3*e.referenceFrame.bpPerPixel:.2;return bo(t,i-n,i+n)}extractPopupData(e,t){const i=new Set(["row","color","chr","start","end","cdStart","cdEnd","strand","alpha"]),n=[];let r,s;for(let t in e)if(e.hasOwnProperty(t)&&!i.has(t)&&Mo(e[t])){let i=e[t];n.push({name:Jt(t),value:i}),"alleles"===t?r=e[t]:"alleleFreqs"===t&&(s=e[t])}if(r&&s){r.endsWith(",")&&(r=r.substr(0,r.length-1)),s.endsWith(",")&&(s=s.substr(0,s.length-1));let i=r.split(","),o=s.split(",");if(o.length>1){let r=[];for(let e=0;e=0;i--){let o=r[i].a;if(1===o.length){t||(t=this.getGenomeId());const i=Il.getCravatLink(e.chr,e.start+1,s,o,t);console.log(i),i&&(n.push("
"),n.push({html:i}),n.push("
"))}}}}if(e.attributes)for(let t of Object.keys(e.attributes))n.push({name:t,value:e.attributes[t]});let o=`${e.chr}:${Xt(e.start+1)}-${Xt(e.end)}`;return e.strand&&(o+=` (${e.strand})`),n.push({name:"Location",value:o}),n}description(){const e=(e,t)=>`
${e}: ${t}
`;let t='
';if(this.url?ti(this.url)?t+=e("Filename",this.url.name):t+=e("URL",this.url):t=this.name,this.config){if(this.config.metadata)for(let i of Object.keys(this.config.metadata)){t+=e(i,this.config.metadata[i])}for(let i of Object.keys(this.config)){if(i.startsWith("_"))continue;let n=i.substr(0,1);if(n!==n.toLowerCase()){const n=this.config[i];n&&Mo(n)&&(t+=e(i,n))}}}return t+="
",t}getColorForFeature(e){return"function"==typeof this.color?this.color(feature):this.color}dispose(){this.disposed=!0;for(let e of Object.keys(this))this[e]=void 0}static getCravatLink(e,t,i,n,r){if("hg38"===r||"GRCh38"===r){return`Cravat ${i}->${n}`}}}function Bl(e){if(!this.hoverTextFields)return;const t=this.clickedFeatures(e);if(t&&t.length>0){let e="";for(let i=0;i0&&(e+="\n"),e="";for(let t of this.hoverTextFields)e.length>0&&(e+="\n"),n.hasOwnProperty(t)?e+=n[t]:"function"==typeof n.getAttribute&&(e+=n.getAttribute(t))}return e}}const Fl=new Map([["wiggle_0","wig"],["bed","bed"],["bigBed","bigBed"],["bigWig","bigWig"]]);class Nl{constructor(e){switch(this.type=e||"seg",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}}async parseHeader(e){let t;for(;void 0!==(t=await e.nextLine());)if(!t.startsWith("#")){const e=t.split("\t");this.header={headings:e};break}return this.header}async parseFeatures(e){const t=[];let i;this.header||(this.header=await this.parseHeader(e)),"seg"===this.type&&(this.dataColumn=this.header.headings.length-1),this.header.headings.length>5&&(i=this.extractExtraColumns(this.header.headings));const n=this.header.headings[this.dataColumn];let r;for(;void 0!==(r=await e.nextLine());){const e=r.split("\t"),s="seg"===this.type?Number(e[this.dataColumn]):e[this.dataColumn];if(e.length>this.dataColumn){const r=new Ol({sample:e[this.sampleColumn],chr:e[this.chrColumn],start:parseInt(e[this.startColumn])-1,end:parseInt(e[this.endColumn]),value:s,valueColumnName:n});if(i){const t=this.extractExtraColumns(e);r.setAttributes({names:i,values:t})}t.push(r)}}return t}extractExtraColumns(e){const t=[];for(let i=0;i=0)return this.attributeValues[t]}}popupData(e,t){const i=new Set(["chr","start","end","sample","value","row","color","sampleKey","uniqueSampleKey","sampleId","chromosome","uniquePatientKey"]),n=this.chr+":"+Xt(this.start+1)+"-"+Xt(this.end),r=[{name:"Sample",value:this.sample},{name:"Location",value:n},{name:this.valueColumnName?Jt(this.valueColumnName):"Value",value:this.value}];if("mut"===e&&"hg38"===t){const e=this.extractCravatLink(t);e&&(r.push("
"),r.push({html:e}),r.push("
"))}if(this.attributeNames&&this.attributeNames.length>0)for(let e=0;e0)for(let n=0;n"===t||"<*>"===t||"."===t)return"NONVARIANT";{const e=t.split(",").map((function(e){return 1===i&&1===e.length?"SNP":""===e?"NONVARIANT":"OTHER"}));let n=e[0];for(let t of e)if(t!==n)return"MIXED";return n}}(e,t)),"NONVARIANT"===this.type&&(this.heterozygosity=0),this.info.END)this.start=this.pos-1,this.info.CHR2&&this.info.CHR2!==this.chr?this.end=this.start+1:this.end=Number.parseInt(this.info.END);else if("NONVARIANT"===this.type)this.start=this.pos-1,this.end=this.start+e.length;else{const i=t.split(",").filter((e=>e.length>0));this.alleles=[],this.start=void 0,this.end=void 0;for(let t of i)if(this.alleles.push(t),"SV"!==this.type&&Hl(t)){let i=t.length,n=e.length;const r=Math.min(i,n);let s=0;for(;s0&&n>0;){const r=s+i-1,o=s+n-1;if(t.charCodeAt(r)!==e.charCodeAt(o))break;i--,n--}for(;i>0&&n>0;){const r=s,o=s;if(t.charCodeAt(r)!==e.charCodeAt(o))break;s++,i--,n--}const o=this.pos+s-1,a=o+n;this.start=void 0===this.start?o:Math.min(this.start,o),this.end=void 0===this.end?a:Math.max(this.end,a)}void 0===this.start&&(this.start=this.pos-1,this.end=this.pos)}}popupData(e,t){const i=`${Xt(this.pos)}`,n=this.start===this.end?`${Xt(this.start)} | ${Xt(this.start+1)}`:`${Xt(this.start+1)}-${Xt(this.end)}`,r=[{name:"Chr",value:this.chr},{name:"Pos",value:i},{name:"Loc",value:n},{name:"ID",value:this.names?this.names:""},{name:"Ref",value:this.referenceBases},{name:"Alt",value:this.alternateBases.replace("<","<")},{name:"Qual",value:this.quality},{name:"Filter",value:this.filter}];if("SNP"===this.type){let e=this.referenceBases;if(1===e.length){let i=this.alternateBases.split(",");for(let n of i)if(1===n.length){let i=Il.getCravatLink(this.chr,this.pos,e,n,t);i&&(r.push("
"),r.push({html:i}))}}}if(this.hasOwnProperty("heterozygosity")&&r.push({name:"Heterozygosity",value:this.heterozygosity}),this.info){r.push({html:'
'});for(let e of Object.keys(this.info))r.push({name:e,value:Vl(decodeURIComponent(this.info[e]))})}return r}getInfo(e){return this.info?this.info[e]:void 0}isRefBlock(){return"NONVARIANT"===this.type}}const zl=new Set(["A","C","T","G"].map((e=>e.charCodeAt(0))));function Hl(e){for(let t=0;t");if(!(t>2&&o>0)){console.log("Malformed VCF header line: "+n);continue}const a=n.substring(2,t-1);i[a]||(i[a]={});const l=Kt(n.substring(t+1,o-1),",");for(let t of l){var r=t.split("=");r.length>1&&("ID"===r[0]?e=r[1]:s[r[0]]=r[1])}e&&(i[a][e]=s)}else if(n.startsWith("##contig")&&t){const e=n.indexOf("",e));const s=n.substring(e+4,r),o=t.getChromosomeName(s);i.chrAliasTable.set(o,s)}}else if(n.startsWith("#CHROM")){const e=n.split("\t");if(e.length>8){i.callSets=[];for(let t=9;t9){const t=ql(e[8].split(":"));n.calls={};for(let r=9;r0?n=e:n?(r.push(n+e),n=void 0):r.push(e));for(let e of r){if(!e)return;var s=e.split("=",2);if(2===s.length){const e=s[0].trim(),i=s[1].trim();t[e]=i}}return t}function Jl(e){let t={},i=e.split(/\s+/);if(2===i.length){i[1].split(";").forEach((function(e){let i=e.split("=");"color"===i[0]?t.colorColumn=Number.parseInt(i[1])-1:"thickness"===i[0]&&(t.thicknessColumn=Number.parseInt(i[1])-1)}))}return t}function eh(e,t){var i,n,r=e.columns;this.aed=e,this.allColumns=t,this.chr=null,this.start=null,this.end=null,this.score=1e3,this.strand=".",this.cdStart=null,this.cdEnd=null,this.name=null,this.color=null;for(let e=0;e0)for(let e=0;e=0;i--)t=256*t+e[i];else for(let i=0;i0&&(n+=String.fromCharCode(i));return n}getFixedLengthTrimmedString(e){var t,i,n="";for(t=0;t32&&(n+=String.fromCharCode(i));return n}getFloat(){var e=this.view.getFloat32(this.position,this.littleEndian);return this.position+=4,e}getDouble(){var e=this.view.getFloat64(this.position,this.littleEndian);return this.position+=8,e}skip(e){return this.position+=e,this.position}getVPointer(){var e=this.position,t=this.view.getUint8(e+1)<<8|this.view.getUint8(e),i=4294967296*(255&this.view.getUint8(e+6))+16777216*(255&this.view.getUint8(e+5))+65536*(255&this.view.getUint8(e+4))+256*(255&this.view.getUint8(e+3))+(255&this.view.getUint8(e+2));return this.position+=8,new nh(i,t)}}class nh{constructor(e,t){this.block=e,this.offset=t}isLessThan(e){return this.blocke.block||this.block===e.block&&this.offset>e.offset}isEqualTo(e){return this.block===e.block&&this.offset===e.offset}print(){return this.block+":"+this.offset}}function rh(e,t){if(0===e.length)return e;if(e.sort((function(e,t){const i=e.minv.block-t.minv.block;return 0!==i?i:e.minv.offset-t.minv.offset})),e.length<=1)return e;t&&(e=e.filter((e=>e.maxv.isGreaterThan(t))));const i=[];let n;for(let t of e)n&&sh(n,t)?t.maxv.isGreaterThan(n.maxv)&&(n.maxv=t.maxv):(i.push(t),n=t);return i}function sh(e,t){t.minv.block,e.maxv.block;return e.maxv.block-e.minv.block<5e6}class oh{constructor(e){this.tabix=!0}parse(e,t){const i=new ih(new DataView(e)),n=i.getInt();if(21582659!==n)throw 38359875===n?Error("CSI version 2 is not supported. Please enter an issue at https://github.com/igvteam/igv.js"):Error("Not a CSI index");this.indices=[],this.blockMin=Number.MAX_SAFE_INTEGER,this.lastBlockPosition=[],this.sequenceIndexMap={},this.minShift=i.getInt(),this.depth=i.getInt();const r=[];let s=0;if(i.getInt()>=28){i.getInt(),i.getInt(),i.getInt(),i.getInt(),i.getInt(),i.getInt();const e=i.getInt(),n=i.position+e;let s=0;for(;i.positiono)i.getInt(),i.getVPointer(),i.getVPointer(),i.getLong(),i.getLong();else{t[e]=[];const n=i.getInt();for(let r=0;rs&&(s=r.block),t[e].push([n,r]))}}}r>0&&(this.indices[e]={binIndex:t,loffset:n})}this.lastBlockPosition=s}get chromosomeNames(){return Object.keys(this.sequenceIndexMap)}chunksForRange(e,t,i){const n=this.indices[e];if(n){const e=this.reg2bins(t,i);if(0==e.length)return[];const r=[];for(let t of e)for(let e=t[0];e<=t[1];e++)if(n.binIndex[e]){const t=n.binIndex[e];for(let i of t){const t=i[0],n=i[1];r.push({minv:t,maxv:n,bin:e})}}return rh(r,n.loffset[e[0]])}return[]}reg2bins(e,t){(e-=1)<1&&(e=1),t>2**34&&(t=2**34),t-=1;let i=0,n=0,r=this.minShift+3*this.depth;const s=[];for(;i<=this.depth;r-=3,n+=1<<3*i,i+=1){const i=n+(e>>r),o=n+(t>>r);s.push([i,o])}return s}bin_limit(){return((1<<3*(this.depth+1))-1)/7}}class ah{constructor(){}async parse(e,t,i){const n=[];let r=Number.MAX_SAFE_INTEGER,s=0;const o=new ih(new DataView(e)),a=o.getInt(),l={};if(!(21578050===a||t&&21578324===a))throw new Error(indexURL+" is not a "+(t?"tabix":"bai")+" file");{const e=o.getInt();if(t){o.getInt(),o.getInt(),o.getInt(),o.getInt(),o.getInt(),o.getInt(),o.getInt();for(let t=0;ts&&(s=n.block),e[t].push([i,n]))}}}const l=o.getInt();for(let e=0;e0&&(n[t]={binIndex:e,linearIndex:i})}this.firstBlockPosition=r,this.lastBlockPosition=s,this.indices=n,this.sequenceIndexMap=l,this.tabix=t}}get chromosomeNames(){return Object.keys(this.sequenceIndexMap)}chunksForRange(e,t,i){const n=this.indices[e];if(n){const e=function(e,t){const i=[];t>=1<<29&&(t=1<<29);return--t,i.push([0,0]),i.push([1+(e>>26),1+(t>>26)]),i.push([9+(e>>23),9+(t>>23)]),i.push([73+(e>>20),73+(t>>20)]),i.push([585+(e>>17),585+(t>>17)]),i.push([4681+(e>>14),4681+(t>>14)]),i}(t,i),r=[];for(let t of e)for(let e=t[0];e<=t[1];e++)if(n.binIndex[e]){const t=n.binIndex[e];for(let e of t){const t=e[0],i=e[1];r.push({minv:t,maxv:i})}}const s=n.linearIndex.length;let o;const a=Math.min(t>>14,s-1),l=Math.min(i>>14,s-1);for(let e=a;e<=l;e++){const t=n.linearIndex[e];if(t){o=t;break}}return rh(r,o)}return[]}}class lh{constructor(){}async parse(e,t){let i=0;this.chrIndex={},this.lastBlockPosition=[];const n=new ih(new DataView(e));!function(e){e.getInt(),e.getInt();const t=e.getInt();if(e.getString(),e.getLong(),e.getLong(),e.getString(),e.getInt(),t>=3){let t=e.getInt();for(;t-- >0;)e.getString(),e.getString()}}(n);let r=n.getInt();for(;r-- >0;){let e=n.getString();t&&(e=t.getChromosomeName(e));const r=n.getInt(),s=n.getInt(),o=n.getInt();n.getInt(),n.getInt();let a=n.getLong();const l=[];for(let e=0;ei&&(i=e)}this.chrIndex[e]={chr:e,blocks:l,longestFeature:o,binWidth:r}}this.lastBlockPosition=i}get chromosomeNames(){return Object.keys(this.chrIndex)}chunksForRange(e,t,i){const n=this.chrIndex[e];if(n){const e=n.blocks,r=n.longestFeature,s=n.binWidth,o=Math.max(t-r,0),a=Math.floor(o/s);if(a>=e.length)return[];{const t=Math.min(Math.floor((i-1)/s),e.length-1),n=e[a].min,r=e[t].max;if(0===r-n)return[];return[{minv:{block:n,offset:0},maxv:{block:r,offset:0}}]}}}}async function hh(e,t,i){let n=await ro.loadArrayBuffer(e,To(t)),r=new DataView(n);if(31===r.getUint8(0)&&139===r.getUint8(1)){n=Ts(n).buffer,r=new DataView(n)}switch(r.getInt32(0,!0)){case 21578050:return async function(e,t){const i=new ah;return await i.parse(e,!1,t),i}(n,i);case 21578324:return async function(e,t){const i=new ah;return await i.parse(e,!0,t),i}(n,i);case 21582659:return async function(e,t){const i=new oh;return i.parse(e,t),i}(n,i);case 1480870228:return async function(e,t){const i=new lh;return i.parse(e,t),i}(n,i);case 38359875:throw Error("CSI version 2 is not supported.");default:throw Error(`Unrecognized index type: ${e}`)}}function ch(e){return"string"==typeof e||e instanceof String?new dh(e):new uh(e)}class dh{constructor(e){this.data=e,this.ptr=0}nextLine(){var e=this.ptr,t=this.data.indexOf("\n",e),i=this.data;if(t>0)return this.ptr=t+1,t>e&&"\r"===i.charAt(t-1)?i.substring(e,t-1):i.substring(e,t);var n=i.length;return this.ptr=n,e>=n?void 0:i.substring(e)}}class uh{constructor(e){this.data=e,this.length=this.data.length,this.ptr=0}nextLine(){var e,t;if(t="",!(this.ptr>=this.length)){for(var i=this.ptr;i{const t=ArrayBuffer.isView(e)?e:new Uint8Array(e);return 1+(t[17]<<8|t[16])};class gh{constructor(e){this.config=e,this.cacheBlocks=0!=e.cacheBlocks,this.cache=void 0}async getData(e,t){const i=e.block,n=t.block,r=0===t.offset,s=await this.getInflatedBlocks(i,n,r);if(1===s.length)return s[0];let o=0;for(const e of s)o+=e.byteLength;const a=new Uint8Array(o);let l=0;for(const e of s)a.set(e,l),l+=e.byteLength;return a}async getInflatedBlocks(e,t,i){if(this.cacheBlocks){const n=this.cache;if(n&&n.startBlock<=e&&(n.endBlock>=t||i&&n.nextEndBlock===t)){const i=e-n.startBlock,r=t-n.startBlock;return bh(n.buffer,i,r)}{let r;if(!n||n.startBlock>t||n.endBlock=n.endBlock)o=n.buffer;else{const i=Math.max(0,e-n.startBlock);let r;if(t>=n.endBlock)r=n.buffer.byteLength;else{const e=mh(n.buffer);for(let i=0;in.endBlock){const e=await this.loadBLockData(n.endBlock,t,{skipStart:!0,skipEnd:i});s.push(e)}r=function(e){if(1===e.length)return e[0];let t=0;for(const i of e)t+=i.byteLength;const i=new Uint8Array(t);let n=0;for(const t of e)i.set(new Uint8Array(t),n),n+=t.byteLength;return i.buffer}(s)}let s=t;if(i){const e=mh(r);t=e[e.length-1]}return this.cache={startBlock:e,endBlock:t,nextEndBlock:s,buffer:r},bh(r)}}return bh(await this.loadBLockData(e,t,{skipEnd:i}))}async loadBLockData(e,t,i){const n=this.config,r=i&&i.skipStart;let s=0;if(!(i&&i.skipEnd)){const e=To(n,{range:{start:t,size:26}}),i=await ro.loadArrayBuffer(n.url,e);s=ph(i)}if(r){const t=To(n,{range:{start:e,size:26}}),i=await ro.loadArrayBuffer(n.url,t);e+=ph(i)}const o=To(n,{range:{start:e,size:t+s-e}});return ro.loadArrayBuffer(n.url,o)}}function mh(e){const t=e.byteLength;let i=0;const n=[0];for(;ie.blocks)).map((e=>e.max)).reduce(((e,t)=>Math.min(e,t)),Number.MAX_SAFE_INTEGER),n=To(this.config,{bgz:e.tabix,range:{start:0,size:i}});t=ch(await ro.loadString(this.config.url,n))}return this.header=await this.parser.parseHeader(t),this.header}{const e=To(this.config),t=await ro.loadString(this.config.url,e);let i=ch(t);return this.header=await this.parser.parseHeader(i),i=ch(t),this.features=await this.parser.parseFeatures(i),this.header}}getParser(e){switch(e.format){case"vcf":return new Ul(e);case"seg":return new Nl("seg");case"mut":return new Nl("mut");case"maf":return new Nl("maf");case"gwas":return new $l(e);case"aed":return new Ql(e);default:return new El(e)}}async loadFeaturesNoIndex(){if(this.features){const e=this.features;return delete this.features,e}{const e=To(this.config),t=await ro.loadString(this.config.url,e);if(!this.header){const e=ch(t);this.header=await this.parser.parseHeader(e)}const i=ch(t);return await this.parser.parseFeatures(i)}}async loadFeaturesWithIndex(e,t,i){this.dataURI||this.header||await this.readHeader();const n=this.config,r=this.parser,s=this.index.tabix,o=s?this.index.sequenceIndexMap[e]:e;if(void 0===o)return[];const a=this.genome,l=this.index.chunksForRange(o,t,i);if(l&&0!==l.length){const o=[];for(let h of l){let l;if(s)l=await this._blockLoader.getData(h.minv,h.maxv);else{const e=To(n,{range:{start:h.minv.block,size:h.maxv.block-h.minv.block+1}});l=await ro.loadString(n.url,e)}const c=ch(h.minv.offset?l.slice(h.minv.offset):l);let d=await r.parseFeatures(c);d=d.filter((e=>void 0===e._f));let u=!1;for(let n=0;ni){o.push(r);break}r.end>=t&&r.start<=i&&(u||(u=!0,n>0&&o.push(d[n-1])),o.push(r))}}return o.sort((function(e,t){return e.start-t.start})),o}return[]}async getIndex(){return this.index?this.index:this.config.indexURL?(this.index=await this.loadIndex(),this.index):void 0}async loadIndex(){return hh(this.config.indexURL,this.config,this.genome)}async loadFeaturesFromDataURI(){if(this.features){const e=this.features;return delete this.features,e}{const e=Fs(this.dataURI);let t=ch(e);return this.header=await this.parser.parseHeader(t),this.header instanceof String&&this.header.startsWith("##gff-version 3")&&(this.format="gff3"),t=ch(e),this.features=await this.parser.parseFeatures(t),this.features}}}const vh=Qt;class yh{constructor(e){this.config=e}async readFeatures(e,t,i){let n;n="function"==typeof this.config.url?this.config.url({chr:e,start:t,end:i}):this.config.url.replace("$CHR",e).replace("$START",t).replace("$END",i);let r,s=Object.assign({},this.config);void 0!==this.config.body&&("function"==typeof this.config.body?s.body=this.config.body({chr:e,start:t,end:i}):s.body=this.config.body.replace("$CHR",e).replace("$START",t).replace("$END",i));const o=await ro.load(n,s);if(o&&(r="function"==typeof this.config.parser?this.config.parser(o):vh(o)?JSON.parse(o):o),this.config.mappings){let e=Object.keys(this.config.mappings);for(let t of r)for(let i of e)t[i]=t[this.config.mappings[i]]}return r}}const _h=function(e,t){this.config=e,this.genome=t,this.expandQueryInterval=!1};_h.prototype.readFeatures=function(e,t,i){const n=Math.max(0,Math.floor(t));let r=Math.ceil(i);if(this.genome){const t=this.genome.getChromosome(e);t&&r>t.bpLength&&(r=t.bpLength)}const s=this.config.url+"?db="+this.config.db+"&table="+this.config.tableName+"&chr="+e+"&start="+n+"&end="+r;return ro.loadJson(s,this.config).then((function(e){return e?(e.forEach((function(e){e.hasOwnProperty("exonStarts")&&e.hasOwnProperty("exonEnds")&&e.hasOwnProperty("exonCount")&&e.hasOwnProperty("cdsStart")&&e.hasOwnProperty("cdsEnd")&&function(e){var t,i,n,r,s,o;t=e.exonCount,i=e.exonStarts.split(","),n=e.exonEnds.split(","),r=[];for(var a=0;ao||e.cdsEnd=s&&e.cdsStart<=o&&(l.cdStart=e.cdsStart),e.cdsEnd>=s&&e.cdsEnd<=o&&(l.cdEnd=e.cdsEnd),r.push(l)}e.exons=r}(e)})),e):null}))};const xh=["Name","gene_name","gene","gene_id","alias","locus","name"];class kh{constructor(e){this.format=e.format,this.nameField=e.nameField,this.filterTypes=void 0===e.filterTypes?new Set(["chromosome"]):new Set(e.filterTypes)}combineFeatures(e,t){let i;const n=this.filterTypes;if(e=e.filter((e=>void 0===n||!n.has(e.type))),"gff3"===this.format){const t=this.combineFeaturesById(e);i=this.combineFeaturesByType(t)}else i=this.combineFeaturesByType(e);return i.sort((function(e,t){return e.start-t.start})),this.numberExons(i,t),this.nameFeatures(i),i}combineFeaturesById(e){const t=new Map,i=[];for(let n of e)if(Ya(n.type)||Xa(n.type)||!n.id)i.push(n);else{let e=t.get(n.chr);e||(e=new Map,t.set(n.chr,e));let i=e.get(n.id);i?i.push(n):e.set(n.id,[n])}for(let e of t.values())for(let t of e.values())if(t.length>1){const e=t[0];e.exons=[];for(let i of t)e.start=Math.min(e.start,i.start),e.end=Math.max(e.end,i.end),e.exons.push({start:i.start,end:i.end});i.push(e)}else i.push(t[0]);return i}combineFeaturesByType(e){const t=e.filter((e=>"gene"===e.type||e.type.endsWith("_gene"))),i=Object.create(null);for(let e of t)i[e.id]=e;const n=Object.create(null),r=[],s=new Set,o=this.filterTypes;e=e.filter((e=>void 0===o||!o.has(e.type)));for(let t of e)if("biological_region"===t.type&&console.log(),Xa(t.type)){const e=t.id;if(void 0!==e){const o=new el(t);n[e]=o,r.push(o),s.add(t);const a=i[t.parent];a&&(o.geneObject=a,s.add(a))}}for(let t of e)if(Ya(t.type)){const e=h(t);if(e)for(let i of e){let o=n[i];if(!o&&"gtf"===this.format){const e=Object.assign({},t);e.type="transcript",o=new el(e),n[i]=o,r.push(o)}if(void 0!==o){if(a=t.type,$a.has(a))if(e.length>1){const e=new Ja(t);o.addExon(e)}else o.addExon(t);else o.addPart(t);s.add(t)}}}var a;r.forEach((function(e){"function"==typeof e.finish&&e.finish()}));const l=e.filter((e=>!s.has(e)));for(let e of l)r.push(e);return r;function h(e){return e.parent&&""!==e.parent.trim()?e.parent.trim().split(","):null}}numberExons(e,t){for(let i of e)if(i.exons&&(!t||i.end<=t.end&&i.start>t.start))for(let e=0;e0){const e=1===this.variant_types.length?"Type":"Types";let i;for(let e of this.variant_types)i?i+=", "+e.display_name:i=e.display_name;t.push({name:e,value:i})}return t.push({name:"Actionability",value:this.actionabilityScore}),t.push({name:"Location",value:this.locationString}),t;function i(e,t){return""+e+""}}}class Ih{constructor(e,t){if(this.config=e,this.genome=t,this.format=e.format?e.format.toUpperCase():"BAM","BAM"!==this.format&&"VCF"!==this.format)throw Error(`htsget format ${e.format} is not supported`)}async readHeaderData(){const e=`${Bh(this.config)}?class=header&format=${this.format}`,t=await ro.loadJson(e,To(this.config));return await this.loadUrls(t.htsget.urls)}async readData(e,t,i){const n=`${Bh(this.config)}?format=${this.format}&referenceName=${e}&start=${Math.floor(t)}&end=${Math.ceil(i)}`,r=await ro.loadJson(n,To(this.config));return this.loadUrls(r.htsget.urls)}async loadUrls(e){const t=[];for(let i of e)if(i.url.startsWith("data:"))t.push(Promise.resolve(Fh(i.url)));else{const e=To(this.config||{});i.headers&&(e.headers=Object.assign(e.headers||{},i.headers)),t.push(ro.loadArrayBuffer(i.url,e))}return function(e){let t=0;for(let i of e)t+=i.byteLength;let i=0;const n=new Uint8Array(t);for(let t of e){const e=new Uint8Array(t);n.set(e,i),i+=e.length}return n}(await Promise.all(t))}static async inferFormat(e){try{const t=Bh(e),i=`${t}${t.includes("?")?"&":"?"}class=header`,n=await ro.loadJson(i,To(e));if(n.htsget){const t=n.htsget.format;if("BAM"!==t&&"VCF"!==t)throw Error(`htsget format ${t} is not supported`);e.format=t.toLowerCase(),e.sourceType="htsget",e.name||(e.name=await Io(e.url))}}catch(e){}}}function Bh(e){if(e.url&&e.endpoint&&e.id)return e.url+e.endpoint+e.id;if(e.endpoint&&e.id)return e.endpoint+e.id;if(e.url)return e.url.startsWith("htsget://")?e.url.replace("htsget://","https://"):e.url;throw Error("Must specify either 'url', or 'endpoint' and 'id")}function Fh(e){const t=e.split(","),i=t[0].split(":")[1];let n=t[1];n=i.indexOf("base64")>=0?atob(n):decodeURI(n);const r=new Uint8Array(n.length);for(var s=0;s=i[e]){n.row=e,i[e]=n.end;break}n.row=e,i[e]=n.end}}function Ph(e,t,i){const n=i||1e4,r=e=>{const i=Object.assign({},e);return i.chr="all",i.start=t.getGenomeCoordinate(e.chr,e.start),i.end=t.getGenomeCoordinate(e.chr,e.end),i._f=e,i.exons&&delete i.exons,i},s=new Set(t.wgChromosomeNames),o=[];let a=0;for(let i of t.wgChromosomeNames){if(Array.isArray(e)){const i={};for(let n of e){const e=t.getChromosomeName(n.chr);i.hasOwnProperty(e)||(i[e]=[]),i[e].push(n)}e=i}const l=e[i];if(l)for(let e of l){let i=t.getChromosomeName(e.chr);if(s.has(i))if(o.lengthi-t&&!1!==this.config.expandQuery){const e=Math.min(4.1*(i-t),n);s=Math.max(0,(t+i)/2-e),o=t+e}let a=await r.readFeatures(e,s,o);void 0===this.queryable&&(this.queryable=r.indexed);const l=this.queryable?new ra(e,s,o):void 0;if(a){if(!1===this.config.assembleGFF||"gtf"!==this.config.format&&"gff3"!==this.config.format&&"gff"!==this.config.format||(a=new kh(this.config).combineFeatures(a,l)),"wig"!==this.config.format&&"junctions"!==this.config.type){Dh(a,this.config.maxRows||Number.MAX_SAFE_INTEGER)}this.featureCache=new po(a,this.genome,l),this.searchable&&this.genome.addFeaturesToDB(a,this.config)}else this.featureCache=new po([],l)}}class Hh{constructor(e,t,i){this.path=e.url,this.bufferSize=i||512e3,this.range={start:-1,size:-1},this.config=e}async dataViewForRange(e,t,i=0){try{if(!(this.data&&this.range.start<=e.start&&this.range.start+this.range.size>=e.start+e.size)){let t;t=e.size?Math.max(this.bufferSize,e.size):this.bufferSize,this.contentLength&&(t=Math.min(t,this.contentLength-e.start));const i={start:e.start,size:t},n=await ro.loadArrayBuffer(this.path,To(this.config,{range:i}));this.data=n,this.range=i}const i=this.data.byteLength,n=e.start-this.range.start;return t?new Uint8Array(this.data,n,i-n):new DataView(this.data,n,i-n)}catch(n){if(0===i&&n.message&&n.message.startsWith("416")){try{return this.contentLength=await ro.getContentLength(this.path,To(this.config)),this.dataViewForRange(e,t,++i)}catch(e){console.error(e)}throw n}}}}let Vh=512e3;class Uh{constructor(e,t){this.path=e.url,this.format=e.format||"bigwig",this.genome=t,this.rpTreeCache={},this.config=e,this.loader=Fo(this.path)?new Jh(this.path):ro}async readWGFeatures(e,t){await this.loadHeader();const i=this.chromTree.idToChrom.length-1,n=this.chromTree.idToChrom[0],r=this.chromTree.idToChrom[i];return this.readFeatures(n,0,r,Number.MAX_VALUE,e,t)}async readFeatures(e,t,i,n,r,s){await this.loadHeader();const o=this.chromTree.chromToID[e],a=this.chromTree.chromToID[i];if(void 0===o||void 0===a)return[];let l,h;if("bigwig"===this.type){const e=await this.getZoomHeaders();let t=r?function(e,t){let i;for(let n=0;n0?Ss(r):r,h.call(this,new DataView(d.buffer),o,t,a,n,c,this.chromTree.idToChrom,s)}return c.sort((function(e,t){return e.start-t.start})),c}return[]}async getZoomHeaders(){return this.zoomLevelHeaders||await this.loadHeader(),this.zoomLevelHeaders}async loadHeader(){if(this.header)return this.header;{let e,t=await this.loader.loadArrayBuffer(this.path,To(this.config,{range:{start:0,size:64}}));this.littleEndian=!0;let i=new ih(new DataView(t)),n=i.getUInt();if(2291137574===n)this.type="bigwig";else if(2273964779===n)this.type="bigbed";else{this.littleEndian=!1,i.littleEndian=!1,i.position=0;let e=i.getUInt();654085990===e?this.type="bigwig":3958540679===e&&(this.type="bigbed")}e={bwVersion:i.getUShort(),nZoomLevels:i.getUShort(),chromTreeOffset:i.getLong(),fullDataOffset:i.getLong(),fullIndexOffset:i.getLong(),fieldCount:i.getUShort(),definedFieldCount:i.getUShort(),autoSqlOffset:i.getLong(),totalSummaryOffset:i.getLong(),uncompressBuffSize:i.getInt(),extensionOffset:i.getLong()};const r=64;let s={start:r,size:e.fullDataOffset-r+5};t=await this.loader.loadArrayBuffer(this.path,To(this.config,{range:s}));const o=e.nZoomLevels;i=new ih(new DataView(t)),this.zoomLevelHeaders=[],this.firstZoomDataOffset=Number.MAX_SAFE_INTEGER;for(let e=1;e<=o;e++){const t=o-e,n=new qh(t,i);this.firstZoomDataOffset=Math.min(n.dataOffset,this.firstZoomDataOffset),this.zoomLevelHeaders[t]=n}if(e.autoSqlOffset>0){i.position=e.autoSqlOffset-r;const t=i.getString();t&&(this.autoSql=function(e){let t;const i=[];let n=!1;const r=e.trim().split(/\s*[\r\n]+\s*/g);for(let e of r)if(e.startsWith("table"))t=e.split(/\s+/)[1].trim();else if(e.startsWith("("))n=!0;else if(e.startsWith(")"));else if(n&&e.length>0){const t=e.indexOf(";"),n=e.substr(0,t).split(/\s+/),r=e.substr(t+1).replace(/"/g,"").trim();i.push({type:n[0],name:n[1],description:r})}return{table:t,fields:i}}(t))}if(e.totalSummaryOffset>0&&(i.position=e.totalSummaryOffset-r,this.totalSummary=new Zh(i)),!(e.chromTreeOffset>0))throw"BigWig chromosome tree offset <= 0";return i.position=e.chromTreeOffset-r,this.chromTree=new Wh(i,r,this.genome),i.position=e.fullDataOffset-r,e.dataCount=i.getInt(),this.setDefaultVisibilityWindow(e),this.header=e,this.header}}async loadRPTree(e){let t=this.rpTreeCache[e];return t||(t=new jh(e,this.config,this.littleEndian,this.loader),await t.load(),this.rpTreeCache[e]=t,t)}async getType(){return await this.loadHeader(),this.type}async getTrackType(){return await this.loadHeader(),"bigwig"===this.type?"wig":this.autoSql&&"chromatinInteract"===this.autoSql.table?"interact":"annotation"}setDefaultVisibilityWindow(e){if("bigwig"===this.type)this.visibilityWindow=-1;else{let t=this.genome?this.genome.getGenomeLength():3088286401;this.visibilityWindow=e.dataCount<1e3?-1:t/e.dataCount*1e3}}}class qh{constructor(e,t){this.index=e,this.reductionLevel=t.getInt(),this.reserved=t.getInt(),this.dataOffset=t.getLong(),this.indexOffset=t.getLong()}}class jh{constructor(e,t,i,n){this.config=t,this.loader=n,this.fileOffset=e,this.path=t.url,this.littleEndian=i}async load(){const e=this.fileOffset+48,t=Fo(this.path)?this.loader:new Hh(this.config,Vh);return this.rootNode=await this.readNode(e,t),this}async readNode(e,t){let i=await t.dataViewForRange({start:e,size:4},!1),n=new ih(i,this.littleEndian);const r=1===n.getByte();n.getByte();const s=n.getUShort();let o={start:e+=4,size:s*(r?32:24)};i=await t.dataViewForRange(o,!1);const a=new Array(s);if(n=new ih(i),r){for(let e=0;e=0&&(n.position=r);let o=n.getByte();n.getByte();let a,l,d,u,f,p,g=n.getUShort();if(1===o)for(a=0;ae.startChrom||n===e.startChrom&&r>=e.startBase)&&(t0){this.mean=this.sumData/e,this.stddev=Math.sqrt(this.sumSquares/(e-1));let t=this.minVal<0?this.mean-2*this.stddev:0,i=this.maxVal>0?this.mean+2*this.stddev:0;this.defaultRange={min:t,max:i}}}function Xh(e,t,i,n,r,s,o){const a=new ih(e),l=a.getInt(),h=a.getInt();let c=h,d=a.getInt();const u=a.getInt(),f=a.getInt(),p=a.getByte();a.getByte();let g=a.getUShort();if(l>=t&&l<=n){let e=0;for(;g-- >0;){let g;switch(p){case 1:c=a.getInt(),d=a.getInt(),g=a.getFloat();break;case 2:c=a.getInt(),g=a.getFloat(),d=c+f;break;case 3:g=a.getFloat(),c=h+e*u,d=c+f,e++}if(!(ln||l===n&&c>=r)break;if(Number.isFinite(g)){const e=o[l];s.push({chr:e,start:c,end:d,value:g})}}}}}function Yh(){const e=function(e,t,i,n){if("biginteract"===n||i&&"chromatinInteract"===i.table||"interact"===i.table)return function(e,t){return e.chr1=t[5],e.start1=Number.parseInt(t[6]),e.end1=Number.parseInt(t[7]),e.chr2=t[10],e.start2=Number.parseInt(t[11]),e.end2=Number.parseInt(t[12]),e.name=t[0],e.score=Number(t[1]),e.value=Number(t[2]),e.color="."===t[4]?void 0:"0"===t[4]?"rgb(0,0,0)":t[4],e};{const n=e-3;return function(r,s){if(n>0&&(r.name=s[0]),n>1&&(r.score=Number(s[1])),n>2&&(r.strand=s[2]),n>3&&(r.cdStart=parseInt(s[3])),n>4&&(r.cdEnd=parseInt(s[4])),n>5&&"."!==s[5]&&"0"!==s[5]&&"-1"!==s[5]){const e=Hs.createColorString(s[5]);r.color=e.startsWith("rgb")?e:void 0}if(n>8){const e=parseInt(s[6]),t=s[7].split(","),i=s[8].split(","),n=[];for(let s=0;si?n.utr=!0:(t>=r&&t<=e&&(n.cdStart=t),i>=r&&i<=e&&(n.cdEnd=i))}}(n,r.cdStart,r.cdEnd),r.exons=n}if(i)for(let n=e;n=13;){const t=l.getInt(),h=a[t],c=l.getInt(),d=l.getInt(),u=l.getString();if(!(tr||t===r&&c>=s)break;if(d>0){const t={chr:h,start:c,end:d};o.push(t);const i=u.split("\t");e(t,i)}}}}}function Kh(e,t,i,n,r,s,o,a){const l=new ih(e);for(;l.remLength()>=32;){const e=l.getInt(),h=o[e],c=l.getInt(),d=l.getInt(),u=l.getInt(),f=l.getFloat(),p=l.getFloat(),g=l.getFloat();let m;switch(l.getFloat(),a){case"min":m=f;break;case"max":m=p;break;default:m=0===u?0:g/u}if(!(en||e===n&&c>=r)break;Number.isFinite(m)&&s.push({chr:h,start:c,end:d,value:m})}}}class Jh{constructor(e){this.data=Fs(e).buffer}loadArrayBuffer(e,t){const i=t.range;return i?this.data.slice(i.start,i.start+i.size):this.data}async dataViewForRange(e,t){const i=Math.min(this.data.byteLength-e.start,e.size);return t?new Uint8Array(this.data,e.start,i):new DataView(this.data,e.start,i)}}class ec{constructor(e,t){this.reader=new Uh(e,t),this.genome=t,this.format=e.format||"bigwig",this.wgValues={},this.queryable=!0}async getFeatures({chr:e,start:t,end:i,bpPerPixel:n,windowFunction:r}){const s="bigwig"===this.reader.type,o="all"===e.toLowerCase()?s?await this.getWGValues(r):[]:await this.reader.readFeatures(e,t,e,i,n,r);return s||Oh(o),o}async getHeader(){return this.reader.loadHeader()}getDefaultRange(){return void 0!==this.reader.totalSummary?this.reader.totalSummary.defaultRange:void 0}async defaultVisibilityWindow(){return this.reader.defaultVisibilityWindow}async getWGValues(e){const t=this.genome;if(this.wgValues[e])return this.wgValues[e];{const i=t.getGenomeLength()/1e3,n=await this.reader.readWGFeatures(i,e);let r=[];for(let e of n){const i=e.chr,n=t.getCumulativeOffset(i),s=Object.assign({},e);s.chr="all",s.start=n+e.start,s.end=n+e.end,r.push(s)}return this.wgValues[e]=r,r}}supportsWholeGenome(){return"bigwig"===this.reader.type}async trackType(){return this.reader.getTrackType()}}class tc{constructor(e,t){this.config=e,this.genome=t,this.path=e.url,this.groupCache={},this.datasetCache={}}async readHeader(){if(void 0!==this.magic)return this;let e=await ro.loadArrayBuffer(this.path,To(this.config,{range:{start:0,size:64e3}})),t=new ih(new DataView(e));if(this.magic=t.getInt(),this.version=t.getInt(),this.indexPos=t.getLong(),this.indexSize=t.getInt(),t.getInt(),this.version>=2){let e=t.getInt();for(this.windowFunctions=[];e-- >0;)this.windowFunctions.push(t.getString())}this.trackType=t.getString(),this.trackLine=t.getString();let i=t.getInt();for(this.trackNames=[];i-- >0;)this.trackNames.push(t.getString());this.genomeID=t.getString(),this.flags=t.getInt(),this.compressed=0!=(1&this.flags),e=await ro.loadArrayBuffer(this.path,To(this.config,{range:{start:this.indexPos,size:this.indexSize}})),t=new ih(new DataView(e)),this.datasetIndex={};let n=t.getInt();for(;n-- >0;){const e=t.getString(),i=t.getLong(),n=t.getInt();this.datasetIndex[e]={position:i,size:n}}for(this.groupIndex={},n=t.getInt();n-- >0;){const e=t.getString(),i=t.getLong(),n=t.getInt();this.groupIndex[e]={position:i,size:n}}return this}async readDataset(e,t,i){const n=e+"_"+t+"_"+i;if(this.datasetCache[n])return this.datasetCache[n];{await this.readHeader();const r=this.version<2?"":"/"+t,s="all"===e.toLowerCase()||void 0===i?"0":i.toString();let o;o="raw"===t?"/"+e+"/raw":"/"+e+"/z"+s+r;const a=this.datasetIndex[o];if(void 0===a)return;const l=await ro.loadArrayBuffer(this.path,To(this.config,{range:{start:a.position,size:a.size}}));if(!l)return;const h=new ih(new DataView(l));let c=h.getInt();const d={};for(;c-- >0;)d[h.getString()]=h.getString();const u=h.getString(),f=h.getFloat();let p=h.getInt();const g=[];for(;p-- >0;)g.push({position:h.getLong(),size:h.getInt()});const m={name:o,attributes:d,dataType:u,tileWidth:f,tiles:g};return this.datasetCache[n]=m,m}}async readRootGroup(){const e=this.genome,t=this.groupCache["/"];if(t)return t;{const t=await this.readGroup("/"),i=t.chromosomes,n=t.maxZoom;n&&(this.maxZoom=Number(n));const r=t.totalCount;r&&(t.totalCount=Number(r));const s={};return i&&i.split(",").forEach((function(t){const i=e.getChromosomeName(t);s[i]=t})),this.chrAliasTable=s,this.groupCache["/"]=t,t}}async readGroup(e){const t=this.groupCache[e];if(t)return t;{await this.readHeader();const t=this.groupIndex[e];if(void 0===t)return;const i=await ro.loadArrayBuffer(this.path,To(this.config,{range:{start:t.position,size:t.size}}));if(!i)return;const n=new ih(new DataView(i)),r={name:e};let s=n.getInt();for(;s-- >0;){const e=n.getString(),t=n.getString();r[e]=t}return this.groupCache[e]=r,r}}async readTiles(e,t){if(e.sort((function(e,t){return e.position-t.position})),0===(e=e.filter((function(e){return e.size>0}))).length)return Promise.resolve([]);const i=e[0],n=e[e.length-1],r=i.position,s=n.position+n.size-r,o=await ro.loadArrayBuffer(this.path,To(this.config,{range:{start:r,size:s}})),a=[];for(let i of e){const e=i.position-r,n=i.size;if(n>0){let i;if(this.compressed){i=Ss(o.slice(e,e+n)).buffer}else i=o.slice(e,e+n);const r=new ih(new DataView(i)),s=r.getString();let l;switch(s){case"fixedStep":l=ic(r,t);break;case"variableStep":l=nc(r,t);break;case"bed":case"bedWithName":l=rc(r,t,s);break;default:throw"Unknown tile type: "+s}a.push(l)}}return a}async readTile(e,t){let i=await ro.loadArrayBuffer(this.path,To(this.config,{range:{start:e.position,size:e.size}}));if(this.compressed){i=Ss(i).buffer}const n=new ih(new DataView(i)),r=n.getString();switch(r){case"fixedStep":return ic(n,t);case"variableStep":return nc(n,t);case"bed":case"bedWithName":return rc(n,t,r);default:throw"Unknown tile type: "+r}}}function ic(e,t){const i=e.getInt(),n=e.getInt(),r=e.getFloat(),s=[];let o=t;for(;o-- >0;){let t=i;const n=[];for(;t-- >0;)n.push(e.getFloat());s.push(n)}return{type:"fixedStep",start:n,span:r,data:s,nTracks:t,nPositions:i}}function nc(e,t){const i=e.getInt(),n=e.getFloat(),r=e.getInt(),s=[];let o=r;for(;o-- >0;)s.push(e.getInt());e.getInt();const a=[];let l=t;for(;l-- >0;){o=r;const t=[];for(;o-- >0;)t.push(e.getFloat());a.push(t)}return{type:"variableStep",tileStart:i,span:n,start:s,data:a,nTracks:t,nPositions:r}}function rc(e,t,i){const n=e.getInt();let r=n;const s=[];for(;r-- >0;)s.push(e.getInt());r=n;const o=[];for(;r-- >0;)o.push(e.getInt());e.getInt();const a=[];let l=t;for(;l-- >0;){let t=n;const i=[];for(;t-- >0;)i.push(e.getFloat());a.push(i)}if("bedWithName"===i){r=n;const t=[];for(;r-- >0;)t.push(e.getString())}return{type:i,start:s,end:o,data:a,nTracks:t,nPositions:n}}class sc{constructor(e,t){this.genome=t,this.windowFunction=e.windowFunction||"mean",this.reader=new tc(e,t),this.queryable=!0}async getFeatures({chr:e,start:t,end:i,bpPerPixel:n}){if("all"===e.toLowerCase()){const e=[],t=this.genome;if(this.genome.wgChromosomeNames)for(let i of t.wgChromosomeNames){const r=t.getChromosome(i).bpLength;n=r/1e3;const s=await this._getFeatures(i,0,r,n);if(s)for(let i of s){const n=Object.assign({},i);n.chr="all",n.start=t.getGenomeCoordinate(i.chr,i.start),n.end=t.getGenomeCoordinate(i.chr,i.end),n._f=i,e.push(n)}}return e}return this._getFeatures(e,t,i,n)}async _getFeatures(e,t,i,n){const r=new ra(e,t,i),s=this.genome;if(!this.rootGroup&&(this.rootGroup=await this.reader.readRootGroup(),!this.normalizationFactor)){const e=this.rootGroup.totalCount;e&&(this.normalizationFactor=1e6/e)}r.bpPerPixel=n;const o=function(e,t,i){var n=i.getChromosome(e).bpLength;return Math.ceil(Math.log(Math.max(0,n/(700*t)))/hc)}(e,n,s);let a=this.reader.chrAliasTable[e],l=this.reader.maxZoom;void 0===a&&(a=e),void 0===l&&(l=-1);const h=o>l?"raw":this.windowFunction,c=await this.reader.readDataset(a,h,o);if(null==c)return[];const d=c.tileWidth,u=Math.floor(t/d),f=Math.floor(i/d),p=await this.reader.readTiles(c.tiles.slice(u,f+1),1),g=[];for(let r of p)switch(r.type){case"bed":oc(r,e,t,i,n,g);break;case"variableStep":ac(r,e,t,i,n,g);break;case"fixedStep":lc(r,e,t,i,n,g);break;default:throw"Unknown tile type: "+r.type}return g.sort((function(e,t){return e.start-t.start})),g}get supportsWholeGenome(){return!0}}function oc(e,t,i,n,r,s){const o=e.nPositions,a=e.start,l=e.end,h=e.data[0];for(let e=0;en)break;s.push({chr:t,start:r,end:o,value:h[e]})}}}function ac(e,t,i,n,r,s){const o=e.nPositions,a=e.start,l=e.span,h=e.data[0];for(let e=0;en)break;s.push({chr:t,start:r,end:o,value:h[e]})}}}function lc(e,t,i,n,r,s){const o=e.nPositions;let a=e.start;const l=e.span,h=e.data[0];for(let e=0;en)break;r>=i&&(Number.isNaN(h[e])||s.push({chr:t,start:a,end:r,value:h[e]})),a=r}}var hc=Math.log(2);class cc{constructor(e,t){this.config=e,this.genome=t,this.queryable=!1,this.searchable=!1!==e.searchable,this.updateFeatures(e.features)}updateFeatures(e){Dh(e=function(e,t){if(t)for(let i of e)i.chr=t.getChromosomeName(i.chr);return e}(e,this.genome)),this.config.mappings&&function(e,t){let i=Object.keys(t);e.forEach((function(e){i.forEach((function(i){e[i]=e[t[i]]}))}))}(e,this.config.mappings),this.featureCache=new po(e,this.genome),(this.searchable||this.config.searchableFields)&&this.genome.addFeaturesToDB(e,this.config)}async getFeatures({chr:e,start:t,end:i,bpPerPixel:n,visibilityWindow:r}){const s=this.genome,o=s?s.getChromosomeName(e):e;return"all"===o.toLowerCase()?Ph(this.featureCache.getAllFeatures(),this.genome,this.maxWGCount):this.featureCache.queryFeatures(o,t,i)}getAllFeatures(){return this.featureCache.getAllFeatures()}supportsWholeGenome(){return!0}}const dc=new Set(["bigwig","bw","bigbed","bb","biginteract","biggenepred","bignarrowpeak"]);function uc(e,t){const i=e.format?e.format.toLowerCase():void 0;return e.features?new cc(e,t):dc.has(i)?new ec(e,t):"tdf"===i?new sc(e,t):new zh(e,t)}const fc=function(e,t){const{r:i,g:n,b:r}=wo[e];return`rgba(${i},${n},${r},${t})`}("nickel",2/16),pc=fc;class gc{constructor(e,t){if(this.url=e.url,e.name?this.name=e.name:e.url&&ti(e.url)?this.name=e.url.name:e.url&&Qt(e.url)&&!e.url.startsWith("data:")&&(this.name=ei(e.url)),this.isUserDefined=e.isUserDefined,e.features)this.featureSource=new bc(e.features,t);else{if(e.format)e.format=e.format.toLowerCase();else{const t=ei(e.url);e.format=jo(t)}this.featureSource=e.featureSource||uc(e,t)}!0===this.isUserDefined?(this.color=e.color||pc,this.headerColor="rgba(155,185,129)"):(this.color=e.color||fc,this.headerColor="rgb(190,190,190)")}async getFeatures(e,t,i){return this.featureSource.getFeatures({chr:e,start:t,end:i})}async getAllFeatures(){return"function"==typeof this.featureSource.getAllFeatures?await this.featureSource.getAllFeatures():{}}addFeature(e){this.isUserDefined?this.featureSource.addFeature(e):console.error("Attempt to add ROI to non user-defined set")}removeFeature(e){this.isUserDefined?this.featureSource.removeFeature(e):console.error("Attempt to remove ROI from non user-defined set")}toJSON(){if(this.url)return{name:this.name,color:this.color,url:this.url,isUserDefined:this.isUserDefined};{const e=this.featureSource.getAllFeatures(),t=[];for(let i of Object.keys(e))for(let n of e[i])t.push(n);return{name:this.name,color:this.color,features:t,isUserDefined:this.isUserDefined}}}dispose(){for(let e of Object.keys(this))this[e]=void 0}}function mc(e,t,i,n){let r=Math.round((e-i)/n);let s=Math.round((t-i)/n)-r;return s<3&&(s=3,r-=1),{x:r,width:s}}class bc{constructor(e,t){this.featureMap={},this.genome=t;for(let i of e){const e=t?t.getChromosomeName(i.chr):i.chr;let n=this.featureMap[e];n||(n=[],this.featureMap[e]=n),n.push(i)}for(let e of Object.keys(this.featureMap))this.featureMap[e].sort(((e,t)=>e.start-t.start))}getFeatures({chr:e,start:t,end:i}){if("all"===e.toLowerCase())return Ph(this.featureMap,this.genome);{const n=this.featureMap[e];return n?n.filter((e=>e.end>t&&e.starte.start-t.start))}removeFeature({chr:e,start:t,end:i}){if(this.featureMap[e]){const n=`${e}-${t}-${i}`;this.featureMap[e]=this.featureMap[e].filter((e=>n!==`${e.chr}-${e.start}-${e.end}`))}}}class wc{constructor(e,t,i,n){this.rulerViewport=e,this.rulerSweeper=_t.div({class:"igv-ruler-sweeper"}),t.appendChild(this.rulerSweeper),this.browser=i,this.referenceFrame=n,this.isMouseHandlers=void 0,this.addBrowserObserver()}addBrowserObserver(){this.boundObserverHandler=(()=>{this.referenceFrame&&(Ba.isWholeGenomeView(this.referenceFrame.chr)?this.removeMouseHandlers():this.addMouseHandlers())}).bind(this),this.browser.on("locuschange",this.boundObserverHandler)}removeBrowserObserver(){this.browser.off("locuschange",this.boundObserverHandler)}addMouseHandlers(){if(!0===this.isMouseHandlers)return;let e,t,i,n,r,s;this.boundContentMouseDownHandler=function(s){e=!0,t=!0;const{x:o}=_t.translateMouseCoordinates(s,this.rulerViewport.contentDiv);n=i=o,r=1,this.rulerSweeper.style.display="block",this.rulerSweeper.style.backgroundColor=!0===s.shiftKey?pc:"rgba(68, 134, 247, 0.25)",this.rulerSweeper.style.left=`${n}px`,this.rulerSweeper.style.width=`${r}px`}.bind(this),this.rulerViewport.contentDiv.addEventListener("mousedown",this.boundContentMouseDownHandler),this.boundDocumentMouseMoveHandler=function(o){let a;if(e&&t){const{x:e}=_t.translateMouseCoordinates(o,this.rulerViewport.contentDiv);a=Math.max(Math.min(e,this.rulerViewport.contentDiv.clientWidth),0),s=a-i,r=Math.abs(s),this.rulerSweeper.style.width=`${r}px`,s<0&&(n=i+s,this.rulerSweeper.style.left=`${n}px`)}}.bind(this),document.addEventListener("mousemove",this.boundDocumentMouseMoveHandler),this.boundDocumentMouseUpHandler=function(i){let s;if(!0===e&&!0===t&&(e=t=void 0,this.rulerSweeper.style.display="none",r>1)){s={start:Math.floor(this.referenceFrame.calculateEnd(n)),end:Math.floor(this.referenceFrame.calculateEnd(n+r))};!0===i.shiftKey?this.browser.roiManager.updateUserDefinedROISet(Object.assign({chr:this.referenceFrame.chr},s)):(Lo(this.browser.genome.getChromosome(this.referenceFrame.chr).bpLength,s,this.browser.minimumBases()),function(e,t,i){e.start=Math.round(t.start),e.end=Math.round(t.end),e.bpPerPixel=(e.end-e.start)/i}(this.referenceFrame,s,this.rulerViewport.contentDiv.clientWidth),this.browser.updateViews(this.referenceFrame))}}.bind(this),document.addEventListener("mouseup",this.boundDocumentMouseUpHandler),this.isMouseHandlers=!0}removeMouseHandlers(){this.rulerViewport.contentDiv.removeEventListener("mousedown",this.boundContentMouseDownHandler),document.removeEventListener("mousemove",this.boundDocumentMouseMoveHandler),document.removeEventListener("mouseup",this.boundDocumentMouseUpHandler),this.isMouseHandlers=!1}dispose(){this.removeBrowserObserver(),this.removeMouseHandlers(),this.rulerSweeper.remove()}}class vc{constructor(e){this.paired=!0,this.firstAlignment=e,this.chr=e.chr,this.readName=e.readName,e.startt.start?this.connectingEnd=e.start:this.connectingStart=e.start+e.lengthOnRef,this.start=Math.min(t.start,e.start),this.end=Math.max(t.start+t.lengthOnRef,e.start+e.lengthOnRef),this.lengthOnRef=this.end-this.start,this.scStart=Math.min(t.scStart,e.scStart);const i=Math.max(t.scStart+t.scLengthOnRef,e.scStart+e.scLengthOnRef);this.scLengthOnRef=i-this.scStart}containsLocation(e,t){const i=t?this.scStart:this.start,n=t?this.scLengthOnRef:this.lengthOnRef;return e>=i&&e<=i+n}alignmentContaining(e,t){return this.firstAlignment.containsLocation(e,t)?this.firstAlignment:this.secondAlignment&&this.secondAlignment.containsLocation(e,t)?this.secondAlignment:void 0}popupData(e){let t=this.firstAlignment.popupData(e);return this.secondAlignment&&(t.push("-------------------------------"),t=t.concat(this.secondAlignment.popupData(e))),t}isPaired(){return!0}isMateMapped(){return!0}isProperPair(){return this.firstAlignment.isProperPair()}get fragmentLength(){return Math.abs(this.firstAlignment.fragmentLength)}firstOfPairStrand(){return this.firstAlignment.isFirstOfPair()?this.firstAlignment.strand:this.secondAlignment&&this.secondAlignment.isFirstOfPair()?this.secondAlignment.strand:this.firstAlignment.mate.strand}hasTag(e){return this.firstAlignment.hasTag(e)||this.secondAlignment&&this.secondAlignment.hasTag(e)}}class yc{constructor(){this.alignments=[],this.score=void 0}findAlignment(e){const t=(e,t)=>t>=e.start&&t=r.start&&e0?c-=e:0===c&&(c=1)}return c}(r,n,e);case"STRAND":return r.strand?1:-1;case"START":return r.start;case"TAG":return r.tags()[i];case"READ_NAME":return r.readName;case"INSERT_SIZE":return-Math.abs(r.fragmentLength);case"GAP_SIZE":return-r.gapSizeAt(e);case"MATE_CHR":return r.mate;case"MQ":return void 0===r.mq?Number.MAX_VALUE:-r.mq;case"ALIGNED_READ_LENGTH":return-r.lengthOnRef;case "SOFT_CLIPPED":var regix=RegExp(/S/);return r.cigar.match(regix)=="S"?-1:1;default:return Number.MAX_VALUE}}}function _c(e){return e.isPaired()&&e.mate&&e.isMateMapped()&&e.chr===e.mate.chr&&(e.isFirstOfPair()||e.isSecondOfPair())&&!(e.isSecondary()||e.isSupplementary())}function xc(e){const t=[];for(let i of e)for(let e of i.alignments)e instanceof vc?(e.firstAlignment&&t.push(e.firstAlignment),e.secondAlignment&&t.push(e.secondAlignment)):t.push(e);return t}function kc(e,t,i,n){if(e){if(0===e.length)return[];{e.sort((function(e,t){return n?e.scStart-t.scStart:e.start-t.start}));const t=[];let i,r=0,s=0;const o=new Set,a=()=>{i=new yc,t.push(i),r=0,s=0,o.clear()};for(a();e.length>0;)if(s>=0&&s(n?e.scStart:e.start)>r),s)}else e=e.filter((e=>!o.has(e))),a();return t}}}function Cc(e,t,i){let n=i-1,r=e.length;for(;1+n>1);t(e[i])?r=i:n=i}return r}class Sc{constructor(e,t,i,{samplingWindowSize:n,samplingDepth:r,pairsSupported:s,alleleFreqThreshold:o}){this.chr=e,this.start=Math.floor(t),this.end=Math.ceil(i),this.length=i-t,this.alleleFreqThreshold=void 0===o?.2:o,this.coverageMap=new Ec(e,t,i,this.alleleFreqThreshold),this.alignments=[],this.downsampledIntervals=[],this.samplingWindowSize=void 0===n?100:n,this.samplingDepth=void 0===r?1e3:r,this.pairsSupported=void 0===s||s,this.paired=!1,this.pairsCache={},this.downsampledReads=new Set,this.currentBucket=new Ac(this.start,this.start+this.samplingWindowSize,this),this.filter=function(e){return e.isMapped()&&!e.isFailsVendorQualityCheck()}}push(e){!1!==this.filter(e)&&(this.coverageMap.incCounts(e),this.pairsSupported&&this.downsampledReads.has(e.readName)||(e.start>=this.currentBucket.end&&(this.finishBucket(),this.currentBucket=new Ac(e.start,e.start+this.samplingWindowSize,this)),this.currentBucket.addAlignment(e)))}forEach(e){this.alignments.forEach(e)}finish(){void 0!==this.currentBucket&&this.finishBucket(),this.alignments.sort((function(e,t){return e.start-t.start})),this.pairsCache=void 0,this.downsampledReads=void 0}contains(e,t,i){return this.chr===e&&this.start<=t&&this.end>=i}hasDownsampledIntervals(){return this.downsampledIntervals&&this.downsampledIntervals.length>0}finishBucket(){this.alignments=this.alignments.concat(this.currentBucket.alignments),this.currentBucket.downsampledCount>0&&this.downsampledIntervals.push(new Tc(this.currentBucket.start,this.currentBucket.end,this.currentBucket.downsampledCount)),this.paired=this.paired||this.currentBucket.paired}setViewAsPairs(e){let t;t=e?function(e){const t={},i=[];for(let n of e)for(let e of n.alignments)if(_c(e)){let n=t[e.readName];n?(n.setSecondAlignment(e),t[e.readName]=void 0):(n=new vc(e),t[e.readName]=n,i.push(n))}else i.push(e);return i}(this.packedAlignmentRows):xc(this.packedAlignmentRows),this.packedAlignmentRows=kc(t,this.start,this.end)}setShowSoftClips(e){const t=this.allAlignments();this.packedAlignmentRows=kc(t,this.start,this.end,e)}repack(e,t){const i=this.allAlignments();this.packedAlignmentRows=kc(i,this.start,this.end,t)}allAlignments(){const e=[];for(let t of this.packedAlignmentRows)for(let i of t.alignments)e.push(i);return e}getMax(e,t){return this.coverageMap.getMax(e,t)}sortRows(e){const t=[],i=[];for(let n of this.packedAlignmentRows){void 0!==n.findAlignment(e.position)?t.push(n):i.push(n)}t.sort(((t,i)=>{const n=e.direction,r=t.getSortValue(e,this),s=i.getSortValue(e,this);if(void 0===s&&void 0!==s)return 1;if(void 0!==r&&void 0===s)return-1;const o=r>s?1:rt)break;const s=this.coverage[r];n>=e&&s&&(i=Math.max(i,s.total))}return i}incCounts(e){var t=this;if(void 0===e.blocks?i(e):e.blocks.forEach((function(e){i(e)})),e.gaps)for(let i of e.gaps)if("D"===i.type){const e=i.start-t.bpStart;for(let n=e;n=t}}class Tc{constructor(e,t,i){this.start=e,this.end=t,this.counts=i}popupData(e){return[{name:"start",value:this.start+1},{name:"end",value:this.end},{name:"# downsampled:",value:this.counts}]}}class Rc{constructor(e){const t=e.split(",");this.chr=t[0],this.start=parseInt(t[1]),this.strand=t[2].charAt(0),this.mapQ=parseInt(t[4]),this.numMismatches=parseInt(t[5]),this.lenOnRef=Gc.computeLengthOnReference(t[3])}printString(){return this.chr+":"+Xt(this.start)+"-"+Xt(this.start+this.lenOnRef)+" ("+this.strand+") = "+Xt(this.lenOnRef)+"bp @MAPQ: "+this.mapQ+" NM: "+this.numMismatches}}function Lc(e){return e.split(";").filter((e=>e.length>0)).map((e=>new Rc(e)))}const Ic={c:1,C:1,s:2,S:2,i:4,I:4,f:4};class Bc{constructor(){this.hidden=!1}isMapped(){return 0==(4&this.flags)}isPaired(){return 0!=(1&this.flags)}isProperPair(){return 0!=(2&this.flags)}isFirstOfPair(){return 0!=(64&this.flags)}isSecondOfPair(){return 0!=(128&this.flags)}isSecondary(){return 0!=(256&this.flags)}isSupplementary(){return 0!=(2048&this.flags)}isFailsVendorQualityCheck(){return 0!=(512&this.flags)}isDuplicate(){return 0!=(1024&this.flags)}isMateMapped(){return 0==(8&this.flags)}isNegativeStrand(){return 0!=(16&this.flags)}isMateNegativeStrand(){return 0!=(32&this.flags)}hasTag(e){return(this.tagDict||Nc(this.tagBA)).hasOwnProperty(e)}tags(){return this.tagDict||(this.tagBA?(this.tagDict=Nc(this.tagBA),this.tagBA=void 0):this.tagDict={}),this.tagDict}containsLocation(e,t){const i=t?this.scStart:this.start,n=t?this.scLengthOnRef:this.lengthOnRef;return e>=i&&e<=i+n}popupData(e){const t=[];if(e=Math.floor(e),this.insertions){const n=this.seq;for(let r of this.insertions){var i=r.start;if(e===i||e===i-1)return t.push({name:"Insertion",value:n.substr(r.seqOffset,r.len)}),t.push({name:"Location",value:i}),t}}t.push({name:"Read Name",value:this.readName}),t.push("
"),t.push({name:"Alignment Start",value:Xt(1+this.start),borderTop:!0}),t.push({name:"Read Strand",value:!0===this.strand?"(+)":"(-)",borderTop:!0});let n=this.cigar;if(n&&n.length>50){const e=25;n=`${n.substring(0,e-2)} ... ${n.substring(n.length-e+2)}`}t.push({name:"Cigar",value:n}),t.push({name:"Mapping Quality",value:this.mq}),t.push({name:"Secondary",value:o(this.isSecondary())}),t.push({name:"Supplementary",value:o(this.isSupplementary())}),t.push({name:"Duplicate",value:o(this.isDuplicate())}),t.push({name:"Failed QC",value:o(this.isFailsVendorQualityCheck())}),this.isPaired()&&(t.push("
"),t.push({name:"First in Pair",value:!this.isSecondOfPair(),borderTop:!0}),t.push({name:"Mate is Mapped",value:o(this.isMateMapped())}),this.pairOrientation&&t.push({name:"Pair Orientation",value:this.pairOrientation}),this.isMateMapped()&&(t.push({name:"Mate Chromosome",value:this.mate.chr}),t.push({name:"Mate Start",value:this.mate.position+1}),t.push({name:"Mate Strand",value:!0===this.mate.strand?"(+)":"(-)"}),t.push({name:"Insert Size",value:this.fragmentLength})));const r=this.tags();if(r.hasOwnProperty("SA")){t.push("
"),t.push({name:"Supplementary Alignments",value:""});const e=Lc(r.SA);if(e){t.push("
    ");for(let i of e)t.push(`
  • ${i.printString()}
  • `);t.push("
")}}const s=new Set(["SA","MD"]);t.push("
");for(let e in r)s.has(e)||t.push({name:e,value:r[e]});return t.push({name:"Hidden Tags",value:"SA, MD"}),t.push("
"),t.push({name:"Genomic Location: ",value:Xt(1+e)}),t.push({name:"Read Base:",value:this.readBaseAt(e)}),t.push({name:"Base Quality:",value:this.readBaseQualityAt(e)}),t;function o(e){return e?"Yes":"No"}}readBaseAt(e){const t=Fc(this.blocks,e);if(t){if("*"===this.seq)return"*";{const i=t.seqIndexAt(e);return this.seq[i]}}}readBaseQualityAt(e){const t=Fc(this.blocks,e);if(t){if("*"===this.qual)return 30;{const i=t.seqIndexAt(e);return i>=0&&this.qual&&i=t.start&&e=n.start&&t>1)+n;a+4=t)return!1;if("B"!==String.fromCharCode(e[a+2])||"I"!==String.fromCharCode(e[a+3]))return!1;var h=Qc(e,a+4),c=a+8;if(c+4*h>t)return!1;var d="",u=0;s.length=0,a=c;for(var f=0;f>4,m=jc[15&p];"M"!==m&&"EQ"!==m&&"X"!==m&&"D"!==m&&"N"!==m&&"="!==m||(u+=g),d=d+g+m,s.push({len:g,ltr:m})}return r.cigar=d,r.lengthOnRef=u,!0},decodeBamRecords:function(e,t,i,n,r,s,o,a){for(;te.length)return;if(p<0){t=u;continue}if(void 0!==r&&(p>r||g>o))return!0;if(void 0!==r&&p>8,w=255&m,v=Qc(e,t+16),y=(4294901760&v)>>16,_=65535&v,x=Qc(e,t+20),k=Qc(e,t+24),C=Qc(e,t+28),S=Qc(e,t+32);let A=[];for(let i=0;i>4,c=jc[15&l];"M"!==c&&"EQ"!==c&&"X"!==c&&"D"!==c&&"N"!==c&&"="!==c||(E+=h),M=M+h+c,T+=4,L={len:h,ltr:c},R.push(L)}if(f.chr=n[p],f.start=g,f.flags=y,f.strand=!(16&y),f.readName=A,f.cigar=M,f.lengthOnRef=E,f.fragmentLength=S,f.mq=b,Gc.bam_tag2cigar(e,u,T,x,f,R),f.end=f.start+f.lengthOnRef,f.end>1;for(let t=0;t>4]),I.push(qc[15&d])}I=I.slice(0,x).join(""),T+=B;const F=[];for(let t=0;t=0&&(f.mate={chr:n[k],position:C,strand:!(32&y)}),f.seq=I,f.qual=F,f.tagBA=new Uint8Array(e.buffer.slice(T,u)),this.setPairOrientation(f),(void 0===a||a.pass(f))&&(Zc(f,R),i.push(f)),t=u}},decodeSamRecords:function(e,t,i,n,r,s){var o,a,l,h,c,d,u,f,p,g;for(h=(o=Yt(e)).length,!1,a=0;ar)break;if(f=0,(g=Xc(p.cigar)).forEach((function(e){var t=e.len,i=e.ltr;"M"!==i&&"EQ"!==i&&"X"!==i&&"D"!==i&&"N"!==i&&"="!==i||(f+=t)})),p.lengthOnRef=f,!(p.start+fWc&&(console.log("Warning: attempt to set sampling depth > maximum value of "+Wc),e.samplingDepth=Wc),t.viewAsPairs?e.pairsSupported=!0:e.pairsSupported=void 0===t.pairsSupported||t.pairsSupported},setPairOrientation:function(e){if(e.isMapped()&&e.mate&&e.isMateMapped()&&e.mate.chr===e.chr){var t=e.strand?"F":"R",i=e.mate,n=i.strand?"F":"R",r=" ",s=" ";e.isFirstOfPair()?(r="1",s="2"):e.isSecondOfPair()&&(r="2",s="1");var o=[],a=e.fragmentLength,l=e.end-e.start;if(0===a)a=(e.start0?(o[0]=t,o[1]=r,o[2]=n,o[3]=s):(o[2]=t,o[3]=r,o[0]=n,o[1]=s),e.pairOrientation=o.join("")}},computeLengthOnReference:function(e){let t=0,i="";for(let n=0;n47&&r<58)i+=e.charAt(n);else{switch(r){case 78:case 68:case 77:case 61:case 88:t+=parseInt(i.toString())}i=""}}return t}};function Zc(e,t){const i=[];let n,r,s=0,o=e.start;e.scStart=e.start,e.scLengthOnRef=e.lengthOnRef;for(let a of t){let t;switch(a.ltr){case"H":case"P":break;case"S":t=o,e.scLengthOnRef+=a.len,0===i.length&&(e.scStart-=a.len,t-=a.len),i.push(new Vc({start:t,seqOffset:s,len:a.len,type:"S"})),s+=a.len;break;case"N":case"D":void 0===r&&(r=[]),r.push({start:o,len:a.len,type:a.ltr}),o+=a.len;break;case"I":void 0===n&&(n=[]),n.push(new Vc({start:o,len:a.len,seqOffset:s,type:"I"})),s+=a.len;break;case"M":case"EQ":case"=":case"X":i.push(new Vc({start:o,seqOffset:s,len:a.len,type:"M"})),s+=a.len,o+=a.len;break;default:console.log("Error processing cigar element: "+a.len+a.ltr)}}e.blocks=i,e.insertions=n,e.gaps=r}function Qc(e,t){return e[t+3]<<24|e[t+2]<<16|e[t+1]<<8|e[t]}function Xc(e){var t,i,n,r,s,o,a,l,h;for(t=[],i=[],s=null,r=e.length,n=0;n=48&&h<=57?i.push(o):(a=o,l=Number.parseInt(i.join("")),i=[],null!==s&&s.ltr===a?s.len+=l:(s={len:l,ltr:a},t.push(s)));return t}function Yc(e){var t={};return e.forEach((function(e){var i=e.split(":");t[i[0]]=i[2]})),t}class Kc{constructor(e,t){this.config=e,this.genome=t,this.bamPath=e.url,this.isDataUri=Fo(e.url),Gc.setReaderDefaults(this,e)}async readAlignments(e,t,i){if(this.alignmentCache){const n=this.header,r=n.chrAliasTable.hasOwnProperty(e)?n.chrAliasTable[e]:e,s=this.alignmentCache.queryFeatures(r,t,i),o=new Sc(e,t,i,this.config);for(let e of s)o.push(e);return o.finish(),o}if(this.isDataUri){const n=Ts(function(e){const t=e.split(","),i=t[0].split(":")[1];let n=t[1];n=i.indexOf("base64")>=0?atob(n):decodeURI(n);const r=new Uint8Array(n.length);for(var s=0;s{var e={368:function(e,t,i){var n=this&&this.__awaiter||function(e,t,i,n){return new(i||(i=Promise))((function(r,s){function o(e){try{l(n.next(e))}catch(e){s(e)}}function a(e){try{l(n.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}l((n=n.apply(e,t||[])).next())}))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const s=r(i(4105)),o=r(i(1269)),a=i(597),l=i(3427),h=i(8577);function c(e,t){if(t.some((e=>void 0===e)))throw new h.CramMalformedError("invalid .crai index file");const[i,n,r,s,o,a]=t;e[i]||(e[i]=[]),e[i].push({start:n,span:r,containerStart:s,sliceStart:o,sliceBytes:a})}t.default=class{constructor(e){this.filehandle=(0,l.open)(e.url,e.path,e.filehandle),this._parseCache=new s.default({cache:new o.default({maxSize:1}),fill:(e,t)=>this.parseIndex()})}parseIndex(){const e={};return this.filehandle.readFile().then((e=>31===e[0]&&139===e[1]?(0,a.unzip)(e):e)).then((t=>{if(t.length>4&&21578050===t.readUInt32LE(0))throw new h.CramMalformedError("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");let i=[],n="";for(let r=0;r=48&&s<=57||!n&&45===s)n+=String.fromCharCode(s);else if(9===s)i.push(Number.parseInt(n,10)),n="";else if(10===s)i.push(Number.parseInt(n,10)),n="",c(e,i),i=[];else if(13!==s&&32!==s)throw new h.CramMalformedError("invalid .crai index file")}return n&&i.push(Number.parseInt(n,10)),6===i.length&&c(e,i),Object.entries(e).forEach((([t,i])=>{e[t]=i.sort(((e,t)=>e.start-t.start||e.span-t.span))})),e}))}getIndex(e={}){return this._parseCache.get("index",null,e.signal)}hasDataForReferenceSequence(e){return n(this,void 0,void 0,(function*(){return!!(yield this.getIndex())[e]}))}getEntriesForRange(e,t,i){return n(this,void 0,void 0,(function*(){const n=(yield this.getIndex())[e];if(!n)return[];const r=e=>{const n=e.start,r=e.start+e.span;return n>i?-1:r<=t?1:0},s=[];for(let e=0;e{Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(e,t){this.parameters=e,this.dataType=t}}},4863:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=i(8577),s=n(i(1050)),o=i(1074);class a extends s.default{constructor(e,t){if(super(e,t),"int"!==this.dataType)throw new r.CramUnimplementedError(`${this.dataType} decoding not yet implemented by BETA codec`)}decode(e,t,i,n){return(0,o.getBits)(t.content,n.coreBlock,this.parameters.length)-this.parameters.offset}}t.default=a},1738:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=i(9488),s=n(i(1050));class o extends s.default{constructor(e,t,i){if(super(e,t),this.instantiateCodec=i,"byteArray"!==t)throw new TypeError(`byteArrayLength does not support data type ${t}`)}decode(e,t,i,n){const r=this._getLengthCodec().decode(e,t,i,n),s=this._getDataCodec(),o=new Uint8Array(r);for(let a=0;a(0,r.tinyMemoize)(o,e)))},1405:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=i(8577),s=n(i(1050)),o=i(1074);class a extends s.default{constructor(e,t){if(super(e,t),"byteArray"!==t)throw new TypeError(`byteArrayStop codec does not support data type ${t}`)}decode(e,t,i,n){const{blockContentId:s}=this.parameters,o=i[s];if(!o)throw new r.CramMalformedError(`no block found with content ID ${s}`);const a=n.externalBlocks.getCursor(s);return this._decodeByteArray(o,a)}_decodeByteArray(e,t){const i=e.content,{stopByte:n}=this.parameters,r=t.bytePosition;let s=t.bytePosition;for(;i[s]!==n&&s=e.content.length)throw new a.CramBufferOverrunError("attempted to read beyond end of block. this file seems truncated.");return e.content[t.bytePosition++]}}t.default=l},4229:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=i(8577),s=n(i(1050)),o=i(1074);class a extends s.default{constructor(e,t){if(super(e,t),"int"!==this.dataType)throw new r.CramUnimplementedError(`${this.dataType} decoding not yet implemented by GAMMA codec`)}decode(e,t,i,n){let r=1;for(;0===(0,o.getBits)(t.content,n.coreBlock,1);)r+=1;return((0,o.getBits)(t.content,n.coreBlock,r-1)|1<{Object.defineProperty(t,"__esModule",{value:!0}),t.getBits=t.CramBufferOverrunError=void 0;class i extends Error{}t.CramBufferOverrunError=i,t.getBits=function(e,t,n){let r=0;if(t.bytePosition+(7-t.bitPosition+n)/8>e.length)throw new i("read error during decoding. the file seems to be truncated.");for(let i=n;i;i--)r<<=1,r|=e[t.bytePosition]>>t.bitPosition&1,t.bitPosition-=1,t.bitPosition<0&&(t.bytePosition+=1),t.bitPosition&=7;return r}},2082:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=i(8577),s=n(i(1050)),o=i(1074);class a extends s.default{constructor(e,t){if(super(e,t),this.codes={},this.codeBook={},this.sortedByValue=[],this.sortedCodes=[],this.sortedValuesByBitCode=[],this.sortedBitCodes=[],this.sortedBitLengthsByBitCode=[],this.bitCodeToValue=[],!["byte","int"].includes(this.dataType))throw new TypeError(`${this.dataType} decoding not yet implemented by HUFFMAN_INT codec`);this.buildCodeBook(),this.buildCodes(),this.buildCaches(),0===this.sortedCodes[0].bitLength&&(this._decode=this._decodeZeroLengthCode)}buildCodeBook(){let e=new Array(this.parameters.numCodes);for(let t=0;te.bitLength-t.bitLength||e.symbol-t.symbol)),this.codeBook={},e.forEach((e=>{this.codeBook[e.bitLength]||(this.codeBook[e.bitLength]=[]),this.codeBook[e.bitLength].push(e.symbol)}))}buildCodes(){this.codes={};let e=0,t=-1;Object.entries(this.codeBook).forEach((([i,n])=>{const s=parseInt(i,10);n.forEach((i=>{const n={bitLength:s,value:i,bitCode:0};t+=1;const o=s-e;if(t<<=o,n.bitCode=t,e+=o,function(e){let t=e-(e>>1)&1431655765;return t=(858993459&t)+(t>>2&858993459),16843009*(t+(t>>4)&252645135)>>24}(t)>s)throw new r.CramMalformedError("Symbol out of range");this.codes[i]=n}))}))}buildCaches(){this.sortedCodes=Object.values(this.codes).sort(((e,t)=>e.bitLength-t.bitLength||e.bitCode-t.bitCode)),this.sortedByValue=Object.values(this.codes).sort(((e,t)=>e.value-t.value)),this.sortedValuesByBitCode=this.sortedCodes.map((e=>e.value)),this.sortedBitCodes=this.sortedCodes.map((e=>e.bitCode)),this.sortedBitLengthsByBitCode=this.sortedCodes.map((e=>e.bitLength));const e=Math.max(...this.sortedBitCodes);this.bitCodeToValue=new Array(e+1).fill(-1);for(let e=0;e-1&&this.sortedBitLengthsByBitCode[i]===t)return this.sortedValuesByBitCode[i];for(let i=e;this.sortedCodes[i+1].bitLength===t&&i{Object.defineProperty(t,"__esModule",{value:!0}),t.default={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}},8543:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=i(8772),r=i(8577),s={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"};t.default=class{constructor(e){this.dataSeriesCodecCache={},this.tagCodecCache={},this.tagEncoding={},this.readNamesIncluded=e.preservation.RN,this.APdelta=e.preservation.AP,this.referenceRequired=!!e.preservation.RR,this.tagIdsDictionary=e.preservation.TD,this.substitutionMatrix=function(e){const t=new Array(5);for(let e=0;e<5;e+=1)t[e]=new Array(4);return t[0][e[0]>>6&3]="C",t[0][e[0]>>4&3]="G",t[0][e[0]>>2&3]="T",t[0][e[0]>>0&3]="N",t[1][e[1]>>6&3]="A",t[1][e[1]>>4&3]="G",t[1][e[1]>>2&3]="T",t[1][e[1]>>0&3]="N",t[2][e[2]>>6&3]="A",t[2][e[2]>>4&3]="C",t[2][e[2]>>2&3]="T",t[2][e[2]>>0&3]="N",t[3][e[3]>>6&3]="A",t[3][e[3]>>4&3]="C",t[3][e[3]>>2&3]="G",t[3][e[3]>>0&3]="N",t[4][e[4]>>6&3]="A",t[4][e[4]>>4&3]="C",t[4][e[4]>>2&3]="G",t[4][e[4]>>0&3]="T",t}(e.preservation.SM),this.dataSeriesEncoding=e.dataSeriesEncoding,this.tagEncoding=e.tagEncoding,this.preservation=e.preservation,this._size=e._size,this._endPosition=e._endPosition}getCodecForTag(e){if(!this.tagCodecCache[e]){const t=this.tagEncoding[e];t&&(this.tagCodecCache[e]=(0,n.instantiateCodec)(t,"byteArray"))}return this.tagCodecCache[e]}getTagNames(e){return this.tagIdsDictionary[e]}getCodecForDataSeries(e){let t=this.dataSeriesCodecCache[e];if(void 0===t){const i=this.dataSeriesEncoding[e];if(i){const o=s[e];if(!o)throw new r.CramMalformedError(`data series name ${e} not defined in file compression header`);t=(0,n.instantiateCodec)(i,o),this.dataSeriesCodecCache[e]=t}}return t}toJSON(){const e={};return Object.keys(this).forEach((t=>{/Cache$/.test(t)||(e[t]=this[t])})),e}}},6284:function(e,t,i){var n=this&&this.__awaiter||function(e,t,i,n){return new(i||(i=Promise))((function(r,s){function o(e){try{l(n.next(e))}catch(e){s(e)}}function a(e){try{l(n.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}l((n=n.apply(e,t||[])).next())}))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const s=i(8577),o=i(9488),a=r(i(6601)),l=r(i(8543));class h{constructor(e,t){this.file=e,this.filePosition=t}getHeader(){return this._readContainerHeader(this.filePosition)}getCompressionHeaderBlock(){return n(this,void 0,void 0,(function*(){if(!(yield this.getHeader()).numRecords)return null;const e=yield this.file.getSectionParsers(),t=yield this.getFirstBlock();if(void 0===t)return;if("COMPRESSION_HEADER"!==t.contentType)throw new s.CramMalformedError(`invalid content type ${t.contentType} in what is supposed to be the compression header block`);const i=(0,o.parseItem)(t.content,e.cramCompressionHeader.parser,0,t.contentPosition);return Object.assign(Object.assign({},t),{parsedContent:i})}))}getFirstBlock(){return n(this,void 0,void 0,(function*(){const e=yield this.getHeader();return this.file.readBlock(e._endPosition)}))}getCompressionScheme(){return n(this,void 0,void 0,(function*(){const e=yield this.getCompressionHeaderBlock();if(e)return new l.default(e.parsedContent)}))}getSlice(e,t){return new a.default(this,e,t)}_readContainerHeader(e){return n(this,void 0,void 0,(function*(){const t=yield this.file.getSectionParsers(),{cramContainerHeader1:i,cramContainerHeader2:n}=t,{size:r}=yield this.file.stat();if(e>=r)return;const s=Buffer.allocUnsafe(i.maxLength);yield this.file.read(s,0,i.maxLength,e);const a=(0,o.parseItem)(s,i.parser),l=(0,o.itf8Size)(a.numLandmarks);if(e+a.length>=r)return void console.warn(`${this.file}: container header at ${e} indicates that the container has length ${a.length}, which extends beyond the length of the file. Skipping this container.`);const h=Buffer.allocUnsafe(n.maxLength(a.numLandmarks));yield this.file.read(h,0,n.maxLength(a.numLandmarks),e+a._size-l);const c=(0,o.parseItem)(h,n.parser);return this.file.validateChecksums&&void 0!==c.crc32&&(yield this.file.checkCrc32(e,a._size+c._size-l-4,c.crc32,`container header beginning at position ${e}`)),Object.assign(a,c,{_size:a._size+c._size-l,_endPosition:a._size+c._size-l+e})}))}}t.default=h,"getHeader getCompressionHeaderBlock getCompressionScheme".split(" ").forEach((e=>(0,o.tinyMemoize)(h,e)))},5457:function(e,t,i){var n=this&&this.__awaiter||function(e,t,i,n){return new(i||(i=Promise))((function(r,s){function o(e){try{l(n.next(e))}catch(e){s(e)}}function a(e){try{l(n.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}l((n=n.apply(e,t||[])).next())}))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const s=i(597),o=r(i(2779)),a=r(i(1269)),l=i(8577),h=r(i(9675)),c=i(6141),d=r(i(3498)),u=r(i(6284)),f=i(3427),p=i(9488),g=i(7578);class m{constructor(e){var t;if(this.file=(0,f.open)(e.url,e.path,e.filehandle),this.validateChecksums=!0,this.fetchReferenceSequenceCallback=e.seqFetch,this.options={checkSequenceMD5:e.checkSequenceMD5,cacheSize:null!==(t=e.cacheSize)&&void 0!==t?t:2e4},this.featureCache=new a.default({maxSize:this.options.cacheSize}),function(){const e=new Uint32Array([287454020]),t=new Uint8Array(e.buffer);return 68===t[0]?0:17===t[0]?1:2}()>0)throw new Error("Detected big-endian machine, may be unable to run")}read(e,t,i,n){return this.file.read(e,t,i,n)}stat(){return this.file.stat()}getDefinition(){return n(this,void 0,void 0,(function*(){const e=Buffer.allocUnsafe(c.cramFileDefinition.maxLength);yield this.file.read(e,0,c.cramFileDefinition.maxLength,0);const t=c.cramFileDefinition.parser.parse(e).result;if(2!==t.majorVersion&&3!==t.majorVersion)throw new l.CramUnimplementedError(`CRAM version ${t.majorVersion} not supported`);return t}))}getSamHeader(){return n(this,void 0,void 0,(function*(){const e=yield this.getContainerById(0);if(!e)throw new l.CramMalformedError("file contains no containers");const t=yield e.getFirstBlock();if(void 0===t)return(0,g.parseHeaderText)("");const i=t.content,n=i.readInt32LE(0),r=i.toString("utf8",4,4+n);return this.header=r,(0,g.parseHeaderText)(r)}))}getHeaderText(){return n(this,void 0,void 0,(function*(){return yield this.getSamHeader(),this.header}))}getSectionParsers(){return n(this,void 0,void 0,(function*(){const{majorVersion:e}=yield this.getDefinition();return(0,c.getSectionParsers)(e)}))}getContainerById(e){return n(this,void 0,void 0,(function*(){const t=yield this.getSectionParsers();let i=t.cramFileDefinition.maxLength;const{size:n}=yield this.file.stat(),{cramContainerHeader1:r}=t;let s;for(let t=0;t<=e;t+=1){if(i+r.maxLength+8>=n)return;s=this.getContainerAtPosition(i);const o=yield s.getHeader();if(!o)throw new l.CramMalformedError(`container ${e} not found in file`);if(0===t){i=o._endPosition;for(let e=0;e=n)return;const r=Buffer.allocUnsafe(i.maxLength);return yield this.file.read(r,0,i.maxLength,e),(0,p.parseItem)(r,i.parser,0,e)}))}_parseSection(e,t,i=e.maxLength,r){return n(this,void 0,void 0,(function*(){let n;if(r)n=r;else{const{size:e}=yield this.file.stat();if(t+i>=e)return;n=Buffer.allocUnsafe(i),yield this.file.read(n,0,i,t)}const s=(0,p.parseItem)(n,e.parser,0,t);if(s._size!==i)throw new l.CramMalformedError(`section read error: requested size ${i} does not equal parsed size ${s._size}`);return s}))}_uncompress(e,t,i){if("gzip"===e)(0,s.unzip)(t).copy(i);else if("bzip2"===e){const e=bzip2.array(t);let n,r=bzip2.header(e),s=0;do{n=bzip2.decompress(e,r),-1!=n&&(Buffer.from(n).copy(i,s),s+=n.length,r-=n.length)}while(-1!=n)}else if("rans"===e)(0,h.default)(t,i);else if("rans4x16"===e)d.default.r4x16_uncompress(t,i);else if("arith"===e)d.default.arith_uncompress(t,i);else if("fqzcomp"===e)d.default.fqzcomp_uncompress(t,i);else{if("tok3"!==e)throw new l.CramUnimplementedError(`${e} decompression not yet implemented`);d.default.tok3_uncompress(t,i)}}readBlock(e){return n(this,void 0,void 0,(function*(){const{majorVersion:t}=yield this.getDefinition(),i=yield this.getSectionParsers(),n=yield this.readBlockHeader(e);if(void 0===n)return;const r=n._endPosition,s=Buffer.allocUnsafe(n.uncompressedSize),o=Object.assign(Object.assign({},n),{_endPosition:r,contentPosition:r,content:s});if("raw"!==n.compressionMethod){const e=Buffer.allocUnsafe(n.compressedSize);yield this.read(e,0,n.compressedSize,r),this._uncompress(n.compressionMethod,e,s)}else yield this.read(s,0,n.uncompressedSize,r);if(t>=3){const t=yield this._parseSection(i.cramBlockCrc32,r+n.compressedSize);if(void 0===t)return;o.crc32=t.crc32,this.validateChecksums&&(yield this.checkCrc32(e,n._size+n.compressedSize,t.crc32,"block data")),o._endPosition=t._endPosition,o._size=o.compressedSize+i.cramBlockCrc32.maxLength}else o._endPosition=r+o.compressedSize,o._size=o.compressedSize;return o}))}}t.default=m,"getDefinition getSectionParsers getSamHeader".split(" ").forEach((e=>(0,p.tinyMemoize)(m,e)))},8222:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.CramRecord=void 0;const r=n(i(5457));var s=i(8631);Object.defineProperty(t,"CramRecord",{enumerable:!0,get:function(){return n(s).default}}),t.default=r.default},8631:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MateFlagsDecoder=t.CramFlagsDecoder=t.BamFlagsDecoder=t.MateFlags=t.CramFlags=t.BamFlags=void 0;const r=n(i(2615)),s={a:0,A:0,c:1,C:1,g:2,G:2,t:3,T:3,n:4,N:4};function o(e){const t={};for(const[i,n]of e)t["is"+n]=e=>!!(e&i),t["set"+n]=e=>e|i;return t}t.BamFlags=[[1,"Paired"],[2,"ProperlyPaired"],[4,"SegmentUnmapped"],[8,"MateUnmapped"],[16,"ReverseComplemented"],[32,"MateReverseComplemented"],[64,"Read1"],[128,"Read2"],[256,"Secondary"],[512,"FailedQc"],[1024,"Duplicate"],[2048,"Supplementary"]],t.CramFlags=[[1,"PreservingQualityScores"],[2,"Detached"],[4,"WithMateDownstream"],[8,"DecodeSequenceAsStar"]],t.MateFlags=[[1,"OnNegativeStrand"],[2,"Unmapped"]],t.BamFlagsDecoder=o(t.BamFlags),t.CramFlagsDecoder=o(t.CramFlags),t.MateFlagsDecoder=o(t.MateFlags),t.default=class{constructor({flags:e,cramFlags:t,readLength:i,mappingQuality:n,lengthOnRef:r,qualityScores:s,mateRecordNumber:o,readBases:a,readFeatures:l,mateToUse:h,readGroupId:c,readName:d,sequenceId:u,uniqueId:f,templateSize:p,alignmentStart:g,tags:m}){this.flags=e,this.cramFlags=t,this.readLength=i,this.mappingQuality=n,this.lengthOnRef=r,this.qualityScores=s,a&&(this.readBases=a),this.readGroupId=c,this.readName=d,this.sequenceId=u,this.uniqueId=f,this.templateSize=p,this.alignmentStart=g,this.tags=m,l&&(this.readFeatures=l),h&&(this.mate={flags:h.mateFlags,readName:h.mateReadName,sequenceId:h.mateSequenceId,alignmentStart:h.mateAlignmentStart}),o&&(this.mateRecordNumber=o)}isPaired(){return!!(this.flags&r.default.BAM_FPAIRED)}isProperlyPaired(){return!!(this.flags&r.default.BAM_FPROPER_PAIR)}isSegmentUnmapped(){return!!(this.flags&r.default.BAM_FUNMAP)}isMateUnmapped(){return!!(this.flags&r.default.BAM_FMUNMAP)}isReverseComplemented(){return!!(this.flags&r.default.BAM_FREVERSE)}isMateReverseComplemented(){return!!(this.flags&r.default.BAM_FMREVERSE)}isRead1(){return!!(this.flags&r.default.BAM_FREAD1)}isRead2(){return!!(this.flags&r.default.BAM_FREAD2)}isSecondary(){return!!(this.flags&r.default.BAM_FSECONDARY)}isFailedQc(){return!!(this.flags&r.default.BAM_FQCFAIL)}isDuplicate(){return!!(this.flags&r.default.BAM_FDUP)}isSupplementary(){return!!(this.flags&r.default.BAM_FSUPPLEMENTARY)}isDetached(){return!!(this.cramFlags&r.default.CRAM_FLAG_DETACHED)}hasMateDownStream(){return!!(this.cramFlags&r.default.CRAM_FLAG_MATE_DOWNSTREAM)}isPreservingQualityScores(){return!!(this.cramFlags&r.default.CRAM_FLAG_PRESERVE_QUAL_SCORES)}isUnknownBases(){return!!(this.cramFlags&r.default.CRAM_FLAG_NO_SEQ)}getReadBases(){if(!this.readBases&&this._refRegion){const e=function(e,t){if(!e.lengthOnRef&&!e.readLength)return null;if(e.isUnknownBases())return null;const i=e.alignmentStart-t.start;if(!e.readFeatures)return t.seq.substr(i,e.lengthOnRef).toUpperCase();let n="",r=i,s=0;for(;n.lengththis.mate.alignmentStart&&s>0&&(s=-s),s>0?(r[0]=e,r[1]=i,r[2]=t,r[3]=n):(r[2]=e,r[3]=i,r[0]=t,r[1]=n),r.join("")}return null}addReferenceSequence(e,t){this.readFeatures&&this.readFeatures.forEach((i=>{"X"===i.code&&function(e,t,i,n){if(!t)return;const r=n.refPos-t.start,o=t.seq.charAt(r);o&&(n.ref=o);let a=s[o];void 0===a&&(a=4);const l=i.substitutionMatrix[a][n.data];l&&(n.sub=l)}(0,e,t,i)})),!this.readBases&&e.start<=this.alignmentStart&&e.end>=this.alignmentStart+(this.lengthOnRef||this.readLength)-1&&(this._refRegion=e)}toJSON(){const e={};return Object.keys(this).forEach((t=>{"_"!==t.charAt(0)&&(e[t]=this[t])})),e.readBases=this.getReadBases(),e}}},6141:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getSectionParsers=t.cramFileDefinition=t.isMappedSliceHeader=void 0;const n=i(9996),r=(new n.Parser).itf8(),s={parser:(new n.Parser).string("magic",{length:4}).uint8("majorVersion").uint8("minorVersion").string("fileId",{length:20,stripNull:!0}),maxLength:26};t.cramFileDefinition=s;const o={parser:(new n.Parser).uint8("compressionMethod",{formatter:e=>{const t=["raw","gzip","bzip2","lzma","rans","rans4x16","arith","fqzcomp","tok3"][e];if(!t)throw new Error(`compression method number ${e} not implemented`);return t}}).uint8("contentType",{formatter:e=>{const t=["FILE_HEADER","COMPRESSION_HEADER","MAPPED_SLICE_HEADER","UNMAPPED_SLICE_HEADER","EXTERNAL_DATA","CORE_DATA"][e];if(!t)throw new Error(`invalid block content type id ${e}`);return t}}).itf8("contentId").itf8("compressedSize").itf8("uncompressedSize"),maxLength:17},a={parser:(new n.Parser).uint32("crc32"),maxLength:4},l=(new n.Parser).itf8("size").buffer("ents",{length:"size",formatter:e=>{function t(t,i){const n=e.toString("utf8",t,i),r=[];for(let e=0;er&&n.push(t(r,i)),n}}),h=(new n.Parser).uint8(null,{formatter:e=>!!e}),c=(new n.Parser).itf8("mapSize").itf8("mapCount").array("ents",{length:"mapCount",type:(new n.Parser).string("key",{length:2,stripNull:!1}).choice("value",{tag:"key",choices:{MI:h,UI:h,PI:h,RN:h,AP:h,RR:h,SM:(new n.Parser).array(null,{type:"uint8",length:5}),TD:(new n.Parser).nest(null,{type:l,formatter:e=>e.ents})}})});function d(e){const t={};for(let i=0;i=3?(i=i.ltf8("recordCounter"),t+=9):2===e&&(i=i.itf8("recordCounter"),t+=5),i=i.itf8("numBlocks").itf8("numContentIds").array("contentIds",{type:r,length:"numContentIds"}),t+=10,e>=2&&(i=i.array("md5",{type:"uint8",length:16}),t+=16),{parser:i,maxLength:e=>t+5*e}},cramMappedSliceHeader(e){let t=(new n.Parser).itf8("refSeqId").itf8("refSeqStart").itf8("refSeqSpan").itf8("numRecords"),i=20;return e>=3?(t=t.ltf8("recordCounter"),i+=9):2===e&&(t=t.itf8("recordCounter"),i+=5),t=t.itf8("numBlocks").itf8("numContentIds").array("contentIds",{type:r,length:"numContentIds"}).itf8("refBaseBlockId"),i+=15,e>=2&&(t=t.array("md5",{type:"uint8",length:16}),i+=16),{parser:t,maxLength:e=>i+5*e}},cramEncoding:e=>({parser:(new n.Parser).namely("cramEncoding").itf8("codecId").itf8("parametersBytes").choice("parameters",{tag:"codecId",choices:{0:new n.Parser,1:(new n.Parser).itf8("blockContentId"),2:(new n.Parser).itf8("offset").itf8("M"),3:n.Parser.start().itf8("numCodes").array("symbols",{length:"numCodes",type:r}).itf8("numLengths").array("bitLengths",{length:"numLengths",type:r}),4:n.Parser.start().nest("lengthsEncoding",{type:"cramEncoding"}).nest("valuesEncoding",{type:"cramEncoding"}),5:(new n.Parser).uint8("stopByte").itf8("blockContentId"),6:(new n.Parser).itf8("offset").itf8("length"),7:(new n.Parser).itf8("offset").itf8("K"),8:(new n.Parser).itf8("offset").itf8("log2m"),9:(new n.Parser).itf8("offset")}})}),cramDataSeriesEncodingMap(e){return(new n.Parser).itf8("mapSize").itf8("mapCount").array("ents",{length:"mapCount",type:(new n.Parser).string("key",{length:2,stripNull:!1}).nest("value",{type:this.cramEncoding(e).parser})})},cramTagEncodingMap(e){return(new n.Parser).itf8("mapSize").itf8("mapCount").array("ents",{length:"mapCount",type:(new n.Parser).itf8("key",{formatter:e=>String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(255&e)}).nest("value",{type:this.cramEncoding(e).parser})})},cramCompressionHeader(e){let t=new n.Parser;return t=t.nest("preservation",{type:c,formatter:d}).nest("dataSeriesEncoding",{type:this.cramDataSeriesEncodingMap(e),formatter:d}).nest("tagEncoding",{type:this.cramTagEncodingMap(e),formatter:d}),{parser:t}},cramContainerHeader1(e){let t=(new n.Parser).int32("length").itf8("refSeqId").itf8("refSeqStart").itf8("alignmentSpan").itf8("numRecords"),i=24;return e>=3?(t=t.ltf8("recordCounter"),i+=9):2===e&&(t=t.itf8("recordCounter"),i+=5),e>1&&(t=t.ltf8("numBases"),i+=9),t=t.itf8("numBlocks").itf8("numLandmarks"),i+=10,{parser:t,maxLength:i}},cramContainerHeader2(e){let t=(new n.Parser).itf8("numLandmarks").array("landmarks",{type:(new n.Parser).itf8(),length:"numLandmarks"}),i=0;return e>=3&&(t=t.uint32("crc32"),i=4),{parser:t,maxLength:e=>5+5*e+i}}};t.getSectionParsers=function(e){const t=Object.assign({},u);return Object.keys(f).forEach((i=>{t[i]=f[i](e)})),t}},3757:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=n(i(3720)),s=i(8577),o=i(8631),a=i(6141);function l(e){let t="";for(let i=0;i1&&-2===n.parsedContent.refSeqId?t("RI"):n.parsedContent.refSeqId;const b=t("RL");let w=t("AP");i.APdelta&&(w+=d.lastAlignmentStart),d.lastAlignmentStart=w;const v=t("RG");let y,_,x,k;if(i.readNamesIncluded&&(y=l(t("RN"))),o.CramFlagsDecoder.isDetached(g)){const e=t("MF");let n;i.readNamesIncluded||(n=l(t("RN")),y=n);const r=t("NS"),s=t("NP");(e||r>-1)&&(_={mateFlags:e,mateSequenceId:r,mateAlignmentStart:s,mateReadName:n}),x=t("TS"),o.MateFlagsDecoder.isUnmapped(e)&&(p=o.BamFlagsDecoder.setMateUnmapped(p)),o.MateFlagsDecoder.isOnNegativeStrand(e)&&(p=o.BamFlagsDecoder.setMateReverseComplemented(p))}else o.CramFlagsDecoder.isWithMateDownstream(g)&&(k=t("NF")+f+1);const C=t("TL");if(C<0)throw new s.CramMalformedError("invalid TL index");const S={},A=i.getTagNames(C),E=A.length;for(let t=0;t1?"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"]}[t];if(!c)throw new s.CramMalformedError(`invalid read feature code "${t}"`);let d=h(c);const u={B:["number","QS"]}[t];u&&(d=[d,h(u)]),o+=n;const f=o;a+=n;const p=a;"D"===t||"N"===t?a+=d:"I"===t||"S"===t?a-=d.length:"i"===t&&(a-=1),l[e]={code:t,pos:f,refPos:p,data:d}}return l}(w,e,t,0,u)),T=b,M)for(const{code:e,data:t}of M)"D"===e||"N"===e?T+=t:"I"===e||"S"===e?T-=t.length:"i"===e&&(T-=1);if(Number.isNaN(T)&&(console.warn(`${y||`${m}:${w}`} record has invalid read features`),T=b),R=t("MQ"),o.CramFlagsDecoder.isPreservingQualityScores(g)){L=new Array(b);for(let e=0;e=0){const r=e[i.mateRecordNumber];if(!r)throw new s.CramMalformedError("intra-slice mate record not found, this file seems malformed");n.push(...t(r))}return n}(i),r=n.map((e=>e.alignmentStart)),o=n.map((e=>e.alignmentStart+e.readLength-1)),a=Math.max(...o)-Math.min(...r)+1;a>=0&&n.forEach((e=>{if(void 0!==e.templateLength)throw new s.CramMalformedError("mate pair group has some members that have template lengths already, this file seems malformed");e.templateLength=a}))}(e,0,i):function(e,t){const i=Math.min(e.alignmentStart,t.alignmentStart),n=Math.max(e.alignmentStart+e.readLength-1,t.alignmentStart+t.readLength-1)-i+1;e.templateLength=n,t.templateLength=n}(i,n)),delete i.mateRecordNumber}class f{constructor(e,t,i){this.container=e,this.containerPosition=t,this.file=e.file}getHeader(){return n(this,void 0,void 0,(function*(){const e=yield this.file.getSectionParsers(),t=yield this.container.getHeader(),i=yield this.file.readBlock(t._endPosition+this.containerPosition);if(void 0===i)throw new Error;if("MAPPED_SLICE_HEADER"===i.contentType){const n=(0,o.parseItem)(i.content,e.cramMappedSliceHeader.parser,0,t._endPosition);return Object.assign(Object.assign({},i),{parsedContent:n})}if("UNMAPPED_SLICE_HEADER"===i.contentType){const n=(0,o.parseItem)(i.content,e.cramUnmappedSliceHeader.parser,0,t._endPosition);return Object.assign(Object.assign({},i),{parsedContent:n})}throw new s.CramMalformedError(`error reading slice header block, invalid content type ${i.contentType}`)}))}getBlocks(){return n(this,void 0,void 0,(function*(){const e=yield this.getHeader();let t=e._endPosition;const i=new Array(e.parsedContent.numBlocks);for(let e=0;e{"EXTERNAL_DATA"===e.contentType&&(t[e.contentId]=e)})),t}))}getBlockByContentId(e){return n(this,void 0,void 0,(function*(){return(yield this._getBlocksContentIdIndex())[e]}))}getReferenceRegion(){return n(this,void 0,void 0,(function*(){const e=(yield this.getHeader()).parsedContent;if(!(0,c.isMappedSliceHeader)(e))throw new Error;if(e.refSeqId<0)return;const t=yield this.container.getCompressionScheme();if(void 0===t)throw new Error;if(e.refBaseBlockId>=0){const t=yield this.getBlockByContentId(e.refBaseBlockId);if(!t)throw new s.CramMalformedError("embedded reference specified, but reference block does not exist");return{seq:t.data.toString("utf8"),start:e.refSeqStart,end:e.refSeqStart+e.refSeqSpan-1,span:e.refSeqSpan}}if(t.referenceRequired||this.file.fetchReferenceSequenceCallback){if(!this.file.fetchReferenceSequenceCallback)throw new Error("reference sequence not embedded, and seqFetch callback not provided, cannot fetch reference sequence");const t=yield this.file.fetchReferenceSequenceCallback(e.refSeqId,e.refSeqStart,e.refSeqStart+e.refSeqSpan-1);if(t.length!==e.refSeqSpan)throw new s.CramArgumentError("seqFetch callback returned a reference sequence of the wrong length");return{seq:t,start:e.refSeqStart,end:e.refSeqStart+e.refSeqSpan-1,span:e.refSeqSpan}}}))}getAllRecords(){return this.getRecords((()=>!0))}_fetchRecords(){return n(this,void 0,void 0,(function*(){const{majorVersion:e}=yield this.file.getDefinition(),t=yield this.container.getCompressionScheme();if(void 0===t)throw new Error;const i=yield this.getHeader();if(void 0===i)throw new Error;const n=yield this._getBlocksContentIdIndex();if(e>1&&this.file.options.checkSequenceMD5&&(0,c.isMappedSliceHeader)(i.parsedContent)&&i.parsedContent.refSeqId>=0&&"0000000000000000"!==i.parsedContent.md5.join("")){const e=yield this.getReferenceRegion();if(e){const{seq:t,start:n,end:r}=e,a=(0,o.sequenceMD5)(t),l=i.parsedContent.md5.map((e=>(e<16?"0":"")+e.toString(16))).join("");if(a!==l)throw new s.CramMalformedError(`MD5 checksum reference mismatch for ref ${i.parsedContent.refSeqId} pos ${n}..${r}. recorded MD5: ${l}, calculated MD5: ${a}`)}}const r=yield this.getCoreDataBlock(),a={lastAlignmentStart:(0,c.isMappedSliceHeader)(i.parsedContent)?i.parsedContent.refSeqStart:0,coreBlock:{bitPosition:7,bytePosition:0},externalBlocks:{map:new Map,getCursor(e){let t=this.map.get(e);return void 0===t&&(t={bitPosition:7,bytePosition:0},this.map.set(e,t)),t}}},f=e=>{const i=t.getCodecForDataSeries(e);if(!i)throw new s.CramMalformedError(`no codec defined for ${e} data series`);return i.decode(this,r,n,a)};let p=new Array(i.parsedContent.numRecords);for(let s=0;s!!e));break}throw e}for(let e=0;e=0&&u(p,e,p[e],p[t])}return p}))}getRecords(e){return n(this,void 0,void 0,(function*(){const t=this.container.filePosition+this.containerPosition;let i=this.file.featureCache.get(t.toString());i||(i=this._fetchRecords(),this.file.featureCache.set(t.toString(),i));const r=(yield i).filter(e);if(r.length&&this.file.fetchReferenceSequenceCallback){const e=yield this.getHeader();if((0,c.isMappedSliceHeader)(e.parsedContent)&&(e.parsedContent.refSeqId>=0||-2===e.parsedContent.refSeqId)){const t=e.parsedContent.refSeqId>=0?e.parsedContent.refSeqId:void 0,i=yield this.container.getCompressionScheme();if(void 0===i)throw new Error;const s={};for(let e=0;en.end&&(n.end=o),r[e].alignmentStartn(this,void 0,void 0,(function*(){-1!==e.id&&e.start<=e.end&&(e.seq=yield this.file.fetchReferenceSequenceCallback(e.id,e.start,e.end))})))));for(let e=0;e(0,o.tinyMemoize)(f,e)))},9488:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.sequenceMD5=t.tinyMemoize=t.parseItem=t.parseItf8=t.itf8Size=void 0;const r=n(i(2568)),s=i(1074);t.itf8Size=function(e){return-128&e?-16384&e?-2097152&e?-268435456&e?5:4:3:2:1},t.parseItf8=function(e,t){let i=t;const n=e[i];let r;if(n<128?(r=n,i+=1):n<192?(r=16383&(n<<8|e[i+1]),i+=2):n<224?(r=2097151&(n<<16|e[i+1]<<8|e[i+2]),i+=3):n<240?(r=268435455&(n<<24|e[i+1]<<16|e[i+2]<<8|e[i+3]),i+=4):(r=(15&n)<<28|e[i+1]<<20|e[i+2]<<12|e[i+3]<<4|15&e[i+4],i+=5),i>e.length)throw new s.CramBufferOverrunError("Attempted to read beyond end of buffer; this file seems truncated.");return[r,i-t]},t.parseItem=function(e,t,i=0,n=0){const{offset:r,result:s}=t.parse(e);return Object.assign(Object.assign({},s),{_endPosition:r+n,_size:r-i})},t.tinyMemoize=function(e,t){const i=e.prototype[t],n=`_memo_${t}`;e.prototype[t]=function(){if(!(n in this)){const e=i.call(this);this[n]=e,Promise.resolve(e).catch((()=>{delete this[n]}))}return this[n]}},t.sequenceMD5=function(e){return(0,r.default)(e.toUpperCase().replace(/[^\x21-\x7e]/g,""))}},8577:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CramArgumentError=t.CramSizeLimitError=t.CramMalformedError=t.CramUnimplementedError=t.CramError=void 0;class i extends Error{}t.CramError=i;class n extends Error{}t.CramUnimplementedError=n,t.CramMalformedError=class extends i{},t.CramSizeLimitError=class extends i{},t.CramArgumentError=class extends i{}},5590:function(e,t,i){var n=this&&this.__createBinding||(Object.create?function(e,t,i,n){void 0===n&&(n=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,n,r)}:function(e,t,i,n){void 0===n&&(n=i),e[n]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&n(t,e,i);return r(t,e),t},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.CramRecord=t.CraiIndex=t.IndexedCramFile=t.CramFile=void 0;const a=s(i(8222));t.CramFile=a.default,Object.defineProperty(t,"CramRecord",{enumerable:!0,get:function(){return a.CramRecord}});const l=o(i(946));t.IndexedCramFile=l.default;const h=o(i(368));t.CraiIndex=h.default},946:function(e,t,i){var n=this&&this.__awaiter||function(e,t,i,n){return new(i||(i=Promise))((function(r,s){function o(e){try{l(n.next(e))}catch(e){s(e)}}function a(e){try{l(n.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}l((n=n.apply(e,t||[])).next())}))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const s=i(8577),o=r(i(8222));t.default=class{constructor(e){if(e.cram?this.cram=e.cram:this.cram=new o.default({url:e.cramUrl,path:e.cramPath,filehandle:e.cramFilehandle,seqFetch:e.seqFetch,checkSequenceMD5:e.checkSequenceMD5,cacheSize:e.cacheSize}),!(this.cram instanceof o.default))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}getRecordsForRange(e,t,i,r={}){return n(this,void 0,void 0,(function*(){if(r.viewAsPairs=r.viewAsPairs||!1,r.pairAcrossChr=r.pairAcrossChr||!1,r.maxInsertSize=r.maxInsertSize||2e5,"string"==typeof e)throw new s.CramUnimplementedError("string sequence names not yet supported");const n=e,o=yield this.index.getEntriesForRange(n,t,i),a=o.map((e=>e.sliceBytes)).reduce(((e,t)=>e+t),0);if(a>this.fetchSizeLimit)throw new s.CramSizeLimitError(`data size of ${a.toLocaleString()} bytes exceeded fetch size limit of ${this.fetchSizeLimit.toLocaleString()} bytes`);const l=n=>n.sequenceId===e&&n.alignmentStart<=i&&void 0!==n.lengthOnRef&&n.alignmentStart+n.lengthOnRef-1>=t,h=yield Promise.all(o.map((e=>this.getRecordsInSlice(e,l))));let c=Array.prototype.concat(...h);if(r.viewAsPairs){const e={},t={};for(let i=0;i{1===t&&(i[e]=!0)}));const s=[];for(let e=0;ee.toString().localeCompare(t.toString()))).filter(((e,t,i)=>!t||e.toString()!==i[t-1].toString()));const l=[],h=a.map((e=>e.sliceBytes)).reduce(((e,t)=>e+t),0);if(h>this.fetchSizeLimit)throw new Error(`mate data size of ${h.toLocaleString()} bytes exceeded fetch size limit of ${this.fetchSizeLimit.toLocaleString()} bytes`);a.forEach((e=>{let n=this.cram.featureCache.get(e.toString());n||(n=this.getRecordsInSlice(e,(()=>!0)),this.cram.featureCache.set(e.toString(),n));const r=n.then((e=>{const n=[];for(let r=0;re.concat(t)));c=c.concat(e)}}return c}))}getRecordsInSlice({containerStart:e,sliceStart:t,sliceBytes:i},n){return this.cram.getContainerAtPosition(e).getSlice(t,i).getRecords(n)}hasDataForReferenceSequence(e){return this.index.hasDataForReferenceSequence(e)}}},3427:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.open=t.fromUrl=t.RemoteFile=t.LocalFile=void 0;const r=n(i(8575)),s=i(4319),o=i(2949);function a(e){const{protocol:t,pathname:i}=r.default.parse(e);return"file:"===t?new o.LocalFile(unescape((0,s.ensureNotNullish)(i))):new o.RemoteFile(e)}Object.defineProperty(t,"LocalFile",{enumerable:!0,get:function(){return o.LocalFile}}),Object.defineProperty(t,"RemoteFile",{enumerable:!0,get:function(){return o.RemoteFile}}),t.fromUrl=a,t.open=function(e,t,i){if(i)return i;if(e)return a(e);if(t)return new o.LocalFile(t);throw new Error("no url, path, or filehandle provided, cannot open")}},5702:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.RANS_BYTE_L=t.TOTFREQ=t.TF_SHIFT=void 0,t.TF_SHIFT=12,t.TOTFREQ=4096,t.RANS_BYTE_L=1<<23},6484:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=i(8577),s=i(5702),o=n(i(7634));t.default=function(e,t,i,n){let a=e.getInt(),l=e.getInt(),h=e.getInt(),c=e.getInt();const d=n.remaining(),u=-4&d;for(let r=0;r>2;let u=0,f=d,p=2*d,g=3*d,m=0,b=0,w=0,v=0;for(;u{Object.defineProperty(t,"__esModule",{value:!0});const n=i(8577),r=i(5702);class s{constructor(){this.F=void 0,this.C=void 0}}function o(e,t,i,n){return i*(e>>n)+(e&(1<>s)+(e&(1<=128&&(t.fc[l].F&=-129,t.fc[l].F=(127&t.fc[l].F)<<8|255&e.get()),t.fc[l].C=r,o.default.symbolInit(i[l],t.fc[l].C,t.fc[l].F),t.R||(t.R=new Array(s.TOTFREQ)),t.R.fill(l,r,r+t.fc[l].F),r+=t.fc[l].F,0===n&&l+1===(255&e.getByteAt(e.position()))?(l=255&e.get(),n=255&e.get()):0!==n?(n-=1,l+=1):l=255&e.get()}while(0!==l);a(r=128&&(t[r].fc[c].F&=-129,t[r].fc[c].F=(127&t[r].fc[c].F)<<8|255&e.get()),t[r].fc[c].C=h,0===t[r].fc[c].F&&(t[r].fc[c].F=s.TOTFREQ),null==i[r][c]&&(i[r][c]=new o.default.RansDecSymbol),o.default.symbolInit(i[r][c],t[r].fc[c].C,t[r].fc[c].F),null==t[r].R&&(t[r].R=new Array(s.TOTFREQ)),t[r].R.fill(c,h,h+t[r].fc[c].F),h+=t[r].fc[c].F,a(h<=s.TOTFREQ),0===l&&c+1===(255&e.getByteAt(e.position()))?(c=255&e.get(),l=255&e.get()):0!==l?(l-=1,c+=1):c=255&e.get()}while(0!==c);0===n&&r+1===(255&e.getByteAt(e.position()))?(r=255&e.get(),n=255&e.get()):0!==n?(n-=1,r+=1):r=255&e.get()}while(0!==r)}},9675:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=i(8577),s=n(i(7634)),o=i(696),a=n(i(6484)),l=n(i(7121));class h{constructor(e,t=0){this._buffer=e,this._position=t,this.length=e.length}get(){const e=this._buffer[this._position];return this._position+=1,e}getByte(){return this.get()}getByteAt(e){return this._buffer[e]}position(){return this._position}put(e){return this._buffer[this._position]=e,this._position+=1,e}putAt(e,t){return this._buffer[e]=t,t}setPosition(e){return this._position=e,e}getInt(){const e=this._buffer.readInt32LE(this._position);return this._position+=4,e}remaining(){return this._buffer.length-this._position}}t.default=function(e,t,i=0){if(0===e.length)return t.fill(0),t;const n=new h(e,i),c=n.get();if(0!==c&&1!==c)throw new r.CramMalformedError(`Invalid rANS order ${c}`);if(n.getInt()!==n.remaining()-4)throw new r.CramMalformedError("Incorrect input length.");const d=n.getInt(),u=new h(t||Buffer.allocUnsafe(d));if(u.length{Object.defineProperty(t,"__esModule",{value:!0}),t.parseHeaderText=void 0,t.parseHeaderText=function(e){const t=e.split(/\r?\n/),i=[];return t.forEach((e=>{const[t,...n]=e.split(/\t/),r=n.map((e=>{const[t,i]=e.split(":",2);return{tag:t,value:i}}));t&&i.push({tag:t.substr(1),data:r})})),i}},4319:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ensureNotNullish=void 0,t.ensureNotNullish=function(e){if(null==e)throw new Error("Value must not be nullish.");return e}},597:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.unzip=void 0;const n=i(9591);t.unzip=function(e){return Buffer.from((0,n.inflate)(e))}},9996:(e,t,i)=>{function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}var r=i(8764).Buffer,s=i(22),o=i(2961)._,a=i(3720);"undefined"!=typeof window&&(window.Buffer=r),"undefined"!=typeof self&&(self.Buffer=r);var l={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},h={},c="___parser_",d=[];!function(){var e;for(e=1;e<=32;e++)d.push(e)}();var u={};Object.keys(l).concat(Object.keys({String:null,Buffer:null,Array:null,Skip:null,Choice:null,Nest:null,Bit:null,Itf8:null,Ltf8:null})).forEach((function(e){u[e.toLowerCase()]=e}));var f=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};f.start=function(){return new f},Object.keys(l).forEach((function(e){f.prototype[e.toLowerCase()]=function(t,i){return this.setNextParser(e.toLowerCase(),t,i)};var t=e.replace(/BE|LE/,"").toLowerCase();t in f.prototype||(f.prototype[t]=function(e,i){return this[t+this.endian](e,i)})})),d.forEach((function(e){f.prototype["bit".concat(e.toString())]=function(t,i){return i||(i={}),i.length=e,this.setNextParser("bit",t,i)}})),f.prototype.namely=function(e){return h[e]=this,this.alias=e,this},f.prototype.skip=function(e,t){if(t&&t.assert)throw new Error("assert option on skip is not allowed.");return this.setNextParser("skip","",{length:e})},f.prototype.string=function(e,t){if(!t.zeroTerminated&&!t.length&&!t.greedy)throw new Error("Neither length, zeroTerminated, nor greedy is defined for string.");if((t.zeroTerminated||t.length)&&t.greedy)throw new Error("greedy is mutually exclusive with length and zeroTerminated for string.");if(t.stripNull&&!t.length&&!t.greedy)throw new Error("Length or greedy must be defined if stripNull is defined.");return t.encoding=t.encoding||"utf8",this.setNextParser("string",e,t)},f.prototype.buffer=function(e,t){if(!t.length&&!t.readUntil)throw new Error("Length nor readUntil is defined in buffer parser");return this.setNextParser("buffer",e,t)},f.prototype.array=function(e,t){if(!t.readUntil&&!t.length&&!t.lengthInBytes)throw new Error("Length option of array is not defined.");if(!t.type)throw new Error("Type option of array is not defined.");if("string"==typeof t.type&&!h[t.type]&&Object.keys(l).indexOf(u[t.type])<0)throw new Error('Specified primitive type "'.concat(t.type,'" is not supported.'));return this.setNextParser("array",e,t)},f.prototype.choice=function(e,t){if(1===arguments.length&&"object"===n(e)&&(t=e,e=null),!t.tag)throw new Error("Tag option of array is not defined.");if(!t.choices)throw new Error("Choices option of array is not defined.");return Object.keys(t.choices).forEach((function(i){if(!t.choices[i])throw new Error("Choice Case ".concat(i," of ").concat(e," is not valid."));if("string"==typeof t.choices[i]&&!h[t.choices[i]]&&Object.keys(l).indexOf(u[t.choices[i]])<0)throw new Error('Specified primitive type "'.concat(t.choices[i],'" is not supported.'))}),this),this.setNextParser("choice",e,t)},f.prototype.nest=function(e,t){if(1===arguments.length&&"object"===n(e)&&(t=e,e=null),!t.type)throw new Error("Type option of nest is not defined.");if(!(t.type instanceof f||h[t.type]))throw new Error("Type option of nest must be a Parser object.");if(!(t.type instanceof f||e))throw new Error("options.type must be a object if variable name is omitted.");return this.setNextParser("nest",e,t)},f.prototype.endianess=function(e){switch(e.toLowerCase()){case"little":this.endian="le";break;case"big":this.endian="be";break;default:throw new Error("Invalid endianess: ".concat(e))}return this},f.prototype.create=function(e){if(!(e instanceof Function))throw new Error("Constructor must be a Function object.");return this.constructorFn=e,this},f.prototype.getCode=function(){var e=new o;return e.pushCode("if (!Buffer.isBuffer(buffer)) {"),e.generateError('"argument buffer is not a Buffer object"'),e.pushCode("}"),this.alias?this.addAliasedCode(e):this.addRawCode(e),this.alias?e.pushCode("return {0}(0)",c+this.alias):e.pushCode("return { offset: offset, result: vars };"),e.code},f.prototype.addRawCode=function(e){e.pushCode("var offset = 0;"),this.constructorFn?e.pushCode("var vars = new constructorFn();"):e.pushCode("var vars = {};"),this.generate(e),this.resolveReferences(e),e.pushCode("return { offset: offset, result: vars };")},f.prototype.addAliasedCode=function(e){return e.pushCode("function {0}(offset) {",c+this.alias),this.constructorFn?e.pushCode("var vars = new constructorFn();"):e.pushCode("var vars = {};"),this.generate(e),e.markResolved(this.alias),this.resolveReferences(e),e.pushCode("return { offset: offset, result: vars };"),e.pushCode("}"),e},f.prototype.resolveReferences=function(e){var t=e.getUnresolvedReferences();e.markRequested(t),t.forEach((function(t){h[t].addAliasedCode(e)}))},f.prototype.compile=function(){var e="(function(buffer, constructorFn, Long) { ".concat(this.getCode()," })");this.compiled=s.runInThisContext(e)},f.prototype.sizeOf=function(){var e=NaN;if(Object.keys(l).indexOf(this.type)>=0)e=l[this.type];else if("String"===this.type&&"number"==typeof this.options.length)e=this.options.length;else if("Buffer"===this.type&&"number"==typeof this.options.length)e=this.options.length;else if("Array"===this.type&&"number"==typeof this.options.length){var t=NaN;"string"==typeof this.options.type?t=l[u[this.options.type]]:this.options.type instanceof f&&(t=this.options.type.sizeOf()),e=this.options.length*t}else"Skip"===this.type?e=this.options.length:"Nest"===this.type?e=this.options.type.sizeOf():this.type||(e=0);return this.next&&(e+=this.next.sizeOf()),e},f.prototype.parse=function(e){return this.compiled||this.compile(),this.compiled(e,this.constructorFn,a)},f.prototype.setNextParser=function(e,t,i){var n=new f;return n.type=u[e],n.varName=t,n.options=i||n.options,n.endian=this.endian,this.head?this.head.next=n:this.next=n,this.head=n,this},f.prototype.generate=function(e){this.type&&(this["generate".concat(this.type)](e),this.generateAssert(e));var t=e.generateVariable(this.varName);return this.options.formatter&&this.generateFormatter(e,t,this.options.formatter),this.generateNext(e)},f.prototype.generateAssert=function(e){if(this.options.assert){var t=e.generateVariable(this.varName);switch(n(this.options.assert)){case"function":e.pushCode("if (!({0}).call(vars, {1})) {",this.options.assert,t);break;case"number":e.pushCode("if ({0} !== {1}) {",this.options.assert,t);break;case"string":e.pushCode('if ("{0}" !== {1}) {',this.options.assert,t);break;default:throw new Error("Assert option supports only strings, numbers and assert functions.")}e.generateError('"Assert error: {0} is " + {0}',t),e.pushCode("}")}},f.prototype.generateNext=function(e){return this.next&&(e=this.next.generate(e)),e},Object.keys(l).forEach((function(e){f.prototype["generate".concat(e)]=function(t){"UInt64"===e?t.pushCode("{0} = Long.fromBytes(buffer.slice(offset,offset+8), true, this.endian === 'le').toNumber();",t.generateVariable(this.varName),e):"Int64"===e?t.pushCode("{0} = Long.fromBytes(buffer.slice(offset,offset+8), false, this.endian === 'le').toNumber();",t.generateVariable(this.varName),e):t.pushCode("{0} = buffer.read{1}(offset);",t.generateVariable(this.varName),e),t.pushCode("offset += {0};",l[e])}})),f.prototype.generateBit=function(e){var t=JSON.parse(JSON.stringify(this));if(t.varName=e.generateVariable(t.varName),e.bitFields.push(t),!this.next||this.next&&["Bit","Nest"].indexOf(this.next.type)<0){var i=0;e.bitFields.forEach((function(e){i+=e.options.length}));var n=e.generateTmpVariable();if(i<=8)e.pushCode("var {0} = buffer.readUInt8(offset);",n),i=8;else if(i<=16)e.pushCode("var {0} = buffer.readUInt16BE(offset);",n),i=16;else if(i<=24){var r=e.generateTmpVariable(),s=e.generateTmpVariable();e.pushCode("var {0} = buffer.readUInt16BE(offset);",r),e.pushCode("var {0} = buffer.readUInt8(offset + 2);",s),e.pushCode("var {2} = ({0} << 8) | {1};",r,s,n),i=24}else{if(!(i<=32))throw new Error("Currently, bit field sequence longer than 4-bytes is not supported.");e.pushCode("var {0} = buffer.readUInt32BE(offset);",n),i=32}e.pushCode("offset += {0};",i/8);var o=0,a="be"===this.endian;e.bitFields.forEach((function(t){e.pushCode("{0} = {1} >> {2} & {3};",t.varName,n,a?i-o-t.options.length:o,(1< offset++);"),e.pushCode("{0} = buffer.toString('{1}', {2}, offset);",t,this.options.encoding,i)),this.options.stripNull&&e.pushCode("{0} = {0}.replace(/\\x00+$/g, '')",t)},f.prototype.generateBuffer=function(e){"eof"===this.options.readUntil?e.pushCode("{0} = buffer.slice(offset);",e.generateVariable(this.varName)):(e.pushCode("{0} = buffer.slice(offset, offset + {1});",e.generateVariable(this.varName),e.generateOption(this.options.length)),e.pushCode("offset += {0};",e.generateOption(this.options.length))),this.options.clone&&e.pushCode("{0} = Buffer.from({0});",e.generateVariable(this.varName))},f.prototype.generateArray=function(e){var t=e.generateOption(this.options.length),i=e.generateOption(this.options.lengthInBytes),n=this.options.type,r=e.generateTmpVariable(),s=e.generateVariable(this.varName),o=e.generateTmpVariable(),a=this.options.key,d="string"==typeof a;if(d?e.pushCode("{0} = {};",s):e.pushCode("{0} = [];",s),"function"==typeof this.options.readUntil?e.pushCode("do {"):"eof"===this.options.readUntil?e.pushCode("for (var {0} = 0; offset < buffer.length; {0}++) {",r):void 0!==i?e.pushCode("for (var {0} = offset; offset - {0} < {1}; ) {",r,i):e.pushCode("for (var {0} = 0; {0} < {1}; {0}++) {",r,t),"string"==typeof n)if(h[n]){var p=e.generateTmpVariable();e.pushCode("var {0} = {1}(offset);",p,c+n),e.pushCode("var {0} = {1}.result; offset = {1}.offset;",o,p),n!==this.alias&&e.addReference(n)}else e.pushCode("var {0} = buffer.read{1}(offset);",o,u[n]),e.pushCode("offset += {0};",l[u[n]]);else n instanceof f&&(e.pushCode("var {0} = {};",o),e.pushScope(o),n.generate(e),e.popScope());d?e.pushCode("{0}[{2}.{1}] = {2};",s,a,o):e.pushCode("{0}.push({1});",s,o),e.pushCode("}"),"function"==typeof this.options.readUntil&&e.pushCode(" while (!({0}).call(this, {1}, buffer.slice(offset)));",this.options.readUntil,o)},f.prototype.generateChoiceCase=function(e,t,i){if("string"==typeof i)if(h[i]){var n=e.generateTmpVariable();e.pushCode("var {0} = {1}(offset);",n,c+i),e.pushCode("{0} = {1}.result; offset = {1}.offset;",e.generateVariable(this.varName),n),i!==this.alias&&e.addReference(i)}else e.pushCode("{0} = buffer.read{1}(offset);",e.generateVariable(this.varName),u[i]),e.pushCode("offset += {0};",l[u[i]]);else i instanceof f&&(e.pushPath(t),i.generate(e),e.popPath(t))},f.prototype.generateChoice=function(e){var t=e.generateOption(this.options.tag);this.varName&&e.pushCode("{0} = {};",e.generateVariable(this.varName)),e.pushCode("switch({0}) {",t),Object.keys(this.options.choices).forEach((function(t){var i=this.options.choices[t];Number.isNaN(parseInt(t,10))?e.pushCode("case '{0}':",t):e.pushCode("case {0}:",t),this.generateChoiceCase(e,this.varName,i),e.pushCode("break;")}),this),e.pushCode("default:"),this.options.defaultChoice?this.generateChoiceCase(e,this.varName,this.options.defaultChoice):e.generateError('"Met undefined tag value " + {0} + " at choice"',t),e.pushCode("}")},f.prototype.generateNest=function(e){var t=e.generateVariable(this.varName);if(this.options.type instanceof f)this.varName&&e.pushCode("{0} = {};",t),e.pushPath(this.varName),this.options.type.generate(e),e.popPath(this.varName);else if(h[this.options.type]){var i=e.generateTmpVariable();e.pushCode("var {0} = {1}(offset);",i,c+this.options.type),e.pushCode("{0} = {1}.result; offset = {1}.offset;",t,i),this.options.type!==this.alias&&e.addReference(this.options.type)}},f.prototype.generateFormatter=function(e,t,i){"function"==typeof i&&e.pushCode("{0} = ({1}).call(this, {0});",t,i)},f.prototype.isInteger=function(){return!!this.type.match(/U?Int[8|16|32][BE|LE]?|Bit\d+/)},f.prototype.itf8=function(e,t){return this.setNextParser("itf8",e,t)},f.prototype.itf8=function(e,t){return this.setNextParser("itf8",e,t)},f.prototype.generateItf8=function(e){var t=e.generateVariable(this.varName),i=e.generateTmpVariable();e.pushCode("\n var ".concat(i," = buffer[offset];\n if (").concat(i," < 0x80) {\n ").concat(t," = ").concat(i,";\n offset += 1;\n } else if (").concat(i," < 0xc0) {\n ").concat(t," = ((").concat(i,"<<8) | buffer[offset+1]) & 0x3fff;\n offset += 2;\n } else if (").concat(i," < 0xe0) {\n ").concat(t," = ((").concat(i,"<<16) | (buffer[offset+1]<< 8) | buffer[offset+2]) & 0x1fffff;\n offset += 3;\n } else if (").concat(i," < 0xf0) {\n ").concat(t," = ((").concat(i,"<<24) | (buffer[offset+1]<<16) | (buffer[offset+2]<<8) | buffer[offset+3]) & 0x0fffffff;\n offset += 4\n } else {\n ").concat(t," = ((").concat(i," & 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 "))},f.prototype.ltf8=function(e,t){return this.setNextParser("ltf8",e,t)},f.prototype.generateLtf8=function(e){var t=e.generateVariable(this.varName),i=e.generateTmpVariable();e.pushCode("\n var ".concat(i," = buffer[offset];\n if (").concat(i," < 0x80) {\n ").concat(t," = ").concat(i,";\n offset += 1;\n } else if (").concat(i," < 0xc0) {\n ").concat(t," = ((buffer[offset]<<8) | buffer[offset+1]) & 0x3fff;\n offset += 2;\n } else if (").concat(i," < 0xe0) {\n ").concat(t," = ((buffer[offset]<<16) | (buffer[offset+1]<<8) | buffer[offset+2]) & 0x1fffff;\n ").concat(t," = (((").concat(i," & 63) << 16) | buffer.readUInt16LE(offset + 1));\n offset += 3;\n } else if (").concat(i," < 0xf0) {\n ").concat(t," = ((buffer[offset]<<24) | (buffer[offset+1]<<16) | (buffer[offset+2]<<8) | buffer[offset+3]) & 0x0fffffff;\n offset += 4;\n } else if (").concat(i," < 0xf8) {\n ").concat(t," = (((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(i," < 0xfc) {\n ").concat(t," = ((((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(i," < 0xfe) {\n ").concat(t," = ((((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(i," < 0xff) {\n ").concat(t," = Long.fromBytesBE(buffer.slice(offset+1,offset+8));\n if (").concat(t,".greaterThan(Number.MAX_SAFE_INTEGER) || ").concat(t,".lessThan(Number.MIN_SAFE_INTEGER))\n throw new Error('integer overflow')\n ").concat(t," = ").concat(t,".toNumber()\n offset += 8;\n } else {\n ").concat(t," = Long.fromBytesBE(buffer.slice(offset+1,offset+9));\n if (").concat(t,".greaterThan(Number.MAX_SAFE_INTEGER) || ").concat(t,".lessThan(Number.MIN_SAFE_INTEGER))\n throw new Error('integer overflow')\n ").concat(t," = ").concat(t,".toNumber()\n offset += 9;\n }\n "))},t.Parser=f},2961:(e,t)=>{function i(e){return i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i(e)}var n=function(){this.code="",this.scopes=[["vars"]],this.isAsync=!1,this.bitFields=[],this.tmpVariableCount=0,this.references={}};n.prototype.generateVariable=function(e){var t=[];for(Array.prototype.push.apply(t,this.scopes[this.scopes.length-1]);/^\$parent\./.test(e);)t.pop(),e=e.replace(/^\$parent\./,"");return e&&t.push(e),t.join(".")},n.prototype.generateOption=function(e){switch(i(e)){case"number":return e.toString();case"string":return this.generateVariable(e);case"function":return"(".concat(e,").call(").concat(this.generateVariable(),", vars)");default:return}},n.prototype.generateError=function(){var e=Array.prototype.slice.call(arguments),t=n.interpolate.apply(this,e);this.isAsync?this.pushCode("return process.nextTick(function() { callback(new Error(".concat(t,"), vars); });")):this.pushCode("throw new Error(".concat(t,");"))},n.prototype.generateTmpVariable=function(){return"$tmp".concat(this.tmpVariableCount++)},n.prototype.pushCode=function(){var e=Array.prototype.slice.call(arguments);this.code+="".concat(n.interpolate.apply(this,e),"\n")},n.prototype.pushPath=function(e){e&&this.scopes[this.scopes.length-1].push(e)},n.prototype.popPath=function(e){e&&this.scopes[this.scopes.length-1].pop()},n.prototype.pushScope=function(e){this.scopes.push([e])},n.prototype.popScope=function(){this.scopes.pop()},n.prototype.addReference=function(e){this.references[e]||(this.references[e]={resolved:!1,requested:!1})},n.prototype.markResolved=function(e){this.references[e].resolved=!0},n.prototype.markRequested=function(e){e.forEach(function(e){this.references[e].requested=!0}.bind(this))},n.prototype.getUnresolvedReferences=function(){var e=this.references;return Object.keys(this.references).filter((function(t){return!e[t].resolved&&!e[t].requested}))},n.interpolate=function(e){var t=e.match(/{\d+}/g),i=Array.prototype.slice.call(arguments,1);return t&&t.forEach((function(t){var n=parseInt(t.substr(1,t.length-2),10);e=e.replace(t,i[n].toString())})),e},t._=n},22:e=>{e.exports.runInThisContext=function(e){return new Function("code","return eval(code);").call(globalThis,e)}},445:(e,t,i)=>{const n=i(7381),r=i(9260),s=i(576),o=i(4693),a=128;e.exports=class{decode(e){return this.stream=new r(e),this.decodeStream(this.stream)}decodeStream(e,t=0){var i=this.stream.ReadByte();16&i||(t=this.stream.ReadUint7());var n,r=t,s=1&i;if(8&i)return this.decodeStripe(this.stream,t);if(i&a&&([n,r]=this.decodePackMeta(this.stream)),32&i)var o=this.decodeCat(this.stream,r);else o=4&i?this.decodeExt(this.stream,r):64&i?s?this.decodeRLE1(this.stream,r):this.decodeRLE0(this.stream,r):s?this.decode1(this.stream,r):this.decode0(this.stream,r);return i&a&&(o=this.decodePack(o,n,t)),o}encode(e,t){if(this.stream=new r("",0,1.1*e.length+100),this.stream.WriteByte(t),16&t||this.stream.WriteUint7(e.length),8&t)return Buffer.concat([this.stream.buf.slice(0,this.stream.pos),this.encodeStripe(this.stream,e,t>>8)]);var i,n=1&t,s=e.length;return t&a&&([i,e,s]=this.encodePack(e)),t&a&&this.stream.WriteStream(i),64&t?n?this.encodeRLE1(e,s,this.stream):this.encodeRLE0(e,s,this.stream):n?this.encode1(e,s,this.stream):this.encode0(e,s,this.stream)}decode0(e,t){var i=new Buffer.allocUnsafe(t),r=e.ReadByte();0==r&&(r=256);var o=new s(r),a=new n(e);a.RangeStartDecode(e);for(var l=0;l=3?3:c;for(l[d].ModelEncode(i,h,u),c-=u,d=256;3==u;)u=c>=3?3:c,l[d].ModelEncode(i,h,u),d=257,c-=u}return h.RangeFinishEncode(i),i.buf.slice(0,i.pos)}decodeRLE1(e,t){var i=new Buffer.allocUnsafe(t),r=e.ReadByte();0==r&&(r=256);for(var o=new Array(r),a=0;a=3?3:d;for(l[u].ModelEncode(i,h,f),d-=f,u=256;3==f;)f=d>=3?3:d,l[u].ModelEncode(i,h,f),u=257,d-=f}return h.RangeFinishEncode(i),i.buf.slice(0,i.pos)}decodePackMeta(e){this.nsym=e.ReadByte();for(var t=new Array(this.nsym),i=0;i>=1}}else if(this.nsym<=4)for(r=0,s=0;r>=2;else{if(!(this.nsym<=16))return e;for(r=0,s=0;r>=4}return n}packMeta(e){for(var t=new r("",0,1024),i=new Array(256),n=0;no),n[o]=new Array(s[o]);for(var a=0,l=0;ls),o[s]=this.decodeStream(e,r[s]);var a=new Buffer.allocUnsafe(t);for(s=0;s{e.exports=class{constructor(e){this.low=0,this.range=4294967295,this.code=0,this.FFnum=0,this.carry=0,this.cache=0}RangeStartDecode(e){for(var t=0;t<5;t++)this.code=(this.code<<8)+e.ReadByte();this.code&=4294967295,this.code>>>=0}RangeGetFrequency(e){return this.range=Math.floor(this.range/e),Math.floor(this.code/this.range)}RangeDecode(e,t,i,n){for(this.code-=t*this.range,this.range*=i;this.range<1<<24;)this.range*=256,this.code=256*this.code+e.ReadByte()}RangeShiftLow(e){if(this.low<4278190080|this.carry){for(e.WriteByte(this.cache+this.carry);this.FFnum;)e.WriteByte(this.carry-1),this.FFnum--;this.cache=this.low>>>24,this.carry=0}else this.FFnum++;this.low<<=8,this.low>>>=0}RangeEncode(e,t,i,n){var r=this.low;for(this.range=Math.floor(this.range/n),this.low+=t*this.range,this.low>>>=0,this.range*=i,this.low{e.exports=class{constructor(e=256){this.total_freq=e,this.max_sym=e-1,this.S=new Array,this.F=new Array;for(var t=0;t<=this.max_sym;t++)this.S[t]=t,this.F[t]=1}ModelDecode(e,t){for(var i=t.RangeGetFrequency(this.total_freq),n=0,r=0;n+this.F[r]<=i;)n+=this.F[r++];t.RangeDecode(e,n,this.F[r],this.total_freq),this.F[r]+=16,this.total_freq+=16,this.total_freq>65519&&this.ModelRenormalise();var s=this.S[r];if(r>0&&this.F[r]>this.F[r-1]){var o=this.F[r];this.F[r]=this.F[r-1],this.F[r-1]=o,o=this.S[r],this.S[r]=this.S[r-1],this.S[r-1]=o}return s}ModelRenormalise(){this.total_freq=0;for(var e=0;e<=this.max_sym;e++)this.F[e]-=Math.floor(this.F[e]/2),this.total_freq+=this.F[e]}ModelEncode(e,t,i){for(var n=0,r=0;this.S[r]!=i;r++)n+=this.F[r];if(t.RangeEncode(e,n,this.F[r],this.total_freq),this.F[r]+=16,this.total_freq+=16,this.total_freq>65519&&this.ModelRenormalise(),i=this.S[r],r>0&&this.F[r]>this.F[r-1]){var s=this.F[r];this.F[r]=this.F[r-1],this.F[r-1]=s,s=this.S[r],this.S[r]=this.S[r-1],this.S[r-1]=s}}}},5260:(e,t,i)=>{const n=i(9260),r=i(576),s=i(7381);function o(e,t,i){for(var n=0,r=0,s=-1,o=new Array(1024);r>4,t.qshift=15&i,i=e.ReadByte(),t.qloc=i>>4,t.sloc=15&i,i=e.ReadByte(),t.ploc=i>>4,t.dloc=15&i,t.qmap=new Array(256),16&t.pflags)for(var n=0;n0&&128&t.pflags)o(e,t.qtab,256);else for(n=0;n<256;n++)t.qtab[n]=n;return t.ptab=new Array(1024),32&t.pflags&&o(e,t.ptab,1024),t.dtab=new Array(256),64&t.pflags&&o(e,t.dtab,256),t}function h(e,t,i,n,r,s){i.max_sel>0?r.s=n.sel.ModelDecode(e,t):r.s=0,r.x=i.stab[r.s];var o=i.params[r.x];if(o.fixed_len>=0){var a=n.len[0].ModelDecode(e,t);a|=n.len[1].ModelDecode(e,t)<<8,a|=n.len[2].ModelDecode(e,t)<<16,a|=n.len[3].ModelDecode(e,t)<<24,o.fixed_len>0&&(o.fixed_len=-a)}else a=-o.fixed_len;r.len=a,i.do_rev&&(s[r.rec]=n.rev.ModelDecode(e,t)),r.is_dup=0,2&o.pflags&&n.dup.ModelDecode(e,t)&&(r.is_dup=1),r.p=a,r.delta=0,r.qctx=0,r.prevq=0,r.rec++}function c(e,t,i){for(var n=0,r=0,s=new Array(2*i),o=0;n1?i.nparam-1:0,s=new Array(256);if(2&i)r=e.ReadByte(),o(e,s,256);else{for(var a=0;a0&&(t.sel=new r(e.max_sel+1)),t}(n),f=new s(e);f.RangeStartDecode(e);for(var p=new Buffer.allocUnsafe(i),g={qctx:0,prevq:0,delta:0,p:0,s:0,x:0,len:0,is_dup:0,rec:0},m=0;m0&&u.dup.ModelDecode(e,f)){for(var b=0;b4),qshift:d,qloc:7,pbits:7,pshift:t[0]>128?1:0,ploc:0,dbits:d>4?0:1,dshift:3,dloc:15,sbits:0,sloc:15,do_stab:0,context:0,max_sym:l,nsym:a,do_qmap:u,do_dedup:0,fixed_len:1==t.length?1:0,do_sel:0,do_rev:0,do_pos:1,do_delta:d<=4?1:0,do_qtab:0,qbits:8+(d>4)-(0==o),sbits:1,sloc:15-(d<=4),do_stab:1,do_sel:1}]}(e,t,i,o),p=function(e,t,i,n,r,s,o){for(var a=[0,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7],l=0;l1?1:0)|(t[0].do_stab?2:0);if(e.WriteByte(h),1&h&&e.WriteByte(t.length),2&h){var d=1<0&&d--,e.WriteByte(d),c(e,o,256)}for(var u=0;u0){for(l=0;l<256;l++)n[u][l]=l;t[u].do_qtab&&c(e,n[u],256)}if(t[u].pbits>0){for(l=0;l<1024;l++)r[u][l]=Math.min((1<>t[u].pshift);c(e,r[u],1024)}if(t[u].dbits>0){for(l=0;l<256;l++)a[l]>(1<>t[u].dshift)];c(e,s[u],256)}}return e}(p,f,o,a,l,h,d);return function(e,t,i,n,o,a,l,h,c,d){var u=1<0&&u--;for(var f=t.length,p=0,g=0;g0&&v.ModelEncode(e,y,x);var k=d[x],C=i[Math.min(i.length-1,_++)];o[k].fixed_len?o[k].fixed_len>0&&(w[0].ModelEncode(e,y,255&C),w[1].ModelEncode(e,y,C>>8&255),w[2].ModelEncode(e,y,C>>16&255),w[3].ModelEncode(e,y,C>>24&255),o[k].fixed_len=-1):(w[0].ModelEncode(e,y,255&C),w[1].ModelEncode(e,y,C>>8&255),w[2].ModelEncode(e,y,C>>16&255),w[3].ModelEncode(e,y,C>>24&255)),o[k].do_dedup&&process.exit(1),g=C;var S=0,A=o[k].context,E=0,M=0}var T=t[b++],R=a[k][T];m[A].ModelEncode(e,y,R),E=(E<0&&(A+=h[k][Math.min(g,1023)]<0&&(A+=c[k][Math.min(S,255)]<{var n=i(4459),r=i(594),s=i(445),o=i(5260),a=i(2881);e.exports={r4x8_uncompress:function(e,t){n.decode(e).copy(t,0,0)},r4x16_uncompress:function(e,t){r.decode(e).copy(t,0,0)},arith_uncompress:function(e,t){s.decode(e).copy(t,0,0)},fqzcomp_uncompress:function(e,t){var i=new Array;o.decode(e,i).copy(t,0,0)},tok3_uncompress:function(e,t){var i=a.decode(e,0,"\0");Buffer.from(i,"binary").copy(t,0,0)}}},9260:e=>{e.exports=class{constructor(e,t=0,i=0){0!=i?(this.buf=Buffer.allocUnsafe(i),this.length=i):(this.buf=e,this.length=e.length),this.pos=t}EOF(){return this.pos>=this.length}ReadData(e){var t=this.buf.slice(this.pos,this.pos+e);return this.pos+=e,t}ReadByte(){const e=this.buf[this.pos];return this.pos++,e}ReadChar(){const e=this.buf[this.pos];return this.pos++,String.fromCharCode(e)}ReadUint16(){return this.ReadByte()|this.ReadByte()<<8}ReadUint32(){const e=this.buf.readInt32LE(this.pos);return this.pos+=4,e}ReadString(){var e="";do{var t=this.buf[this.pos++];t&&(e+=String.fromCharCode(t))}while(t);return e}ReadUint7(){var e=0;do{var t=this.ReadByte();e=e<<7|127&t}while(128&t);return e}ReadITF8(){var e=this.buf[this.pos];return this.pos++,e>=240?(e=(15&e)<<28,e+=(this.buf[this.pos+0]<<20)+(this.buf[this.pos+1]<<12)+(this.buf[this.pos+2]<<4)+(this.buf[this.pos+3]>>4),this.pos+=4):e>=224?(e=(15&e)<<24,e+=(this.buf[this.pos+0]<<16)+(this.buf[this.pos+1]<<8)+(this.buf[this.pos+2]<<0),this.pos+=3):e>=192?(e=(31&e)<<16,e+=(this.buf[this.pos+0]<<8)+(this.buf[this.pos+1]<<0),this.pos+=2):e>=128&&(e=(63&e)<<8,e+=this.buf[this.pos],this.pos++),e}WriteByte(e){this.buf[this.pos++]=e}WriteChar(e){this.buf[this.pos++]=e.charCodeAt(0)}WriteString(e){for(var t=0;t>8&255)}WriteUint32(e){this.buf.writeInt32LE(e,this.pos),this.pos+=4}WriteUint7(e){var t=0,i=e;do{t+=7,i>>=7}while(i>0);do{t-=7,this.WriteByte((e>>t&127)+((t>0)<<7))}while(t>0)}WriteITF8(e){e<0&&(e=1+e),e<=127?this.buf[this.pos++]=e:e<=16383?(this.buf[this.pos++]=128|Math.floor(e/256),this.buf[this.pos++]=255&e):e<131071?(this.buf[this.pos++]=192|Math.floor(e/65536),this.buf[this.pos++]=255&Math.floor(e/256),this.buf[this.pos++]=255&e):e<268435455?(this.buf[this.pos++]=224|Math.floor(e/16777216),this.buf[this.pos++]=255&Math.floor(e/65536),this.buf[this.pos++]=255&Math.floor(e/256),this.buf[this.pos++]=255&e):(this.buf[this.pos++]=240|Math.floor(e/268435456),this.buf[this.pos++]=255&Math.floor(e/1048576),this.buf[this.pos++]=255&Math.floor(e/4096),this.buf[this.pos++]=255&Math.floor(e/4),this.buf[this.pos++]=15&e)}WriteByteNeg(e){this.buf[--this.pos]=e}}},4459:(e,t,i)=>{const n=i(9260);function r(e){return 4095&e}function s(e,t){for(var i=0;t>=e[i+1];)i++;return i}function o(e){for(var t=new Array(4096),i=0,n=0;n<4096;n++){for(;n>=e[i+1];)i++;t[n]=i}return t}function a(e,t,i){return i*(e>>12)+(4095&e)-t}function l(e,t){for(;t<1<<23;)t=(t<<8)+e.ReadByte();return t}function h(e,t){t.WriteByteNeg(e>>24&255),t.WriteByteNeg(e>>16&255),t.WriteByteNeg(e>>8&255),t.WriteByteNeg(e>>0&255)}function c(e,t,i,n,r){return e=function(e,t,i,n){for(var r=(1<<23>>n<<8)*i;e>=r;)t.WriteByteNeg(255&e),e>>=8;return e}(e,t,n,r),(Math.floor(e/n)<0?(o--,r++):(r=e.ReadByte())==s+1&&(o=e.ReadByte()),s=r}while(0!=r);for(i[0]=0,n=0;n<=255;n++)i[n+1]=i[n]+t[n]}function u(e){for(var t=0,i=0;i<256;i++)t+=e[i];const n=4096;var r=n/t;do{var s=0,o=0,a=0;for(t=0,i=0;i<256;i++)0!=e[i]&&(s2?e[o]-=t-n:t!=n&&(r*=.99,a=1)}while(a)}function f(e,t){for(var i=0,n=0;n<256;n++)if(t[n]){if(i>0)i--;else if(e.WriteByte(n),n>0&&t[n-1]>0){for(i=n+1;i<256&&t[i];i++);i-=n+1,e.WriteByte(i)}e.WriteITF8(t[n])}e.WriteByte(0)}e.exports={decode:function(e){var t=new n(e),i=t.ReadByte(),h=(t.ReadUint32(),t.ReadUint32());return 0==i?function(e,t){var i=new Array(256),n=new Array(256);d(e,i,n);for(var s=o(n),h=new Array(4),c=0;c<4;c++)h[c]=e.ReadUint32();var u=new Buffer.allocUnsafe(t);for(c=0;c0?(a--,s++):(s=e.ReadByte())==o+1&&(a=e.ReadByte()),o=s}while(0!=s)}(e,i,n);for(var h=new Array(256),c=0;c<256;c++)h[c]=o(n[c]);for(var u=new Array(4),f=new Array(4),p=0;p<4;p++)u[p]=e.ReadUint32(),f[p]=0;var g=new Buffer.allocUnsafe(t),m=Math.floor(t/4);for(c=0;c=0;o--)a[o%4]=c(a[o%4],d,s[e[o]],r[e[o]],12);for(o=3;o>=0;o--)h(a[o],d);var p=i.pos;return i.buf.writeInt32LE(p-9+(d.length-d.pos),1),i.buf.writeInt32LE(t,5),Buffer.concat([i.buf.slice(0,i.pos),d.buf.slice(d.pos,d.length)],i.pos+d.length-d.pos)}(e):function(e){const t=e.length;var i=new n("",0,198156);i.WriteByte(1),i.WriteUint32(0),i.WriteUint32(0);for(var r=new Array(256),s=new Array(256),o=new Array(256),a=0;a<256;a++)s[a]=new Array(256),o[a]=new Array(256);for(function(e,t,i){for(var n=0;n<256;n++){i[n]=0;for(var r=0;r<256;r++)t[n][r]=0}var s=0;for(n=0;n>2)]]++,t[0][e[2*(e.length>>2)]]++,t[0][e[3*(e.length>>2)]]++,i[0]+=3}(e,s,r),function(e,t){for(var i=0;i<256;i++)t[i]&&u(e[i])}(s,r),function(e,t,i){for(var n=0,r=0;r<256;r++)if(i[r]){if(n>0)n--;else if(e.WriteByte(r),r>0&&i[r-1]>0){for(n=r+1;n<256&&i[n];n++);n-=r+1,e.WriteByte(n)}f(e,t[r])}e.WriteByte(0)}(i,s,r),a=0;a<256;a++)if(r[a]){o[a][0]=0;for(var l=1;l<256;l++)o[a][l]=o[a][l-1]+s[a][l-1]}var d=new Array(4),p=new Array(4);for(l=0;l<4;l++)d[l]=1<<23,p[l]=0;var g=new n("",t,t),m=Math.floor(t/4),b=new Array(4),w=new Array(4);for(l=0;l<4;l++)b[l]=(l+1)*m-2,w[l]=e[b[l]+1];for(w[3]=e[t-1],a=t-2;a>4*m-2;a--)d[3]=c(d[3],g,o[e[a]][w[3]],s[e[a]][w[3]],12),w[3]=e[a];for(;b[0]>=0;)for(l=3;l>=0;l--){var v=e[b[l]];d[l]=c(d[l],g,o[v][w[l]],s[v][w[l]],12),w[l]=v,b[l]--}for(l=3;l>=0;l--)d[l]=c(d[l],g,o[0][w[l]],s[0][w[l]],12);for(a=3;a>=0;a--)h(d[a],g);var y=i.pos;return i.buf.writeInt32LE(y-9+(g.length-g.pos),1),i.buf.writeInt32LE(t,5),Buffer.concat([i.buf.slice(0,i.pos),g.buf.slice(g.pos,g.length)],i.pos+g.length-g.pos)}(e)}}},594:(e,t,i)=>{const n=i(9260);function r(e,t){return e&(1<=e[i+1];)i++;return i}function o(e,t){for(var i=1<=e[r+1];)r++;n[s]=r}return n}function a(e,t,i,n){return i*(e>>n)+(e&(1<>24&255),t.WriteByteNeg(e>>16&255),t.WriteByteNeg(e>>8&255),t.WriteByteNeg(e>>0&255)}function c(e,t,i,n,r){return e=function(e,t,i,n){for(var r=(1<<31-n)*i;e>=r;)t.WriteByteNeg(e>>8&255),t.WriteByteNeg(255&e),e>>=16;return e}(e,t,n,r),(Math.floor(e/n)<o),r[o]=new Array(s[o]);for(var a=0,l=0;ls),o[s]=u(e,r[s]);var a=new Buffer.allocUnsafe(t);for(s=0;s>4,h=e;if(1&u){var c=e.ReadUint7(),d=e.ReadUint7(),u=new n(e.ReadData(d));h=new n(g(u,c))}var f=new Array(256),m=new Array(256);!function(e,t,i,n){for(var r=0;r<256;r++){t[r]=new Array(256),i[r]=new Array(256);for(var s=0;s<256;s++)t[r][s]=0}var o=p(e);for(r=0;r<256;r++)if(o[r]){var a=0;for(s=0;s<256;s++)o[s]&&(a>0?a--:(t[r][s]=e.ReadUint7(),0==t[r][s]&&(a=e.ReadByte())));for(b(t[r],n),i[r][0]=0,s=0;s<256;s++)i[r][s+1]=i[r][s]+t[r][s]}}(h,f,m,i);for(var w=new Array(256),v=0;v<256;v++)w[v]=o(m[v],i);for(var y=new Array(4),_=new Array(4),x=0;x<4;x++)y[x]=e.ReadUint32(),_[x]=0;var k=new Buffer.allocUnsafe(t),C=Math.floor(t/4);for(v=0;v>=1}else if(i<=4)for(o=0;o>=2;else if(i<=16)for(o=0;o>=4;return r}(C,v,y,w)),C}function f(e,t){var i=new n("",0,10);i.WriteByte(t);var r=1&t,s=8&t,o=32&t,a=64&t,l=128&t,u=t>>8;if(16&t||i.WriteUint7(e.length),s)return Buffer.concat([i.buf.slice(0,i.pos),d(0,e,u)]);var f=new Buffer.alloc(0);l&&([f,e]=function(e){for(var t=new Array(256),i=0;i<256;i++)t[i]=0;for(i=0;i0&&(r[i]=s++);if(!(s>16)){if(s<=1)var o=new Buffer.allocUnsafe(0);else if(s<=2){o=new Buffer.allocUnsafe(Math.ceil(e.length/8));var a=-1;for(i=0;i0&&(t[i]=a++,l.WriteByte(i));return l.WriteUint7(o.length),[l.buf.slice(0,l.pos),o]}}(e));var p=new Buffer.alloc(0);if(a&&([p,e]=function(e){for(var t=new Array(256),i=0;i<256;i++)t[i]=0;var r=-1;for(i=0;i0&&s++;for(s||(s=1,t[0]=1),(h=new n("",0,s+1+e.length)).WriteByte(s),i=0;i<256;i++)t[i]>0&&h.WriteByte(i);var o=new Buffer.allocUnsafe(e.length),a=0;for(i=0;i0){r=e[i];for(var l=0;i+l+1>2)]]++,t[0][e[2*(e.length>>2)]]++,t[0][e[3*(e.length>>2)]]++,i[0]+=3})(e,s,r),function(e,t,i){for(var n=0;n<256;n++)if(t[n]){var r=Math.ceil(Math.log2(t[n]));r>12&&(r=12),m(e[n],r)}}(s,r);var l=new n("",0,198156);!function(e,t,i){w(e,i);for(var n=0;n<256;n++)if(i[n])for(var r=0,s=0;s<256;s++)if(i[s])if(r)r--;else if(e.WriteUint7(t[n][s]),!t[n][s]){for(var o=s+1;o<256;o++)if(i[o]){if(0!=t[n][o])break;r++}e.WriteByte(r)}}(l,s,r);var d=v(l.buf.slice(0,l.pos));for(d.length>0,1.05*t+100>>0),y=Math.floor(t/4),_=new Array(4),x=new Array(4);for(u=0;u<4;u++)_[u]=(u+1)*y-2,x[u]=e[_[u]+1];for(x[3]=e[t-1],a=t-2;a>4*y-2;a--)f[3]=c(f[3],g,o[e[a]][x[3]],s[e[a]][x[3]],12),x[3]=e[a];for(;_[0]>=0;)for(u=3;u>=0;u--){var k=e[_[u]];f[u]=c(f[u],g,o[k][x[u]],s[k][x[u]],12),x[u]=k,_[u]--}for(u=3;u>=0;u--)f[u]=c(f[u],g,o[0][x[u]],s[0][x[u]],12);for(a=3;a>=0;a--)h(f[a],g);return Buffer.concat([i.buf.slice(0,i.pos),g.buf.slice(g.pos,g.length)],i.pos+g.length-g.pos)}(e);return Buffer.concat([i.buf.slice(0,i.pos),f,p,g])}function p(e){for(var t=new Array(256),i=0;i<256;i++)t[i]=0;var n=0,r=e.ReadByte(),s=r;do{t[r]=1,n>0?(n--,r++):(r=e.ReadByte())==s+1&&(n=e.ReadByte()),s=r}while(0!=r);return t}function g(e,t){var i=new Array(256),n=new Array(256);!function(e,t,i){for(var n=0;n<256;n++)t[n]=0;var r=p(e);for(n=0;n<256;n++)r[n]>0&&(t[n]=e.ReadUint7());for(b(t,12),i[0]=0,n=0;n<=255;n++)i[n+1]=i[n]+t[n]}(e,i,n);for(var s=o(n,12),h=new Array(4),c=0;c<4;c++)h[c]=e.ReadUint32();var d=new Buffer.allocUnsafe(t);for(c=0;c2?e[a]-=i-r:i!=r&&(s=r/i,l=1)}while(l)}function b(e,t){for(var i=0,n=0;n<256;n++)i+=e[n];if(0!=i&&i!=1<0)i--;else if(e.WriteByte(n),n>0&&t[n-1]>0){for(i=n+1;i<256&&t[i];i++);i-=n+1,e.WriteByte(i)}e.WriteByte(0)}function v(e){const t=e.length;var i=new n("",0,780),r=new Array(256);!function(e,t){for(var i=0;i<256;i++)t[i]=0;for(i=0;i12&&(s=12),m(r,s),function(e,t){w(e,t);for(var i=0;i<256;i++)t[i]&&e.WriteUint7(t[i])}(i,r),m(r,12);var o=new Array(256);o[0]=0;for(var a=1;a<256;a++)o[a]=o[a-1]+r[a-1];var l=new Array(4);for(a=0;a<4;a++)l[a]=32768;var d=new n("",1.05*t+100>>0,1.05*t+100>>0);for(a=t-1;a>=0;a--)l[a%4]=c(l[a%4],d,o[e[a]],r[e[a]],12);for(a=3;a>=0;a--)h(l[a],d);return Buffer.concat([i.buf.slice(0,i.pos),d.buf.slice(d.pos,d.length)],i.pos+d.length-d.pos)}e.exports={decode:function(e){return u(new n(e),0)},encode:f}},2881:(e,t,i)=>{const n=i(9260),r=i(594);var s=new(i(445));function o(e,t){for(var i=e+"";i.length>0)+e[a][8].ReadByte();break;case 9:l=(i[s][a]>>0)+e[a][9].ReadByte(),h=i[s][a].length,i[n][a]=o(l,h);break;case 10:i[n][a]=i[s][a];break;default:i[n][a]=""}t[n]+=i[n][a++]}while(12!=r);return t[n]}function l(e,t,i,n,r,s){for(var o=0;o0&&5==t[o][0].type)&&t[o][i])switch(e[0].WriteByte(t[o][i].type),t[o][i].type){case 6:e[6].WriteUint32(t[o][i].val);break;case 5:e[5].WriteUint32(t[o][i].val);break;case 1:e[1].WriteString(t[o][i].val);break;case 2:e[2].WriteChar(t[o][i].val);break;case 7:e[7].WriteUint32(t[o][i].val);break;case 3:e[3].WriteUint32(t[o][i].val),e[4].WriteByte(t[o][i].val.length);break;case 8:case 9:e[t[o][i].type].WriteByte(t[o][i].val)}}function h(e,t,i,n){for(var r=0;r<=12;r++)if(!(e[r].pos<=0)){n.WriteByte(r+(0==r?128:0)),e[r]=e[r].buf.slice(0,e[r].pos);var s=c(e[r],i);n.WriteUint7(s.length),n.WriteData(s,s.length)}}function c(e,t){var i,n=1<<30,o=[0,1,64,65,128,129,201];for(var a in o){var l=o[a];if(!(1&l&&e.length<100||8&l&&e.length%4!=0)){try{var h=t?s.encode(e,l):r.encode(e,l)}catch(e){h=0}h&&n>h.length&&(n=h.length,i=h)}}return i}function d(e,t,i,n,r){var s=0,o=r-1;e[r]=new Array(256),t[n]?e[r][0]={type:5,val:r-t[n]}:e[r][0]={type:6,val:0==r?0:1},t[n]=r;for(var a=n.match(/([a-zA-Z0-9]{1,9})|([^a-zA-Z0-9]+)/g),l=0;l=0&&e[o][h])if(e[o][h].str==a[l])c=10,d="";else if(7==e[o][h].type||8==e[o][h].type){var u=d-e[o][h].str;i[h]++,u>=0&&u<256&&i[h]>r/2&&(c=8,d=u)}else 3!=e[o][h].type&&9!=e[o][h].type||e[o][h].str.length!=d.length||(u=d-e[o][h].str,i[h]++,u>=0&&u<256&&i[h]>r/2&&(c=9,d=u));e[r][h]={str:a[l],val:d,type:c},s{a.callback(e)})),settled:!1,statusReporter:a,get aborted(){return this.aborter.signal.aborted}};l.aborter.addSignal(i),l.aborter.signal.addEventListener("abort",(()=>{l.settled||this.evict(e,l)})),l.promise.then((()=>{l.settled=!0}),(()=>{l.settled=!0,this.evict(e,l)})).catch((e=>{throw console.error(e),e})),this.cache.set(e,l)}static checkSinglePromise(e,t){function i(){if(t&&t.aborted)throw Object.assign(new Error("aborted"),{code:"ERR_ABORTED"})}return e.then((e=>(i(),e)),(e=>{throw i(),e}))}has(e){return this.cache.has(e)}get(e,t,i,n){if(!i&&t instanceof r.AbortSignal)throw new TypeError("second get argument appears to be an AbortSignal, perhaps you meant to pass `null` for the fill data?");const s=this.cache.get(e);return s?s.aborted&&!s.settled?(this.evict(e,s),this.get(e,t,i,n)):s.settled?s.promise:(s.aborter.addSignal(i),s.statusReporter.addCallback(n),a.checkSinglePromise(s.promise,i)):(this.fill(e,t,i,n),a.checkSinglePromise(this.cache.get(e).promise,i))}delete(e){const t=this.cache.get(e);t&&(t.settled||t.aborter.abort(),this.cache.delete(e))}clear(){const e=this.cache.keys();let t=0;for(let i=e.next();!i.done;i=e.next())this.delete(i.value),t+=1;return t}}t.default=a},9049:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=i(8904);class r{}t.default=class{constructor(){this.signals=new Set,this.abortController=new n.AbortController}addSignal(e=new r){if(this.signal.aborted)throw new Error("cannot add a signal, already aborted!");this.signals.add(e),e.aborted?this.handleAborted(e):"function"==typeof e.addEventListener&&e.addEventListener("abort",(()=>{this.handleAborted(e)}))}handleAborted(e){this.signals.delete(e),0===this.signals.size&&this.abortController.abort()}get signal(){return this.abortController.signal}abort(){this.abortController.abort()}}},450:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(){this.callbacks=new Set}addCallback(e=(()=>{})){this.callbacks.add(e),e(this.currentMessage)}callback(e){this.currentMessage=e,this.callbacks.forEach((t=>{t(e)}))}}},8904:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AbortSignal=t.AbortController=void 0;const n=i(5988);var r=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==i.g)return i.g;throw new Error("unable to locate global object")};let s=void 0===r().AbortController?n.AbortController:r().AbortController;t.AbortController=s;let o=void 0===r().AbortController?n.AbortSignal:r().AbortSignal;t.AbortSignal=o},4105:function(e,t,i){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=n(i(1422));t.default=r.default},5988:(e,t)=>{function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var i=0;i{t.byteLength=function(e){var t=a(e),i=t[0],n=t[1];return 3*(i+n)/4-n},t.toByteArray=function(e){var t,i,s=a(e),o=s[0],l=s[1],h=new r(function(e,t,i){return 3*(t+i)/4-i}(0,o,l)),c=0,d=l>0?o-4:o;for(i=0;i>16&255,h[c++]=t>>8&255,h[c++]=255&t;return 2===l&&(t=n[e.charCodeAt(i)]<<2|n[e.charCodeAt(i+1)]>>4,h[c++]=255&t),1===l&&(t=n[e.charCodeAt(i)]<<10|n[e.charCodeAt(i+1)]<<4|n[e.charCodeAt(i+2)]>>2,h[c++]=t>>8&255,h[c++]=255&t),h},t.fromByteArray=function(e){for(var t,n=e.length,r=n%3,s=[],o=16383,a=0,h=n-r;ah?h:a+o));return 1===r?(t=e[n-1],s.push(i[t>>2]+i[t<<4&63]+"==")):2===r&&(t=(e[n-2]<<8)+e[n-1],s.push(i[t>>10]+i[t>>4&63]+i[t<<2&63]+"=")),s.join("")};for(var i=[],n=[],r="undefined"!=typeof Uint8Array?Uint8Array:Array,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0;o<64;++o)i[o]=s[o],n[s.charCodeAt(o)]=o;function a(e){var t=e.length;if(t%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var i=e.indexOf("=");return-1===i&&(i=t),[i,i===t?0:4-i%4]}function l(e,t,n){for(var r,s,o=[],a=t;a>18&63]+i[s>>12&63]+i[s>>6&63]+i[63&s]);return o.join("")}n["-".charCodeAt(0)]=62,n["_".charCodeAt(0)]=63},2779:(e,t,i)=>{var n=i(8764).Buffer,r=[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 s(e){if(n.isBuffer(e))return e;var t="function"==typeof n.alloc&&"function"==typeof n.from;if("number"==typeof e)return t?n.alloc(e):new n(e);if("string"==typeof e)return t?n.from(e):new n(e);throw new Error("input must be buffer, number, or string, received "+typeof e)}function o(e,t){e=s(e),n.isBuffer(t)&&(t=t.readUInt32BE(0));for(var i=-1^~~t,o=0;o>>8;return-1^i}function a(){return function(e){var t=s(4);return t.writeInt32BE(e,0),t}(o.apply(null,arguments))}"undefined"!=typeof Int32Array&&(r=new Int32Array(r)),a.signed=function(){return o.apply(null,arguments)},a.unsigned=function(){return o.apply(null,arguments)>>>0},e.exports=a},8764:(e,t,i)=>{const n=i(9742),r=i(645),s="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=l,t.SlowBuffer=function(e){return+e!=e&&(e=0),l.alloc(+e)},t.INSPECT_MAX_BYTES=50;const o=2147483647;function a(e){if(e>o)throw new RangeError('The value "'+e+'" is invalid for option "size"');const t=new Uint8Array(e);return Object.setPrototypeOf(t,l.prototype),t}function l(e,t,i){if("number"==typeof e){if("string"==typeof t)throw new TypeError('The "string" argument must be of type string. Received type number');return d(e)}return h(e,t,i)}function h(e,t,i){if("string"==typeof e)return function(e,t){if("string"==typeof t&&""!==t||(t="utf8"),!l.isEncoding(t))throw new TypeError("Unknown encoding: "+t);const i=0|g(e,t);let n=a(i);const r=n.write(e,t);return r!==i&&(n=n.slice(0,r)),n}(e,t);if(ArrayBuffer.isView(e))return function(e){if(Q(e,Uint8Array)){const t=new Uint8Array(e);return f(t.buffer,t.byteOffset,t.byteLength)}return u(e)}(e);if(null==e)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);if(Q(e,ArrayBuffer)||e&&Q(e.buffer,ArrayBuffer))return f(e,t,i);if("undefined"!=typeof SharedArrayBuffer&&(Q(e,SharedArrayBuffer)||e&&Q(e.buffer,SharedArrayBuffer)))return f(e,t,i);if("number"==typeof e)throw new TypeError('The "value" argument must not be of type number. Received type number');const n=e.valueOf&&e.valueOf();if(null!=n&&n!==e)return l.from(n,t,i);const r=function(e){if(l.isBuffer(e)){const t=0|p(e.length),i=a(t);return 0===i.length||e.copy(i,0,0,t),i}return void 0!==e.length?"number"!=typeof e.length||X(e.length)?a(0):u(e):"Buffer"===e.type&&Array.isArray(e.data)?u(e.data):void 0}(e);if(r)return r;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof e[Symbol.toPrimitive])return l.from(e[Symbol.toPrimitive]("string"),t,i);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e)}function c(e){if("number"!=typeof e)throw new TypeError('"size" argument must be of type number');if(e<0)throw new RangeError('The value "'+e+'" is invalid for option "size"')}function d(e){return c(e),a(e<0?0:0|p(e))}function u(e){const t=e.length<0?0:0|p(e.length),i=a(t);for(let n=0;n=o)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+o.toString(16)+" bytes");return 0|e}function g(e,t){if(l.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||Q(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);const i=e.length,n=arguments.length>2&&!0===arguments[2];if(!n&&0===i)return 0;let r=!1;for(;;)switch(t){case"ascii":case"latin1":case"binary":return i;case"utf8":case"utf-8":return W(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*i;case"hex":return i>>>1;case"base64":return G(e).length;default:if(r)return n?-1:W(e).length;t=(""+t).toLowerCase(),r=!0}}function m(e,t,i){let n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===i||i>this.length)&&(i=this.length),i<=0)return"";if((i>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return R(this,t,i);case"utf8":case"utf-8":return A(this,t,i);case"ascii":return M(this,t,i);case"latin1":case"binary":return T(this,t,i);case"base64":return S(this,t,i);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return L(this,t,i);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}function b(e,t,i){const n=e[t];e[t]=e[i],e[i]=n}function w(e,t,i,n,r){if(0===e.length)return-1;if("string"==typeof i?(n=i,i=0):i>2147483647?i=2147483647:i<-2147483648&&(i=-2147483648),X(i=+i)&&(i=r?0:e.length-1),i<0&&(i=e.length+i),i>=e.length){if(r)return-1;i=e.length-1}else if(i<0){if(!r)return-1;i=0}if("string"==typeof t&&(t=l.from(t,n)),l.isBuffer(t))return 0===t.length?-1:v(e,t,i,n,r);if("number"==typeof t)return t&=255,"function"==typeof Uint8Array.prototype.indexOf?r?Uint8Array.prototype.indexOf.call(e,t,i):Uint8Array.prototype.lastIndexOf.call(e,t,i):v(e,[t],i,n,r);throw new TypeError("val must be string, number or Buffer")}function v(e,t,i,n,r){let s,o=1,a=e.length,l=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;o=2,a/=2,l/=2,i/=2}function h(e,t){return 1===o?e[t]:e.readUInt16BE(t*o)}if(r){let n=-1;for(s=i;sa&&(i=a-l),s=i;s>=0;s--){let i=!0;for(let n=0;nr&&(n=r):n=r;const s=t.length;let o;for(n>s/2&&(n=s/2),o=0;o>8,r=i%256,s.push(r),s.push(n);return s}(t,e.length-i),e,i,n)}function S(e,t,i){return 0===t&&i===e.length?n.fromByteArray(e):n.fromByteArray(e.slice(t,i))}function A(e,t,i){i=Math.min(e.length,i);const n=[];let r=t;for(;r239?4:t>223?3:t>191?2:1;if(r+o<=i){let i,n,a,l;switch(o){case 1:t<128&&(s=t);break;case 2:i=e[r+1],128==(192&i)&&(l=(31&t)<<6|63&i,l>127&&(s=l));break;case 3:i=e[r+1],n=e[r+2],128==(192&i)&&128==(192&n)&&(l=(15&t)<<12|(63&i)<<6|63&n,l>2047&&(l<55296||l>57343)&&(s=l));break;case 4:i=e[r+1],n=e[r+2],a=e[r+3],128==(192&i)&&128==(192&n)&&128==(192&a)&&(l=(15&t)<<18|(63&i)<<12|(63&n)<<6|63&a,l>65535&&l<1114112&&(s=l))}}null===s?(s=65533,o=1):s>65535&&(s-=65536,n.push(s>>>10&1023|55296),s=56320|1023&s),n.push(s),r+=o}return function(e){const t=e.length;if(t<=E)return String.fromCharCode.apply(String,e);let i="",n=0;for(;nn.length?(l.isBuffer(t)||(t=l.from(t)),t.copy(n,r)):Uint8Array.prototype.set.call(n,t,r);else{if(!l.isBuffer(t))throw new TypeError('"list" argument must be an Array of Buffers');t.copy(n,r)}r+=t.length}return n},l.byteLength=g,l.prototype._isBuffer=!0,l.prototype.swap16=function(){const e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let t=0;ti&&(e+=" ... "),""},s&&(l.prototype[s]=l.prototype.inspect),l.prototype.compare=function(e,t,i,n,r){if(Q(e,Uint8Array)&&(e=l.from(e,e.offset,e.byteLength)),!l.isBuffer(e))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===t&&(t=0),void 0===i&&(i=e?e.length:0),void 0===n&&(n=0),void 0===r&&(r=this.length),t<0||i>e.length||n<0||r>this.length)throw new RangeError("out of range index");if(n>=r&&t>=i)return 0;if(n>=r)return-1;if(t>=i)return 1;if(this===e)return 0;let s=(r>>>=0)-(n>>>=0),o=(i>>>=0)-(t>>>=0);const a=Math.min(s,o),h=this.slice(n,r),c=e.slice(t,i);for(let e=0;e>>=0,isFinite(i)?(i>>>=0,void 0===n&&(n="utf8")):(n=i,i=void 0)}const r=this.length-t;if((void 0===i||i>r)&&(i=r),e.length>0&&(i<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");let s=!1;for(;;)switch(n){case"hex":return y(this,e,t,i);case"utf8":case"utf-8":return _(this,e,t,i);case"ascii":case"latin1":case"binary":return x(this,e,t,i);case"base64":return k(this,e,t,i);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,e,t,i);default:if(s)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),s=!0}},l.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const E=4096;function M(e,t,i){let n="";i=Math.min(e.length,i);for(let r=t;rn)&&(i=n);let r="";for(let n=t;ni)throw new RangeError("Trying to access beyond buffer length")}function B(e,t,i,n,r,s){if(!l.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>r||te.length)throw new RangeError("Index out of range")}function F(e,t,i,n,r){U(t,n,r,e,i,7);let s=Number(t&BigInt(4294967295));e[i++]=s,s>>=8,e[i++]=s,s>>=8,e[i++]=s,s>>=8,e[i++]=s;let o=Number(t>>BigInt(32)&BigInt(4294967295));return e[i++]=o,o>>=8,e[i++]=o,o>>=8,e[i++]=o,o>>=8,e[i++]=o,i}function N(e,t,i,n,r){U(t,n,r,e,i,7);let s=Number(t&BigInt(4294967295));e[i+7]=s,s>>=8,e[i+6]=s,s>>=8,e[i+5]=s,s>>=8,e[i+4]=s;let o=Number(t>>BigInt(32)&BigInt(4294967295));return e[i+3]=o,o>>=8,e[i+2]=o,o>>=8,e[i+1]=o,o>>=8,e[i]=o,i+8}function O(e,t,i,n,r,s){if(i+n>e.length)throw new RangeError("Index out of range");if(i<0)throw new RangeError("Index out of range")}function P(e,t,i,n,s){return t=+t,i>>>=0,s||O(e,0,i,4),r.write(e,t,i,n,23,4),i+4}function D(e,t,i,n,s){return t=+t,i>>>=0,s||O(e,0,i,8),r.write(e,t,i,n,52,8),i+8}l.prototype.slice=function(e,t){const i=this.length;(e=~~e)<0?(e+=i)<0&&(e=0):e>i&&(e=i),(t=void 0===t?i:~~t)<0?(t+=i)<0&&(t=0):t>i&&(t=i),t>>=0,t>>>=0,i||I(e,t,this.length);let n=this[e],r=1,s=0;for(;++s>>=0,t>>>=0,i||I(e,t,this.length);let n=this[e+--t],r=1;for(;t>0&&(r*=256);)n+=this[e+--t]*r;return n},l.prototype.readUint8=l.prototype.readUInt8=function(e,t){return e>>>=0,t||I(e,1,this.length),this[e]},l.prototype.readUint16LE=l.prototype.readUInt16LE=function(e,t){return e>>>=0,t||I(e,2,this.length),this[e]|this[e+1]<<8},l.prototype.readUint16BE=l.prototype.readUInt16BE=function(e,t){return e>>>=0,t||I(e,2,this.length),this[e]<<8|this[e+1]},l.prototype.readUint32LE=l.prototype.readUInt32LE=function(e,t){return e>>>=0,t||I(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},l.prototype.readUint32BE=l.prototype.readUInt32BE=function(e,t){return e>>>=0,t||I(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},l.prototype.readBigUInt64LE=K((function(e){q(e>>>=0,"offset");const t=this[e],i=this[e+7];void 0!==t&&void 0!==i||j(e,this.length-8);const n=t+256*this[++e]+65536*this[++e]+this[++e]*2**24,r=this[++e]+256*this[++e]+65536*this[++e]+i*2**24;return BigInt(n)+(BigInt(r)<>>=0,"offset");const t=this[e],i=this[e+7];void 0!==t&&void 0!==i||j(e,this.length-8);const n=t*2**24+65536*this[++e]+256*this[++e]+this[++e],r=this[++e]*2**24+65536*this[++e]+256*this[++e]+i;return(BigInt(n)<>>=0,t>>>=0,i||I(e,t,this.length);let n=this[e],r=1,s=0;for(;++s=r&&(n-=Math.pow(2,8*t)),n},l.prototype.readIntBE=function(e,t,i){e>>>=0,t>>>=0,i||I(e,t,this.length);let n=t,r=1,s=this[e+--n];for(;n>0&&(r*=256);)s+=this[e+--n]*r;return r*=128,s>=r&&(s-=Math.pow(2,8*t)),s},l.prototype.readInt8=function(e,t){return e>>>=0,t||I(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},l.prototype.readInt16LE=function(e,t){e>>>=0,t||I(e,2,this.length);const i=this[e]|this[e+1]<<8;return 32768&i?4294901760|i:i},l.prototype.readInt16BE=function(e,t){e>>>=0,t||I(e,2,this.length);const i=this[e+1]|this[e]<<8;return 32768&i?4294901760|i:i},l.prototype.readInt32LE=function(e,t){return e>>>=0,t||I(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},l.prototype.readInt32BE=function(e,t){return e>>>=0,t||I(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},l.prototype.readBigInt64LE=K((function(e){q(e>>>=0,"offset");const t=this[e],i=this[e+7];void 0!==t&&void 0!==i||j(e,this.length-8);const n=this[e+4]+256*this[e+5]+65536*this[e+6]+(i<<24);return(BigInt(n)<>>=0,"offset");const t=this[e],i=this[e+7];void 0!==t&&void 0!==i||j(e,this.length-8);const n=(t<<24)+65536*this[++e]+256*this[++e]+this[++e];return(BigInt(n)<>>=0,t||I(e,4,this.length),r.read(this,e,!0,23,4)},l.prototype.readFloatBE=function(e,t){return e>>>=0,t||I(e,4,this.length),r.read(this,e,!1,23,4)},l.prototype.readDoubleLE=function(e,t){return e>>>=0,t||I(e,8,this.length),r.read(this,e,!0,52,8)},l.prototype.readDoubleBE=function(e,t){return e>>>=0,t||I(e,8,this.length),r.read(this,e,!1,52,8)},l.prototype.writeUintLE=l.prototype.writeUIntLE=function(e,t,i,n){e=+e,t>>>=0,i>>>=0,n||B(this,e,t,i,Math.pow(2,8*i)-1,0);let r=1,s=0;for(this[t]=255&e;++s>>=0,i>>>=0,n||B(this,e,t,i,Math.pow(2,8*i)-1,0);let r=i-1,s=1;for(this[t+r]=255&e;--r>=0&&(s*=256);)this[t+r]=e/s&255;return t+i},l.prototype.writeUint8=l.prototype.writeUInt8=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,1,255,0),this[t]=255&e,t+1},l.prototype.writeUint16LE=l.prototype.writeUInt16LE=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},l.prototype.writeUint16BE=l.prototype.writeUInt16BE=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},l.prototype.writeUint32LE=l.prototype.writeUInt32LE=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},l.prototype.writeUint32BE=l.prototype.writeUInt32BE=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},l.prototype.writeBigUInt64LE=K((function(e,t=0){return F(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),l.prototype.writeBigUInt64BE=K((function(e,t=0){return N(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),l.prototype.writeIntLE=function(e,t,i,n){if(e=+e,t>>>=0,!n){const n=Math.pow(2,8*i-1);B(this,e,t,i,n-1,-n)}let r=0,s=1,o=0;for(this[t]=255&e;++r>0)-o&255;return t+i},l.prototype.writeIntBE=function(e,t,i,n){if(e=+e,t>>>=0,!n){const n=Math.pow(2,8*i-1);B(this,e,t,i,n-1,-n)}let r=i-1,s=1,o=0;for(this[t+r]=255&e;--r>=0&&(s*=256);)e<0&&0===o&&0!==this[t+r+1]&&(o=1),this[t+r]=(e/s>>0)-o&255;return t+i},l.prototype.writeInt8=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},l.prototype.writeInt16LE=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},l.prototype.writeInt16BE=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},l.prototype.writeInt32LE=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},l.prototype.writeInt32BE=function(e,t,i){return e=+e,t>>>=0,i||B(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},l.prototype.writeBigInt64LE=K((function(e,t=0){return F(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),l.prototype.writeBigInt64BE=K((function(e,t=0){return N(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),l.prototype.writeFloatLE=function(e,t,i){return P(this,e,t,!0,i)},l.prototype.writeFloatBE=function(e,t,i){return P(this,e,t,!1,i)},l.prototype.writeDoubleLE=function(e,t,i){return D(this,e,t,!0,i)},l.prototype.writeDoubleBE=function(e,t,i){return D(this,e,t,!1,i)},l.prototype.copy=function(e,t,i,n){if(!l.isBuffer(e))throw new TypeError("argument should be a Buffer");if(i||(i=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n=this.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t>>=0,i=void 0===i?this.length:i>>>0,e||(e=0),"number"==typeof e)for(r=t;r=n+4;i-=3)t=`_${e.slice(i-3,i)}${t}`;return`${e.slice(0,i)}${t}`}function U(e,t,i,n,r,s){if(e>i||e3?0===t||t===BigInt(0)?`>= 0${n} and < 2${n} ** ${8*(s+1)}${n}`:`>= -(2${n} ** ${8*(s+1)-1}${n}) and < 2 ** ${8*(s+1)-1}${n}`:`>= ${t}${n} and <= ${i}${n}`,new z.ERR_OUT_OF_RANGE("value",r,e)}!function(e,t,i){q(t,"offset"),void 0!==e[t]&&void 0!==e[t+i]||j(t,e.length-(i+1))}(n,r,s)}function q(e,t){if("number"!=typeof e)throw new z.ERR_INVALID_ARG_TYPE(t,"number",e)}function j(e,t,i){if(Math.floor(e)!==e)throw q(e,i),new z.ERR_OUT_OF_RANGE(i||"offset","an integer",e);if(t<0)throw new z.ERR_BUFFER_OUT_OF_BOUNDS;throw new z.ERR_OUT_OF_RANGE(i||"offset",`>= ${i?1:0} and <= ${t}`,e)}H("ERR_BUFFER_OUT_OF_BOUNDS",(function(e){return e?`${e} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"}),RangeError),H("ERR_INVALID_ARG_TYPE",(function(e,t){return`The "${e}" argument must be of type number. Received type ${typeof t}`}),TypeError),H("ERR_OUT_OF_RANGE",(function(e,t,i){let n=`The value of "${e}" is out of range.`,r=i;return Number.isInteger(i)&&Math.abs(i)>2**32?r=V(String(i)):"bigint"==typeof i&&(r=String(i),(i>BigInt(2)**BigInt(32)||i<-(BigInt(2)**BigInt(32)))&&(r=V(r)),r+="n"),n+=` It must be ${t}. Received ${r}`,n}),RangeError);const $=/[^+/0-9A-Za-z-_]/g;function W(e,t){let i;t=t||1/0;const n=e.length;let r=null;const s=[];for(let o=0;o55295&&i<57344){if(!r){if(i>56319){(t-=3)>-1&&s.push(239,191,189);continue}if(o+1===n){(t-=3)>-1&&s.push(239,191,189);continue}r=i;continue}if(i<56320){(t-=3)>-1&&s.push(239,191,189),r=i;continue}i=65536+(r-55296<<10|i-56320)}else r&&(t-=3)>-1&&s.push(239,191,189);if(r=null,i<128){if((t-=1)<0)break;s.push(i)}else if(i<2048){if((t-=2)<0)break;s.push(i>>6|192,63&i|128)}else if(i<65536){if((t-=3)<0)break;s.push(i>>12|224,i>>6&63|128,63&i|128)}else{if(!(i<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;s.push(i>>18|240,i>>12&63|128,i>>6&63|128,63&i|128)}}return s}function G(e){return n.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace($,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function Z(e,t,i,n){let r;for(r=0;r=t.length||r>=e.length);++r)t[r+i]=e[r];return r}function Q(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function X(e){return e!=e}const Y=function(){const e="0123456789abcdef",t=new Array(256);for(let i=0;i<16;++i){const n=16*i;for(let r=0;r<16;++r)t[n+r]=e[i]+e[r]}return t}();function K(e){return"undefined"==typeof BigInt?J:e}function J(){throw new Error("BigInt not supported")}},4693:e=>{var t={array:function(e){var t=0,i=0,n=[0,1,3,7,15,31,63,127,255];return function(r){for(var s=0;r>0;){var o=8-t;r>=o?(s<<=o,s|=n[o]&e[i++],t=0,r-=o):(s<<=r,s|=(e[i]&n[r]<<8-r-t)>>8-r-t,t+=r,r=0)}return s}},simple:function(e){var i,n,r=t.header(e),s=[],o=0;do{-1!=(n=t.decompress(e,r))&&(s.push(n),o+=n.byteLength)}while(-1!=n);i=new Uint8Array(o),o=0;for(var a=0;a9)throw"Not a BZIP archive";return t},decompress:function(e,t,i){for(var n=9e5,r="",s=0;s<6;s++)r+=e(8).toString(16);if("177245385090"==r)return-1;if("314159265359"!=r)throw"eek not valid bzip data";if(e(32),e(1))throw"unsupported obsolete version";var o=e(24);if(o>n)throw"Initial position larger than buffer size";var a=e(16),l=new Uint8Array(256),h=0;for(s=0;s<16;s++)if(a&1<<15-s){var c=e(16);for(g=0;g<16;g++)c&1<<15-g&&(l[h++]=16*s+g)}var d=e(3);if(d<2||d>6)throw"another error";var u=e(15);if(0==u)throw"meh";var f=[];for(s=0;s=d)throw"whoops another error";var m=f[g];f.splice(g,1),f.splice(0,0,m),p[s]=m}var b=h+2,w=[];for(g=0;g20)throw"I gave up a while ago on writing error messages";if(!e(1))break;e(1)?a--:a++}x[s]=a}for(v=y=x[0],s=1;sy?y=x[s]:x[s]=u)throw"meow i'm a kitty, that's an error";C=(_=w[p[T++]]).base.subarray(1),S=_.limit.subarray(1)}for(g=e(s=_.minLen);;){if(s>_.maxLen)throw"rawr i'm a dinosaur";if(g<=S[s])break;s++,g=g<<1|e(1)}if((g-=C[s])<0||g>=258)throw"moo i'm a cow";var I=_.permute[g];if(0!=I&&1!=I){if(E){if(E=0,M+a>=n)throw"Boom.";for(R[m=l[f[0]]]+=a;a--;)L[M++]=m}if(I>h)break;if(M>=n)throw"I can't think of anything. Error";m=f[s=I-1],f.splice(s,1),f.splice(0,0,m),R[m=l[m]]++,L[M++]=m}else E||(E=1,a=0),a+=0==I?E:2*E,E<<=1}if(o<0||o>=M)throw"I'm a monkey and I'm throwing something at someone, namely you";for(g=0,s=0;s<256;s++)c=g+R[s],R[s]=g,g=c;for(s=0;s>=8,N=-1);var O,P,D,z=new Uint8Array(n),H=0;for(i||(i=1/0);M;){for(M--,P=F,F=255&(B=L[B]),B>>=8,3==N++?(O=F,D=P,F=-1):(O=1,D=F);O--;)if(z[H++]=D,!--i)return z;F!=P&&(N=0)}return z.subarray(0,H)}};e.exports=t},487:e=>{var t={utf8:{stringToBytes:function(e){return t.bin.stringToBytes(unescape(encodeURIComponent(e)))},bytesToString:function(e){return decodeURIComponent(escape(t.bin.bytesToString(e)))}},bin:{stringToBytes:function(e){for(var t=[],i=0;i{var t,i;t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",i={rotl:function(e,t){return e<>>32-t},rotr:function(e,t){return e<<32-t|e>>>t},endian:function(e){if(e.constructor==Number)return 16711935&i.rotl(e,8)|4278255360&i.rotl(e,24);for(var t=0;t0;e--)t.push(Math.floor(256*Math.random()));return t},bytesToWords:function(e){for(var t=[],i=0,n=0;i>>5]|=e[i]<<24-n%32;return t},wordsToBytes:function(e){for(var t=[],i=0;i<32*e.length;i+=8)t.push(e[i>>>5]>>>24-i%32&255);return t},bytesToHex:function(e){for(var t=[],i=0;i>>4).toString(16)),t.push((15&e[i]).toString(16));return t.join("")},hexToBytes:function(e){for(var t=[],i=0;i>>6*(3-s)&63)):i.push("=");return i.join("")},base64ToBytes:function(e){e=e.replace(/[^A-Z0-9+\/]/gi,"");for(var i=[],n=0,r=0;n>>6-2*r);return i}},e.exports=i},2949:(e,t,i)=>{i.r(t),i.d(t,{BlobFile:()=>h,LocalFile:()=>r(),RemoteFile:()=>a,fromUrl:()=>c,open:()=>d});var n=i(7067),r=i.n(n),s=i(8764);const o="undefined"!=typeof window?window:"undefined"!=typeof self?self:{fetch:void 0};class a{constructor(e,t={}){this.baseOverrides={},this.url=e;const i=t.fetch||o.fetch&&o.fetch.bind(o);if(!i)throw new TypeError("no fetch function supplied, and none found in global environment");t.overrides&&(this.baseOverrides=t.overrides),this.fetchImplementation=i}async getBufferFromResponse(e){if("function"==typeof e.buffer)return e.buffer();if("function"==typeof e.arrayBuffer){const t=await e.arrayBuffer();return s.Buffer.from(t)}throw new TypeError("invalid HTTP response object, has no buffer method, and no arrayBuffer method")}async fetch(e,t){let i;try{i=await this.fetchImplementation(e,t)}catch(n){if(!`${n}`.includes("Failed to fetch"))throw n;console.warn(`generic-filehandle: refetching ${e} to attempt to work around chrome CORS header caching bug`),i=await this.fetchImplementation(e,{...t,cache:"reload"})}return i}async read(e,t=0,i,n=0,r={}){const{headers:s={},signal:o,overrides:a={}}=r;i<1/0?s.range=`bytes=${n}-${n+i}`:i===1/0&&0!==n&&(s.range=`bytes=${n}-`);const l={...this.baseOverrides,...a,headers:{...s,...a.headers,...this.baseOverrides.headers},method:"GET",redirect:"follow",mode:"cors",signal:o},h=await this.fetch(this.url,l);if(!h.ok)throw new Error(`HTTP ${h.status} ${h.statusText} ${this.url}`);if(200===h.status&&0===n||206===h.status){const n=await this.getBufferFromResponse(h),r=n.copy(e,t,0,Math.min(i,n.length)),s=h.headers.get("content-range"),o=/\/(\d+)$/.exec(s||"");return o&&o[1]&&(this._stat={size:parseInt(o[1],10)}),{bytesRead:r,buffer:e}}if(200===h.status)throw new Error("${this.url} fetch returned status 200, expected 206");throw new Error(`HTTP ${h.status} fetching ${this.url}`)}async readFile(e={}){let t,i;"string"==typeof e?(t=e,i={}):(t=e.encoding,i=e,delete i.encoding);const{headers:n={},signal:r,overrides:s={}}=i,o={headers:n,method:"GET",redirect:"follow",mode:"cors",signal:r,...this.baseOverrides,...s},a=await this.fetch(this.url,o);if(!a)throw new Error("generic-filehandle failed to fetch");if(200!==a.status)throw Object.assign(new Error(`HTTP ${a.status} fetching ${this.url}`),{status:a.status});if("utf8"===t)return a.text();if(t)throw new Error(`unsupported encoding: ${t}`);return this.getBufferFromResponse(a)}async stat(){if(!this._stat){const e=s.Buffer.allocUnsafe(10);if(await this.read(e,0,10,0),!this._stat)throw new Error(`unable to determine size of file at ${this.url}`)}return this._stat}async close(){}}function l(e){const t=new FileReader;return new Promise(((i,n)=>{t.onerror=()=>{t.abort(),n(new Error("problem reading blob"))},t.onabort=()=>{n(new Error("blob reading was aborted"))},t.onload=()=>{t.result&&"string"!=typeof t.result?i(t.result):n(new Error("unknown error reading blob"))},t.readAsArrayBuffer(e)}))}class h{constructor(e){this.blob=e,this.size=e.size}async read(e,t=0,i,n=0){if(!i)return{bytesRead:0,buffer:e};const r=n,o=r+i,a=await l(this.blob.slice(r,o)),h=s.Buffer.from(a);return{bytesRead:h.copy(e,t),buffer:h}}async readFile(e){let t;if(t="string"==typeof e?e:e&&e.encoding,"utf8"===t)return function(e){const t=new FileReader;return new Promise(((i,n)=>{t.onerror=()=>{t.abort(),n(new Error("problem reading blob"))},t.onabort=()=>{n(new Error("blob reading was aborted"))},t.onload=()=>{t.result&&"string"==typeof t.result?i(t.result):n(new Error("unknown error reading blob"))},t.readAsText(e)}))}(this.blob);if(t)throw new Error(`unsupported encoding: ${t}`);const i=await l(this.blob);return s.Buffer.from(i)}async stat(){return{size:this.size}}async close(){}}function c(e,t={}){return new a(e,t)}function d(e,t,i,n={}){if(void 0!==i)return i;if(void 0!==e)return c(e,n);if(void 0!==t)return new(r())(t,n);throw new Error("no url, path, or filehandle provided, cannot open")}},645:(e,t)=>{t.read=function(e,t,i,n,r){var s,o,a=8*r-n-1,l=(1<>1,c=-7,d=i?r-1:0,u=i?-1:1,f=e[t+d];for(d+=u,s=f&(1<<-c)-1,f>>=-c,c+=a;c>0;s=256*s+e[t+d],d+=u,c-=8);for(o=s&(1<<-c)-1,s>>=-c,c+=n;c>0;o=256*o+e[t+d],d+=u,c-=8);if(0===s)s=1-h;else{if(s===l)return o?NaN:1/0*(f?-1:1);o+=Math.pow(2,n),s-=h}return(f?-1:1)*o*Math.pow(2,s-n)},t.write=function(e,t,i,n,r,s){var o,a,l,h=8*s-r-1,c=(1<>1,u=23===r?Math.pow(2,-24)-Math.pow(2,-77):0,f=n?0:s-1,p=n?1:-1,g=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(a=isNaN(t)?1:0,o=c):(o=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-o))<1&&(o--,l*=2),(t+=o+d>=1?u/l:u*Math.pow(2,1-d))*l>=2&&(o++,l/=2),o+d>=c?(a=0,o=c):o+d>=1?(a=(t*l-1)*Math.pow(2,r),o+=d):(a=t*Math.pow(2,d-1)*Math.pow(2,r),o=0));r>=8;e[i+f]=255&a,f+=p,a/=256,r-=8);for(o=o<0;e[i+f]=255&o,f+=p,o/=256,h-=8);e[i+f-p]|=128*g}},8738:e=>{function t(e){return!!e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}e.exports=function(e){return null!=e&&(t(e)||function(e){return"function"==typeof e.readFloatLE&&"function"==typeof e.slice&&t(e.slice(0,0))}(e)||!!e._isBuffer)}},3720:e=>{e.exports=i;var t=null;try{t=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(e){}function i(e,t,i){this.low=0|e,this.high=0|t,this.unsigned=!!i}function n(e){return!0===(e&&e.__isLong__)}i.prototype.__isLong__,Object.defineProperty(i.prototype,"__isLong__",{value:!0}),i.isLong=n;var r={},s={};function o(e,t){var i,n,o;return t?(o=0<=(e>>>=0)&&e<256)&&(n=s[e])?n:(i=l(e,(0|e)<0?-1:0,!0),o&&(s[e]=i),i):(o=-128<=(e|=0)&&e<128)&&(n=r[e])?n:(i=l(e,e<0?-1:0,!1),o&&(r[e]=i),i)}function a(e,t){if(isNaN(e))return t?b:m;if(t){if(e<0)return b;if(e>=f)return x}else{if(e<=-p)return k;if(e+1>=p)return _}return e<0?a(-e,t).neg():l(e%u|0,e/u|0,t)}function l(e,t,n){return new i(e,t,n)}i.fromInt=o,i.fromNumber=a,i.fromBits=l;var h=Math.pow;function c(e,t,i){if(0===e.length)throw Error("empty string");if("NaN"===e||"Infinity"===e||"+Infinity"===e||"-Infinity"===e)return m;if("number"==typeof t?(i=t,t=!1):t=!!t,(i=i||10)<2||360)throw Error("interior hyphen");if(0===n)return c(e.substring(1),t,i).neg();for(var r=a(h(i,8)),s=m,o=0;o>>0:this.low},C.toNumber=function(){return this.unsigned?(this.high>>>0)*u+(this.low>>>0):this.high*u+(this.low>>>0)},C.toString=function(e){if((e=e||10)<2||36>>0).toString(e);if((s=l).isZero())return c+o;for(;c.length<6;)c="0"+c;o=""+c+o}},C.getHighBits=function(){return this.high},C.getHighBitsUnsigned=function(){return this.high>>>0},C.getLowBits=function(){return this.low},C.getLowBitsUnsigned=function(){return this.low>>>0},C.getNumBitsAbs=function(){if(this.isNegative())return this.eq(k)?64:this.neg().getNumBitsAbs();for(var e=0!=this.high?this.high:this.low,t=31;t>0&&0==(e&1<=0},C.isOdd=function(){return 1==(1&this.low)},C.isEven=function(){return 0==(1&this.low)},C.equals=function(e){return n(e)||(e=d(e)),(this.unsigned===e.unsigned||this.high>>>31!=1||e.high>>>31!=1)&&this.high===e.high&&this.low===e.low},C.eq=C.equals,C.notEquals=function(e){return!this.eq(e)},C.neq=C.notEquals,C.ne=C.notEquals,C.lessThan=function(e){return this.comp(e)<0},C.lt=C.lessThan,C.lessThanOrEqual=function(e){return this.comp(e)<=0},C.lte=C.lessThanOrEqual,C.le=C.lessThanOrEqual,C.greaterThan=function(e){return this.comp(e)>0},C.gt=C.greaterThan,C.greaterThanOrEqual=function(e){return this.comp(e)>=0},C.gte=C.greaterThanOrEqual,C.ge=C.greaterThanOrEqual,C.compare=function(e){if(n(e)||(e=d(e)),this.eq(e))return 0;var t=this.isNegative(),i=e.isNegative();return t&&!i?-1:!t&&i?1:this.unsigned?e.high>>>0>this.high>>>0||e.high===this.high&&e.low>>>0>this.low>>>0?-1:1:this.sub(e).isNegative()?-1:1},C.comp=C.compare,C.negate=function(){return!this.unsigned&&this.eq(k)?k:this.not().add(w)},C.neg=C.negate,C.add=function(e){n(e)||(e=d(e));var t=this.high>>>16,i=65535&this.high,r=this.low>>>16,s=65535&this.low,o=e.high>>>16,a=65535&e.high,h=e.low>>>16,c=0,u=0,f=0,p=0;return f+=(p+=s+(65535&e.low))>>>16,u+=(f+=r+h)>>>16,c+=(u+=i+a)>>>16,c+=t+o,l((f&=65535)<<16|(p&=65535),(c&=65535)<<16|(u&=65535),this.unsigned)},C.subtract=function(e){return n(e)||(e=d(e)),this.add(e.neg())},C.sub=C.subtract,C.multiply=function(e){if(this.isZero())return m;if(n(e)||(e=d(e)),t)return l(t.mul(this.low,this.high,e.low,e.high),t.get_high(),this.unsigned);if(e.isZero())return m;if(this.eq(k))return e.isOdd()?k:m;if(e.eq(k))return this.isOdd()?k:m;if(this.isNegative())return e.isNegative()?this.neg().mul(e.neg()):this.neg().mul(e).neg();if(e.isNegative())return this.mul(e.neg()).neg();if(this.lt(g)&&e.lt(g))return a(this.toNumber()*e.toNumber(),this.unsigned);var i=this.high>>>16,r=65535&this.high,s=this.low>>>16,o=65535&this.low,h=e.high>>>16,c=65535&e.high,u=e.low>>>16,f=65535&e.low,p=0,b=0,w=0,v=0;return w+=(v+=o*f)>>>16,b+=(w+=s*f)>>>16,w&=65535,b+=(w+=o*u)>>>16,p+=(b+=r*f)>>>16,b&=65535,p+=(b+=s*u)>>>16,b&=65535,p+=(b+=o*c)>>>16,p+=i*f+r*u+s*c+o*h,l((w&=65535)<<16|(v&=65535),(p&=65535)<<16|(b&=65535),this.unsigned)},C.mul=C.multiply,C.divide=function(e){if(n(e)||(e=d(e)),e.isZero())throw Error("division by zero");var i,r,s;if(t)return this.unsigned||-2147483648!==this.high||-1!==e.low||-1!==e.high?l((this.unsigned?t.div_u:t.div_s)(this.low,this.high,e.low,e.high),t.get_high(),this.unsigned):this;if(this.isZero())return this.unsigned?b:m;if(this.unsigned){if(e.unsigned||(e=e.toUnsigned()),e.gt(this))return b;if(e.gt(this.shru(1)))return v;s=b}else{if(this.eq(k))return e.eq(w)||e.eq(y)?k:e.eq(k)?w:(i=this.shr(1).div(e).shl(1)).eq(m)?e.isNegative()?w:y:(r=this.sub(e.mul(i)),s=i.add(r.div(e)));if(e.eq(k))return this.unsigned?b:m;if(this.isNegative())return e.isNegative()?this.neg().div(e.neg()):this.neg().div(e).neg();if(e.isNegative())return this.div(e.neg()).neg();s=m}for(r=this;r.gte(e);){i=Math.max(1,Math.floor(r.toNumber()/e.toNumber()));for(var o=Math.ceil(Math.log(i)/Math.LN2),c=o<=48?1:h(2,o-48),u=a(i),f=u.mul(e);f.isNegative()||f.gt(r);)f=(u=a(i-=c,this.unsigned)).mul(e);u.isZero()&&(u=w),s=s.add(u),r=r.sub(f)}return s},C.div=C.divide,C.modulo=function(e){return n(e)||(e=d(e)),t?l((this.unsigned?t.rem_u:t.rem_s)(this.low,this.high,e.low,e.high),t.get_high(),this.unsigned):this.sub(this.div(e).mul(e))},C.mod=C.modulo,C.rem=C.modulo,C.not=function(){return l(~this.low,~this.high,this.unsigned)},C.and=function(e){return n(e)||(e=d(e)),l(this.low&e.low,this.high&e.high,this.unsigned)},C.or=function(e){return n(e)||(e=d(e)),l(this.low|e.low,this.high|e.high,this.unsigned)},C.xor=function(e){return n(e)||(e=d(e)),l(this.low^e.low,this.high^e.high,this.unsigned)},C.shiftLeft=function(e){return n(e)&&(e=e.toInt()),0==(e&=63)?this:e<32?l(this.low<>>32-e,this.unsigned):l(0,this.low<>>e|this.high<<32-e,this.high>>e,this.unsigned):l(this.high>>e-32,this.high>=0?0:-1,this.unsigned)},C.shr=C.shiftRight,C.shiftRightUnsigned=function(e){if(n(e)&&(e=e.toInt()),0==(e&=63))return this;var t=this.high;return e<32?l(this.low>>>e|t<<32-e,t>>>e,this.unsigned):l(32===e?t:t>>>e-32,0,this.unsigned)},C.shru=C.shiftRightUnsigned,C.shr_u=C.shiftRightUnsigned,C.toSigned=function(){return this.unsigned?l(this.low,this.high,!1):this},C.toUnsigned=function(){return this.unsigned?this:l(this.low,this.high,!0)},C.toBytes=function(e){return e?this.toBytesLE():this.toBytesBE()},C.toBytesLE=function(){var e=this.high,t=this.low;return[255&t,t>>>8&255,t>>>16&255,t>>>24,255&e,e>>>8&255,e>>>16&255,e>>>24]},C.toBytesBE=function(){var e=this.high,t=this.low;return[e>>>24,e>>>16&255,e>>>8&255,255&e,t>>>24,t>>>16&255,t>>>8&255,255&t]},i.fromBytes=function(e,t,n){return n?i.fromBytesLE(e,t):i.fromBytesBE(e,t)},i.fromBytesLE=function(e,t){return new i(e[0]|e[1]<<8|e[2]<<16|e[3]<<24,e[4]|e[5]<<8|e[6]<<16|e[7]<<24,t)},i.fromBytesBE=function(e,t){return new i(e[4]<<24|e[5]<<16|e[6]<<8|e[7],e[0]<<24|e[1]<<16|e[2]<<8|e[3],t)}},2568:(e,t,i)=>{var n,r,s,o,a;n=i(1012),r=i(487).utf8,s=i(8738),o=i(487).bin,(a=function(e,t){e.constructor==String?e=t&&"binary"===t.encoding?o.stringToBytes(e):r.stringToBytes(e):s(e)?e=Array.prototype.slice.call(e,0):Array.isArray(e)||e.constructor===Uint8Array||(e=e.toString());for(var i=n.bytesToWords(e),l=8*e.length,h=1732584193,c=-271733879,d=-1732584194,u=271733878,f=0;f>>24)|4278255360&(i[f]<<24|i[f]>>>8);i[l>>>5]|=128<>>9<<4)]=l;var p=a._ff,g=a._gg,m=a._hh,b=a._ii;for(f=0;f>>0,c=c+v>>>0,d=d+y>>>0,u=u+_>>>0}return n.endian([h,c,d,u])})._ff=function(e,t,i,n,r,s,o){var a=e+(t&i|~t&n)+(r>>>0)+o;return(a<>>32-s)+t},a._gg=function(e,t,i,n,r,s,o){var a=e+(t&n|i&~n)+(r>>>0)+o;return(a<>>32-s)+t},a._hh=function(e,t,i,n,r,s,o){var a=e+(t^i^n)+(r>>>0)+o;return(a<>>32-s)+t},a._ii=function(e,t,i,n,r,s,o){var a=e+(i^(t|~n))+(r>>>0)+o;return(a<>>32-s)+t},a._blocksize=16,a._digestsize=16,e.exports=function(e,t){if(null==e)throw new Error("Illegal argument "+e);var i=n.wordsToBytes(a(e,t));return t&&t.asBytes?i:t&&t.asString?o.bytesToString(i):n.bytesToHex(i)}},9591:(e,t,i)=>{var n={};(0,i(4236).assign)(n,i(4555),i(8843),i(1619)),e.exports=n},4555:(e,t,i)=>{var n=i(405),r=i(4236),s=i(9373),o=i(8898),a=i(2292),l=Object.prototype.toString;function h(e){if(!(this instanceof h))return new h(e);this.options=r.assign({level:-1,method:8,chunkSize:16384,windowBits:15,memLevel:8,strategy:0,to:""},e||{});var t=this.options;t.raw&&t.windowBits>0?t.windowBits=-t.windowBits:t.gzip&&t.windowBits>0&&t.windowBits<16&&(t.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new a,this.strm.avail_out=0;var i=n.deflateInit2(this.strm,t.level,t.method,t.windowBits,t.memLevel,t.strategy);if(0!==i)throw new Error(o[i]);if(t.header&&n.deflateSetHeader(this.strm,t.header),t.dictionary){var c;if(c="string"==typeof t.dictionary?s.string2buf(t.dictionary):"[object ArrayBuffer]"===l.call(t.dictionary)?new Uint8Array(t.dictionary):t.dictionary,0!==(i=n.deflateSetDictionary(this.strm,c)))throw new Error(o[i]);this._dict_set=!0}}function c(e,t){var i=new h(t);if(i.push(e,!0),i.err)throw i.msg||o[i.err];return i.result}h.prototype.push=function(e,t){var i,o,a=this.strm,h=this.options.chunkSize;if(this.ended)return!1;o=t===~~t?t:!0===t?4:0,"string"==typeof e?a.input=s.string2buf(e):"[object ArrayBuffer]"===l.call(e)?a.input=new Uint8Array(e):a.input=e,a.next_in=0,a.avail_in=a.input.length;do{if(0===a.avail_out&&(a.output=new r.Buf8(h),a.next_out=0,a.avail_out=h),1!==(i=n.deflate(a,o))&&0!==i)return this.onEnd(i),this.ended=!0,!1;0!==a.avail_out&&(0!==a.avail_in||4!==o&&2!==o)||("string"===this.options.to?this.onData(s.buf2binstring(r.shrinkBuf(a.output,a.next_out))):this.onData(r.shrinkBuf(a.output,a.next_out)))}while((a.avail_in>0||0===a.avail_out)&&1!==i);return 4===o?(i=n.deflateEnd(this.strm),this.onEnd(i),this.ended=!0,0===i):2!==o||(this.onEnd(0),a.avail_out=0,!0)},h.prototype.onData=function(e){this.chunks.push(e)},h.prototype.onEnd=function(e){0===e&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=r.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg},t.Deflate=h,t.deflate=c,t.deflateRaw=function(e,t){return(t=t||{}).raw=!0,c(e,t)},t.gzip=function(e,t){return(t=t||{}).gzip=!0,c(e,t)}},8843:(e,t,i)=>{var n=i(7948),r=i(4236),s=i(9373),o=i(1619),a=i(8898),l=i(2292),h=i(2401),c=Object.prototype.toString;function d(e){if(!(this instanceof d))return new d(e);this.options=r.assign({chunkSize:16384,windowBits:0,to:""},e||{});var t=this.options;t.raw&&t.windowBits>=0&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(t.windowBits>=0&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),t.windowBits>15&&t.windowBits<48&&0==(15&t.windowBits)&&(t.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new l,this.strm.avail_out=0;var i=n.inflateInit2(this.strm,t.windowBits);if(i!==o.Z_OK)throw new Error(a[i]);if(this.header=new h,n.inflateGetHeader(this.strm,this.header),t.dictionary&&("string"==typeof t.dictionary?t.dictionary=s.string2buf(t.dictionary):"[object ArrayBuffer]"===c.call(t.dictionary)&&(t.dictionary=new Uint8Array(t.dictionary)),t.raw&&(i=n.inflateSetDictionary(this.strm,t.dictionary))!==o.Z_OK))throw new Error(a[i])}function u(e,t){var i=new d(t);if(i.push(e,!0),i.err)throw i.msg||a[i.err];return i.result}d.prototype.push=function(e,t){var i,a,l,h,d,u=this.strm,f=this.options.chunkSize,p=this.options.dictionary,g=!1;if(this.ended)return!1;a=t===~~t?t:!0===t?o.Z_FINISH:o.Z_NO_FLUSH,"string"==typeof e?u.input=s.binstring2buf(e):"[object ArrayBuffer]"===c.call(e)?u.input=new Uint8Array(e):u.input=e,u.next_in=0,u.avail_in=u.input.length;do{if(0===u.avail_out&&(u.output=new r.Buf8(f),u.next_out=0,u.avail_out=f),(i=n.inflate(u,o.Z_NO_FLUSH))===o.Z_NEED_DICT&&p&&(i=n.inflateSetDictionary(this.strm,p)),i===o.Z_BUF_ERROR&&!0===g&&(i=o.Z_OK,g=!1),i!==o.Z_STREAM_END&&i!==o.Z_OK)return this.onEnd(i),this.ended=!0,!1;u.next_out&&(0!==u.avail_out&&i!==o.Z_STREAM_END&&(0!==u.avail_in||a!==o.Z_FINISH&&a!==o.Z_SYNC_FLUSH)||("string"===this.options.to?(l=s.utf8border(u.output,u.next_out),h=u.next_out-l,d=s.buf2string(u.output,l),u.next_out=h,u.avail_out=f-h,h&&r.arraySet(u.output,u.output,l,h,0),this.onData(d)):this.onData(r.shrinkBuf(u.output,u.next_out)))),0===u.avail_in&&0===u.avail_out&&(g=!0)}while((u.avail_in>0||0===u.avail_out)&&i!==o.Z_STREAM_END);return i===o.Z_STREAM_END&&(a=o.Z_FINISH),a===o.Z_FINISH?(i=n.inflateEnd(this.strm),this.onEnd(i),this.ended=!0,i===o.Z_OK):a!==o.Z_SYNC_FLUSH||(this.onEnd(o.Z_OK),u.avail_out=0,!0)},d.prototype.onData=function(e){this.chunks.push(e)},d.prototype.onEnd=function(e){e===o.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=r.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg},t.Inflate=d,t.inflate=u,t.inflateRaw=function(e,t){return(t=t||{}).raw=!0,u(e,t)},t.ungzip=u},4236:(e,t)=>{var i="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.assign=function(e){for(var t=Array.prototype.slice.call(arguments,1);t.length;){var i=t.shift();if(i){if("object"!=typeof i)throw new TypeError(i+"must be non-object");for(var r in i)n(i,r)&&(e[r]=i[r])}}return e},t.shrinkBuf=function(e,t){return e.length===t?e:e.subarray?e.subarray(0,t):(e.length=t,e)};var r={arraySet:function(e,t,i,n,r){if(t.subarray&&e.subarray)e.set(t.subarray(i,i+n),r);else for(var s=0;s{var n=i(4236),r=!0,s=!0;try{String.fromCharCode.apply(null,[0])}catch(e){r=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(e){s=!1}for(var o=new n.Buf8(256),a=0;a<256;a++)o[a]=a>=252?6:a>=248?5:a>=240?4:a>=224?3:a>=192?2:1;function l(e,t){if(t<65534&&(e.subarray&&s||!e.subarray&&r))return String.fromCharCode.apply(null,n.shrinkBuf(e,t));for(var i="",o=0;o>>6,t[o++]=128|63&i):i<65536?(t[o++]=224|i>>>12,t[o++]=128|i>>>6&63,t[o++]=128|63&i):(t[o++]=240|i>>>18,t[o++]=128|i>>>12&63,t[o++]=128|i>>>6&63,t[o++]=128|63&i);return t},t.buf2binstring=function(e){return l(e,e.length)},t.binstring2buf=function(e){for(var t=new n.Buf8(e.length),i=0,r=t.length;i4)h[n++]=65533,i+=s-1;else{for(r&=2===s?31:3===s?15:7;s>1&&i1?h[n++]=65533:r<65536?h[n++]=r:(r-=65536,h[n++]=55296|r>>10&1023,h[n++]=56320|1023&r)}return l(h,n)},t.utf8border=function(e,t){var i;for((t=t||e.length)>e.length&&(t=e.length),i=t-1;i>=0&&128==(192&e[i]);)i--;return i<0||0===i?t:i+o[e[i]]>t?i:t}},6069:e=>{e.exports=function(e,t,i,n){for(var r=65535&e|0,s=e>>>16&65535|0,o=0;0!==i;){i-=o=i>2e3?2e3:i;do{s=s+(r=r+t[n++]|0)|0}while(--o);r%=65521,s%=65521}return r|s<<16|0}},1619:e=>{e.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}},2869:e=>{var t=function(){for(var e,t=[],i=0;i<256;i++){e=i;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[i]=e}return t}();e.exports=function(e,i,n,r){var s=t,o=r+n;e^=-1;for(var a=r;a>>8^s[255&(e^i[a])];return-1^e}},405:(e,t,i)=>{var n,r=i(4236),s=i(342),o=i(6069),a=i(2869),l=i(8898),h=-2,c=258,d=262,u=103,f=113,p=666;function g(e,t){return e.msg=l[t],t}function m(e){return(e<<1)-(e>4?9:0)}function b(e){for(var t=e.length;--t>=0;)e[t]=0}function w(e){var t=e.state,i=t.pending;i>e.avail_out&&(i=e.avail_out),0!==i&&(r.arraySet(e.output,t.pending_buf,t.pending_out,i,e.next_out),e.next_out+=i,t.pending_out+=i,e.total_out+=i,e.avail_out-=i,t.pending-=i,0===t.pending&&(t.pending_out=0))}function v(e,t){s._tr_flush_block(e,e.block_start>=0?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,w(e.strm)}function y(e,t){e.pending_buf[e.pending++]=t}function _(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function x(e,t,i,n){var s=e.avail_in;return s>n&&(s=n),0===s?0:(e.avail_in-=s,r.arraySet(t,e.input,e.next_in,s,i),1===e.state.wrap?e.adler=o(e.adler,t,s,i):2===e.state.wrap&&(e.adler=a(e.adler,t,s,i)),e.next_in+=s,e.total_in+=s,s)}function k(e,t){var i,n,r=e.max_chain_length,s=e.strstart,o=e.prev_length,a=e.nice_match,l=e.strstart>e.w_size-d?e.strstart-(e.w_size-d):0,h=e.window,u=e.w_mask,f=e.prev,p=e.strstart+c,g=h[s+o-1],m=h[s+o];e.prev_length>=e.good_match&&(r>>=2),a>e.lookahead&&(a=e.lookahead);do{if(h[(i=t)+o]===m&&h[i+o-1]===g&&h[i]===h[s]&&h[++i]===h[s+1]){s+=2,i++;do{}while(h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&h[++s]===h[++i]&&so){if(e.match_start=t,o=n,n>=a)break;g=h[s+o-1],m=h[s+o]}}}while((t=f[t&u])>l&&0!=--r);return o<=e.lookahead?o:e.lookahead}function C(e){var t,i,n,s,o,a=e.w_size;do{if(s=e.window_size-e.lookahead-e.strstart,e.strstart>=a+(a-d)){r.arraySet(e.window,e.window,a,a,0),e.match_start-=a,e.strstart-=a,e.block_start-=a,t=i=e.hash_size;do{n=e.head[--t],e.head[t]=n>=a?n-a:0}while(--i);t=i=a;do{n=e.prev[--t],e.prev[t]=n>=a?n-a:0}while(--i);s+=a}if(0===e.strm.avail_in)break;if(i=x(e.strm,e.window,e.strstart+e.lookahead,s),e.lookahead+=i,e.lookahead+e.insert>=3)for(o=e.strstart-e.insert,e.ins_h=e.window[o],e.ins_h=(e.ins_h<=3&&(e.ins_h=(e.ins_h<=3)if(n=s._tr_tally(e,e.strstart-e.match_start,e.match_length-3),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=3){e.match_length--;do{e.strstart++,e.ins_h=(e.ins_h<=3&&(e.ins_h=(e.ins_h<4096)&&(e.match_length=2)),e.prev_length>=3&&e.match_length<=e.prev_length){r=e.strstart+e.lookahead-3,n=s._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-3),e.lookahead-=e.prev_length-1,e.prev_length-=2;do{++e.strstart<=r&&(e.ins_h=(e.ins_h<15&&(a=2,n-=16),s<1||s>9||8!==i||n<8||n>15||t<0||t>9||o<0||o>4)return g(e,h);8===n&&(n=9);var l=new M;return e.state=l,l.strm=e,l.wrap=a,l.gzhead=null,l.w_bits=n,l.w_size=1<e.pending_buf_size-5&&(i=e.pending_buf_size-5);;){if(e.lookahead<=1){if(C(e),0===e.lookahead&&0===t)return 1;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+i;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,v(e,!1),0===e.strm.avail_out))return 1;if(e.strstart-e.block_start>=e.w_size-d&&(v(e,!1),0===e.strm.avail_out))return 1}return e.insert=0,4===t?(v(e,!0),0===e.strm.avail_out?3:4):(e.strstart>e.block_start&&(v(e,!1),e.strm.avail_out),1)})),new E(4,4,8,4,S),new E(4,5,16,8,S),new E(4,6,32,32,S),new E(4,4,16,16,A),new E(8,16,32,32,A),new E(8,16,128,128,A),new E(8,32,128,256,A),new E(32,128,258,1024,A),new E(32,258,258,4096,A)],t.deflateInit=function(e,t){return L(e,t,8,15,8,0)},t.deflateInit2=L,t.deflateReset=R,t.deflateResetKeep=T,t.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?h:(e.state.gzhead=t,0):h},t.deflate=function(e,t){var i,r,o,l;if(!e||!e.state||t>5||t<0)return e?g(e,h):h;if(r=e.state,!e.output||!e.input&&0!==e.avail_in||r.status===p&&4!==t)return g(e,0===e.avail_out?-5:h);if(r.strm=e,i=r.last_flush,r.last_flush=t,42===r.status)if(2===r.wrap)e.adler=0,y(r,31),y(r,139),y(r,8),r.gzhead?(y(r,(r.gzhead.text?1:0)+(r.gzhead.hcrc?2:0)+(r.gzhead.extra?4:0)+(r.gzhead.name?8:0)+(r.gzhead.comment?16:0)),y(r,255&r.gzhead.time),y(r,r.gzhead.time>>8&255),y(r,r.gzhead.time>>16&255),y(r,r.gzhead.time>>24&255),y(r,9===r.level?2:r.strategy>=2||r.level<2?4:0),y(r,255&r.gzhead.os),r.gzhead.extra&&r.gzhead.extra.length&&(y(r,255&r.gzhead.extra.length),y(r,r.gzhead.extra.length>>8&255)),r.gzhead.hcrc&&(e.adler=a(e.adler,r.pending_buf,r.pending,0)),r.gzindex=0,r.status=69):(y(r,0),y(r,0),y(r,0),y(r,0),y(r,0),y(r,9===r.level?2:r.strategy>=2||r.level<2?4:0),y(r,3),r.status=f);else{var d=8+(r.w_bits-8<<4)<<8;d|=(r.strategy>=2||r.level<2?0:r.level<6?1:6===r.level?2:3)<<6,0!==r.strstart&&(d|=32),d+=31-d%31,r.status=f,_(r,d),0!==r.strstart&&(_(r,e.adler>>>16),_(r,65535&e.adler)),e.adler=1}if(69===r.status)if(r.gzhead.extra){for(o=r.pending;r.gzindex<(65535&r.gzhead.extra.length)&&(r.pending!==r.pending_buf_size||(r.gzhead.hcrc&&r.pending>o&&(e.adler=a(e.adler,r.pending_buf,r.pending-o,o)),w(e),o=r.pending,r.pending!==r.pending_buf_size));)y(r,255&r.gzhead.extra[r.gzindex]),r.gzindex++;r.gzhead.hcrc&&r.pending>o&&(e.adler=a(e.adler,r.pending_buf,r.pending-o,o)),r.gzindex===r.gzhead.extra.length&&(r.gzindex=0,r.status=73)}else r.status=73;if(73===r.status)if(r.gzhead.name){o=r.pending;do{if(r.pending===r.pending_buf_size&&(r.gzhead.hcrc&&r.pending>o&&(e.adler=a(e.adler,r.pending_buf,r.pending-o,o)),w(e),o=r.pending,r.pending===r.pending_buf_size)){l=1;break}l=r.gzindexo&&(e.adler=a(e.adler,r.pending_buf,r.pending-o,o)),0===l&&(r.gzindex=0,r.status=91)}else r.status=91;if(91===r.status)if(r.gzhead.comment){o=r.pending;do{if(r.pending===r.pending_buf_size&&(r.gzhead.hcrc&&r.pending>o&&(e.adler=a(e.adler,r.pending_buf,r.pending-o,o)),w(e),o=r.pending,r.pending===r.pending_buf_size)){l=1;break}l=r.gzindexo&&(e.adler=a(e.adler,r.pending_buf,r.pending-o,o)),0===l&&(r.status=u)}else r.status=u;if(r.status===u&&(r.gzhead.hcrc?(r.pending+2>r.pending_buf_size&&w(e),r.pending+2<=r.pending_buf_size&&(y(r,255&e.adler),y(r,e.adler>>8&255),e.adler=0,r.status=f)):r.status=f),0!==r.pending){if(w(e),0===e.avail_out)return r.last_flush=-1,0}else if(0===e.avail_in&&m(t)<=m(i)&&4!==t)return g(e,-5);if(r.status===p&&0!==e.avail_in)return g(e,-5);if(0!==e.avail_in||0!==r.lookahead||0!==t&&r.status!==p){var x=2===r.strategy?function(e,t){for(var i;;){if(0===e.lookahead&&(C(e),0===e.lookahead)){if(0===t)return 1;break}if(e.match_length=0,i=s._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,i&&(v(e,!1),0===e.strm.avail_out))return 1}return e.insert=0,4===t?(v(e,!0),0===e.strm.avail_out?3:4):e.last_lit&&(v(e,!1),0===e.strm.avail_out)?1:2}(r,t):3===r.strategy?function(e,t){for(var i,n,r,o,a=e.window;;){if(e.lookahead<=c){if(C(e),e.lookahead<=c&&0===t)return 1;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=3&&e.strstart>0&&(n=a[r=e.strstart-1])===a[++r]&&n===a[++r]&&n===a[++r]){o=e.strstart+c;do{}while(n===a[++r]&&n===a[++r]&&n===a[++r]&&n===a[++r]&&n===a[++r]&&n===a[++r]&&n===a[++r]&&n===a[++r]&&re.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=3?(i=s._tr_tally(e,1,e.match_length-3),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(i=s._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),i&&(v(e,!1),0===e.strm.avail_out))return 1}return e.insert=0,4===t?(v(e,!0),0===e.strm.avail_out?3:4):e.last_lit&&(v(e,!1),0===e.strm.avail_out)?1:2}(r,t):n[r.level].func(r,t);if(3!==x&&4!==x||(r.status=p),1===x||3===x)return 0===e.avail_out&&(r.last_flush=-1),0;if(2===x&&(1===t?s._tr_align(r):5!==t&&(s._tr_stored_block(r,0,0,!1),3===t&&(b(r.head),0===r.lookahead&&(r.strstart=0,r.block_start=0,r.insert=0))),w(e),0===e.avail_out))return r.last_flush=-1,0}return 4!==t?0:r.wrap<=0?1:(2===r.wrap?(y(r,255&e.adler),y(r,e.adler>>8&255),y(r,e.adler>>16&255),y(r,e.adler>>24&255),y(r,255&e.total_in),y(r,e.total_in>>8&255),y(r,e.total_in>>16&255),y(r,e.total_in>>24&255)):(_(r,e.adler>>>16),_(r,65535&e.adler)),w(e),r.wrap>0&&(r.wrap=-r.wrap),0!==r.pending?0:1)},t.deflateEnd=function(e){var t;return e&&e.state?42!==(t=e.state.status)&&69!==t&&73!==t&&91!==t&&t!==u&&t!==f&&t!==p?g(e,h):(e.state=null,t===f?g(e,-3):0):h},t.deflateSetDictionary=function(e,t){var i,n,s,a,l,c,d,u,f=t.length;if(!e||!e.state)return h;if(2===(a=(i=e.state).wrap)||1===a&&42!==i.status||i.lookahead)return h;for(1===a&&(e.adler=o(e.adler,t,f,0)),i.wrap=0,f>=i.w_size&&(0===a&&(b(i.head),i.strstart=0,i.block_start=0,i.insert=0),u=new r.Buf8(i.w_size),r.arraySet(u,t,f-i.w_size,i.w_size,0),t=u,f=i.w_size),l=e.avail_in,c=e.next_in,d=e.input,e.avail_in=f,e.next_in=0,e.input=t,C(i);i.lookahead>=3;){n=i.strstart,s=i.lookahead-2;do{i.ins_h=(i.ins_h<{e.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}},4264:e=>{e.exports=function(e,t){var i,n,r,s,o,a,l,h,c,d,u,f,p,g,m,b,w,v,y,_,x,k,C,S,A;i=e.state,n=e.next_in,S=e.input,r=n+(e.avail_in-5),s=e.next_out,A=e.output,o=s-(t-e.avail_out),a=s+(e.avail_out-257),l=i.dmax,h=i.wsize,c=i.whave,d=i.wnext,u=i.window,f=i.hold,p=i.bits,g=i.lencode,m=i.distcode,b=(1<>>=y=v>>>24,p-=y,0==(y=v>>>16&255))A[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=g[(65535&v)+(f&(1<>>=y,p-=y),p<15&&(f+=S[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=m[(65535&v)+(f&(1<l){e.msg="invalid distance too far back",i.mode=30;break e}if(f>>>=y,p-=y,x>(y=s-o)){if((y=x-y)>c&&i.sane){e.msg="invalid distance too far back",i.mode=30;break e}if(k=0,C=u,0===d){if(k+=h-y,y<_){_-=y;do{A[s++]=u[k++]}while(--y);k=s-x,C=A}}else if(d2;)A[s++]=C[k++],A[s++]=C[k++],A[s++]=C[k++],_-=3;_&&(A[s++]=C[k++],_>1&&(A[s++]=C[k++]))}else{k=s-x;do{A[s++]=A[k++],A[s++]=A[k++],A[s++]=A[k++],_-=3}while(_>2);_&&(A[s++]=A[k++],_>1&&(A[s++]=A[k++]))}break}}break}}while(n>3,f&=(1<<(p-=_<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n{var n=i(4236),r=i(6069),s=i(2869),o=i(4264),a=i(9241),l=-2,h=12,c=30;function d(e){return(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function u(){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 f(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=1,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new n.Buf32(852),t.distcode=t.distdyn=new n.Buf32(592),t.sane=1,t.back=-1,0):l}function p(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,f(e)):l}function g(e,t){var i,n;return e&&e.state?(n=e.state,t<0?(i=0,t=-t):(i=1+(t>>4),t<48&&(t&=15)),t&&(t<8||t>15)?l:(null!==n.window&&n.wbits!==t&&(n.window=null),n.wrap=i,n.wbits=t,p(e))):l}function m(e,t){var i,n;return e?(n=new u,e.state=n,n.window=null,0!==(i=g(e,t))&&(e.state=null),i):l}var b,w,v=!0;function y(e){if(v){var t;for(b=new n.Buf32(512),w=new n.Buf32(32),t=0;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(a(1,e.lens,0,288,b,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;a(2,e.lens,0,32,w,0,e.work,{bits:5}),v=!1}e.lencode=b,e.lenbits=9,e.distcode=w,e.distbits=5}function _(e,t,i,r){var s,o=e.state;return null===o.window&&(o.wsize=1<=o.wsize?(n.arraySet(o.window,t,i-o.wsize,o.wsize,0),o.wnext=0,o.whave=o.wsize):((s=o.wsize-o.wnext)>r&&(s=r),n.arraySet(o.window,t,i-r,s,o.wnext),(r-=s)?(n.arraySet(o.window,t,i-r,r,0),o.wnext=r,o.whave=o.wsize):(o.wnext+=s,o.wnext===o.wsize&&(o.wnext=0),o.whave>>8&255,i.check=s(i.check,D,2,0),w=0,v=0,i.mode=2;break}if(i.flags=0,i.head&&(i.head.done=!1),!(1&i.wrap)||(((255&w)<<8)+(w>>8))%31){e.msg="incorrect header check",i.mode=c;break}if(8!=(15&w)){e.msg="unknown compression method",i.mode=c;break}if(v-=4,B=8+(15&(w>>>=4)),0===i.wbits)i.wbits=B;else if(B>i.wbits){e.msg="invalid window size",i.mode=c;break}i.dmax=1<>8&1),512&i.flags&&(D[0]=255&w,D[1]=w>>>8&255,i.check=s(i.check,D,2,0)),w=0,v=0,i.mode=3;case 3:for(;v<32;){if(0===m)break e;m--,w+=u[p++]<>>8&255,D[2]=w>>>16&255,D[3]=w>>>24&255,i.check=s(i.check,D,4,0)),w=0,v=0,i.mode=4;case 4:for(;v<16;){if(0===m)break e;m--,w+=u[p++]<>8),512&i.flags&&(D[0]=255&w,D[1]=w>>>8&255,i.check=s(i.check,D,2,0)),w=0,v=0,i.mode=5;case 5:if(1024&i.flags){for(;v<16;){if(0===m)break e;m--,w+=u[p++]<>>8&255,i.check=s(i.check,D,2,0)),w=0,v=0}else i.head&&(i.head.extra=null);i.mode=6;case 6:if(1024&i.flags&&((C=i.length)>m&&(C=m),C&&(i.head&&(B=i.head.extra_len-i.length,i.head.extra||(i.head.extra=new Array(i.head.extra_len)),n.arraySet(i.head.extra,u,p,C,B)),512&i.flags&&(i.check=s(i.check,u,C,p)),m-=C,p+=C,i.length-=C),i.length))break e;i.length=0,i.mode=7;case 7:if(2048&i.flags){if(0===m)break e;C=0;do{B=u[p+C++],i.head&&B&&i.length<65536&&(i.head.name+=String.fromCharCode(B))}while(B&&C>9&1,i.head.done=!0),e.adler=i.check=0,i.mode=h;break;case 10:for(;v<32;){if(0===m)break e;m--,w+=u[p++]<>>=7&v,v-=7&v,i.mode=27;break}for(;v<3;){if(0===m)break e;m--,w+=u[p++]<>>=1)){case 0:i.mode=14;break;case 1:if(y(i),i.mode=20,6===t){w>>>=2,v-=2;break e}break;case 2:i.mode=17;break;case 3:e.msg="invalid block type",i.mode=c}w>>>=2,v-=2;break;case 14:for(w>>>=7&v,v-=7&v;v<32;){if(0===m)break e;m--,w+=u[p++]<>>16^65535)){e.msg="invalid stored block lengths",i.mode=c;break}if(i.length=65535&w,w=0,v=0,i.mode=15,6===t)break e;case 15:i.mode=16;case 16:if(C=i.length){if(C>m&&(C=m),C>b&&(C=b),0===C)break e;n.arraySet(f,u,p,C,g),m-=C,p+=C,b-=C,g+=C,i.length-=C;break}i.mode=h;break;case 17:for(;v<14;){if(0===m)break e;m--,w+=u[p++]<>>=5,v-=5,i.ndist=1+(31&w),w>>>=5,v-=5,i.ncode=4+(15&w),w>>>=4,v-=4,i.nlen>286||i.ndist>30){e.msg="too many length or distance symbols",i.mode=c;break}i.have=0,i.mode=18;case 18:for(;i.have>>=3,v-=3}for(;i.have<19;)i.lens[z[i.have++]]=0;if(i.lencode=i.lendyn,i.lenbits=7,N={bits:i.lenbits},F=a(0,i.lens,0,19,i.lencode,0,i.work,N),i.lenbits=N.bits,F){e.msg="invalid code lengths set",i.mode=c;break}i.have=0,i.mode=19;case 19:for(;i.have>>16&255,T=65535&P,!((E=P>>>24)<=v);){if(0===m)break e;m--,w+=u[p++]<>>=E,v-=E,i.lens[i.have++]=T;else{if(16===T){for(O=E+2;v>>=E,v-=E,0===i.have){e.msg="invalid bit length repeat",i.mode=c;break}B=i.lens[i.have-1],C=3+(3&w),w>>>=2,v-=2}else if(17===T){for(O=E+3;v>>=E)),w>>>=3,v-=3}else{for(O=E+7;v>>=E)),w>>>=7,v-=7}if(i.have+C>i.nlen+i.ndist){e.msg="invalid bit length repeat",i.mode=c;break}for(;C--;)i.lens[i.have++]=B}}if(i.mode===c)break;if(0===i.lens[256]){e.msg="invalid code -- missing end-of-block",i.mode=c;break}if(i.lenbits=9,N={bits:i.lenbits},F=a(1,i.lens,0,i.nlen,i.lencode,0,i.work,N),i.lenbits=N.bits,F){e.msg="invalid literal/lengths set",i.mode=c;break}if(i.distbits=6,i.distcode=i.distdyn,N={bits:i.distbits},F=a(2,i.lens,i.nlen,i.ndist,i.distcode,0,i.work,N),i.distbits=N.bits,F){e.msg="invalid distances set",i.mode=c;break}if(i.mode=20,6===t)break e;case 20:i.mode=21;case 21:if(m>=6&&b>=258){e.next_out=g,e.avail_out=b,e.next_in=p,e.avail_in=m,i.hold=w,i.bits=v,o(e,k),g=e.next_out,f=e.output,b=e.avail_out,p=e.next_in,u=e.input,m=e.avail_in,w=i.hold,v=i.bits,i.mode===h&&(i.back=-1);break}for(i.back=0;M=(P=i.lencode[w&(1<>>16&255,T=65535&P,!((E=P>>>24)<=v);){if(0===m)break e;m--,w+=u[p++]<>R)])>>>16&255,T=65535&P,!(R+(E=P>>>24)<=v);){if(0===m)break e;m--,w+=u[p++]<>>=R,v-=R,i.back+=R}if(w>>>=E,v-=E,i.back+=E,i.length=T,0===M){i.mode=26;break}if(32&M){i.back=-1,i.mode=h;break}if(64&M){e.msg="invalid literal/length code",i.mode=c;break}i.extra=15&M,i.mode=22;case 22:if(i.extra){for(O=i.extra;v>>=i.extra,v-=i.extra,i.back+=i.extra}i.was=i.length,i.mode=23;case 23:for(;M=(P=i.distcode[w&(1<>>16&255,T=65535&P,!((E=P>>>24)<=v);){if(0===m)break e;m--,w+=u[p++]<>R)])>>>16&255,T=65535&P,!(R+(E=P>>>24)<=v);){if(0===m)break e;m--,w+=u[p++]<>>=R,v-=R,i.back+=R}if(w>>>=E,v-=E,i.back+=E,64&M){e.msg="invalid distance code",i.mode=c;break}i.offset=T,i.extra=15&M,i.mode=24;case 24:if(i.extra){for(O=i.extra;v>>=i.extra,v-=i.extra,i.back+=i.extra}if(i.offset>i.dmax){e.msg="invalid distance too far back",i.mode=c;break}i.mode=25;case 25:if(0===b)break e;if(C=k-b,i.offset>C){if((C=i.offset-C)>i.whave&&i.sane){e.msg="invalid distance too far back",i.mode=c;break}C>i.wnext?(C-=i.wnext,S=i.wsize-C):S=i.wnext-C,C>i.length&&(C=i.length),A=i.window}else A=f,S=g-i.offset,C=i.length;C>b&&(C=b),b-=C,i.length-=C;do{f[g++]=A[S++]}while(--C);0===i.length&&(i.mode=21);break;case 26:if(0===b)break e;f[g++]=i.length,b--,i.mode=21;break;case 27:if(i.wrap){for(;v<32;){if(0===m)break e;m--,w|=u[p++]<{var n=i(4236),r=[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],s=[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],o=[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],a=[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];e.exports=function(e,t,i,l,h,c,d,u){var f,p,g,m,b,w,v,y,_,x=u.bits,k=0,C=0,S=0,A=0,E=0,M=0,T=0,R=0,L=0,I=0,B=null,F=0,N=new n.Buf16(16),O=new n.Buf16(16),P=null,D=0;for(k=0;k<=15;k++)N[k]=0;for(C=0;C=1&&0===N[A];A--);if(E>A&&(E=A),0===A)return h[c++]=20971520,h[c++]=20971520,u.bits=1,0;for(S=1;S0&&(0===e||1!==A))return-1;for(O[1]=0,k=1;k<15;k++)O[k+1]=O[k]+N[k];for(C=0;C852||2===e&&L>592)return 1;for(;;){v=k-T,d[C]w?(y=P[D+d[C]],_=B[F+d[C]]):(y=96,_=0),f=1<>T)+(p-=f)]=v<<24|y<<16|_|0}while(0!==p);for(f=1<>=1;if(0!==f?(I&=f-1,I+=f):I=0,C++,0==--N[k]){if(k===A)break;k=t[i+d[C]]}if(k>E&&(I&m)!==g){for(0===T&&(T=E),b+=S,R=1<<(M=k-T);M+T852||2===e&&L>592)return 1;h[g=I&m]=E<<24|M<<16|b-c|0}}return 0!==I&&(h[b+I]=k-T<<24|64<<16|0),u.bits=E,0}},8898:e=>{e.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"}},342:(e,t,i)=>{var n=i(4236);function r(e){for(var t=e.length;--t>=0;)e[t]=0}var s=[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],o=[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],a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],l=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],h=new Array(576);r(h);var c=new Array(60);r(c);var d=new Array(512);r(d);var u=new Array(256);r(u);var f=new Array(29);r(f);var p,g,m,b=new Array(30);function w(e,t,i,n,r){this.static_tree=e,this.extra_bits=t,this.extra_base=i,this.elems=n,this.max_length=r,this.has_stree=e&&e.length}function v(e,t){this.dyn_tree=e,this.max_code=0,this.stat_desc=t}function y(e){return e<256?d[e]:d[256+(e>>>7)]}function _(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function x(e,t,i){e.bi_valid>16-i?(e.bi_buf|=t<>16-e.bi_valid,e.bi_valid+=i-16):(e.bi_buf|=t<>>=1,i<<=1}while(--t>0);return i>>>1}function S(e,t,i){var n,r,s=new Array(16),o=0;for(n=1;n<=15;n++)s[n]=o=o+i[n-1]<<1;for(r=0;r<=t;r++){var a=e[2*r+1];0!==a&&(e[2*r]=C(s[a]++,a))}}function A(e){var t;for(t=0;t<286;t++)e.dyn_ltree[2*t]=0;for(t=0;t<30;t++)e.dyn_dtree[2*t]=0;for(t=0;t<19;t++)e.bl_tree[2*t]=0;e.dyn_ltree[512]=1,e.opt_len=e.static_len=0,e.last_lit=e.matches=0}function E(e){e.bi_valid>8?_(e,e.bi_buf):e.bi_valid>0&&(e.pending_buf[e.pending++]=e.bi_buf),e.bi_buf=0,e.bi_valid=0}function M(e,t,i,n){var r=2*t,s=2*i;return e[r]>1;i>=1;i--)T(e,s,i);r=l;do{i=e.heap[1],e.heap[1]=e.heap[e.heap_len--],T(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=i,e.heap[--e.heap_max]=n,s[2*r]=s[2*i]+s[2*n],e.depth[r]=(e.depth[i]>=e.depth[n]?e.depth[i]:e.depth[n])+1,s[2*i+1]=s[2*n+1]=r,e.heap[1]=r++,T(e,s,1)}while(e.heap_len>=2);e.heap[--e.heap_max]=e.heap[1],function(e,t){var i,n,r,s,o,a,l=t.dyn_tree,h=t.max_code,c=t.stat_desc.static_tree,d=t.stat_desc.has_stree,u=t.stat_desc.extra_bits,f=t.stat_desc.extra_base,p=t.stat_desc.max_length,g=0;for(s=0;s<=15;s++)e.bl_count[s]=0;for(l[2*e.heap[e.heap_max]+1]=0,i=e.heap_max+1;i<573;i++)(s=l[2*l[2*(n=e.heap[i])+1]+1]+1)>p&&(s=p,g++),l[2*n+1]=s,n>h||(e.bl_count[s]++,o=0,n>=f&&(o=u[n-f]),a=l[2*n],e.opt_len+=a*(s+o),d&&(e.static_len+=a*(c[2*n+1]+o)));if(0!==g){do{for(s=p-1;0===e.bl_count[s];)s--;e.bl_count[s]--,e.bl_count[s+1]+=2,e.bl_count[p]--,g-=2}while(g>0);for(s=p;0!==s;s--)for(n=e.bl_count[s];0!==n;)(r=e.heap[--i])>h||(l[2*r+1]!==s&&(e.opt_len+=(s-l[2*r+1])*l[2*r],l[2*r+1]=s),n--)}}(e,t),S(s,h,e.bl_count)}function I(e,t,i){var n,r,s=-1,o=t[1],a=0,l=7,h=4;for(0===o&&(l=138,h=3),t[2*(i+1)+1]=65535,n=0;n<=i;n++)r=o,o=t[2*(n+1)+1],++a>=7;n<30;n++)for(b[n]=r<<7,e=0;e<1<0?(2===e.strm.data_type&&(e.strm.data_type=function(e){var t,i=4093624447;for(t=0;t<=31;t++,i>>>=1)if(1&i&&0!==e.dyn_ltree[2*t])return 0;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return 1;for(t=32;t<256;t++)if(0!==e.dyn_ltree[2*t])return 1;return 0}(e)),L(e,e.l_desc),L(e,e.d_desc),o=function(e){var t;for(I(e,e.dyn_ltree,e.l_desc.max_code),I(e,e.dyn_dtree,e.d_desc.max_code),L(e,e.bl_desc),t=18;t>=3&&0===e.bl_tree[2*l[t]+1];t--);return e.opt_len+=3*(t+1)+5+5+4,t}(e),r=e.opt_len+3+7>>>3,(s=e.static_len+3+7>>>3)<=r&&(r=s)):r=s=i+5,i+4<=r&&-1!==t?N(e,t,i,n):4===e.strategy||s===r?(x(e,2+(n?1:0),3),R(e,h,c)):(x(e,4+(n?1:0),3),function(e,t,i,n){var r;for(x(e,t-257,5),x(e,i-1,5),x(e,n-4,4),r=0;r>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&i,e.last_lit++,0===t?e.dyn_ltree[2*i]++:(e.matches++,t--,e.dyn_ltree[2*(u[i]+256+1)]++,e.dyn_dtree[2*y(t)]++),e.last_lit===e.lit_bufsize-1},t._tr_align=function(e){x(e,2,3),k(e,256,h),function(e){16===e.bi_valid?(_(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):e.bi_valid>=8&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},2292:e=>{e.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}},2587:e=>{function t(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,i,n,r){i=i||"&",n=n||"=";var s={};if("string"!=typeof e||0===e.length)return s;var o=/\+/g;e=e.split(i);var a=1e3;r&&"number"==typeof r.maxKeys&&(a=r.maxKeys);var l=e.length;a>0&&l>a&&(l=a);for(var h=0;h=0?(c=p.substr(0,g),d=p.substr(g+1)):(c=p,d=""),u=decodeURIComponent(c),f=decodeURIComponent(d),t(s,u)?Array.isArray(s[u])?s[u].push(f):s[u]=[s[u],f]:s[u]=f}return s}},2361:e=>{var t=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,i,n,r){return i=i||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?Object.keys(e).map((function(r){var s=encodeURIComponent(t(r))+n;return Array.isArray(e[r])?e[r].map((function(e){return s+encodeURIComponent(t(e))})).join(i):s+encodeURIComponent(t(e[r]))})).join(i):r?encodeURIComponent(t(r))+n+encodeURIComponent(t(e)):""}},7673:(e,t,i)=>{t.decode=t.parse=i(2587),t.encode=t.stringify=i(2361)},1269:e=>{class t{constructor(e={}){if(!(e.maxSize&&e.maxSize>0))throw new TypeError("`maxSize` must be a number greater than 0");this.maxSize=e.maxSize,this.cache=new Map,this.oldCache=new Map,this._size=0}_set(e,t){this.cache.set(e,t),this._size++,this._size>=this.maxSize&&(this._size=0,this.oldCache=this.cache,this.cache=new Map)}get(e){if(this.cache.has(e))return this.cache.get(e);if(this.oldCache.has(e)){const t=this.oldCache.get(e);return this.oldCache.delete(e),this._set(e,t),t}}set(e,t){return this.cache.has(e)?this.cache.set(e,t):this._set(e,t),this}has(e){return this.cache.has(e)||this.oldCache.has(e)}peek(e){return this.cache.has(e)?this.cache.get(e):this.oldCache.has(e)?this.oldCache.get(e):void 0}delete(e){const t=this.cache.delete(e);return t&&this._size--,this.oldCache.delete(e)||t}clear(){this.cache.clear(),this.oldCache.clear(),this._size=0}*keys(){for(const[e]of this)yield e}*values(){for(const[,e]of this)yield e}*[Symbol.iterator](){for(const e of this.cache)yield e;for(const e of this.oldCache){const[t]=e;this.cache.has(t)||(yield e)}}get size(){let e=0;for(const t of this.oldCache.keys())this.cache.has(t)||e++;return this._size+e}}e.exports=t},2511:function(e,t,i){var n;e=i.nmd(e),function(r){t&&t.nodeType,e&&e.nodeType;var s="object"==typeof i.g&&i.g;s.global!==s&&s.window!==s&&s.self;var o,a=2147483647,l=36,h=/^xn--/,c=/[^\x20-\x7E]/,d=/[\x2E\u3002\uFF0E\uFF61]/g,u={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},f=Math.floor,p=String.fromCharCode;function g(e){throw RangeError(u[e])}function m(e,t){for(var i=e.length,n=[];i--;)n[i]=t(e[i]);return n}function b(e,t){var i=e.split("@"),n="";return i.length>1&&(n=i[0]+"@",e=i[1]),n+m((e=e.replace(d,".")).split("."),t).join(".")}function w(e){for(var t,i,n=[],r=0,s=e.length;r=55296&&t<=56319&&r65535&&(t+=p((e-=65536)>>>10&1023|55296),e=56320|1023&e),t+p(e)})).join("")}function y(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function _(e,t,i){var n=0;for(e=i?f(e/700):e>>1,e+=f(e/t);e>455;n+=l)e=f(e/35);return f(n+36*e/(e+38))}function x(e){var t,i,n,r,s,o,h,c,d,u,p,m=[],b=e.length,w=0,y=128,x=72;for((i=e.lastIndexOf("-"))<0&&(i=0),n=0;n=128&&g("not-basic"),m.push(e.charCodeAt(n));for(r=i>0?i+1:0;r=b&&g("invalid-input"),((c=(p=e.charCodeAt(r++))-48<10?p-22:p-65<26?p-65:p-97<26?p-97:l)>=l||c>f((a-w)/o))&&g("overflow"),w+=c*o,!(c<(d=h<=x?1:h>=x+26?26:h-x));h+=l)o>f(a/(u=l-d))&&g("overflow"),o*=u;x=_(w-s,t=m.length+1,0==s),f(w/t)>a-y&&g("overflow"),y+=f(w/t),w%=t,m.splice(w++,0,y)}return v(m)}function k(e){var t,i,n,r,s,o,h,c,d,u,m,b,v,x,k,C=[];for(b=(e=w(e)).length,t=128,i=0,s=72,o=0;o=t&&mf((a-i)/(v=n+1))&&g("overflow"),i+=(h-t)*v,t=h,o=0;oa&&g("overflow"),m==t){for(c=i,d=l;!(c<(u=d<=s?1:d>=s+26?26:d-s));d+=l)k=c-u,x=l-u,C.push(p(y(u+k%x,0))),c=f(k/x);C.push(p(y(c,0))),s=_(i,v,n==r),i=0,++n}++i,++t}return C.join("")}o={version:"1.3.2",ucs2:{decode:w,encode:v},decode:x,encode:k,toASCII:function(e){return b(e,(function(e){return c.test(e)?"xn--"+k(e):e}))},toUnicode:function(e){return b(e,(function(e){return h.test(e)?x(e.slice(4).toLowerCase()):e}))}},void 0===(n=function(){return o}.call(t,i,t,e))||(e.exports=n)}()},8575:(e,t,i)=>{var n=i(2511),r=i(2502);function s(){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}t.parse=v,t.resolve=function(e,t){return v(e,!1,!0).resolve(t)},t.resolveObject=function(e,t){return e?v(e,!1,!0).resolveObject(t):t},t.format=function(e){return r.isString(e)&&(e=v(e)),e instanceof s?e.format():s.prototype.format.call(e)},t.Url=s;var o=/^([a-z0-9.+-]+:)/i,a=/:[0-9]*$/,l=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,h=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),c=["'"].concat(h),d=["%","/","?",";","#"].concat(c),u=["/","?","#"],f=/^[+a-z0-9A-Z_-]{0,63}$/,p=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,g={javascript:!0,"javascript:":!0},m={javascript:!0,"javascript:":!0},b={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},w=i(7673);function v(e,t,i){if(e&&r.isObject(e)&&e instanceof s)return e;var n=new s;return n.parse(e,t,i),n}s.prototype.parse=function(e,t,i){if(!r.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var s=e.indexOf("?"),a=-1!==s&&s127?B+="x":B+=I[F];if(!B.match(f)){var O=R.slice(0,E),P=R.slice(E+1),D=I.match(p);D&&(O.push(D[1]),P.unshift(D[2])),P.length&&(v="/"+P.join(".")+v),this.hostname=O.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),T||(this.hostname=n.toASCII(this.hostname));var z=this.port?":"+this.port:"",H=this.hostname||"";this.host=H+z,this.href+=this.host,T&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==v[0]&&(v="/"+v))}if(!g[x])for(E=0,L=c.length;E0)&&i.host.split("@"))&&(i.auth=T.shift(),i.host=i.hostname=T.shift())),i.search=e.search,i.query=e.query,r.isNull(i.pathname)&&r.isNull(i.search)||(i.path=(i.pathname?i.pathname:"")+(i.search?i.search:"")),i.href=i.format(),i;if(!k.length)return i.pathname=null,i.search?i.path="/"+i.search:i.path=null,i.href=i.format(),i;for(var S=k.slice(-1)[0],A=(i.host||e.host||k.length>1)&&("."===S||".."===S)||""===S,E=0,M=k.length;M>=0;M--)"."===(S=k[M])?k.splice(M,1):".."===S?(k.splice(M,1),E++):E&&(k.splice(M,1),E--);if(!_&&!x)for(;E--;E)k.unshift("..");!_||""===k[0]||k[0]&&"/"===k[0].charAt(0)||k.unshift(""),A&&"/"!==k.join("/").substr(-1)&&k.push("");var T,R=""===k[0]||k[0]&&"/"===k[0].charAt(0);return C&&(i.hostname=i.host=R?"":k.length?k.shift():"",(T=!!(i.host&&i.host.indexOf("@")>0)&&i.host.split("@"))&&(i.auth=T.shift(),i.host=i.hostname=T.shift())),(_=_||i.host&&k.length)&&!R&&k.unshift(""),k.length?i.pathname=k.join("/"):(i.pathname=null,i.path=null),r.isNull(i.pathname)&&r.isNull(i.search)||(i.path=(i.pathname?i.pathname:"")+(i.search?i.search:"")),i.auth=e.auth||i.auth,i.slashes=i.slashes||e.slashes,i.href=i.format(),i},s.prototype.parseHost=function(){var e=this.host,t=a.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},2502:e=>{e.exports={isString:function(e){return"string"==typeof e},isObject:function(e){return"object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},7067:()=>{}},t={};function i(n){var r=t[n];if(void 0!==r)return r.exports;var s=t[n]={id:n,loaded:!1,exports:{}};return e[n].call(s.exports,s,s.exports,i),s.loaded=!0,s.exports}return i.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return i.d(t,{a:t}),t},i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),i(5590)})();class sd{constructor(e,t,i){this.config=e,this.browser=i,this.genome=t,this.cramFile=new rd.CramFile({filehandle:new od(e.url,e),seqFetch:e.seqFetch||function(e,t,i){const n=this.genome.sequence,r=this.genome;return this.getHeader().then((function(s){const o=r.getChromosomeName(s.chrNames[e]);return n.getSequence(o,t-1,i)}))}.bind(this),checkSequenceMD5:void 0===e.checkSequenceMD5||e.checkSequenceMD5});const n=new od(e.indexURL,e);this.indexedCramFile=new rd.IndexedCramFile({cram:this.cramFile,index:new rd.CraiIndex({filehandle:n}),fetchSizeLimit:3e7}),Gc.setReaderDefaults(this,e)}async getHeader(){if(!this.header){const e=this.genome,t=await this.cramFile.getSamHeader(),i={},n=[],r={},s=[];for(let o of t)if("SQ"===o.tag){for(let t of o.data)if("SN"===t.tag){const s=t.value;if(i[s]=n.length,n.push(s),e){r[e.getChromosomeName(s)]=s}break}}else"RG"===o.tag&&s.push(o.data);this.header={chrNames:n,chrToIndex:i,chrAliasTable:r,readGroups:s}}return this.header}async readAlignments(e,t,i){this.browser;const n=await this.getHeader(),r=n.chrAliasTable.hasOwnProperty(e)?n.chrAliasTable[e]:e,s=n.chrToIndex[r],o=new Sc(e,t,i,this.config);if(void 0===s)return o;try{const e=await this.indexedCramFile.getRecordsForRange(s,t,i);for(let r of e){const e=r.sequenceId,l=r.alignmentStart,h=l+r.lengthOnRef;if(e<0)continue;if(e>s||l>i)return;if(e=0&&(t="Sequence mismatch. Is this the correct genome for the loaded CRAM?"),this.browser.alert.present(new Error(t)),e}function a(e,t){const i=new Bc;if(i.chr=t[e.sequenceId],i.start=e.alignmentStart-1,i.lengthOnRef=e.lengthOnRef,i.flags=e.flags,i.strand=!(16&e.flags),i.fragmentLength=e.templateLength||e.templateSize,i.mq=e.mappingQuality,i.end=e.alignmentStart+e.lengthOnRef,i.readGroupId=e.readGroupId,e.mate&&void 0!==e.mate.sequenceId){const n=void 0!==e.mate.flags?!(1&e.mate.flags):!(32&e.flags);i.mate={chr:t[e.mate.sequenceId],position:e.mate.alignmentStart,strand:n}}return i.seq=e.getReadBases(),i.qual=e.qualityScores,i.tagDict=e.tags,i.readName=e.readName,function(e,t){const i=[];let n,r,s=0,o="";if(t.scStart=t.start,t.scLengthOnRef=t.lengthOnRef,e.readFeatures)for(let a of e.readFeatures){const e=a.code,l=a.data,h=a.pos-1,c=a.refPos-1;switch(e){case"S":case"I":case"i":case"N":case"D":if(h>s){const e=h-s;i.push(new Vc({start:c-e,seqOffset:s,len:e,type:"M"})),s+=e,o+=e+"M"}if("S"===e){let n=c;t.scLengthOnRef+=l.length,0===h&&(t.scStart-=l.length,n-=l.length);const r=l.length;i.push(new Vc({start:n,seqOffset:s,len:r,type:"S"})),s+=r,o+=r+e}else if("I"===e||"i"===e){void 0===n&&(n=[]);const t="i"===e?1:l.length;n.push(new Vc({start:c,len:t,seqOffset:s,type:"I"})),s+=t,o+=t+e}else"D"!==e&&"N"!==e||(r||(r=[]),r.push({start:c,len:l,type:e}),o+=l+e);break;case"H":case"P":o+=l+e}}const a=e.readLength-s;a>0&&(i.push(new Vc({start:e.alignmentStart+e.lengthOnRef-a-1,seqOffset:s,len:a,type:"M"})),o+=a+"M");t.blocks=i,t.insertions=n,t.gaps=r,t.cigar=o}(e,i),i.mate&&i.start>i.mate.position&&i.fragmentLength>0&&(i.fragmentLength=-i.fragmentLength),Gc.setPairOrientation(i),i}}}class od{constructor(e,t){this.position=0,this.url=e,this.config=t,this.cache=new ad({fetch:(e,t)=>this._fetch(e,t)})}async _fetch(e,t){const i={start:e,size:t};this._stat={size:void 0};const n=await ro.loadArrayBuffer(this.url,To(this.config,{range:i}));return Buffer.from(n)}async read(e,t=0,i=1/0,n=0){let r=n;return null===n&&(r=this.position,this.position+=i),this.cache.get(e,t,i,n)}async readFile(){const e=await ro.loadArrayBuffer(this.url,To(this.config));return Buffer.from(e)}async stat(){if(!this._stat){const e=Buffer.allocUnsafe(10);if(await this.read(e,0,10,0),!this._stat)throw new Error(`unable to determine size of file at ${this.url}`)}return this._stat}}class ad{constructor({fetch:e,size:t=1e7,chunkSize:i=32768}){this.fetch=e,this.chunkSize=i,this.lruCache=new ld({maxSize:Math.floor(t/i)})}async get(e,t,i,n){if(e.length({data:t,chunkNumber:e})));const a=await Promise.all(o),l=n-a[0].chunkNumber*this.chunkSize;a.forEach((({data:o,chunkNumber:a})=>{const h=a*this.chunkSize;let c=0,d=this.chunkSize,u=t+(a-r)*this.chunkSize-l;a===r&&(u=t,c=l),a===s&&(d=n+i-h),o.copy(e,u,c,d)}))}_getChunk(e){const t=this.lruCache.get(e);if(t)return t;const i=this.fetch(e*this.chunkSize,this.chunkSize);return this.lruCache.set(e,i),i}}class ld{constructor(e={}){if(!(e.maxSize&&e.maxSize>0))throw new TypeError("`maxSize` must be a number greater than 0");this.maxSize=e.maxSize,this.cache=new Map,this.oldCache=new Map,this._size=0}_set(e,t){this.cache.set(e,t),this._size++,this._size>=this.maxSize&&(this._size=0,this.oldCache=this.cache,this.cache=new Map)}get(e){if(this.cache.has(e))return this.cache.get(e);if(this.oldCache.has(e)){const t=this.oldCache.get(e);return this._set(e,t),t}}set(e,t){return this.cache.has(e)?this.cache.set(e,t):this._set(e,t),this}has(e){return this.cache.has(e)||this.oldCache.has(e)}peek(e){return this.cache.has(e)?this.cache.get(e):this.oldCache.has(e)?this.oldCache.get(e):void 0}delete(e){const t=this.cache.delete(e);return t&&this._size--,this.oldCache.delete(e)||t}clear(){this.cache.clear(),this.oldCache.clear(),this._size=0}*keys(){for(const[e]of this)yield e}*values(){for(const[,e]of this)yield e}*[Symbol.iterator](){for(const e of this.cache)yield e;for(const e of this.oldCache){const[t]=e;this.cache.has(t)||(yield e)}}get size(){let e=0;for(const t of this.oldCache.keys())this.cache.has(t)||e++;return this._size+e}}var hd={ALIGNMENT_MATCH:"M",INSERT:"I",DELETE:"D",SKIP:"N",CLIP_SOFT:"S",CLIP_HARD:"H",PAD:"P",SEQUENCE_MATCH:"=",SEQUENCE_MISMATCH:"X"};const cd=function(e,t){this.config=e,this.genome=t,this.url=e.url,this.filter=new Uc(e.filter),this.readGroupSetIds=e.readGroupSetIds,this.authKey=e.authKey,this.samplingWindowSize=void 0===e.samplingWindowSize?100:e.samplingWindowSize,this.samplingDepth=void 0===e.samplingDepth?1e3:e.samplingDepth,e.viewAsPairs?this.pairsSupported=!0:this.pairsSupported=void 0===e.pairsSupported||e.pairsSupported};cd.prototype.readAlignments=function(e,t,i){const n=this.genome,r=this;return(r.chrAliasTable?Promise.resolve(r.chrAliasTable):r.readMetadata().then((function(e){if(r.chrAliasTable={},n&&e.readGroups&&e.readGroups.length>0){var t=e.readGroups[0].referenceSetId;return t?Mh({url:r.url+"/references/search",body:{referenceSetId:t},decode:function(e){return e.references}}).then((function(e){return e.forEach((function(e){var t=e.name,i=n.getChromosomeName(t);r.chrAliasTable[i]=t})),r.chrAliasTable})):(function(e,t){var i;if("461916304629"===t||"337315832689"===t){for(i=1;i<23;i++)e["chr"+i]=i;e.chrX="X",e.chrY="Y",e.chrM="MT"}}(r.chrAliasTable,r.config.datasetId),r.chrAliasTable)}return r.chrAliasTable}))).then((function(n){var o=n.hasOwnProperty(e)?n[e]:e;return Mh({url:r.url+"/reads/search",body:{readGroupSetIds:[r.readGroupSetIds],referenceName:o,start:t,end:i,pageSize:"10000"},decode:s,results:new Sc(e,t,i,r.config)})}));function s(e){var t,i,s,o,a,l=e.alignments,h=l.length,c=[];for(t=0;t0;if(s.packedAlignmentRows=kc(o,s.start,s.end,r),this.alignmentContainer=s,a){const t=await n.sequence.getSequence(e,s.start,s.end);if(t)return s.coverageMap.refSeq=t,s.sequence=t,s;console.error("No sequence for: "+e+":"+s.start+"-"+s.end)}return s}}function ud(e,t,i){var n,r,s,o,a,l,h,c={font:"normal 10px Arial",textAlign:"right",strokeStyle:"black"};if(void 0===this.dataRange||void 0===this.dataRange.max||void 0===this.dataRange.min)return;let d=void 0!==this.flipAxis&&this.flipAxis;function u(e){return 0===e?"0":Math.abs(e)>=10?e.toFixed():Math.abs(e)>=1?e.toFixed(1):e.toFixed(2)}Co.fillRect(e,0,0,t,i,{fillStyle:"rgb(255, 255, 255)"}),n=(h=.95*t)-8,a={x:r=h,y:s=o=.01*i},Co.strokeLine(e,n,s,r,o,c),Co.fillText(e,u(d?this.dataRange.min:this.dataRange.max),n+4,s+12,c),l={x:r,y:s=o=.99*i},Co.strokeLine(e,n,s,r,o,c),Co.fillText(e,u(d?this.dataRange.max:this.dataRange.min),n+4,s-4,c),Co.strokeLine(e,a.x,a.y,l.x,l.y,c)}class fd{constructor({chr:e,start:t,end:i}){this.chr=e,this.start=t,this.end=i}contains(e){return e.chr===this.chr&&e.start>=this.start&&e.end<=this.end}overlaps(e){return e.chr===this.chr&&!(e.endthis.end)}extend(e){e.chr===this.chr&&(this.start=Math.min(e.start,this.start),this.end=Math.max(e.end,this.end))}getLocusString(){if("all"===this.chr)return"all";{const e=Xt(Math.floor(this.start)+1),t=Xt(Math.round(this.end));return`${this.chr}:${e}-${t}`}}static fromLocusString(e){if("all"===e)return new fd({chr:"all"});const t=e.split(":"),i=t[0],n=t[1].split("-"),r=Number.parseInt(n[0].replace(/,/g,""))-1,s=Number.parseInt(n[1].replace(/,/g,""));return new fd({chr:i,start:r,end:s})}} +/*! + * 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 pd=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},gd=function(){function e(e,t){for(var i=0;i1&&void 0!==arguments[1]?arguments[1]:1;return(t>0?e.toFixed(t).replace(/0+$/,"").replace(/\.$/,""):e.toString())||"0"}var vd=function(){function e(t,i,n,r){pd(this,e);var s=this;if(void 0===t);else if(Array.isArray(t))this.rgba=t;else if(void 0===n){var o=t&&""+t;o&&function(t){if(t.startsWith("hsl")){var i=t.match(/([\-\d\.e]+)/g).map(Number),n=md(i,4),r=n[0],o=n[1],a=n[2],l=n[3];void 0===l&&(l=1),r/=360,o/=100,a/=100,s.hsla=[r,o,a,l]}else if(t.startsWith("rgb")){var h=t.match(/([\-\d\.e]+)/g).map(Number),c=md(h,4),d=c[0],u=c[1],f=c[2],p=c[3];void 0===p&&(p=1),s.rgba=[d,u,f,p]}else t.startsWith("#")?s.rgba=e.hexToRgb(t):s.rgba=e.nameToRgb(t)||e.hexToRgb(t)}(o.toLowerCase())}else this.rgba=[t,i,n,void 0===r?1:r]}return gd(e,[{key:"printRGB",value:function(e){var t=(e?this.rgba:this.rgba.slice(0,3)).map((function(e,t){return wd(e,3===t?3:0)}));return e?"rgba("+t+")":"rgb("+t+")"}},{key:"printHSL",value:function(e){var t=[360,100,100,1],i=["","%","%",""],n=(e?this.hsla:this.hsla.slice(0,3)).map((function(e,n){return wd(e*t[n],3===n?3:1)+i[n]}));return e?"hsla("+n+")":"hsl("+n+")"}},{key:"printHex",value:function(e){var t=this.hex;return e?t:t.substring(0,7)}},{key:"rgba",get:function(){if(this._rgba)return this._rgba;if(!this._hsla)throw new Error("No color is set");return this._rgba=e.hslToRgb(this._hsla)},set:function(e){3===e.length&&(e[3]=1),this._rgba=e,this._hsla=null}},{key:"rgbString",get:function(){return this.printRGB()}},{key:"rgbaString",get:function(){return this.printRGB(!0)}},{key:"hsla",get:function(){if(this._hsla)return this._hsla;if(!this._rgba)throw new Error("No color is set");return this._hsla=e.rgbToHsl(this._rgba)},set:function(e){3===e.length&&(e[3]=1),this._hsla=e,this._rgba=null}},{key:"hslString",get:function(){return this.printHSL()}},{key:"hslaString",get:function(){return this.printHSL(!0)}},{key:"hex",get:function(){return"#"+this.rgba.map((function(e,t){return t<3?e.toString(16):Math.round(255*e).toString(16)})).map((function(e){return e.padStart(2,"0")})).join("")},set:function(t){this.rgba=e.hexToRgb(t)}}],[{key:"hexToRgb",value:function(e){var t=(e.startsWith("#")?e.slice(1):e).replace(/^(\w{3})$/,"$1F").replace(/^(\w)(\w)(\w)(\w)$/,"$1$1$2$2$3$3$4$4").replace(/^(\w{6})$/,"$1FF");if(!t.match(/^([0-9a-fA-F]{8})$/))throw new Error("Unknown hex color; "+e);var i=t.match(/^(\w\w)(\w\w)(\w\w)(\w\w)$/).slice(1).map((function(e){return parseInt(e,16)}));return i[3]=i[3]/255,i}},{key:"nameToRgb",value:function(t){var i=t.toLowerCase().replace("at","T").replace(/[aeiouyldf]/g,"").replace("ght","L").replace("rk","D").slice(-5,4),n=bd[i];return void 0===n?n:e.hexToRgb(n.replace(/\-/g,"00").padStart(6,"f"))}},{key:"rgbToHsl",value:function(e){var t=md(e,4),i=t[0],n=t[1],r=t[2],s=t[3];i/=255,n/=255,r/=255;var o=Math.max(i,n,r),a=Math.min(i,n,r),l=void 0,h=void 0,c=(o+a)/2;if(o===a)l=h=0;else{var d=o-a;switch(h=c>.5?d/(2-o-a):d/(o+a),o){case i:l=(n-r)/d+(n1&&(i-=1),i<1/6?e+6*(t-e)*i:i<.5?t:i<2/3?e+(t-e)*(2/3-i)*6:e},c=r<.5?r*(1+n):r+n-r*n,d=2*r-c;o=h(d,c,i+1/3),a=h(d,c,i),l=h(d,c,i-1/3)}var u=[255*o,255*a,255*l].map(Math.round);return u[3]=s,u}}]),e}(),yd=function(){function e(){pd(this,e),this._events=[]}return gd(e,[{key:"add",value:function(e,t,i){e.addEventListener(t,i,!1),this._events.push({target:e,type:t,handler:i})}},{key:"remove",value:function(t,i,n){this._events=this._events.filter((function(r){var s=!0;return t&&t!==r.target&&(s=!1),i&&i!==r.type&&(s=!1),n&&n!==r.handler&&(s=!1),s&&e._doRemove(r.target,r.type,r.handler),!s}))}},{key:"destroy",value:function(){this._events.forEach((function(t){return e._doRemove(t.target,t.type,t.handler)})),this._events=[]}}],[{key:"_doRemove",value:function(e,t,i){e.removeEventListener(t,i,!1)}}]),e}();function _d(e,t,i){var n=!1;function r(e,t,i){return Math.max(t,Math.min(e,i))}function s(e,s,o){if(o&&(n=!0),n){e.preventDefault();var a=t.getBoundingClientRect(),l=a.width,h=a.height,c=s.clientX,d=s.clientY,u=r(c-a.left,0,l),f=r(d-a.top,0,h);i(u/l,f/h)}}function o(e,t){1===(void 0===e.buttons?e.which:e.buttons)?s(e,e,t):n=!1}function a(e,t){1===e.touches.length?s(e,e.touches[0],t):n=!1}e.add(t,"mousedown",(function(e){o(e,!0)})),e.add(t,"touchstart",(function(e){a(e,!0)})),e.add(window,"mousemove",o),e.add(t,"touchmove",a),e.add(window,"mouseup",(function(e){n=!1})),e.add(t,"touchend",(function(e){n=!1})),e.add(t,"touchcancel",(function(e){n=!1}))}var xd="keydown",kd="mousedown",Cd="focusin";function Sd(e,t){return(t||document).querySelector(e)}function Ad(e){e.preventDefault(),e.stopPropagation()}function Ed(e,t,i,n,r){e.add(t,xd,(function(e){i.indexOf(e.key)>=0&&(r&&Ad(e),n(e))}))}var Md=function(){function e(t){pd(this,e),this.settings={popup:"right",layout:"default",alpha:!0,editor:!0,editorFormat:"hex",cancelButton:!1,defaultColor:"#0cf"},this._events=new yd,this.onChange=null,this.onDone=null,this.onOpen=null,this.onClose=null,this.setOptions(t)}return gd(e,[{key:"setOptions",value:function(e){var t=this;if(e){var i=this.settings;if(e instanceof HTMLElement)i.parent=e;else{i.parent&&e.parent&&i.parent!==e.parent&&(this._events.remove(i.parent),this._popupInited=!1),function(e,t,i){for(var n in e)i&&i.indexOf(n)>=0||(t[n]=e[n])}(e,i),e.onChange&&(this.onChange=e.onChange),e.onDone&&(this.onDone=e.onDone),e.onOpen&&(this.onOpen=e.onOpen),e.onClose&&(this.onClose=e.onClose);var n=e.color||e.colour;n&&this._setColor(n)}var r=i.parent;if(r&&i.popup&&!this._popupInited){var s=function(e){return t.openHandler(e)};this._events.add(r,"click",s),Ed(this._events,r,[" ","Spacebar","Enter"],s),this._popupInited=!0}else e.parent&&!i.popup&&this.show()}}},{key:"openHandler",value:function(e){if(this.show()){e&&e.preventDefault(),this.settings.parent.style.pointerEvents="none";var t=e&&e.type===xd?this._domEdit:this.domElement;setTimeout((function(){return t.focus()}),100),this.onOpen&&this.onOpen(this.colour)}}},{key:"closeHandler",value:function(e){var t=e&&e.type,i=!1;if(e)if(t===kd||t===Cd){var n=(this.__containedEvent||0)+100;e.timeStamp>n&&(i=!0)}else Ad(e),i=!0;else i=!0;i&&this.hide()&&(this.settings.parent.style.pointerEvents="",t!==kd&&this.settings.parent.focus(),this.onClose&&this.onClose(this.colour))}},{key:"movePopup",value:function(e,t){this.closeHandler(),this.setOptions(e),t&&this.openHandler()}},{key:"setColor",value:function(e,t){this._setColor(e,{silent:t})}},{key:"_setColor",value:function(e,t){if("string"==typeof e&&(e=e.trim()),e){t=t||{};var i=void 0;try{i=new vd(e)}catch(e){if(t.failSilently)return;throw e}if(!this.settings.alpha){var n=i.hsla;n[3]=1,i.hsla=n}this.colour=this.color=i,this._setHSLA(null,null,null,null,t)}}},{key:"setColour",value:function(e,t){this.setColor(e,t)}},{key:"show",value:function(){if(!this.settings.parent)return!1;if(this.domElement){var e=this._toggleDOM(!0);return this._setPosition(),e}var t=function(e){var t=document.createElement("div");return t.innerHTML=e,t.firstElementChild}(this.settings.template||'
');return this.domElement=t,this._domH=Sd(".picker_hue",t),this._domSL=Sd(".picker_sl",t),this._domA=Sd(".picker_alpha",t),this._domEdit=Sd(".picker_editor input",t),this._domSample=Sd(".picker_sample",t),this._domOkay=Sd(".picker_done button",t),this._domCancel=Sd(".picker_cancel button",t),t.classList.add("layout_"+this.settings.layout),this.settings.alpha||t.classList.add("no_alpha"),this.settings.editor||t.classList.add("no_editor"),this.settings.cancelButton||t.classList.add("no_cancel"),this._ifPopup((function(){return t.classList.add("popup")})),this._setPosition(),this.colour?this._updateUI():this._setColor(this.settings.defaultColor),this._bindEvents(),!0}},{key:"hide",value:function(){return this._toggleDOM(!1)}},{key:"destroy",value:function(){this._events.destroy(),this.domElement&&this.settings.parent.removeChild(this.domElement)}},{key:"_bindEvents",value:function(){var e=this,t=this,i=this.domElement,n=this._events;function r(e,t,i){n.add(e,t,i)}r(i,"click",(function(e){return e.preventDefault()})),_d(n,this._domH,(function(e,i){return t._setHSLA(e)})),_d(n,this._domSL,(function(e,i){return t._setHSLA(null,e,1-i)})),this.settings.alpha&&_d(n,this._domA,(function(e,i){return t._setHSLA(null,null,null,1-i)}));var s=this._domEdit;r(s,"input",(function(e){t._setColor(this.value,{fromEditor:!0,failSilently:!0})})),r(s,"focus",(function(e){var t=this;t.selectionStart===t.selectionEnd&&t.select()})),this._ifPopup((function(){var t=function(t){return e.closeHandler(t)};r(window,kd,t),r(window,Cd,t),Ed(n,i,["Esc","Escape"],t);var s=function(t){e.__containedEvent=t.timeStamp};r(i,kd,s),r(i,Cd,s),r(e._domCancel,"click",t)}));var o=function(t){e._ifPopup((function(){return e.closeHandler(t)})),e.onDone&&e.onDone(e.colour)};r(this._domOkay,"click",o),Ed(n,i,["Enter"],o)}},{key:"_setPosition",value:function(){var e=this.settings.parent,t=this.domElement;e!==t.parentNode&&e.appendChild(t),this._ifPopup((function(i){"static"===getComputedStyle(e).position&&(e.style.position="relative");var n=!0===i?"popup_right":"popup_"+i;["popup_top","popup_bottom","popup_left","popup_right"].forEach((function(e){e===n?t.classList.add(e):t.classList.remove(e)})),t.classList.add(n)}))}},{key:"_setHSLA",value:function(e,t,i,n,r){r=r||{};var s=this.colour,o=s.hsla;[e,t,i,n].forEach((function(e,t){(e||0===e)&&(o[t]=e)})),s.hsla=o,this._updateUI(r),this.onChange&&!r.silent&&this.onChange(s)}},{key:"_updateUI",value:function(e){if(this.domElement){e=e||{};var t=this.colour,i=t.hsla,n="hsl("+360*i[0]+", 100%, 50%)",r=t.hslString,s=t.hslaString,o=this._domH,a=this._domSL,l=this._domA,h=Sd(".picker_selector",o),c=Sd(".picker_selector",a),d=Sd(".picker_selector",l);w(0,h,i[0]),this._domSL.style.backgroundColor=this._domH.style.color=n,w(0,c,i[1]),v(0,c,1-i[2]),a.style.color=r,v(0,d,1-i[3]);var u=r,f=u.replace("hsl","hsla").replace(")",", 0)"),p="linear-gradient("+[u,f]+")";if(this._domA.style.background=p+", 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",!e.fromEditor){var g=this.settings.editorFormat,m=this.settings.alpha,b=void 0;switch(g){case"rgb":b=t.printRGB(m);break;case"hsl":b=t.printHSL(m);break;default:b=t.printHex(m)}this._domEdit.value=b}this._domSample.style.color=s}function w(e,t,i){t.style.left=100*i+"%"}function v(e,t,i){t.style.top=100*i+"%"}}},{key:"_ifPopup",value:function(e,t){this.settings.parent&&this.settings.popup?e&&e(this.settings.popup):t&&t()}},{key:"_toggleDOM",value:function(e){var t=this.domElement;if(!t)return!1;var i=e?"":"none",n=t.style.display!==i;return n&&(t.style.display=i),n}}]),e}(),Td=document.createElement("style");function Rd(e){if(Ld[e])return Ld[e];if(Ld["chr"+e]){const t=Ld["chr"+e];return Ld[e]=t,t}{const r=(t=Math.round(255*Math.random()).toString(10),i=Math.round(255*Math.random()).toString(10),n=Math.round(255*Math.random()).toString(10),"rgb("+t+","+i+","+n+")");return Ld[e]=r,r}var t,i,n}Td.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(Td),Md.StyleElement=Td;const Ld={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 Id{constructor(e){this.tracks=[],this.chordSets=[]}addChordSet(e){this.chordSets=this.chordSets.filter((t=>t.name!==e.name)),this.chordSets.push(e);let t=this.tracks.find((t=>e.trackName===t.name));t&&(t.chordSets=t.chordSets.filter((t=>t.name!==e.name)),t.chordSets.push(e)),t||(t=new Bd(e),this.tracks.push(t))}clearChords(){this.tracks=[],this.chordSets=[]}getTrack(e){return this.tracks.find((t=>e===t.name))}getChordSet(e){return this.chordSets.find((t=>e===t.name))}}class Bd{constructor(e){this.name=e.trackName,this.color=e.trackColor,this.visible=!0,this.chordSets=[e],this.id=("0000"+(Math.random()*Math.pow(36,4)<<0).toString(36)).slice(-4)}get chords(){if(1===this.chordSets.length)return this.chordSets[0].chords;const e=[];for(let t of this.chordSets)for(let i of t.chords)e.push(i);return e}}const Fd=Math.exp(5);class Nd{static isInstalled(){return void 0!==window.JBrowseReactCircularGenomeView&&void 0!==window.React&&void 0!==window.ReactDOM}constructor(e,t){if(t=t||{},this.config=t,Nd.isInstalled()){this.parent=e,this.groupByTrack=!0===t.groupByTrack,this.chordManager=new Id(t);const i=document.createElement("div");i.className="igv-circview-container",e.appendChild(i),this.createControls(i),this.resetControlPanel();const n=document.createElement("div");n.className="igv-circview-circular-genome-view",i.appendChild(n),this.container=n,t.assembly&&this.setAssembly(t.assembly),this.width=t.width||500,this.height=t.height||500,this.setSize(this.width,this.height)}else console.error("JBrowse circular view is not installed")}createControls(e){const t=document.createElement("div");t.className="igv-circview-toolbar",e.appendChild(t),this.toolbar=t;const i=document.createElement("div");i.className="igv-circview-track-panel",e.appendChild(i),this.controlPanel=i,this.controlPanel.style.display="none";const n=document.createElement("div");n.className="igv-circview-toolbar-button-container",this.toolbar.appendChild(n),this.showControlsButton=document.createElement("div"),this.showControlsButton.className="igv-circview-button",n.appendChild(this.showControlsButton),this.showControlsButton.innerText="none"===this.controlPanel.style.display?"Show Controls":"Hide Controls",this.showControlsButton.addEventListener("click",(e=>{this.controlPanel.querySelectorAll("div").length>0&&("none"===this.controlPanel.style.display?(this.controlPanel.style.display="flex",e.target.innerText="Hide Controls"):(this.controlPanel.style.display="none",e.target.innerText="Show Controls"))}));let r=document.createElement("div");r.className="igv-circview-button",n.appendChild(r),r.innerText="Clear All",r.addEventListener("click",(()=>{this.clearChords()})),!1!==this.config.showCloseButton&&(r=document.createElement("div"),r.className="igv-circview-button",n.appendChild(r),r.innerText="Close",r.addEventListener("click",(()=>{this.visible=!1})))}resetControlPanel(){this.controlPanel.innerHTML="",this.controlPanel.appendChild(this.createGroupByCB());const e=this.groupByTrack?this.chordManager.tracks:this.chordManager.chordSets;for(let t of e)this.addToControlPanel(t)}createGroupByCB(){const e=document.createElement("input");e.type="checkbox",e.id="groupByCB",e.style.width="1.4em",e.style.height="1.4em",e.checked=this.groupByTrack,e.onclick=e=>{this.groupByTrack=e.target.checked,this.resetControlPanel(),this.render()};const t=document.createElement("label");t.for="groupByCB",t.innerText="Group by track",t.style.color="black",t.style.paddingLeft="10px";const i=document.createElement("div");return i.style.width="100%",i.style.paddingTop="5px",i.style.paddingBottom="5px",i.style.background="rgb(216, 230, 234)",i.appendChild(e),i.appendChild(t),i}addToControlPanel(e){const t=document.createElement("div");this.controlPanel.appendChild(t);const i=document.createElement("div");i.className="igv-circview-button",t.appendChild(i),i.innerText=!0===e.visible?"Hide":"Show",i.addEventListener("click",(t=>{!0===e.visible?(this.hideChordSet(e.name),t.target.innerText="Show"):(this.showChordSet(e.name),t.target.innerText="Hide")}));const n=document.createElement("input"),r=e=>200*Math.log(e*Fd),s=document.createElement("div");s.className="igv-circview-button",s.innerHTML="    ",t.appendChild(s),s.style.backgroundColor=Od(e.color,1);const o={parent:s,popup:"right",editorFormat:"rgb",color:e.color,onChange:({rgbaString:t})=>{s.style.backgroundColor=Od(t,1),this.setColor(e.name,t),n.value=r(Pd(e.color))}},a=new Md(o);n.setAttribute("title","Adjust transparency of arcs"),n.type="range",n.style.width="100px",n.style.marginRight="10px",n.setAttribute("class","range"),n.setAttribute("min","0"),n.setAttribute("max","1000"),n.value=r(Pd(e.color)),n.oninput=()=>{const t=(i=n.value,Math.exp(i/200)/Fd);var i;this.setColor(e.name,Od(e.color,t)),a.setColor(e.color)},t.appendChild(n);const l=document.createElement("div");l.style.color="black",t.appendChild(l),l.innerText=l.title=e.name}setAssembly(e){const t=this.genomeId||Hd();if(this.genomeId===t)return;this.chordManager.clearChords(),this.genomeId=t,this.chrNames=new Set(e.chromosomes.map((e=>Dd(e.name))));const i=[],n=[];for(let t of e.chromosomes){const e=Dd(t.name);n.push(t.color||Rd(e)),i.push({refName:e,uniqueId:e,start:0,end:t.bpLength})}this.assembly={name:e.name,sequence:{trackId:t,type:"ReferenceSequenceTrack",adapter:{type:"FromConfigSequenceAdapter",features:i}},refNameColors:n},this.render()}addChords(e,t={}){const i=t.name||t.track||"*",n=i.split(" ")[0].replaceAll("%20"," "),r={name:i.replaceAll("%20"," "),trackName:n,chords:e,color:t.color||"black",trackColor:t.trackColor||t.color||"black",visible:!0,id:t.id||Hd()};this.chordManager.addChordSet(r),this.resetControlPanel(),this.render()}setSize(e,t){if(t=t||e,this.width=e,this.height=t,this.viewState){const i=this.viewState.session.view;i.setWidth(e),i.setHeight(t),i.setBpPerPx(i.minBpPerPx)}}getSize(){return Math.min(this.width,this.height)}clearChords(){this.chordManager.clearChords(),this.resetControlPanel(),this.render()}clearSelection(){this.viewState.pluginManager.rootModel.session.clearSelection()}show(){this.parent.style.display="block"}hide(){this.parent.style.display="none"}get visible(){return"none"!==this.parent.style.display}set visible(e){this.parent.style.display=e?"block":"none"}hideChordSet(e){let t=this.getChordSet(e);t?(t.visible=!1,this.render()):console.warn(`No track with name: ${name}`)}showChordSet(e){let t=this.getChordSet(e);t?(t.visible=!0,this.render()):console.warn(`No track with name: ${e}`)}deleteTrack(e){let t=this.tracks.findIndex((t=>e===t.name));t>=0&&this.tracks.splice(t,1),this.render()}getChordSet(e){return this.groupByTrack?this.chordManager.getTrack(e):this.chordManager.getChordSet(e)}setColor(e,t){const i=this.getChordSet(e);if(i){i.color=t;const e=i.id;for(let i of this.viewState.config.tracks)if(e===i.trackId){i.displays[0].renderer.strokeColor.set(t);break}}}render(){const{createViewState:e,JBrowseCircularGenomeView:t}=JBrowseReactCircularGenomeView;ReactDOM.unmountComponentAtNode(this.container);const i=(this.groupByTrack?this.chordManager.tracks:this.chordManager.chordSets).filter((e=>e.visible)),n=[],r=[];for(let e of i)n.push({trackId:e.id,name:e.name,assemblyNames:["forIGV"],type:"VariantTrack",adapter:{type:"FromConfigAdapter",features:e.chords}}),r.push(e.color);this.viewState=e({assembly:this.assembly,tracks:n});for(let e=0;e 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',document.head.insertBefore(e,document.head.childNodes[document.head.childNodes.length-1])}()}const Vd=e=>e.startsWith("chr")?e.substring(3):e;function Ud(e){const t=[],i=[];for(let n of e.wgChromosomeNames){const r=e.getChromosome(n);i.push(mu(r.name)),t.push({name:r.name,bpLength:r.bpLength})}return t}function qd(e,t,i,n){const r=t.color||"rgb(0,0,255)",s=Hs.addAlpha("all"===i.chr?r:mu(i.chr),n),o=Hs.addAlpha(r,n),a=t.name.replaceAll(" ","%20"),l="all"===i.chr?a:`${a} ${i.chr}:${i.start}-${i.end}`;t.browser.circularView.addChords(e,{track:l,color:s,trackColor:o}),t.browser.circularViewVisible||(t.browser.circularViewVisible=!0)}class jd{constructor(e,{minTLENPercentile:t,maxTLENPercentile:i}){this.totalCount=0,this.frCount=0,this.rfCount=0,this.ffCount=0,this.sumF=0,this.sumF2=0,this.lp=void 0===t?.1:t,this.up=void 0===i?99.5:i,this.isizes=[],this.compute(e)}compute(e){for(let n of e)if(n.isProperPair()){var t=Math.abs(n.fragmentLength);this.sumF+=t,this.sumF2+=t*t,this.isizes.push(t);var i=n.pairOrientation;if("string"==typeof i&&4===i.length)switch(""+i.charAt(0)+i.charAt(2)){case"FF":case"RR":this.ffCount++;break;case"FR":this.frCount++;break;case"RF":this.rfCount++}this.totalCount++}this.ffCount/this.totalCount>.9?this.orienation="ff":this.frCount/this.totalCount>.9?this.orienation="fr":this.rfCount/this.totalCount>.9&&(this.orienation="rf"),this.minTLEN=0===this.lp?0:$d(this.isizes,this.lp),this.maxTLEN=$d(this.isizes,this.up)}}function $d(e,t){if(0!==e.length){var i=Math.floor(e.length*(t/100));return e.sort((function(e,t){return e-t})),e[i]}}const Wd={getTissueInfo:function(e,t){let i=(t=t||"https://gtexportal.org/rest/v1")+"/dataset/tissueInfo?datasetId="+(e=e||"gtex_v8");return ro.loadJson(i,{})},trackConfiguration:function(e,t){return{type:"eqtl",sourceType:"gtex-ws",url:(t=t||"https://gtexportal.org/rest/v1")+"/association/singleTissueEqtlByLocation",tissueSiteDetailId:e.tissueSiteDetailId,name:e.tissueSiteDetailId.split("_").join(" "),visibilityWindow:25e4}}};function Gd(e,t,i){let n=(e.start-t)/i,r=(e.end-t)/i,s=r-n;return s<3&&(s=3,n-=1.5),{px:n,px1:r,pw:s}}function Zd(e,t,i,n,r,s){try{r.save(),r.fillStyle=this.color,r.strokeStyle=this.color;const n=this.getColorForFeature(e);let o,a;r.fillStyle=n,r.strokeStyle=n,"SQUISHED"===this.displayMode&&void 0!==e.row?(o=this.featureHeight/2,a=this.margin+this.squishedRowHeight*e.row):"EXPANDED"===this.displayMode&&void 0!==e.row?(o=this.featureHeight,a=this.margin+this.expandedRowHeight*e.row):(o=this.featureHeight,a=this.margin);const l=s.pixelWidth,h=a+o/2,c=o/2,d=h-c/2,u=e.exons?e.exons.length:0,f=Gd(e,t,i),p=this.arrowSpacing,g="+"===e.strand?1:"-"===e.strand?-1:0;if(0===u){const e=Math.max(0,f.px),t=Math.min(l,f.px1),i=t-e;if(r.fillRect(e,a,i,o),0!==g){r.fillStyle="white",r.strokeStyle="white";for(let i=e+p/2;il)break;if(u.utr)r.fillRect(m,d,w,c);else if(u.cdStart&&(f=Math.round((u.cdStart-t)/i),r.fillRect(m,d,f-m,c),w-=f-m,m=f),u.cdEnd&&(f=Math.round((u.cdEnd-t)/i),r.fillRect(f,d,b-f,c),w-=b-f,b=f),w=Math.max(w,1),r.fillRect(m,a,w,o),w>p+5&&0!==g){r.fillStyle="white",r.strokeStyle="white";for(let e=m+p/2;ey||g)&&(o.rowLastLabelX[t.row]=v,"y"===o.axis?(e.save(),Co.labelTransformWithContext(e,d),Co.fillText(e,a,d,u,m,c),e.restore()):Co.fillText(e,a,d,u,m,c))}finally{e.restore()}}const Xd=new Set(["nonsense","missense","stop-loss","frameshift","cds-indel"]),Yd=new Set(["coding-synon"]),Kd=new Set(["splice-3","splice-5"]),Jd=new Set(["untranslated-5","untranslated-3"]);function eu(e,t,i,n,r){var s,o,a,l,h=Gd(e,t,i),c=this.margin,d=this.snpColors.length;switch(s="squished"===this.displayMode?this.squishedRowHeight:this.expandedRowHeight,this.colorBy){case"function":l=e.func,o=l.split(",").map((function(e){return Xd.has(e)||Kd.has(e)?d-1:Yd.has(e)?d-2:Jd.has(e)?d-3:0})).reduce((function(e,t){return Math.max(e,t)}));break;case"class":o="deletion"===(a=e.class)?d-1:"mnp"===a?d-2:"microsatellite"===a||"named"===a?d-3:0}r.fillStyle=this.snpColors[o],r.fillRect(h.px,c,h.pw,s)}function tu(e,t,i,n,r){const s="EXPANDED"===this.displayMode?this.expandedRowHeight:this.squishedRowHeight;let o=this.margin;"COLLAPSED"!==this.displayMode&&void 0!==e.row&&(o+=e.row*s);const a=o+.5*s,l=a-.5*s,h=a+.5*s,c=Math.round((e.junction_left-t)/i),d=Math.round((e.junction_right-t)/i);r.beginPath(),r.moveTo(c,a),r.bezierCurveTo(c,l,d,l,d,a),r.lineWidth=1+Math.log(e.num_junction_reads)/Math.log(2),r.strokeStyle="blue",r.stroke();const u=e.spanning_frag_coords;for(let e=0;et&&(t=i.row);return this.margin+(t+1)*("SQUISHED"===this.displayMode?this.squishedRowHeight:this.expandedRowHeight)}}draw(e){const t=e.features,i=e.context,n=e.bpPerPixel,r=e.bpStart,s=e.pixelWidth,o=e.pixelHeight,a=r+s*n+1;if(this.config.isMergedTrack||Co.fillRect(i,0,e.pixelTop,s,o,{fillStyle:"rgb(255, 255, 255)"}),t){const l=[];e.rowLastX=[],e.rowLastLabelX=[];for(let i of t)if(i.start>r&&i.enda)break;const t="COLLAPSED"===this.displayMode?0:s.row;e.drawLabel=e.labelAllFeatures||h>10;const l=Math.ceil((s.end-r)/n),d=c[t];if(!d||l>d){this.render.call(this,s,r,n,o,i,e);const a=Math.floor((s.start-r)/n);d&&a-d<=0&&(i.globalAlpha=.5,Co.strokeLine(i,a,0,a,o,{strokeStyle:"rgb(255, 255, 255)"}),i.globalAlpha=1),c[t]=l}}}else console.log("No feature list")}clickedFeatures(e){const t=e.y-this.margin,i=super.clickedFeatures(e);let n;switch(this.displayMode){case"SQUISHED":n=Math.floor(t/this.squishedRowHeight);break;case"EXPANDED":n=Math.floor(t/this.expandedRowHeight);break;default:n=void 0}return i.filter((function(e){return void 0===n||void 0===e.row||n===e.row}))}popupData(e,t){void 0===t&&(t=this.clickedFeatures(e));const i=e.genomicLocation,n=[];for(let e of t){const t=e._f||e,r="function"==typeof t.popupData?t.popupData(i):this.extractPopupData(t);if(r){n.length>0&&n.push("

");const s=this.infoURL||this.config.infoURL;for(let t of r)if(n.push(t),s&&t.name&&"name"===t.name.toLowerCase()&&t.value&&Qt(t.value)&&!t.value.startsWith("<")){const i=s.replace("$$",e.name);t.value=`${t.value}`}const o="gff"===this.config.format||"gff3"===this.config.format||"gtf"===this.config.format;if(t.exons)for(let e=0;e=r.start&&i<=r.end){const i=o?r.number:"-"===t.strand?t.exons.length-e:e+1;i&&(n.push("
"),n.push({name:"Exon Number",value:i}));break}}}}return n}menuItemList(){const e=[];if(this.render===eu){e.push("
");for(let t of["function","class"])e.push({object:ft(jt("Color by "+t,t===this.colorBy)),click:()=>{this.colorBy=t,this.trackView.repaintViews()}})}e.push("
");for(let t of["COLLAPSED","SQUISHED","EXPANDED"]){const i={COLLAPSED:"Collapse",SQUISHED:"Squish",EXPANDED:"Expand"};e.push({object:ft(jt(i[t],t===this.displayMode)),click:()=>{this.displayMode=t,this.config.displayMode=t,this.trackView.checkContentHeight(),this.trackView.repaintViews()}})}return e}contextMenuItemList(e){const t=this.clickedFeatures(e);if(void 0===t||0===t.length)return;t.length>1&&t.sort(((e,t)=>t.end-t.start-(e.end-e.start)));const i=t[0];if(i.end-i.start<=1e6){const e=[{label:"View feature sequence",click:async()=>{let e=await this.browser.genome.getSequence(i.chr,i.start,i.end);e?"-"===i.strand&&(e=Ko(e)):e="Unknown sequence",this.browser.alert.present(e)}}];return Do()&&void 0!==navigator.clipboard&&e.push({label:"Copy feature sequence",click:async()=>{let e=await this.browser.genome.getSequence(i.chr,i.start,i.end);e?"-"===i.strand&&(e=Ko(e)):e="Unknown sequence";try{await navigator.clipboard.writeText(e)}catch(e){console.error(e),this.browser.alert.present(`error copying sequence to clipboard ${e}`)}}}),e.push("
"),e}}description(){if(eu===this.render){let e=""+this.name+"
";return e+="Color By Function:
",e+='Red: Coding-Non-Synonymous, Splice Site
',e+='Green: Coding-Synonymous
',e+='Blue: Untranslated
',e+='Black: Intron, Locus, Unknown

',e+="Color By Class:
",e+='Red: Deletion
',e+='Green: MNP
',e+='Blue: Microsatellite, Named
',e+='Black: Indel, Insertion, SNP',e+="",e}return super.description()}getColorForFeature(e){let t;if(this.altColor&&"-"===e.strand)t="function"==typeof this.altColor?this.altColor(e):this.altColor;else if(this.color)t="function"==typeof this.color?this.color(e):this.color;else if(this.colorBy){const i=e.getAttributeValue?e.getAttributeValue(this.colorBy):e[this.colorBy];t=this.colorTable.getColor(i)}else e.color&&(t=e.color);if(t||(t="rgb(0, 0, 150)"),e.alpha&&1!==e.alpha)t=Hs.addAlpha(t,e.alpha);else if(this.useScore&&e.score&&!Number.isNaN(e.score)){const i=function(e,t,i){const n=(t-e)/9,r=Math.floor((i-e)/n);return Math.min(1,.2+.8*r/9)}(this.config.min?this.config.min:this.viewLimitMin?this.viewLimitMin:0,this.config.max?this.config.max:this.viewLimitMax?this.viewLimitMax:1e3,e.score);e.alpha=i,t=Hs.addAlpha(t,i)}return t}dispose(){this.trackView=void 0}}class nu{constructor(e){this.config=e,this.browser=e.browser,this.columnFormat=e.columnFormat,this.tableRowSelectionList=[],this.tableDOM=_t.div({class:"igv-roi-table"}),e.parent.appendChild(this.tableDOM),this.headerDOM=e,this.tableColumnTitles=this.tableDOM,this.tableRowContainer=this.tableDOM,this.footerDOM=e.gotoButtonHandler}set headerDOM({browser:e,parent:t,headerTitle:i,dismissHandler:n}){const r=_t.div();this.tableDOM.appendChild(r);const s=_t.div();r.appendChild(s),s.innerHTML=i;const o=_t.div();r.appendChild(o),o.appendChild(At.createIcon("times")),this.boundDismissHandler=function(e){e.stopPropagation(),n()}.bind(this),o.addEventListener("click",this.boundDismissHandler);const{y:a}=e.root.getBoundingClientRect(),{y:l}=t.getBoundingClientRect(),h=-(l-a);Rt(this.tableDOM,r,{minX:0,minY:h}),this.tableDOM.style.display="none",this._headerDOM=r}set tableColumnTitles(e){const t=_t.div({class:"igv-roi-table-column-titles"});e.appendChild(t);for(const{label:e,width:i}of this.columnFormat){const n=_t.div();t.appendChild(n),n.style.width=i,n.innerText=e}this._tableColumnTitlesDOM=t}get tableColumnTitles(){return this._tableColumnTitlesDOM}set tableRowContainer(e){const t=_t.div({class:"igv-roi-table-row-container"});e.appendChild(t),this._tableRowContainerDOM=t}get tableRowContainer(){return this._tableRowContainerDOM}set footerDOM(e){const t=_t.div();this.tableDOM.appendChild(t);const i=_t.div({class:"igv-roi-table-button"});t.appendChild(i),i.id="igv-roi-table-view-button",i.textContent="Go To",i.style.pointerEvents="none",this._footerDOM=t,this.gotoButton=i,this.boundGotoButtonHandler=e.bind(this),this.gotoButton.addEventListener("click",this.boundGotoButtonHandler)}tableRowDOMHelper(e){e.addEventListener("mousedown",(t=>{t.stopPropagation(),e.classList.toggle("igv-roi-table-row-selected"),e.classList.contains("igv-roi-table-row-selected")?e.classList.remove("igv-roi-table-row-hover"):e.classList.add("igv-roi-table-row-hover"),this.setTableRowSelectionState(e.classList.contains("igv-roi-table-row-selected"))})),e.addEventListener("mouseover",(t=>{e.classList.contains("igv-roi-table-row-selected")?e.classList.remove("igv-roi-table-row-hover"):e.classList.add("igv-roi-table-row-hover")})),e.addEventListener("mouseout",(t=>{e.classList.remove("igv-roi-table-row-hover")}))}clearTable(){const e=this.tableRowContainer.querySelectorAll(".igv-roi-table-row");for(let t of e)t.remove()}setTableRowSelectionState(e){e?this.tableRowSelectionList.push(1):this.tableRowSelectionList.pop(),this.gotoButton.style.pointerEvents=this.tableRowSelectionList.length>0?"auto":"none"}present(){this.tableDOM.style.left="0px";const{y:e}=this.browser.root.getBoundingClientRect(),{y:t}=this.config.parent.getBoundingClientRect();this.tableDOM.style.top=e-t+"px",this.tableDOM.style.display="flex"}dismiss(){this.tableDOM.style.display="none"}dispose(){this.tableDOM.innerHTML="",this.tableDOM.remove();for(const e of Object.keys(this))this[e]=void 0;document.removeEventListener("click",this.boundDismissHandler)}}class ru extends nu{constructor(e){super(Object.assign({width:"1024px"},e)),this.descriptionDOM=e}set descriptionDOM(e){if(e.description){let t;t=_t.div({class:"igv-roi-table-description"}),this.tableDOM.insertBefore(t,this.tableColumnTitles),t.style.height="auto",t.innerHTML="BLAT result for query sequence:",t=_t.div({class:"igv-roi-table-description"}),this.tableDOM.insertBefore(t,this.tableColumnTitles),t.style.height="auto",t.style.maxHeight="128px",t.innerHTML=e.description,t=_t.div({class:"igv-roi-table-goto-explainer"}),this.tableDOM.insertBefore(t,this.tableColumnTitles),t.innerHTML="Select one or more rows and click Go To to view the regions"}}tableRowDOM(e){const t=_t.div({class:"igv-roi-table-row"}),i=e.map((e=>isFinite(e)?Xt(e):e));for(let e=0;ee.remove())),e.length>0)for(let t of e){const e=this.tableRowDOM(t);this.tableRowContainer.appendChild(e)}}static getColumnFormatConfiguration(){return[{label:"chr",width:"7%"},{label:"start",width:"12%"},{label:"end",width:"12%"},{label:"strand",width:"5%"},{label:"score",width:"5%"},{label:"match",width:"5%"},{label:"mis-match",width:"7%"},{label:"rep. match",width:"7%"},{label:"N's",width:"3%"},{label:"Q gap count",width:"9%"},{label:"Q gap bases",width:"9%"},{label:"T gap count",width:"9%"},{label:"T gap bases",width:"9%"}]}static gotoButtonHandler(e){e.stopPropagation();const t=this.tableDOM.querySelectorAll(".igv-roi-table-row-selected"),i=[];for(const e of t){const t=[];e.querySelectorAll("div").forEach((e=>t.push(e.innerText)));const[n,r,s]=t;i.push(`${n}:${r}-${s}`)}for(const e of this.tableDOM.querySelectorAll(".igv-roi-table-row"))e.classList.remove("igv-roi-table-row-selected");this.setTableRowSelectionState(!1),this.browser.search(i.join(" "))}}const su="https://igv.org/services/blatUCSC.php";async function ou({url:e,userSeq:t,db:i}){e=e||su;const n=await async function(e="",t,i){const n=new URLSearchParams;n.append("userSeq",t),n.append("db",i);const r=await fetch(e,{method:"post",body:n});return r.json()}(e,t,i);n.fields;return n.blat.map(pl)}const au=25e3;class lu extends iu{constructor(e,t){super(e,t),this.name||(this.name="Blat Results"),this.sequence=e.sequence,this.table=void 0}openTableView(){if(void 0===this.table){const e=this.config.features.map((e=>[e.chr,e.start+1,e.end,e.strand,e.score,e.matches,e.misMatches,e.repMatches,e.nCount,e.qNumInsert,e.qBaseInsert,e.tNumInsert,e.tBaseInsert])),t={browser:this.browser,parent:this.browser.parent,headerTitle:this.config.title,description:this.sequence,dismissHandler:()=>{this.table.dismiss(),this.table.dispose(),this.table=void 0},columnFormat:ru.getColumnFormatConfiguration(),gotoButtonHandler:ru.gotoButtonHandler};this.table=new ru(t),this.table.renderTable(e)}this.table.present()}menuItemList(){const e=super.menuItemList();return e.push("
"),e.push({label:"Open table view",click:()=>this.openTableView()}),e}dispose(){super.dispose(),this.table&&this.table.popover.parentElement.removeChild(this.table.popover)}}async function hu({sequence:e,browser:t,name:i,title:n}){if(e.length>au)return void t.alert.present(`Sequence size exceeds maximum allowed length (${e.length} > 25000)`);const r=t.genome.id,s=t.config.blatServerURL;try{const o={type:"blat",name:i||"blat results",title:n||"blat results",sequence:e,altColor:"rgb(176, 176, 236)",color:"rgb(236, 176, 176)",features:await ou({url:s,userSeq:e,db:r})};(await t.loadTrack(o)).openTableView()}catch(e){t.alert.present(`Error performing blat search: ${e}`)}}const cu="rgb(185, 185, 185)";class du extends Il{static defaults={alleleFreqThreshold:.2,visibilityWindow:3e4,showCoverage:!0,showAlignments:!0,viewAsPairs:!1,pairsSupported:!0,showSoftClips:!1,showAllBases:!1,showInsertions:!0,showMismatches:!0,height:300,coverageTrackHeight:50};constructor(e,t){super(e,t)}init(e){this.type="alignment",this.featureSource=new dd(e,this.browser),this.coverageTrack=new uu(e,this),this.alignmentTrack=new fu(e,this),super.init(e),this.alignmentTrack.setTop(this.coverageTrack,this.showCoverage),this.showAlignments||(this._height=this.coverageTrackHeight),e.sort&&(Array.isArray(e.sort)?this.assignSort(e.sort[0]):this.assignSort(e.sort))}set height(e){this._height=e,this.showAlignments&&(this.alignmentTrack.height=this.showCoverage?e-this.coverageTrackHeight:e)}get height(){return this._height}get minTemplateLength(){const e=void 0!==this.config.minTLEN?this.config.minTLEN:this.config.minFragmentLength;return void 0!==e?e:this._pairedEndStats?this._pairedEndStats.minTLEN:0}get maxTemplateLength(){const e=void 0!==this.config.maxTLEN?this.config.maxTLEN:this.config.maxFragmentLength;return void 0!==e?e:this._pairedEndStats?this._pairedEndStats.maxTLEN:1e3}sort(e){e=this.assignSort(e);for(let t of this.trackView.viewports)if(t.containsPosition(e.chr,e.position)){const i=t.cachedFeatures;i&&(i.sortRows(e),t.repaint())}}assignSort(e){if(e.locus){const t=function(e){const t=e.split(":"),i=t[1].split("-"),n={chr:t[0],start:Number.parseInt(i[0].replace(/,/g,""))-1};return i.length>1?n.end=Number.parseInt(i[1].replace(/,/g,"")):n.end=n.start+1,n}(e.locus);e.chr=t.chr,e.position=t.start}else e.position--;return e.direction="ASC"===e.direction||!0===e.direction,e.chr=this.browser.genome.getChromosomeName(e.chr),this.sortObject=e,this.sortObject}async getFeatures(e,t,i,n,r){const s=await this.featureSource.getAlignments(e,t,i);if(s.paired&&!this._pairedEndStats&&!this.config.maxFragmentLength){const e=new jd(s.alignments,this.config);e.totalCount>99&&(this._pairedEndStats=e)}s.alignments=void 0;const o=this.sortObject;return o&&o.chr===e&&o.position>=t&&o.position<=i&&s.sortRows(o),s}computePixelHeight(e){return(this.showCoverage?this.coverageTrackHeight:0)+(this.showAlignments?this.alignmentTrack.computePixelHeight(e):0)}draw(e){Co.fillRect(e.context,0,e.pixelTop,e.pixelWidth,e.pixelHeight,{fillStyle:"rgb(255, 255, 255)"}),!0===this.showCoverage&&this.coverageTrackHeight>0?(this.trackView.axisCanvas.style.display="block",this.coverageTrack.draw(e)):this.trackView.axisCanvas.style.display="none",!0===this.showAlignments&&(this.alignmentTrack.setTop(this.coverageTrack,this.showCoverage),this.alignmentTrack.draw(e))}paintAxis(e,t,i){this.coverageTrack.paintAxis(e,t,this.coverageTrackHeight)}contextMenuItemList(e){return this.alignmentTrack.contextMenuItemList(e)}popupData(e){return!0===this.showCoverage&&e.y>=this.coverageTrack.top&&e.y=this.coverageTrack.top&&e.y=this.coverageTrack.top&&e.y");const t=ft('
');t.text("Color by:"),e.push({name:void 0,object:t,click:void 0,init:void 0});const i=[{key:"strand",label:"read strand"}];this.alignmentTrack.hasPairs&&(i.push({key:"firstOfPairStrand",label:"first-of-pair strand"}),i.push({key:"pairOrientation",label:"pair orientation"}),i.push({key:"tlen",label:"insert size (TLEN)"}),i.push({key:"unexpectedPair",label:"pair orientation & insert size (TLEN)"}));const n="tag"+(this.alignmentTrack.colorByTag?" ("+this.alignmentTrack.colorByTag+")":"");i.push({key:"tag",label:n});for(let t of i){const i=this.alignmentTrack.colorBy===t.key;e.push(this.colorByCB(t,i))}const r=()=>{if(!this.autoHeight){const e=(this.showCoverage?this.coverageTrackHeight:0)+(this.showAlignments?this.alignmentTrack.height:0);this.trackView.setTrackHeight(e)}};e.push("
"),e.push({object:ft(jt("Show Coverage",this.showCoverage)),click:()=>{this.showCoverage=!this.showCoverage,r(),this.trackView.checkContentHeight(),this.trackView.repaintViews()}}),e.push({object:ft(jt("Show Alignments",this.showAlignments)),click:()=>{this.showAlignments=!this.showAlignments,r(),this.trackView.checkContentHeight(),this.trackView.repaintViews()}}),e.push("
"),e.push({object:ft(jt("Show all bases",this.showAllBases)),click:()=>{this.showAllBases=!this.showAllBases,this.config.showAllBases=this.showAllBases,this.trackView.repaintViews()}}),e.push("
"),e.push({object:ft(jt("Show mismatches",this.showMismatches)),click:()=>{this.showMismatches=!this.showMismatches,this.config.showMismatches=this.showMismatches,this.trackView.repaintViews()}}),e.push({object:ft(jt("Show insertions",this.showInsertions)),click:()=>{this.showInsertions=!this.showInsertions,this.config.showInsertions=this.showInsertions,this.getCachedAlignmentContainers(),this.trackView.repaintViews()}}),e.push({object:ft(jt("Show soft clips",this.showSoftClips)),click:()=>{this.showSoftClips=!this.showSoftClips,this.config.showSoftClips=this.showSoftClips,this.featureSource.setShowSoftClips(this.showSoftClips);const e=this.getCachedAlignmentContainers();for(let t of e)t.setShowSoftClips(this.showSoftClips);this.trackView.repaintViews()}}),this.pairsSupported&&this.alignmentTrack.hasPairs&&(e.push("
"),e.push({object:ft(jt("View as pairs",this.viewAsPairs)),click:()=>{this.viewAsPairs=!this.viewAsPairs,this.config.viewAsPairs=this.viewAsPairs,this.featureSource.setViewAsPairs(this.viewAsPairs);const e=this.getCachedAlignmentContainers();for(let t of e)t.setViewAsPairs(this.viewAsPairs);this.trackView.repaintViews()}})),this.browser.circularView&&(this.alignmentTrack.hasPairs||this.alignmentTrack.hasSupplemental)&&(e.push("
"),this.alignmentTrack.hasPairs&&e.push({label:"Add discordant pairs to circular view",click:()=>{for(let e of this.trackView.viewports)this.addPairedChordsForViewport(e)}}),this.alignmentTrack.hasSupplemental&&e.push({label:"Add split reads to circular view",click:()=>{for(let e of this.trackView.viewports)this.addSplitChordsForViewport(e)}})),e.push("
");const s=ft('
');return s.text("Display mode:"),e.push({name:void 0,object:s,click:void 0,init:void 0}),e.push({object:ft(jt("expand","EXPANDED"===this.alignmentTrack.displayMode)),click:()=>{this.alignmentTrack.displayMode="EXPANDED",this.config.displayMode="EXPANDED",this.trackView.checkContentHeight(),this.trackView.repaintViews()}}),e.push({object:ft(jt("squish","SQUISHED"===this.alignmentTrack.displayMode)),click:()=>{this.alignmentTrack.displayMode="SQUISHED",this.config.displayMode="SQUISHED",this.trackView.checkContentHeight(),this.trackView.repaintViews()}}),e}colorByCB(e,t){return{name:void 0,object:ft(jt(e.label,t)),click:t=>{"tag"!==e.key?e.key===this.alignmentTrack.colorBy?(this.alignmentTrack.colorBy="none",this.config.colorBy="none",this.trackView.repaintViews()):(this.alignmentTrack.colorBy=e.key,this.config.colorBy=e.key,this.trackView.repaintViews()):this.browser.inputDialog.present({label:"Tag Name",value:this.alignmentTrack.colorByTag?this.alignmentTrack.colorByTag:"",callback:e=>{e?(this.alignmentTrack.colorBy="tag",this.alignmentTrack.colorByTag=e,this.alignmentTrack.tagColors||(this.alignmentTrack.tagColors=new yo("Set1"))):(this.alignmentTrack.colorBy="none",this.alignmentTrack.colorByTag=""),this.trackView.repaintViews()}},t)},init:void 0}}getState(){const e=super.getState();return this.sortObject&&(e.sort={chr:this.sortObject.chr,position:this.sortObject.position+1,option:this.sortObject.option,direction:this.sortObject.direction?"ASC":"DESC"}),e}getCachedAlignmentContainers(){return this.trackView.viewports.map((e=>e.cachedFeatures))}get dataRange(){return this.coverageTrack.dataRange}set dataRange(e){this.coverageTrack.dataRange=e}get logScale(){return this.coverageTrack.logScale}set logScale(e){this.coverageTrack.logScale=e}get autoscale(){return this.coverageTrack.autoscale}set autoscale(e){this.coverageTrack.autoscale=e}addPairedChordsForViewport(e){const t=this.maxTemplateLength,i=[],n=e.referenceFrame;for(let r of e.cachedFeatures.allAlignments())r.end>=n.start&&r.start<=n.end&&(r.paired?r.end-r.start>t&&i.push(r):r.mate&&r.mate.chr&&(r.mate.chr!==r.chr||Math.max(r.fragmentLength)>t)&&i.push(r));qd((e=>{const t=[];for(let i of e)if(i.paired)i.firstAlignment&&i.secondAlignment&&t.push({uniqueId:i.readName,refName:Vd(i.firstAlignment.chr),start:i.firstAlignment.start,end:i.firstAlignment.end,mate:{refName:Vd(i.secondAlignment.chr),start:i.secondAlignment.start,end:i.secondAlignment.end}});else{const e=i.mate;e&&e.chr&&e.position&&t.push({uniqueId:i.readName,refName:Vd(i.chr),start:i.start,end:i.end,mate:{refName:Vd(e.chr),start:e.position-1,end:e.position}})}return t})(i),this,n,.02)}addSplitChordsForViewport(e){const t=[],i=e.referenceFrame;for(let n of e.cachedFeatures.allAlignments()){const e=n.hasTag("SA");n.end>=i.start&&n.start<=i.end&&e&&t.push(n)}qd((e=>{const t=e=>{const t=Lc(e.tags().SA);let n=0;for(let r of t)r.start!==e.start&&i.push({uniqueId:`${e.readName}_${n++}`,refName:Vd(e.chr),start:e.start,end:e.end,mate:{refName:Vd(r.chr),start:r.start,end:r.start+r.lenOnRef}})},i=[];for(let i of e)i.paired?(t(i.firstAlignment),i.secondAlignment&&t(i.secondAlignment)):t(i);return i})(t),this,i,.02)}}class uu{constructor(e,t){this.parent=t,this.featureSource=t.featureSource,this.paintAxis=ud,this.top=0,this.autoscale=e.autoscale||void 0===e.max,this.autoscale||(this.dataRange={min:e.min||0,max:e.max})}get height(){return this.parent.coverageTrackHeight}draw(e){const t=e.pixelTop;e.pixelHeight;const i=this.parent.browser.nucleotideColors;if(t>this.height)return;const n=e.context,r=e.features.coverageMap;let s;r.refSeq&&(s=r.refSeq.toUpperCase());const o=e.bpPerPixel,a=e.bpStart,l=a+e.pixelWidth*o+1;let h;h=this.parent.coverageColor?this.parent.coverageColor:this.parent.color&&"function"!=typeof this.parent.color?Hs.darkenLighten(this.parent.color,-35):"rgb(150, 150, 150)",Co.setProperties(n,{fillStyle:h,strokeStyle:h});const c=Math.max(1,Math.ceil(1/o));for(let e=0,t=r.coverage.length;el)break;const i=r.coverage[e];if(!i)continue;const s=Math.round(i.total/this.dataRange.max*this.height),h=this.height-s,d=Math.floor((t-a)/o);Co.fillRect(n,d,h,c,s)}if(s)for(let e=0,t=r.coverage.length;el)break;const h=r.coverage[e];if(!h)continue;const d=h.total/this.dataRange.max*this.height;let u=this.height-d;const f=Math.floor((t-a)/o),p=s[e];if(h.isMismatch(p)){Co.setProperties(n,{fillStyle:i[p]}),Co.fillRect(n,f,u,c,d);let e=0;for(let t of["A","C","T","G"]){const r=(h["pos"+t]+h["neg"+t])/this.dataRange.max*this.height;u=this.height-r-e,e+=r,Co.setProperties(n,{fillStyle:i[t]}),Co.fillRect(n,f,u,c,r)}}}}getClickedObject(e){let t=e.viewport.cachedFeatures;if(!t||0===t.length)return;const i=Math.floor(e.genomicLocation),n=t.coverageMap,r=Math.floor(i-n.bpStart);return n.coverage[r]}popupData(e){const t=[],i=this.getClickedObject(e);if(i){const n=Math.floor(e.genomicLocation),r=e.viewport.referenceFrame;t.push(r.chr+":"+Xt(1+n)),t.push({name:"Total Count",value:i.total});let s=i.posA+i.negA;s>0&&(s=s.toString()+" ("+Math.round(s/i.total*100)+"%, "+i.posA+"+, "+i.negA+"- )"),t.push({name:"A",value:s}),s=i.posC+i.negC,s>0&&(s=s.toString()+" ("+Math.round(s/i.total*100)+"%, "+i.posC+"+, "+i.negC+"- )"),t.push({name:"C",value:s}),s=i.posG+i.negG,s>0&&(s=s.toString()+" ("+Math.round(s/i.total*100)+"%, "+i.posG+"+, "+i.negG+"- )"),t.push({name:"G",value:s}),s=i.posT+i.negT,s>0&&(s=s.toString()+" ("+Math.round(s/i.total*100)+"%, "+i.posT+"+, "+i.negT+"- )"),t.push({name:"T",value:s}),s=i.posN+i.negN,s>0&&(s=s.toString()+" ("+Math.round(s/i.total*100)+"%, "+i.posN+"+, "+i.negN+"- )"),t.push({name:"N",value:s}),t.push("
"),t.push({name:"DEL",value:i.del.toString()}),t.push({name:"INS",value:i.ins.toString()})}return t}}class fu{constructor(e,t){this.parent=t,this.browser=t.browser,this.featureSource=t.featureSource,this.top=0===e.coverageTrackHeight?0:e.coverageTrackHeight+5,this.displayMode=e.displayMode||"EXPANDED",this.alignmentRowHeight=e.alignmentRowHeight||14,this.squishedRowHeight=e.squishedRowHeight||3,this.negStrandColor=e.negStrandColor||"rgba(150, 150, 230, 0.75)",this.posStrandColor=e.posStrandColor||"rgba(230, 150, 150, 0.75)",this.insertionColor=e.insertionColor||"rgb(138, 94, 161)",this.insertionTextColor=e.insertionTextColor||"white",this.showInsertionText=void 0!==e.showInsertionText&&!!e.showInsertionText,this.deletionColor=e.deletionColor||"black",this.deletionTextColor=e.deletionTextColor||"black",this.showDeletionText=void 0!==e.showDeletionText&&!!e.showDeletionText,this.skippedColor=e.skippedColor||"rgb(150, 170, 170)",this.pairConnectorColor=e.pairConnectorColor,this.smallTLENColor=e.smallTLENColor||e.smallFragmentLengthColor||"rgb(0, 0, 150)",this.largeTLENColor=e.largeTLENColor||e.largeFragmentLengthColor||"rgb(200, 0, 0)",this.pairOrientation=e.pairOrienation||"fr",this.pairColors={},this.pairColors.RL=e.rlColor||"rgb(0, 150, 0)",this.pairColors.RR=e.rrColor||"rgb(20, 50, 200)",this.pairColors.LL=e.llColor||"rgb(0, 150, 150)",this.colorBy=e.colorBy||"unexpectedPair",this.colorByTag=e.colorByTag?e.colorByTag.toUpperCase():void 0,this.bamColorTag=void 0===e.bamColorTag?"YC":e.bamColorTag,this.hideSmallIndels=e.hideSmallIndels,this.indelSizeThreshold=e.indelSizeThreshold||1,this.hasPairs=!1,this.hasSupplemental=!1}setTop(e,t){this.top=0===e.height||!1===t?0:5+e.height}computePixelHeight(e){if(e.packedAlignmentRows){return(e.hasDownsampledIntervals()?10:0)+("SQUISHED"===this.displayMode?this.squishedRowHeight:this.alignmentRowHeight)*e.packedAlignmentRows.length+5}return 0}draw(e){const t=e.features,i=e.context,n=e.bpPerPixel,r=e.bpStart,s=e.pixelWidth,o=r+s*n+1,a=this.parent.showSoftClips,l=this.parent.showAllBases,h=this.browser.nucleotideColors,c=t.packedAlignmentRows;i.save();let d=t.sequence;d&&(d=d.toUpperCase());let u=0,f=e.pixelTop;this.top&&i.translate(0,this.top);const p=f+e.pixelHeight;t.hasDownsampledIntervals()?(u=10,t.downsampledIntervals.forEach((function(e){var t=(e.start-r)/n,s=(e.end-r)/n;s-t>5&&(t+=1,s-=1),Co.fillRect(i,t,2,s-t,3,{fillStyle:"black"})}))):u=0,this.alignmentsYOffset=u;const g="SQUISHED"===this.displayMode?this.squishedRowHeight:this.alignmentRowHeight;if(c){const e=c.length;for(let t=0;tp)break;if(!(i+no)break;!0!==t.hidden&&(t instanceof vc?(m.call(this,t,i,n),b.call(this,t.firstAlignment,i,n),t.secondAlignment&&b.call(this,t.secondAlignment,i,n)):b.call(this,t,i,n))}}}function m(e,t,s){var a=this.getConnectorColor(e.firstAlignment),l=(e.connectingStart-r)/n,h=(e.connectingEnd-r)/n,c=t+s/2;e.connectingEndo||(e.mq<=0&&(a=Hs.addAlpha(a,.15)),Co.setProperties(i,{fillStyle:a,strokeStyle:a}),Co.strokeLine(i,l,c,h,c))}function b(e,c,u){if(e.start+e.lengthOnRefo)return;const f=a?e.blocks:e.blocks.filter((e=>"S"!==e.type));let p=this.getAlignmentColor(e);const m=p;e.mq<=0&&(p=Hs.addAlpha(p,.15)),Co.setProperties(i,{fillStyle:p,strokeStyle:m});const b=[];for(let e=0;eo))break}if(e.gaps){const t=c+u/2;for(let s of e.gaps){const e=(s.start-r)/n,o=(s.start+s.len-r)/n,a=o-e,l=s.len.toString(),h=6*l.length,d=e+a/2,u="D"===s.type?this.deletionColor:this.skippedColor;if(Co.strokeLine(i,e,t,o,t,{strokeStyle:u,lineWidth:2}),this.showDeletionText&&s.len>1&&a>=h+8){const e=d-h/2;Co.fillRect(i,e-1,c-1,h+2,12,{fillStyle:"white"}),Co.fillText(i,l,e,c+10,{font:"normal 10px monospace",fillStyle:this.deletionTextColor})}}}if(e.insertions&&this.parent.showInsertions){let t=-1;for(let s of e.insertions){if(this.hideSmallIndels&&s.len<=this.indelSizeThreshold)continue;if(s.starto)break;const e=s.start-r,a=s.len.toString(),l=2+6*a.length,h=this.showInsertionText&&1!==s.len?Math.round(s.len/n):2,d=Math.max(Math.min(l,h),2),f=e/n-d/2;if(f-t>2){const e={fillStyle:this.insertionColor};Co.fillRect(i,f-2,c,d+4,2,e),Co.fillRect(i,f,c+2,d,u-4,e),Co.fillRect(i,f-2,c+u-2,d+4,2,e),this.showInsertionText&&s.len>1&&h>l&&Co.fillText(i,a,f+1,c+10,{font:"normal 10px monospace",fillStyle:this.insertionTextColor}),t=f}}}function w(o,a){const b=[],w=o.start-t.start,v=(o.start-r)/n,y=(o.start+o.len-r)/n,_=Math.max(1,y-v),x=100/n,k=Math.min(g/2,x/6),C="S"===o.type,S=e.mq<=0||this.highlightedAlignmentReadNamed===e.readName||C;let A=m;this.highlightedAlignmentReadNamed===e.readName?A="red":C&&(A="rgb(50,50,50)");const E=!0===e.strand&&a===f.length-1,M=!1===e.strand&&0===a;if(E|M){let e,t;E?(e=[v,y,y+k,y,v,v],t=[c,c,c+u/2,c+u,c+u,c]):M&&(e=[y,v,v-k,v,y,y],t=[c,c,c+u/2,c+u,c+u,c]),Co.fillPolygon(i,e,t,{fillStyle:p}),S&&Co.strokePolygon(i,e,t,{strokeStyle:A})}else Co.fillRect(i,v,c,_,u,{fillStyle:p}),S&&(i.save(),i.strokeStyle=A,i.strokeRect(v,c,_,u),i.restore());if(C||l||this.parent.showMismatches&&d&&e.seq&&"*"!==e.seq){const t=e.seq?e.seq.toUpperCase():void 0,i=e.qual,a=o.seqOffset,f=Math.max(1,1/n);for(let e=0,p=o.len;es)break;let g=t?t.charAt(a+e):"";const m=w+e>=0?d.charAt(w+e):"";if("="===g&&(g=m),"X"===g||m!==g||C||l){let t;if(!C&&void 0!==i&&i.length>a+e){t=pu(i[a+e],h[g])}else t=h[g];t&&b.push({bbox:{x:p,y:c,width:f,height:u},baseColor:t,readChar:g})}}}return b}b.forEach((({bbox:e,baseColor:t,readChar:r})=>{!function(e,t,i,n,r){var s;if(t<=.1&&i.height>=8){const t=Math.min(10,i.height);e.font=t+"px sans-serif",s=i.x+i.width/2,Co.strokeText(e,r,s-e.measureText(r).width/2,t-1+i.y,{strokeStyle:n})}else Co.fillRect(e,i.x,i.y,i.width,i.height,{fillStyle:n})}(i,n,e,t,r)}))}i.restore()}popupData(e){const t=this.getClickedObject(e);return t?t.popupData(e.genomicLocation):void 0}contextMenuItemList(e){const t=e.viewport,i=[],n=i=>{const n=this.parent.sortObject,r=!n||n.position!==Math.floor(e.genomicLocation)||!n.direction,s={chr:t.referenceFrame.chr,position:Math.floor(e.genomicLocation),option:i,direction:r};this.parent.sortObject=s,t.cachedFeatures.sortRows(s),t.repaint()};i.push("Sort by..."),i.push({label:"  base",click:()=>n("BASE")}),i.push({label:"  read strand",click:()=>n("STRAND")}),i.push({label:"  insert size",click:()=>n("INSERT_SIZE")}),i.push({label:"  gap size",click:()=>n("GAP_SIZE")}),i.push({label:"  chromosome of mate",click:()=>n("MATE_CHR")}),i.push({label:"  mapping quality",click:()=>n("MQ")}),i.push({label:"  read name",click:()=>n("READ_NAME")}),i.push({label:"  aligned read length",click:()=>n("ALIGNED_READ_LENGTH")}),i.push({label:"  soft clipped reads",click:()=>n("SOFT_CLIPPED")}),i.push({label:"  tag",click:()=>{const i=this.parent.sortObject,n=!i||i.position!==Math.floor(e.genomicLocation)||!i.direction,r={label:"Tag Name",value:this.sortByTag?this.sortByTag:"",callback:i=>{if(i){const r={chr:t.referenceFrame.chr,position:Math.floor(e.genomicLocation),option:"TAG",tag:i,direction:n};this.sortByTag=i,this.parent.sortObject=r,t.cachedFeatures.sortRows(r),t.repaint()}}};this.browser.inputDialog.present(r,e.event)}}),i.push("
");const r=this.getClickedObject(e);if(r){const t=this.parent.showSoftClips,n="function"==typeof r.alignmentContaining?r.alignmentContaining(e.genomicLocation,t):r;if(n){n.isPaired()&&n.isMateMapped()&&i.push({label:"View mate in split screen",click:()=>{if(n.mate){const t=e.viewport.referenceFrame;if(this.browser.genome.getChromosome(n.mate.chr)){this.highlightedAlignmentReadNamed=n.readName;const e=t.end-t.start,i=n.mate.position-e/2,r=n.mate.position+e/2;this.browser.addMultiLocusPanel(n.mate.chr,i,r,t)}else this.browser.alert.present(`Reference does not contain chromosome: ${n.mate.chr}`)}},init:void 0}),i.push({label:"View read sequence",click:()=>{const e=n.seq;e&&"*"!==e?this.browser.alert.present(e):this.browser.alert.present("Read sequence: *")}}),Do()&&i.push({label:"Copy read sequence",click:async()=>{const e=n.seq;try{await navigator.clipboard.writeText(e)}catch(e){console.error(e),this.browser.alert.present(`error copying sequence to clipboard ${e}`)}}});const t=n.seq;if(t&&"*"!=t){t.length{const e=n.isNegativeStrand()?Ko(t):t,i=`${n.readName} - blat`,r=`${this.parent.name} - ${i}`;hu({sequence:e,browser:this.browser,name:i,title:r})}});const e=n.softClippedBlocks();e.left&&e.left.len>20&&e.left.len{const i=t.substr(e.left.seqOffset,e.left.len),r=n.isNegativeStrand()?Ko(i):i,s=`${n.readName} - blat left clip`,o=`${this.parent.name} - ${s}`;hu({sequence:r,browser:this.browser,name:s,title:o})}}),e.right&&e.right.len>20&&e.right.len{const i=t.substr(e.right.seqOffset,e.right.len),r=n.isNegativeStrand()?Ko(i):i,s=`${n.readName} - blat right clip`,o=`${this.parent.name} - ${s}`;hu({sequence:r,browser:this.browser,name:s,title:o})}})}i.push("
")}}return this.browser.circularView&&(this.hasPairs||this.hasSupplemental)&&(this.hasPairs&&i.push({label:"Add discordant pairs to circular view",click:()=>{this.parent.addPairedChordsForViewport(t)}}),this.hasSupplemental&&i.push({label:"Add split reads to circular view",click:()=>{this.parent.addSplitChordsForViewport(t)}}),i.push("
")),i}getClickedObject(e){const t=e.viewport,i=e.y,n=e.genomicLocation,r=this.parent.showSoftClips;let s=t.cachedFeatures;if(!s||0===s.length)return;let o=s.packedAlignmentRows,a=s.downsampledIntervals;const l="SQUISHED"===this.displayMode?this.squishedRowHeight:this.alignmentRowHeight;let h=Math.floor((i-this.top-this.alignmentsYOffset)/l);if(h<0){for(let e=0;e=n)return a[e]}else if(he.containsLocation(n,r)));if(e.length>0)return e[0]}}getConnectorColor(e){if(this.pairConnectorColor)return this.pairConnectorColor;switch(this.colorBy){case"strand":case"firstOfPairStrand":case"pairOrientation":case"tag":return this.parent.color?"function"==typeof this.parent.color?this.parent.color(e):this.parent.color:"rgb(200, 200, 200)";default:return this.getAlignmentColor(e)}}getAlignmentColor(e){let t=cu;t=this.parent.color?"function"==typeof this.parent.color?this.parent.color(e):this.parent.color:cu;const i=this.colorBy;switch(i){case"strand":t=e.strand?this.posStrandColor:this.negStrandColor;break;case"firstOfPairStrand":e instanceof vc?t=e.firstOfPairStrand()?this.posStrandColor:this.negStrandColor:e.isPaired()&&(e.isFirstOfPair()?t=e.strand?this.posStrandColor:this.negStrandColor:e.isSecondOfPair()?t=e.strand?this.negStrandColor:this.posStrandColor:console.error("ERROR. Paired alignments are either first or second."));break;case"unexpectedPair":case"pairOrientation":if(this.pairOrientation&&e.pairOrientation){const i=gu[this.pairOrientation];if(i){const n=this.pairColors[i[e.pairOrientation]];if(n){t=n;break}}}if("pairOrientation"===i)break;case"tlen":case"fragmentLength":e.mate&&e.isMateMapped()&&(e.mate.chr!==e.chr?t=mu(e.mate.chr):this.parent.minTemplateLength&&Math.abs(e.fragmentLength)this.parent.maxTemplateLength&&(t=this.largeTLENColor));break;case"tag":const n=e.tags()[this.colorByTag];void 0!==n&&(this.bamColorTag===this.colorByTag?t="rgb("+n+")":(this.tagColors||(this.tagColors=new yo("Set1")),t=this.tagColors.getColor(n)))}return t}}function pu(e,t){let i;return i=e<5?.1:Math.max(.1,Math.min(1,.1+.9*(e-5)/15)),i=Math.round(10*i)/10,i<1&&(t=Hs.addAlpha(t,i)),t}const gu={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 mu(e){if(bu[e])return bu[e];if(bu["chr"+e]){const t=bu["chr"+e];return bu[e]=t,t}{const t=Hs.randomRGB(0,255);return bu[e]=t,t}}const bu={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 wu,vu;class yu extends Da{constructor(e,t,i,n){super(e,t,i,n)}get contentDiv(){return this.$viewport.get(0)}initializationHelper(){let e;this.$multiLocusCloseButton=ft("
",{class:"igv-multi-locus-close-button"}),this.$viewport.append(this.$multiLocusCloseButton),this.$multiLocusCloseButton.get(0).appendChild(At.createIcon("times-circle")),this.$multiLocusCloseButton.click((()=>{this.browser.removeMultiLocusPanel(this.referenceFrame)})),this.$rulerLabel=ft("
",{class:"igv-multi-locus-ruler-label"}),this.$viewport.append(this.$rulerLabel),e=document.createElement("div"),this.$rulerLabel.append(ft(e)),this.$rulerLabel.get(0).addEventListener("click",(async e=>{e.stopPropagation(),await this.browser.gotoMultilocusPanel(this.referenceFrame)})),this.$tooltip=ft("
",{class:"igv-ruler-tooltip"}),this.$tooltip.height(this.$viewport.height()),this.$viewport.append(this.$tooltip),this.$tooltipContent=ft("
"),this.$tooltip.append(this.$tooltipContent),this.rulerSweeper=new wc(this,this.$viewport.get(0).parentElement,this.browser,this.referenceFrame),this.attachMouseHandlers(Ba.isWholeGenomeView(this.referenceFrame.chr)),this.$tooltip.hide(),this.dismissLocusLabel()}presentLocusLabel(e){this.$multiLocusCloseButton.show(),this.$rulerLabel.show(),this.$rulerLabel.get(0).style.backgroundColor=mu(this.referenceFrame.chr);const t=this.$rulerLabel.get(0).querySelector("div"),{width:i}=this.$rulerLabel.get(0).getBoundingClientRect();t.innerHTML=`${this.referenceFrame.getMultiLocusLabel(e)}`;const{width:n}=t.getBoundingClientRect();n/i>.5&&(t.innerHTML=`${this.referenceFrame.getMultiLocusLabelBPLengthOnly(e)}`)}dismissLocusLabel(){this.$rulerLabel.hide(),this.$multiLocusCloseButton.hide()}attachMouseHandlers(e){if(this.namespace=`.ruler_track_viewport_${this.browser.referenceFrameList.indexOf(this.referenceFrame)}`,this.$viewport.off(this.namespace),!0===e){const e=this.browser.referenceFrameList.indexOf(this.referenceFrame),t=`click${this.namespace}`;this.$viewport.on(t,(t=>{const{x:i}=_t.translateMouseCoordinates(t,this.$viewport.get(0)),n=Math.round(this.referenceFrame.start+this.referenceFrame.toBP(i));let r;const{chr:s}=this.browser.genome.getChromosomeCoordinate(n);if(1===this.browser.referenceFrameList.length)r=s;else{let t=this.browser.referenceFrameList.map((({locusSearchString:e})=>e));t[e]=s,r=t.join(" ")}this.browser.search(r)})),this.$viewport.get(0).style.cursor="pointer"}else this.$viewport.get(0).style.cursor="default"}mouseMove(e){if(!0===this.browser.cursorGuideVisible){void 0===vu?(vu=this,this.$tooltip.show()):vu.guid!==this.guid?(vu.$tooltip&&vu.$tooltip.hide(),this.$tooltip.show(),vu=this):this.$tooltip.show();if(this.browser.isMultiLocusWholeGenomeView()||Ba.isWholeGenomeView(this.referenceFrame.chr))return void this.$tooltip.hide();const{x:t}=_t.translateMouseCoordinates(e,this.$viewport.get(0)),{start:i,bpPerPixel:n}=this.referenceFrame,r=Math.round(.5+i+Math.max(0,t)*n);this.$tooltipContent.text(Xt(r));const{width:s}=this.$tooltipContent.get(0).getBoundingClientRect(),{width:o}=this.$viewport.get(0).getBoundingClientRect();this.$tooltip.css({left:`${Os(t,0,o-s)}px`}),clearTimeout(wu),wu=setTimeout((()=>{this.$tooltip&&this.$tooltip.hide()}),1e4)}}startSpinner(){}stopSpinner(){}dispose(){this.rulerSweeper.dispose(),super.dispose()}}class _u extends Da{constructor(e,t,i,n){super(e,t,i,n)}initializationHelper(){this.canvas=document.createElement("canvas"),this.canvas.className="igv-ideogram-canvas",this.$viewport.append(ft(this.canvas)),this.ideogram_ctx=this.canvas.getContext("2d"),this.addMouseHandlers()}addMouseHandlers(){this.addViewportClickHandler(this.$viewport.get(0))}addViewportClickHandler(e){this.boundClickHandler=function(e){const{xNormalized:t,width:i}=_t.translateMouseCoordinates(e,this.ideogram_ctx.canvas),{bpLength:n}=this.browser.genome.getChromosome(this.referenceFrame.chr),r=this.referenceFrame.bpPerPixel*i/n;let s=t;s-r/2<0&&(s=r/2);s+r/2>1&&(s=1-r/2);const o=Math.round((s-r/2)*n),a=Math.round((s+r/2)*n);this.referenceFrame.start=o,this.referenceFrame.end=a,this.referenceFrame.bpPerPixel=(a-o)/i,this.browser.updateViews(this.referenceFrame,this.browser.trackViews,!0)}.bind(this),e.addEventListener("click",this.boundClickHandler)}setWidth(e){this.$viewport.width(e)}drawSVGWithContext(e,t,i,n,r,s,o){e.saveWithTranslationAndClipRect(n,r,s,t,i,o),this.trackView.track.draw({context:e,referenceFrame:this.referenceFrame,pixelWidth:t,pixelHeight:i}),e.restore()}repaint(){this.draw({referenceFrame:this.referenceFrame})}draw({referenceFrame:e}){Co.configureHighDPICanvas(this.ideogram_ctx,this.$viewport.width(),this.$viewport.height()),this.trackView.track.draw({context:this.ideogram_ctx,referenceFrame:e,pixelWidth:this.$viewport.width(),pixelHeight:this.$viewport.height()})}startSpinner(){}stopSpinner(){}}function xu(e,t,i,n){if("ruler"===e.track.type)return new yu(e,t,i,n);if("ideogram"===e.track.id)return new _u(e,t,i,n);return new Da(e,t,i,n)}const ku={textAlign:"start",textBaseline:"bottom",strokeStyle:"black",fillStyle:"black"};class Cu{constructor(e,t,i,n){this.guid=_t.guid(),this.trackView=e,this.browser=e.browser,this.viewport=_t.div({class:"igv-viewport"}),t.appendChild(this.viewport),e.track.height&&(this.viewport.style.height=`${e.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(n),!1===this.browser.showSampleNames&&this.hide(),this.addMouseHandlers()}checkCanvas(){const e=window.devicePixelRatio,t=this.viewport.clientHeight,i=this.browser.sampleNameViewportWidth;if(this.canvas.width!==i*e||this.canvas.height!==t*e){const n=this.canvas;n.width=i*e,n.height=t*e,n.style.width=`${i}px`,n.style.height=`${t}px`,this.ctx=this.canvas.getContext("2d"),this.ctx.scale(e,e)}}setTop(e){if("function"==typeof this.trackView.track.getSamples){this.contentTop=e;const t=this.trackView.track.getSamples();this.repaint(t)}}setWidth(e){this.viewport.innerWidth=e,this.checkCanvas()}show(){this.viewport.style.display="block"}hide(){this.viewport.style.display="none"}async repaint(e){this.checkCanvas(),this.draw({context:this.ctx,samples:e})}draw({context:e,samples:t}){if(!t||0===t.names.length)return;!function(e,{textAlign:t,textBaseline:i,strokeStyle:n,fillStyle:r},s){const o=Math.min(s,10);e.font=`${o}px sans-serif`,e.textAlign=t,e.textBaseline=i,e.fillStyle=r}(e,ku,t.height);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillStyle=function(e){const{r:t,g:i,b:n}=wo[e];return`rgb(${t},${i},${n})`}("lead");const i=this.viewport.getBoundingClientRect().height;let n=(t.yOffset||0)+this.contentTop;for(let r of t.names){if(n>i)break;if(n+t.height>0){const i=r,s=Su(e,i,n,t.height);e.fillText(i,4,s)}n+=t.height}}renderSVGContext(e,{deltaX:t,deltaY:i}){if("function"==typeof this.trackView.track.getSamples){const n=this.trackView.track.getSamples(),r=0,{width:s,height:o}=this.viewport.getBoundingClientRect(),a=`${(this.trackView.track.name||this.trackView.track.id).replace(/\W/g,"")}_sample_names_guid_${_t.guid()}`;e.saveWithTranslationAndClipRect(a,t,i+r,s,o,-r),this.draw({context:e,samples:n}),e.restore()}}addMouseHandlers(){this.addViewportContextMenuHandler(this.viewport)}removeMouseHandlers(){this.removeViewportContextMenuHandler(this.viewport)}addViewportContextMenuHandler(e){this.boundContextMenuHandler=function(e){e.preventDefault(),e.stopPropagation();const t={label:"Name Panel Width",value:this.browser.sampleNameViewportWidth,callback:e=>{this.browser.sampleNameViewportWidth=parseInt(e);for(let{sampleNameViewport:e}of this.browser.trackViews)e.setWidth(this.browser.sampleNameViewportWidth);this.browser.layoutChange()}};this.browser.inputDialog.present(t,e)}.bind(this),e.addEventListener("contextmenu",this.boundContextMenuHandler)}removeViewportContextMenuHandler(e){e.removeEventListener("contextmenu",this.boundContextMenuHandler)}dispose(){this.removeMouseHandlers(),this.viewport.remove()}}function Su(e,t,i,n){return i+n-function(e,t,i){const{actualBoundingBoxAscent:n,actualBoundingBoxDescent:r}=e.measureText(t);return(i-(n+r))/2}(e,t,n)}const Au=function(e){this.popover=_t.div({class:"igv-menu-popup"}),e.appendChild(this.popover);const t=_t.div({class:"igv-menu-popup-header"});this.popover.appendChild(t),Mt.attachDialogCloseHandlerWithParent(t,(()=>this.hide())),this.popoverContent=_t.div(),this.popover.appendChild(this.popoverContent),Rt(this.popover,t),t.addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault()})),this.hide()};Au.prototype.hide=function(){this.popover.style.display="none"},Au.prototype.presentMenuList=function(e){if(Eu(),e.length>0){this.popoverContent.innerHTML="",e=$t.trackMenuItemListHelper(e,this);for(let t of e){t.init&&t.init();let i=t.object;0===e.indexOf(t)&&i.removeClass("igv-track-menu-border-top"),i.hasClass("igv-track-menu-border-top")||i.hasClass("igv-menu-popup-check-container")||i.is("div")&&i.addClass("igv-menu-popup-shim"),this.popoverContent.appendChild(i.get(0))}this.popover.style.display="flex";const{width:t}=this.popover.getBoundingClientRect();this.popover.style.left=-t+"px",this.popover.style.top="0px"}},Au.prototype.presentTrackContextMenu=function(e,t){this.popoverContent.innerHTML="";const i=(n=t,r=this.popover,n.map((e=>{let t;if("string"==typeof e&&"
"===e)t=document.createElement("hr");else if("string"==typeof e)t=_t.div({class:"context-menu"}),t.innerHTML=e;else if("Node"==typeof e)t=e;else{if("function"==typeof e.init&&e.init(),"checkbox"===e.type)t=jt("Show all bases",e.value);else if("color"===e.type){const i=new qt({parent:r.parentElement,width:364});i.configure(void 0,{color:t=>e.click(t)}),t=_t.div({class:"context-menu"}),"string"==typeof e.label&&(t.innerHTML=e.label);const n=e=>{i.show(),_t.hide(r),e.preventDefault(),e.stopPropagation()};t.addEventListener("click",n),t.addEventListener("touchend",n),t.addEventListener("mouseup",(function(e){e.preventDefault(),e.stopPropagation()}))}else t=_t.div({class:"context-menu"}),"string"==typeof e.label&&(t.innerHTML=e.label);if(e.click&&"color"!==e.type){function s(t){e.click(),_t.hide(r),t.preventDefault(),t.stopPropagation()}t.addEventListener("click",s),t.addEventListener("touchend",s),t.addEventListener("mouseup",(function(e){e.preventDefault(),e.stopPropagation()}))}}return{el:t,init:e.init}})));var n,r;for(let{el:e}of i)this.popoverContent.appendChild(e);!function(e,t){t.style.display="flex";const{x:i,y:n}=_t.translateMouseCoordinates(e,t.parentNode),{width:r}=t.getBoundingClientRect(),s=i+r,{width:o}=t.parentNode.getBoundingClientRect();t.style.left=`${s>o?i-(s-o):i}px`,t.style.top=`${n}px`}(e,this.popover)},Au.prototype.dispose=function(){this.popoverContent.innerHTML="",this.popover.innerHTML="",Object.keys(this).forEach((function(e){this[e]=void 0}))};const Eu=()=>{const e=document.querySelectorAll(".igv-menu-popup");for(let t=0;t0&&this.viewports[0].startSpinner()}stopSpinner(){this.viewports&&this.viewports.length>0&&this.viewports[0].stopSpinner()}addDOMToColumnContainer(e,t,i){this.axis=this.createAxis(e,this.track),this.viewports=[];const n=e.calculateViewportWidth(i.length),r=t.querySelectorAll(".igv-column");for(let e=0;e{e.dataRangeDialog.configure(this),e.dataRangeDialog.present(ft(e.columnContainer))}));const{width:n,height:r}=i.getBoundingClientRect();this.axisCanvas=document.createElement("canvas"),this.axisCanvas.style.width=`${n}px`,this.axisCanvas.style.height=`${r}px`,i.appendChild(this.axisCanvas)}return i}resizeAxisCanvas(e,t){this.axis.style.width=`${e}px`,this.axis.style.height=`${t}px`,"function"==typeof this.track.paintAxis&&(this.axisCanvas.style.width=`${e}px`,this.axisCanvas.style.height=`${t}px`)}removeDOMFromColumnContainer(){this.boundAxisClickHander&&this.removeAxisEventListener(this.axis),this.axis.remove();for(let e of this.viewports)e.$viewport.remove();this.sampleNameViewport.dispose(),this.removeTrackScrollMouseHandlers(),this.outerScroll.remove(),this.removeTrackDragMouseHandlers(),this.dragHandle.remove(),this.removeTrackGearMouseHandlers(),this.gearContainer.remove()}renderSVGContext(e,{deltaX:t,deltaY:i}){!function(e,t,i,n,r){if("function"==typeof t.paintAxis){const{y:s,width:o,height:a}=i.getBoundingClientRect(),l=`${(t.name||t.id).replace(/\W/g,"")}_axis_guid_${_t.guid()}`;e.saveWithTranslationAndClipRect(l,n,s+r,o,a,0),t.paintAxis(e,o,a),e.restore()}}(e,this.track,this.axisCanvas,t,i);const{width:n}=this.axis.getBoundingClientRect(),{y:r}=this.viewports[0].$viewport.get(0).getBoundingClientRect();let s={deltaX:n+t,deltaY:r+i};for(let t of this.viewports){t.renderSVGContext(e,s);const{width:i}=t.$viewport.get(0).getBoundingClientRect();s.deltaX+=i}!0===this.browser.showSampleNames&&this.sampleNameViewport.renderSVGContext(e,s)}dataRange(){return this.track.dataRange?this.track.dataRange:void 0}setDataRange(e,t){void 0!==e&&(this.track.dataRange.min=e),void 0!==t&&(this.track.dataRange.max=t),this.track.autoscale=!1,this.repaintViews()}presentColorPicker(e){if(!1===Tu.has(this.track.type)){const t=[],i=this.track.color||this.track.defaultColor;Qt(i)&&t.push(i),this.track.altColor&&Qt(this.track.altColor)&&t.push(this.track.altColor);const n=t.map((e=>e.startsWith("#")?e:e.startsWith("rgb(")?Hs.rgbToHex(e):Hs.colorNameToHex(e))),r={color:e=>{this.track.color=e,this.repaintViews()},altColor:e=>{this.track.altColor=e,this.repaintViews()}};this.browser.genericColorPicker.configure(n,r),this.browser.genericColorPicker.setActiveColorHandler(e),this.browser.genericColorPicker.show()}}setTrackHeight(e,t){t||(this.track.minHeight&&(e=Math.max(this.track.minHeight,e)),this.track.maxHeight&&(e=Math.min(this.track.maxHeight,e))),this.track.height=e,this.resizeAxisCanvas(this.axis.clientWidth,this.track.height),"function"==typeof this.track.paintAxis&&this.paintAxis();for(let{$viewport:t}of this.viewports)t.height(e);if(this.sampleNameViewport.viewport.style.height=`${e}px`,"function"!=typeof this.track.computePixelHeight)for(let t of this.viewports)t.setContentHeight(e);this.repaintViews(),!1===Mu.has(this.track.type)&&this.updateScrollbar(),this.dragHandle.style.height=`${e}px`,this.gearContainer.style.height=`${e}px`}updateScrollbar(){const e=this.viewports[0].$viewport.height();this.outerScroll.style.height=`${e}px`;const t=this.maxViewportContentHeight(),i=Math.round(e/t*e);t>e?(this.innerScroll.style.display="block",this.innerScroll.style.height=`${i}px`):this.innerScroll.style.display="none"}moveScroller(e){const t=ft(this.innerScroll).position().top+e,i=Math.min(Math.max(0,t),this.outerScroll.clientHeight-this.innerScroll.clientHeight);ft(this.innerScroll).css("top",`${i}px`);const n=this.maxViewportContentHeight(),r=-Math.round(i*(n/this.viewports[0].$viewport.height()));for(let e of this.viewports)e.setTop(r);this.sampleNameViewport.trackScrollDelta=e,this.sampleNameViewport.setTop(r)}isLoading(){for(let e of this.viewports)if(e.isLoading())return!0}repaintViews(){for(let e of this.viewports)e.isVisible()&&e.repaint();"function"==typeof this.track.paintAxis&&this.paintAxis(),this.repaintSamples()}repaintSamples(){if("function"==typeof this.track.getSamples){const e=this.track.getSamples();this.sampleNameViewport.repaint(e)}}setTrackLabelName(e){this.viewports.forEach((t=>t.setTrackLabel(e)))}resize(e){for(let t of this.viewports)t.setWidth(e)}async updateViews(){if(!this.browser||!this.browser.referenceFrameList)return;const e=this.viewports.filter((e=>e.isVisible()));if(e.forEach((e=>e.shift())),this.browser.dragObject)return;const t=e.filter((e=>e.needsRepaint())).filter((e=>e.checkZoomIn())),i=t.filter((e=>e.needsReload()));for(let e of i)await e.loadFeatures();if(this.disposed)return;if(this.track&&"function"==typeof this.track.variantRowCount&&i.length>0){let e=0;for(let t of this.viewports)t.featureCache&&t.featureCache.features&&(e=Math.max(e,t.featureCache.features.reduce(((e,t)=>Math.max(e,t.row||0)),0)));if(this.track.nVariantRows!==e+1){this.track.variantRowCount(e+1);for(let e of this.viewports)e.checkContentHeight()}}if(this.track.autoscale){let t=[];for(let i of e){const e=i.referenceFrame,n=e.start,r=n+e.toBP(i.getWidth());if(i.featureCache&&i.featureCache.features)if("function"==typeof i.featureCache.features.getMax){const e=i.featureCache.features.getMax(n,r);t.push({value:e})}else{const e=bo(i.featureCache.features,n,r);for(let i of e)t.push(i)}}"function"==typeof this.track.doAutoscale?this.track.dataRange=this.track.doAutoscale(t):this.track.dataRange=Ro(t)}const n=this.track.autoscale||this.track.autoscaleGroup||"ruler"===this.track.type;for(let i of e)t.includes(i)?i.repaint():n&&i.refresh();this.adjustTrackHeight(),this.repaintSamples(),this.updateRulerViewportLabels()}clearCachedFeatures(){for(let e of this.viewports)e.clearCache()}updateRulerViewportLabels(){const e=this.browser.calculateViewportWidth(this.viewports.length);for(let t of this.viewports)"ruler"===this.track.type&&(this.viewports.length>1?t.presentLocusLabel(e):t.dismissLocusLabel())}async getInViewFeatures(){if(!this.browser||!this.browser.referenceFrameList)return[];let e=[];const t=this.viewports.filter((e=>e.isVisible()));for(let i of t){const t=i.referenceFrame,{chr:n,start:r,bpPerPixel:s}=i.referenceFrame,o=r+t.toBP(i.getWidth());if((!i.featureCache||!i.featureCache.containsRange(n,r,o,s))&&await i.loadFeatures(),i.featureCache&&i.featureCache.features)if("function"==typeof i.featureCache.features.getMax){const t=i.featureCache.features.getMax(r,o);e.push({value:t})}else{const t="function"==typeof i.featureCache.queryFeatures?i.featureCache.queryFeatures(n,r,o):bo(i.featureCache.features,r,o);e=e.concat(t)}}return e}checkContentHeight(){for(let e of this.viewports)e.checkContentHeight();this.adjustTrackHeight()}adjustTrackHeight(){var e=this.maxViewportContentHeight();if(this.track.autoHeight?this.setTrackHeight(e,!1):this.track.paintAxis&&this.paintAxis(),!1===Mu.has(this.track.type)){const t=this.viewports[0].getContentTop(),i=this.viewports[0].$viewport.height(),n=Math.min(0,i-e);if(t{e.preventDefault(),void 0===t&&e.target.classList.remove("igv-track-drag-handle-hover")})),this.boundTrackDragMouseOutHandler=s.bind(this),this.dragHandle.addEventListener("mouseout",this.boundTrackDragMouseOutHandler)}}removeTrackDragMouseHandlers(){"ideogram"===this.track.id||"ruler"===this.track.id||(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(!0===this.track.ignoreTrackMenu);else{function e(e){e.preventDefault(),e.stopPropagation(),this.trackGearPopup.presentMenuList($t.trackMenuItemList(this))}this.boundTrackGearClickHandler=e.bind(this),this.gear.addEventListener("click",this.boundTrackGearClickHandler)}}removeTrackGearMouseHandlers(){!0===this.track.ignoreTrackMenu||this.gear.removeEventListener("click",this.boundTrackGearClickHandler)}dispose(){this.removeAxisEventListener(this.axis),this.axis.remove();for(let e of this.viewports)e.dispose();this.sampleNameViewport.dispose(),this.removeTrackScrollMouseHandlers(),this.outerScroll.remove(),this.removeTrackDragMouseHandlers(),this.dragHandle.remove(),this.removeTrackGearMouseHandlers(),this.gearContainer.remove(),"function"==typeof this.track.dispose&&this.track.dispose();for(let e of Object.keys(this))this[e]=void 0;this.alert&&this.alert.container.remove(),this.disposed=!0}paintAxis(){if("function"==typeof this.track.paintAxis){const{width:e,height:t}=this.axisCanvas.getBoundingClientRect(),i=window.devicePixelRatio||1;this.axisCanvas.height=i*t,this.axisCanvas.width=i*e;const n=this.axisCanvas.getContext("2d");n.scale(i,i),this.track.paintAxis(n,e,t)}}maxViewportContentHeight(){return Math.max(this.viewports.map((e=>e.getContentHeight())))}}const Lu="rgb(150, 150, 150)";class Iu extends Il{static defaults={height:50,flipAxis:!1,logScale:!1,windowFunction:"mean",graphType:"bar",autoscale:!0,normalize:void 0,scaleFactor:void 0};constructor(e,t){super(e,t)}init(e){super.init(e),this.type="wig",this.featureType="numeric",this.paintAxis=ud;const t=e.format?e.format.toLowerCase():e.format;e.featureSource?(this.featureSource=e.featureSource,delete e.featureSource):this.featureSource="bigwig"===t?new ec(e,this.browser.genome):"tdf"===t?new sc(e,this.browser.genome):uc(e,this.browser.genome),void 0===e.max||!0===e.autoscale?this.autoscale=!0:this.dataRange={min:e.min||0,max:e.max}}async postInit(){const e=await this.getHeader();this.disposed||e&&this.setTrackProperties(e)}async getFeatures(e,t,i,n){const r=await this.featureSource.getFeatures({chr:e,start:t,end:i,bpPerPixel:n,visibilityWindow:this.visibilityWindow,windowFunction:this.windowFunction});if(this.normalize&&this.featureSource.normalizationFactor){const e=this.featureSource.normalizationFactor;for(let t of r)t.value*=e}if(this.scaleFactor){const e=this.scaleFactor;for(let t of r)t.value*=e}return r}menuItemList(){let e=[];return void 0!==this.flipAxis&&(e.push("
"),e.push({label:"Flip y-axis",click:()=>{this.flipAxis=!this.flipAxis,this.trackView.repaintViews()}})),e=e.concat($t.numericDataMenuItems(this.trackView)),e}async getHeader(){return"function"==typeof this.featureSource.getHeader&&(this.header=await this.featureSource.getHeader()),this.header}getScaleFactor(e,t,i,n){return n?i/(Math.log10(t+1)-(e<=0?0:Math.log10(e+1))):i/(t-e)}computeYPixelValue(e,t){return(this.flipAxis?e-this.dataRange.min:this.dataRange.max-e)*t}computeYPixelValueInLogScale(e,t){let i=this.dataRange.max,n=this.dataRange.min;return i<=0?0:(n<=-1&&(n=0),n=n<=0?0:Math.log10(n+1),i=Math.log10(i+1),e=Math.log10(e+1),(this.flipAxis?e-n:i-e)*t)}draw(e){const t=e.features,i=e.context,n=e.bpPerPixel,r=e.bpStart,s=e.pixelWidth;e.pixelHeight;const o=r+s*n+1,a=this.color||Lu;let l;"string"==typeof a&&a.startsWith("rgb(")&&(l=Hs.addAlpha(a,.1));const h=this.getScaleFactor(this.dataRange.min,this.dataRange.max,e.pixelHeight,this.logScale),c=e=>this.logScale?this.computeYPixelValueInLogScale(e,h):this.computeYPixelValue(e,h);if(t&&t.length>0&&(void 0===this.dataRange.min&&(this.dataRange.min=0),this.dataRange.max>this.dataRange.min)){let s,a=-1;const h=c(0);for(let e of t){if(e.endo)break;const t=Math.floor((e.start-r)/n);if(isNaN(t))continue;let l=c(e.value);const d=Math.ceil((e.end-r)/n),u=Math.max(1,d-t),f=this.getColorForFeature(e);if("points"===this.graphType){const e=this.config.pointSize||3,n=t+u/2;Co.fillCircle(i,n,l,e/2,{fillStyle:f,strokeStyle:f})}else if("line"===this.graphType)null!=s&&Co.strokeLine(i,a,s,t,l,{fillStyle:f,strokeStyle:f}),Co.strokeLine(i,t,l,t+u,l,{fillStyle:f,strokeStyle:f});else{const e=l-h;Co.fillRect(i,t,h,u,e,{fillStyle:f})}a=t+u,s=l}if(this.dataRange.min<0){const t=this.dataRange.max/(this.dataRange.max-this.dataRange.min)*e.pixelHeight;Co.strokeLine(i,0,t,e.pixelWidth,t,{strokeStyle:l})}}if(this.config.hasOwnProperty("guideLines"))for(let t of this.config.guideLines)if(t.hasOwnProperty("color")&&t.hasOwnProperty("y")&&t.hasOwnProperty("dotted")){let i=c(t.y),n={strokeStyle:t.color,strokeWidth:2};t.dotted?Co.dashedLine(e.context,0,i,e.pixelWidth,i,5,n):Co.strokeLine(e.context,0,i,e.pixelWidth,i,n)}}popupData(e,t){if(void 0===t&&(t=this.clickedFeatures(e)),t&&t.length>0){const i=e.genomicLocation,n=[];t.sort((function(e,t){return Math.abs((e.start+e.end)/2-i)-Math.abs((t.start+t.end)/2-i)}));const r=t.length>10?t.slice(0,10):t;r.sort((function(e,t){return e.start-t.start}));for(let e of r)if(e){n.length>0&&n.push("
");let t=e.end-e.start==1?Xt(e.start+1):Xt(e.start+1)+"-"+Xt(e.end);n.push({name:"Position:",value:t}),n.push({name:"Value:     ",value:Xt(e.value)})}return r.length..."),n}return[]}get supportsWholeGenome(){return!this.config.indexURL&&!1!==this.config.supportsWholeGenome}getColorForFeature(e){let t=e.value<0&&this.altColor?this.altColor:this.color||Lu;return"function"==typeof t?t(e.value):t}dispose(){this.trackView=void 0}}function Bu(e){this.thresholds=e.thresholds,this.colors=e.colors}function Fu(e){this.scale=e,this.lowColor="rgb("+e.lowR+","+e.lowG+","+e.lowB+")",this.highColor="rgb("+e.highR+","+e.highG+","+e.highB+")",this.diff=e.high-e.low}Bu.prototype.getColor=function(e){for(let t of this.thresholds)if(e=i.high?this.highColor:(t=(e-i.low)/this.diff,"rgb("+Math.floor(i.lowR+t*(i.highR-i.lowR))+","+Math.floor(i.lowG+t*(i.highG-i.lowG))+","+Math.floor(i.lowB+t*(i.highB-i.lowB))+")")};class Nu{constructor(e){this.color=e}getColor(){return this.color}}class Ou extends Il{constructor(e,t){super(e,t)}init(e){if(super.init(e),this.type=e.type||"seg","maf"===this.type&&(this.type="mut"),this.isLog=e.isLog,this.displayMode=e.displayMode||"EXPANDED",this.height=e.height||300,this.maxHeight=e.maxHeight||500,this.squishedRowHeight=e.sampleSquishHeight||e.squishedRowHeight||2,this.expandedRowHeight=e.sampleExpandHeight||e.expandedRowHeight||13,this.sampleHeight=this.squishedRowHeight,e.color?this.color=e.color:(this.posColorScale=e.posColorScale||new Fu({low:.1,lowR:255,lowG:255,lowB:255,high:1.5,highR:255,highG:0,highB:0}),this.negColorScale=e.negColorScale||new Fu({low:-1.5,lowR:0,lowG:0,lowB:255,high:-.1,highR:255,highG:255,highB:255}),"mut"===this.type&&(this.colorTable=new _o(e.colorTable||Pu))),this.sampleKeys=[],this.sampleNames=new Map,e.samples){for(let t of e.samples)this.sampleKeys.push(t),this.sampleNames.set(t,t);this.explicitSamples=!0}const t=Object.assign({},this.config);t.maxWGCount=t.maxWGCount||Number.MAX_SAFE_INTEGER,this.featureSource=uc(t,this.browser.genome),this.initialSort=e.sort}async postInit(){"function"==typeof this.featureSource.getHeader&&(this.header=await this.featureSource.getHeader(),this.disposed)||this.header&&this.setTrackProperties(this.header)}menuItemList(){const e=[],t={SQUISHED:"Squish",EXPANDED:"Expand",FILL:"Fill"};e.push("
"),e.push("DisplayMode:");const i="seg"===this.type?["SQUISHED","EXPANDED","FILL"]:["SQUISHED","EXPANDED"];for(let n of i){const i=jt(t[n],n===this.displayMode);e.push({object:ft(i),click:()=>{this.displayMode=n,this.config.displayMode=n,this.trackView.checkContentHeight(),this.trackView.repaintViews(),this.trackView.moveScroller(this.trackView.sampleNameViewport.trackScrollDelta)}})}return e}hasSamples(){return!0}getSamples(){return{names:this.sampleKeys.map((e=>this.sampleNames.get(e))),height:this.sampleHeight,yOffset:0}}async getFeatures(e,t,i){const n=await this.featureSource.getFeatures({chr:e,start:t,end:i});if(this.initialSort){const e=this.initialSort;this.sortSamples(e.chr,e.start,e.end,e.direction,n),this.initialSort=void 0}return n}draw({context:e,renderSVG:t,pixelTop:i,pixelWidth:n,pixelHeight:r,features:s,bpPerPixel:o,bpStart:a}){if(Co.fillRect(e,0,i,n,r,{fillStyle:"rgb(255, 255, 255)"}),s&&s.length>0){this.checkForLog(s),this.updateSampleKeys(s);const t={};let l;switch(this.sampleKeys.forEach((function(e,i){t[e]=i})),this.displayMode){case"FILL":this.sampleHeight=r/this.sampleKeys.length,l=0;break;case"SQUISHED":this.sampleHeight=this.squishedRowHeight,l=0;break;default:this.sampleHeight=this.expandedRowHeight,l=1}const h=this.sampleHeight;for(let e of s)e.pixelRect=void 0;const c=i+r,d=a+n*o+1,u=o;this.sampleYStart=void 0;for(let n of s){if(n.endd)break;const r=n.sampleKey||n.sample;n.row=t[r];const s=n.row*h+l;void 0===this.sampleYStart&&(this.sampleYStart=s);if(s+hc)continue;const o=Math.max(n.start,a);let f=Math.round((o-a)/u);const p=Math.min(n.end,d),g=Math.round((p-a)/u);let m,b,w=Math.max(1,g-f);if(this.color?m="function"==typeof this.color?this.color(n):this.color:this.colorTable&&(m=this.colorTable.getColor(n.value.toLowerCase())),"mut"===this.type)b=h-2*l,w<3&&(w=3,f-=1);else{let e=n.value;this.isLog||(e=Ps(e/2)),m=e<-.1?this.negColorScale.getColor(e):e>.1?this.posColorScale.getColor(e):"white";let t=h;if(h<.25){const i=.1+2*Math.abs(e);t=Math.min(1,i*h)}b=t-2*l}n.pixelRect={x:f,y:s,w:w,h:b},e.fillStyle=m,e.fillRect(f,s,w,b)}}}checkForLog(e){if(void 0===this.isLog){this.isLog=!1;for(let t of e)if(t.value<0)return void(this.isLog=!0)}}computePixelHeight(e){if(!e)return 0;const t="SQUISHED"===this.displayMode?this.squishedRowHeight:this.expandedRowHeight;return this.updateSampleKeys(e),this.sampleKeys.length*t}async sortSamples(e,t,i,n,r){if(r||(r=await this.featureSource.getFeatures({chr:e,start:t,end:i})),!r)return;this.updateSampleKeys(r);const s={},o="ASC"===n?1:-1,a=()=>{const e=i-t+1;for(let n of r){if(n.endi)break;const r=Math.max(t,n.start),o=(Math.min(i,n.end)-r)/e,a=n.sampleKey||n.sample,l=s[a]||0;s[a]=l+o*n.value}this.sampleKeys.sort((function(e,t){let i=s[e],n=s[t];return i||(i=o*Number.MAX_VALUE),n||(n=o*Number.MAX_VALUE),i===n?0:i>n?o:-1*o}))},l=()=>{for(let e of r){if(e.endi)break;const n=e.sampleKey||e.sample;(!s.hasOwnProperty(n)||e.value.localeCompare(s[n])>0)&&(s[n]=e.value)}this.sampleKeys.sort((function(e,t){let i=s[e]||"",n=s[t]||"";return o*i.localeCompare(n)}))};"mut"===this.type?l():a(),this.trackView.repaintViews()}clickedFeatures(e){const t=super.clickedFeatures(e),i=e.y;return t.filter((function(e){const t=e.pixelRect;return t&&i>=t.y&&i<=t.y+t.h}))}hoverText(e){const t=this.clickedFeatures(e);if(t&&t.length>0)return`${t[0].sample}: ${t[0].value}`}popupData(e,t){void 0===t&&(t=this.clickedFeatures(e));const i=[];for(let e of t){i.length>0&&(i.push("
"),i.push("
"));const t=e._f||e,n="function"==typeof t.popupData?t.popupData(this.type,this.browser.genome.id):this.extractPopupData(t);Array.prototype.push.apply(i,n)}return i}contextMenuItemList(e){const t=e.viewport.referenceFrame,i=e.genomicLocation,n=this.config.sort?"ASC"===this.config.sort.direction?"DESC":"ASC":"DESC",r=t.toBP(2.5),s=t=>{const i=e.viewport.cachedFeatures;this.sortSamples(t.chr,t.start,t.end,t.direction,i)};return[{label:"seg"===this.type?"Sort by value":"Sort by type",click:t=>{const o={direction:n,chr:e.viewport.referenceFrame.chr,start:i-r,end:i+r};s(o),this.config.sort=o}}]}get supportsWholeGenome(){return(!1===this.config.indexed||!this.config.indexURL)&&!1!==this.config.supportsWholeGenome}updateSampleKeys(e){if(!this.explicitSamples)for(let t of e){const e=t.sampleKey||t.sample;this.sampleNames.has(e)||(this.sampleNames.set(e,t.sample),this.sampleKeys.push(e))}}}const Pu={indel:"rgb(0,200,0)","targeted region":"rgb(236,155,43)",truncating:"rgb(\t150,0,0)","non-coding transcript":"rgb(0,0,150)",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)"};class Du extends Il{constructor(e,t){super(e,t),this.type="merged",this.featureType="numeric",this.paintAxis=ud,this.graphType=e.graphType}init(e){if(!e.tracks)throw Error("Error: no tracks defined for merged track"+e);super.init(e)}async postInit(){this.tracks=[];const e=[];for(let t of this.config.tracks){t.isMergedTrack=!0;const i=await this.browser.createTrack(t);i?(i.autoscale=!1,this.tracks.push(i)):console.warn("Could not create track "+t),"function"==typeof i.postInit&&e.push(i.postInit())}this.flipAxis=!!this.config.flipAxis&&this.config.flipAxis,this.logScale=!!this.config.logScale&&this.config.logScale,this.autoscale=this.config.autoscale||void 0===this.config.max,this.autoscale||(this.dataRange={min:this.config.min||0,max:this.config.max});for(let e of this.tracks)e.autoscale=!1,e.dataRange=this.dataRange;return this.height=this.config.height||50,Promise.all(e)}get height(){return this._height}set height(e){if(this._height=e,this.tracks)for(let t of this.tracks)t.height=e,t.config.height=e}menuItemList(){let e=[];return void 0!==this.flipAxis&&e.push({label:"Flip y-axis",click:()=>{this.flipAxis=!this.flipAxis,this.trackView.repaintViews()}}),e=e.concat($t.numericDataMenuItems(this.trackView)),e}async getFeatures(e,t,i,n){const r=this.tracks.map((r=>r.getFeatures(e,t,i,n))),s=await Promise.all(r);return new zu(s)}draw(e){const t=e.features;for(let i=0,n=this.tracks.length;i0&&i.push("
"),i.push(`
${this.tracks[n].name}
`);const r=this.tracks[n].popupData(e,t[n]);i.push(...r)}return i}}}clickedFeatures(e){const t=e.viewport.cachedFeatures;if(!t)return[];const i=e.genomicLocation,n=[];for(let r of t.featureArrays){const t=e.referenceFrame.bpPerPixel>.2?3*e.referenceFrame.bpPerPixel:.2,s=bo(r,i-t,i+t);for(let e of s)n.push(e)}return n}get supportsWholeGenome(){return this.tracks.every((e=>e.supportsWholeGenome))}}class zu{constructor(e){this.featureArrays=e}getMax(e,t){let i=-Number.MAX_VALUE;for(let n of this.featureArrays)for(let r of n)if(void 0!==r.value&&!Number.isNaN(r.value)){if(r.endt)break;i=Math.max(i,r.value)}return i}}class Hu extends Il{static defaults={height:250,theta:Math.PI/4,arcOrientation:!0,showBlocks:!0,blockHeight:3,thickness:1,alpha:.02,logScale:!0};constructor(e,t){super(e,t)}init(e){super.init(e),this.sinTheta=Math.sin(this.theta),this.cosTheta=Math.cos(this.theta),this.arcType=function(e){if(!e.arcType)return"nested";switch(e.arcType){case"chiapet":return"inView";case"chiapetoutbound":return"partialInView";default:return e.arcType}}(e),this.alpha=e.alpha||.02,this.painter={flipAxis:!this.arcOrientation,dataRange:this.dataRange,paintAxis:ud},e.valueColumn?(this.valueColumn=e.valueColumn,this.hasValue=!0):e.useScore&&(this.hasValue=!0,this.valueColumn="score"),e.max?(this.dataRange={min:e.min||0,max:e.max},this.autoscale=!1):this.autoscale=!0,e.featureSource?(this.featureSource=e.featureSource,delete e._featureSource):(this.featureSource=uc(e,this.browser.genome),this.featureSource.getWGFeatures=$u)}async postInit(){if("function"!=typeof this.featureSource.getHeader||(this.header=await this.featureSource.getHeader(),!this.disposed))return this.header&&this.setTrackProperties(this.header),void 0===this.visibilityWindow&&"function"==typeof this.featureSource.defaultVisibilityWindow&&(this.visibilityWindow=await this.featureSource.defaultVisibilityWindow(),this.featureSource.visibilityWindow=this.visibilityWindow),this}get supportsWholeGenome(){return!0}async getFeatures(e,t,i){const n=this.visibilityWindow,r=await this.featureSource.getFeatures({chr:e,start:t,end:i,visibilityWindow:n});return void 0===this.hasValue&&r&&r.length>0&&(this.hasValue=void 0!==r[0].score),r}draw(e){"proportional"===this.arcType||"inView"===this.arcType||"partialInView"===this.arcType?this.drawProportional(e):this.drawNested(e)}drawNested(e){const t=e.context,i=e.pixelWidth,n=e.pixelHeight,r=e.viewportWidth,s=e.bpPerPixel,o=e.bpStart,a=s;Co.fillRect(t,0,e.pixelTop,i,n,{fillStyle:"rgb(255, 255, 255)"});const l=e.features;if(l){(function(){let e=0;for(let t of l){let n=(t.start-o)/a,r=(t.end-o)/a;n>=0&&r<=i&&(e=Math.max(e,r-n))}let t=Math.min(r,e)/2;if(e>0){let e=(n-10)/t;this.theta=function(e){let t,i=[.01570925532366355,.15838444032453644,.3249196962329063,.5095254494944288,.7265425280053609,.9999999999999999],n=[.031415926535897934,.3141592653589793,.6283185307179586,.9424777960769379,1.2566370614359172,1.5707963267948966];for(t=0;te);t++);let r=0===t?0:i[t-1],s=ti)continue;let f=u-d;f<3&&(f=3,d--);const p=f/2,g=p/this.sinTheta,m=this.cosTheta*g,b=d+p;let w,v,y;if(c?(w=this.height+m,v=Math.PI+Math.PI/2-this.theta,y=Math.PI+Math.PI/2+this.theta):(w=-m,v=Math.PI/2-this.theta,y=Math.PI/2+this.theta),this.showBlocks&&"all"!==e.chr){const i=(e.start1-o)/a,n=(e.end1-o)/a,r=(e.start2-o)/a,l=(e.end2-o)/a,h=this.arcOrientation?-this.blockHeight:this.blockHeight;t.fillRect(i,s,n-i,h),t.fillRect(r,s,l-r,h)}n&&!this.config.useScore&&f>r&&(n=ju(n,this.alpha)),t.strokeStyle=n,t.fillStyle=n,t.beginPath(),t.arc(b,w,g,v,y,!1),t.stroke(),e.drawState={xc:b,yc:w,r:g}}else{let r=Math.round((e.start-o)/a),s=Math.round((e.end-o)/a);if(s<0||r>i)continue;let l=s-r;l<3&&(l=3,r--);const h=e.chr===e.chr1?e.chr2:e.chr1;t.strokeStyle=n,t.fillStyle=ju(mu(h),.5),c?(t.fillRect(r,this.height/2,l,this.height/2),t.fillText(h,r+l/2,this.height/2-5),e.drawState={x:r,y:this.height/2,w:l,h:this.height/2}):(t.fillRect(r,0,l,this.height/2),t.fillText(h,r+l/2,this.height/2+13),e.drawState={x:r,y:0,w:l,h:this.height/2})}}}var h}getScaleFactor(e,t,i,n){return n?i/(Math.log10(t+1)-(e<=0?0:Math.log10(e+1))):i/(t-e)}drawProportional(e){const t=e.context,i=e.pixelWidth,n=e.pixelHeight,r=e.bpPerPixel,s=e.bpStart,o=r,a=e.referenceFrame.start,l=e.referenceFrame.end;Co.fillRect(t,0,e.pixelTop,i,n,{fillStyle:"rgb(255, 255, 255)"});const h=e.features;if(h&&h.length>0){const n=0,r=this.getScaleFactor(n,this.dataRange.max,e.pixelHeight-1,this.logScale),c=this.arcOrientation?e.pixelHeight:0;for(let e of h){e.drawState=void 0;const n=this.valueColumn?e[this.valueColumn]:e.score;if(void 0===n||Number.isNaN(n))continue;const h=Math.round((this.logScale?Math.log10(n+1):n)*r);if(e.chr1===e.chr2||"all"===e.chr){const{m1:i,m2:r}=Vu(e,this.browser.genome);let d=Math.round((i-s)/o),u=Math.round((r-s)/o)-d;if(u<3&&(u=3,d--),nthis.dataRange.max)continue;if("proportional"!==this.arcType){const e="partialInView"===this.arcType,t=i>=a&&r<=l;let n=!1,s=!1;if(!t&&e&&(n=a<=i&&i<=l,n||(s=a<=r&&r<=l)),!(t||n||s))continue}const f=u/2,p=d+u/2;e.drawState={xc:p,yc:c,radiusX:f,radiusY:h};const g=!!this.arcOrientation,m=e.color||this.color;if(t.strokeStyle=m,t.lineWidth=e.thickness||this.thickness||1,!0===t.isSVG?t.strokeEllipse(p,c,f,h,0,0,Math.PI,g):(t.beginPath(),t.ellipse(p,c,f,h,0,0,Math.PI,g),t.stroke()),this.alpha&&(t.fillStyle=ju(m,this.alpha),!0===t.isSVG?t.fillEllipse(p,c,f,h,0,0,Math.PI,g):t.fill()),this.showBlocks&&"all"!==e.chr){t.fillStyle=m;const i=(e.start1-s)/o,n=(e.end1-s)/o,r=(e.start2-s)/o,a=(e.end2-s)/o,l=this.arcOrientation?-this.blockHeight:this.blockHeight;t.fillRect(i,c,n-i,l),t.fillRect(r,c,a-r,l)}}else{let r=Math.round((e.start-s)/o),a=Math.round((e.end-s)/o);if(a<0||r>i||nthis.dataRange.max)continue;const l=Math.min(h,this.height-13);let c=a-r;c<3&&(c=3,r--);const d=e.chr===e.chr1?e.chr2:e.chr1;if(t.font="8px sans-serif",t.textAlign="center",t.fillStyle=ju(mu(d),.5),this.arcOrientation){const i=this.height-l;t.fillRect(r,i,c,l),t.fillText(d,r+c/2,i-5),e.drawState={x:r,y:i,w:c,h:l}}else t.fillRect(r,0,c,l),t.fillText(d,r+c/2,l+13),e.drawState={x:r,y:0,w:c,h:l}}}}}clearAxis(e,t,i){Co.fillRect(e,0,0,t,i,{fillStyle:"rgb(255, 255, 255)"})}paintAxis(e,t,i){const n={min:0,max:this.dataRange.max};"proportional"===this.arcType||"inView"===this.arcType||"partialInView"===this.arcType?(this.painter.flipAxis=!this.arcOrientation,this.painter.dataRange=n,this.painter.paintAxis(e,t,i)):this.clearAxis(e,t,i)}menuItemList(){let e=[];if(this.hasValue){e.push("
");const t={nested:"Nested",proportional:"Proportional - All",inView:"Proportional - Both Ends in View",partialInView:"Proportional - One End in View"};e.push("Arc Type");for(let i of["nested","proportional","inView","partialInView"])e.push({object:ft(jt(t[i],i===this.arcType)),click:()=>{this.arcType=i,this.trackView.repaintViews()}})}return e.push("
"),e.push({name:"Toggle arc direction",click:()=>{this.arcOrientation=!this.arcOrientation,this.trackView.repaintViews()}}),e.push({name:this.showBlocks?"Hide Blocks":"Show Blocks",click:()=>{this.showBlocks=!this.showBlocks,this.trackView.repaintViews()}}),"proportional"!==this.arcType&&"inView"!==this.arcType&&"partialInView"!==this.arcType||(e=e.concat($t.numericDataMenuItems(this.trackView))),this.browser.circularView&&(e.push("
"),e.push({label:"Add interactions to circular view",click:()=>{for(let e of this.trackView.viewports)this.addChordsForViewport(e.referenceFrame)}})),e}contextMenuItemList(e){if(this.browser.circularView){const t=e.viewport,i=[];return i.push({label:"Add interactions to circular view",click:()=>{const e=t.referenceFrame;this.addChordsForViewport(e)}}),i.push("
"),i}}addChordsForViewport(e){const t=("all"===e.chr?this.featureSource.getAllFeatures():this.featureSource.featureCache.queryFeatures(e.chr,e.start,e.end)).filter((e=>e.drawState));if(0===t.length)return;qd(t.map((e=>{const t=e._f||e;return{uniqueId:`${t.chr1}:${t.start1}-${t.end1}_${t.chr2}:${t.start2}-${t.end2}`,refName:Vd(t.chr1),start:t.start1,end:t.end1,mate:{refName:Vd(t.chr2),start:t.start2,end:t.end2}}})),this,e,.5)}doAutoscale(e){let t=0;if(e)for(let i of e){const e=this.valueColumn?i[this.valueColumn]:i.score;Number.isNaN(e)||(t=Math.max(t,e))}return{min:0,max:t}}popupData(e,t){void 0===t&&(t=this.clickedFeatures(e));const i=[];for(let e of t){const t=e._||e;if(i.push({name:"Region 1",value:Uu(t.chr1,t.start1,t.end1,t.strand1)}),i.push({name:"Region 2",value:Uu(t.chr2,t.start2,t.end2,t.strand2)}),t.name&&i.push({name:"Name",value:t.name}),void 0!==t.value&&i.push({name:"Value",value:t.value}),void 0!==t.score&&i.push({name:"Score",value:t.score}),t.extras&&this.header&&this.header.columnNames){const e=this.header.columnNames,n=this.header.hiccups?6:10;for(let r=n;r=t-a&&e.canvasX<=t+s+a&&e.canvasY>=n&&e.canvasY<=n+o){const n=-Math.abs(e.canvasX-(t+s/2));i.push({score:n,feature:r});break}}}return i.length>1&&i.sort(((e,t)=>e.score-t.score)),i.map((e=>e.feature))}}function Vu(e,t){let i=(e.start1+e.end1)/2,n=(e.start2+e.end2)/2;if("all"===e.chr&&(i=t.getGenomeCoordinate(e.chr1,i),n=t.getGenomeCoordinate(e.chr2,n)),i>n){const e=i;i=n,n=e}return{m1:i,m2:n}}function Uu(e,t,i,n){return n&&"."!==n?`${e}:${Xt(t+1)}-${Xt(i)} (${n})`:`${e}:${Xt(t+1)}-${Xt(i)}`}const qu=new Map;function ju(e,t){const i=`${e}_${t}`;let n=qu.get(i);return n||(n=Hs.addAlpha(e,t),qu.set(i,n)),n}function $u(e){const t=e=>{const t=Object.assign({},e);return t.chr="all",t.start=i.getGenomeCoordinate(e.chr1,e.start1),t.end=i.getGenomeCoordinate(e.chr2,e.end2),t},i=this.genome;let n,r=0;for(let t of i.wgChromosomeNames){let i=e[t];if(i)for(let e of i)e.dup||(r++,e.score&&(!n||e.score>n.score)&&(n=e))}const s=this.maxWGCount,o=n&&n.score>0&&r>s?5:1,a=Math.floor(s/o),l=n&&n.score>0?Math.log(n.score)/o:Number.MAX_SAFE_INTEGER;let h,c=[],d=[];for(let e=0;ee.name)):[]}return this.header}getCallsetsLength(){return this.callSets?this.callSets.length:0}async getFeatures(e,t,i,n){return void 0===this.header&&(this.header=await this.getHeader()),this.featureSource.getFeatures({chr:e,start:t,end:i,bpPerPixel:n,visibilityWindow:this.visibilityWindow})}hasSamples(){return this.getCallsetsLength()>0}getSamples(){return{yOffset:this.sampleYOffset,names:this.sampleNames,height:this.sampleHeight}}computePixelHeight(e){if(!e||0==e.length)return 10;const t="COLLAPSED"===this.displayMode?1:this.nVariantRows,i="SQUISHED"===this.displayMode?this.squishedVGap:this.expandedVGap,n="SQUISHED"===this.displayMode?this.squishedVariantHeight:this.expandedVariantHeight,r="SQUISHED"===this.displayMode?this.squishedCallHeight:this.expandedCallHeight;return 10+t*(n+i)+i+((!1===this.showGenotypes?0:this.getCallsetsLength()*t)+1)*(r+i)}variantRowCount(e){this.nVariantRows=e}draw({context:e,pixelWidth:t,pixelHeight:i,bpPerPixel:n,bpStart:r,pixelTop:s,features:o}){Co.fillRect(e,0,s,t,i,{fillStyle:"rgb(255, 255, 255)"});const a="SQUISHED"===this.displayMode?this.squishedVGap:this.expandedVGap,l="COLLAPSED"===this.displayMode?1:this.nVariantRows,h="SQUISHED"===this.displayMode?this.squishedVariantHeight:this.expandedVariantHeight;this.variantBandHeight=10+l*(h+a);let c=this.callSets;!c&&this._f&&(c=this._f.callSets);const d=this.getCallsetsLength();if(c&&d>0&&!1!==this.showGenotypes&&Co.strokeLine(e,0,this.variantBandHeight,t,this.variantBandHeight,{strokeStyle:"rgb(224,224,224) "}),o){const i="SQUISHED"===this.displayMode?this.squishedCallHeight:this.expandedCallHeight,s="SQUISHED"===this.displayMode?this.squishedVGap:this.expandedVGap,a=r+t*n+1;for(let t of o){if(t.enda)break;const o="SQUISHED"===this.displayMode?this.squishedVariantHeight:this.expandedVariantHeight,l=10+("COLLAPSED"===this.displayMode?0:t.row*(o+s)),h=o;let u=Math.round((t.start-r)/n),f=Math.round((t.end-r)/n),p=Math.max(1,f-u);p<3?(p=3,u-=1):p>5&&(u+=1,p-=2),e.fillStyle=this.getColorForFeature(t),e.fillRect(u,l,p,h);let g=this.getVariantStrokecolor(t);if(g&&(e.strokeStyle=g,e.strokeRect(u,l,p,h)),this.callContextHook(t,e,u,l,p,h),t.pixelRect={x:u,y:l,w:p,h:h},d>0&&!1!==this.showGenotypes){const n="COLLAPSED"===this.displayMode?1:this.nVariantRows;this.sampleYOffset=this.variantBandHeight+s,this.sampleHeight=n*(i+s);let r=0;if(c&&t.calls)for(let n of c){const o=t.calls[n.id];if(o){const a="COLLAPSED"===this.displayMode?0:t.row,l=this.sampleYOffset+r*this.sampleHeight+a*(i+s);let h=!0,c=!0,d=!1;if(o.genotype)for(let e of o.genotype){if("."===e){d=!0;break}0!==e&&(c=!1),0===e&&(h=!1)}o.genotype?e.fillStyle=d?this.noCallColor:c?this.homrefColor:h?this.homvarColor:this.hetvarColor:e.fillStyle=this.noGenotypeColor,e.fillRect(u,l,p,i),n.pixelRect={x:u,y:l,w:p,h:i}}r++}}}}else console.log("No feature list")}getColorForFeature(e){const t=e._f||e;let i;if(this.colorBy){const e=this.colorBy;let n;if(t.info.hasOwnProperty(e))n=t.info[e];else if(Zu.has(e)){n=t[Zu.get(e)]}i=this.getVariantColorTable(e).getColor(n),i||(i="gray")}else i=this._color?"function"==typeof this._color?this._color(e):this._color:"NONVARIANT"===t.type?this.nonRefColor:"MIXED"===t.type?this.mixedColor:this.color;return i}getVariantStrokecolor(e){const t=e._f||e;let i;return i=this._strokecolor?"function"==typeof this._strokecolor?this._strokecolor(t):this._strokecolor:void 0,i}callContextHook(e,t,i,n,r,s){if(this._context_hook&&"function"==typeof this._context_hook){const o=e._f||e;t.save(),this._context_hook(o,t,i,n,r,s),t.restore()}}clickedFeatures(e){let t=super.clickedFeatures(e);const i="EXPANDED"===this.displayMode?this.expandedVGap:this.squishedVGap,n=i+("SQUISHED"===this.displayMode?this.squishedCallHeight:this.expandedCallHeight),r=e.y;if(r<=this.variantBandHeight){const e="SQUISHED"===this.displayMode?this.squishedVariantHeight:this.expandedVariantHeight,n=Math.floor((r-10)/(e+i));"COLLAPSED"!==this.displayMode&&(t=t.filter((e=>e.row===n)))}else if(this.callSets){const e=this.callSets,i=r-this.variantBandHeight,s=Math.floor(i/this.sampleHeight);if(s>=0&&se.row===r)),a=e[s];t=o.map((e=>{const t=e.calls[a.id];return function(e,t){if(e.genotype){let i="";if("."===t.alternateBases)i="No Call";else{const n=t.alternateBases.split(",");for(let r of e.genotype)if(i.length>0&&(i+="|"),"."===r)i+=".";else if(0===r)i+=t.referenceBases;else{i+=n[r-1].replace("<","<")}}e.genotypeName=i}}(t,e),t}))}}return t}popupData(e,t){void 0===t&&(t=this.clickedFeatures(e));const i=e.genomicLocation,n=this.browser.genome.id,r=this.browser.sampleInformation;let s=[];for(let e of t){const t=e._f||e;if(s.length>0&&s.push({html:'
'}),"function"==typeof t.popupData){const e=t.popupData(i,n);Array.prototype.push.apply(s,e)}else{const e=t;if(void 0!==e.callSetName&&s.push({name:"Name",value:e.callSetName}),e.genotypeName&&s.push({name:"Genotype",value:e.genotypeName}),void 0!==e.phaseset&&s.push({name:"Phase set",value:e.phaseset}),void 0!==e.genotypeLikelihood&&s.push({name:"genotypeLikelihood",value:e.genotypeLikelihood.toString()}),r){var o=r.getAttributes(e.callSetName);o&&Object.keys(o).forEach((function(e){var t=e.replace(/([A-Z])/g," $1");t=t.charAt(0).toUpperCase()+t.slice(1),s.push({name:t,value:o[e]})}))}var a=Object.keys(e.info);a.length&&s.push("
"),a.forEach((function(t){s.push({name:t,value:decodeURIComponent(e.info[t])})}))}}return s}menuItemList(){const e=[];if(this.header.INFO&&this.header.INFO){const t=this.header.INFO.SVTYPE?["SVTYPE"]:[];if(this._initColorBy&&"SVTYPE"!==this._initColorBy&&t.push(this._initColorBy),t.length>0){e.push("
");const i=ft('
');i.text("Color by:"),e.push({name:void 0,object:i,click:void 0,init:void 0}),t.sort();for(let i of t){const t=this.colorBy===i,n=i||"None";e.push(this.colorByCB({key:i,label:n},t))}e.push(this.colorByCB({key:void 0,label:"None"},void 0===this.colorBy)),e.push("
")}}this.getCallsetsLength()>0&&(e.push({object:ft('
')}),e.push({object:ft(jt("Show Genotypes",this.showGenotypes)),click:()=>{this.showGenotypes=!this.showGenotypes,this.trackView.checkContentHeight(),this.trackView.repaintViews()}})),e.push({object:ft('
')});for(let t of["COLLAPSED","SQUISHED","EXPANDED"]){e.push({object:ft(jt({COLLAPSED:"Collapse",SQUISHED:"Squish",EXPANDED:"Expand"}[t],t===this.displayMode)),click:()=>{this.displayMode=t,this.trackView.checkContentHeight(),this.trackView.repaintViews()}})}return this.browser.circularView&&(e.push("
"),e.push({label:"Add SVs to circular view",click:()=>{for(let e of this.trackView.viewports)this.sendChordsForViewport(e)}})),e}contextMenuItemList(e){if(this.browser.circularView){const t=e.viewport,i=[];return i.push({label:"Add SVs to Circular View",click:()=>{this.sendChordsForViewport(t)}}),i.push("
"),i}}sendChordsForViewport(e){const t=e.referenceFrame;let i;if("all"===t.chr){const e=this.featureSource.getAllFeatures(),t=Object.keys(e).map((t=>e[t]));i=[].concat(...t)}else i=this.featureSource.featureCache.queryFeatures(t.chr,t.start,t.end);qd(i.filter((e=>{const t=e._f||e;return t.info&&t.info.CHR2&&t.info.END&&(t.info.CHR2!==t.chr||Math.abs(Number.parseInt(t.info.END)-t.pos)>1e6)})).map((e=>{const t=e._f||e,i=Number.parseInt(t.info.END),n=i-100,r=i+100;return{uniqueId:`${t.chr}:${t.start}-${t.end}_${t.info.CHR2}:${t.info.END}`,refName:Vd(t.chr),start:t.start,end:t.end,mate:{refName:Vd(t.info.CHR2),start:n,end:r}}})),this,t,.5)}colorByCB(e,t){return{name:void 0,object:ft(jt(e.label,t)),click:()=>{e.key===this.colorBy?(this.colorBy=void 0,delete this.config.colorBy,this.trackView.repaintViews()):(this.colorBy=e.key,this.config.colorBy=e.key,this.trackView.repaintViews())},init:void 0}}getState(){const e=super.getState();return this._color&&"function"!=typeof this._color&&(e.color=this._color),e}getVariantColorTable(e){if(this.colorTables||(this.colorTables=new Map),!this.colorTables.has(e)){let t;if("SVTYPE"===e)t=Xu;else t=new yo("Set1");this.colorTables.set(e,t)}return this.colorTables.get(e)}}const Xu=new _o({DEL:"#ff2101",INS:"#001888",DUP:"#028401",INV:"#008688",CNV:"#8931ff",BND:"#891100","*":"#002eff"});class Yu extends Il{constructor(e,t){super(e,t)}init(e){super.init(e),this.name=e.name,this.pValueField=e.pValueField||"pValue",this.geneField=e.geneField||"geneSymbol",this.snpField=e.snpField||"snp";const t=e.minLogP||e.min,i=e.maxLogP||e.max;this.dataRange={min:t||3.5,max:i||25},this.autoscale=!i||e.autoscale,this.autoscalePercentile=void 0===e.autoscalePercentile?98:e.autoscalePercentile,this.background=e.background,this.divider=e.divider||"rgb(225,225,225)",this.dotSize=e.dotSize||2,this.height=e.height||100,this.autoHeight=!1,this.disableButtons=e.disableButtons,this.visibilityWindow=void 0===e.visibilityWindow?2e6:e.visibilityWindow>=0?Math.min(2e6,e.visibilityWindow):2e6,this.featureSource=uc(e,this.browser.genome),Wd.gtexLoaded=!0}paintAxis(e,t,i){const n=(this.dataRange.max-this.dataRange.min)/i,r={font:"normal 10px Arial",textAlign:"right",strokeStyle:"black"};Co.fillRect(e,0,0,t,i,{fillStyle:"rgb(255, 255, 255)"});const s=Math.ceil(10*(this.dataRange.max-this.dataRange.min)/i);for(let o=4;o<=this.dataRange.max;o+=s){const s=.85*t,a=s-5,l=s,h=i-(o-this.dataRange.min)/n;Co.strokeLine(e,a,h,l,h,r),h>8&&Co.fillText(e,o,a-1,h+2,r)}r.textAlign="center",Co.fillText(e,"-log10(pvalue)",t/4,i/2,r,{rotate:{angle:-90}})}async getFeatures(e,t,i){const n=this.pValueField,r=this.visibilityWindow,s=await this.featureSource.getFeatures({chr:e,start:t,end:i,visibilityWindow:r});return s.forEach((function(e){e.value=e[n]})),s}draw(e){const t=e.context,i=e.pixelWidth,n=e.pixelHeight;this.background&&Co.fillRect(t,0,0,i,n,{fillStyle:this.background}),Co.strokeLine(t,0,n-1,i,n-1,{strokeStyle:this.divider});const r=r=>{const s=r?2*this.dotSize:this.dotSize,o=e.bpStart,a=(this.dataRange.max-this.dataRange.min)/n,l=e.referenceFrame.selection;for(let c of e.features){const d=(c.start-o+.5)/e.bpPerPixel;if(d<0)continue;if(d>i)break;const u=c.snp.toUpperCase(),f=c[this.geneField].toUpperCase(),p=l&&(l.snp===u||l.gene===f);if(!r||p){l&&l.snp===u&&l.addGene(f);var h=-Math.log(c[this.pValueField])/Math.LN10;if(h>=this.dataRange.min){let e;h>this.dataRange.max?(h=this.dataRange.max,e=!0):e=!1;const i=Math.max(0+s,n-Math.round((h-this.dataRange.min)/a));let o;c.px=d,c.py=i,c.radius=s,r&&l?(o=l.colorForGene(f),Co.setProperties(t,{fillStyle:o,strokeStyle:"black"})):(o=e?"rgb(150, 150, 150)":"rgb(180, 180, 180)",Co.setProperties(t,{fillStyle:o,strokeStyle:o})),Co.fillCircle(t,d,i,s),Co.strokeCircle(t,d,i,s)}}}};r(!1),r(!0)}popupData(e,t){if(void 0===t&&(t=e.viewport.cachedFeatures),!t||0===t.length)return[];const i=this.name,n=[];for(let r of t)Math.abs(r.px-e.canvasX)0&&n.push("
"),n.push({name:"snp id",value:r.snp},{name:"gene id",value:r.geneId},{name:"gene name",value:r.geneName},{name:"p value",value:r.pValue},{name:"tissue",value:i}));return n}menuItemList(){return $t.numericDataMenuItems(this.trackView)}doAutoscale(e){if(e.length>0){var t=e.map((function(e){return-Math.log(e.value)/Math.LN10}));this.dataRange.max=Ns(t,this.autoscalePercentile)}else{const e=this.config.maxLogP||this.config.max;this.dataRange.max=e||25}return this.dataRange}}const Ku={X:"rgb(204, 153, 0)",Y:"rgb(153, 204, 0)",Un:"darkGray)",1:"rgb(80, 80, 255)",2:"rgb(206, 61, 50)","2a":"rgb(210, 65, 55)","2b":"rgb(215, 70, 60)",3:"rgb(116, 155, 88)",4:"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)"};for(let Kb of Object.keys(Ku)){Ku["chr"+Kb]=Ku[Kb]}for(let Jb=1;Jb<=48;Jb++){if(10===Jb)continue;const ew=Ju(Jb);Ku[ew]=Ku[Jb.toString()]}function Ju(e){if(!+e)return!1;for(var t=String(+e).split(""),i=["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM","","X","XX","XXX","XL","L","LX","LXX","LXXX","XC","","I","II","III","IV","V","VI","VII","VIII","IX"],n="",r=3;r--;)n=(i[+t.pop()+10*r]||"")+n;return Array(+t.join("")+1).join("M")+n}class ef extends Il{constructor(e,t){super(e,t)}init(e){super.init(e),this.useChrColors=void 0===e.useChrColors||e.useChrColors,this.trait=e.trait,this.posteriorProbability=e.posteriorProbability,this.valueProperty="bed"===e.format?"score":"value",this.height=e.height||100,this.autoscale=e.autoscale,this.autoscalePercentile=void 0===e.autoscalePercentile?98:e.autoscalePercentile,this.background=e.background,this.divider=e.divider||"rgb(225,225,225)",this.dotSize=e.dotSize||3,this.popoverWindow=void 0===e.popoverWindow?1e8:e.popoverWindow,this.useChrColors?this.colorScale=new _o(e.colorTable||Ku):e.color?this.colorScale=new Nu(e.color):this.colorScale=new Bu(e.colorScale||{thresholds:[5e-8,5e-4,.5],colors:["rgb(255,50,50)","rgb(251,100,100)","rgb(251,170,170)","rgb(227,238,249)"]}),this.featureSource=uc(e,this.browser.genome)}async postInit(){if("function"!=typeof this.featureSource.getHeader||(this.header=await this.featureSource.getHeader(),!this.disposed))return this.header&&this.setTrackProperties(this.header),this.autoscale||(this.posteriorProbability?this.dataRange={min:void 0===this.config.min?0:this.config.min,max:void 0===this.config.max?1:this.config.max}:this.dataRange={min:void 0===this.config.min?0:this.config.min,max:void 0===this.config.max?25:this.config.max}),this}get supportsWholeGenome(){return!0}async getFeatures(e,t,i){const n=this.visibilityWindow;return this.featureSource.getFeatures({chr:e,start:t,end:i,visibilityWindow:n})}draw(e){const t=e.features,i=e.context,n=e.pixelWidth,r=e.pixelHeight;if(this.background&&Co.fillRect(i,0,0,n,r,{fillStyle:this.background}),Co.strokeLine(i,0,r-1,n,r-1,{strokeStyle:this.divider}),t){const s=e.bpPerPixel,o=e.bpStart,a=o+n*s+1;for(let e of t){const t=e.start;if(ta)break;let n;if(this.posteriorProbability)n=e[this.valueProperty];else{const t=e[this.valueProperty];if(!t)continue;n=-Math.log10(t)}const l=this.useChrColors?e._f?e._f.chr:e.chr:n,h=this.colorScale.getColor(l),c=(this.dataRange.max-this.dataRange.min)/r,d=Math.round((t-o)/s),u=Math.max(this.dotSize,r-Math.round((n-this.dataRange.min)/c));h&&Co.setProperties(i,{fillStyle:h,strokeStyle:"black"}),Co.fillCircle(i,d,u,this.dotSize),e.px=d,e.py=u}}}paintAxis(e,t,i){Co.fillRect(e,0,0,t,i,{fillStyle:"rgb(255, 255, 255)"});var n={font:"normal 10px Arial",textAlign:"right",strokeStyle:"black"};const r=(this.dataRange.max-this.dataRange.min)/i;if(this.posteriorProbability){const t=.1;for(let s=this.dataRange.min;s0&&i.push("
"),5==r){i.push("...");break}if("function"==typeof s.popupData)i=i.concat(s.popupData());else{const e=s.realChr||s.chr,t=(s.realStart||s.start)+1;i.push({name:"chromosome",value:e}),i.push({name:"position",value:t}),i.push({name:"name",value:s.name}),n.posteriorProbability?i.push({name:"posterior probability",value:a}):i.push({name:"pValue",value:a})}r++}}}return i}menuItemList(){return $t.numericDataMenuItems(this.trackView)}doAutoscale(e){if(e.length>0){const t=this.valueProperty,i=this.posteriorProbability,n=e.map((function(e){const n=e[t];return{value:i?n:-Math.log(n)/Math.LN10}}));this.dataRange=Ro(n)}else this.posteriorProbability?this.dataRange={min:this.config.min||0,max:this.config.max||1}:this.dataRange={min:this.config.max||25,max:this.config.min||0};return this.dataRange}}class tf extends Il{constructor(e,t){super(e,t)}init(e){super.init(e),this.autoscale=e.autoscale||void 0===e.max,this.dataRange={min:e.min||0,max:e.max},this.windowFunction=e.windowFunction||"mean",this.paintAxis=ud,this.graphType=e.graphType||"bar",e._featureSource?(this.featureSource=e._featureSource,delete e._featureSource):this.featureSource=uc(this.config,this.browser.genome),this.visibilityWindow=-1,this.featureSource.visibilityWindow=this.visibilityWindow}async postInit(){if("function"==typeof this.featureSource.getHeader){if(this.header=await this.featureSource.getHeader(),this.disposed)return;if(this.sampleNames=this.header.columnNames.slice(3),this.setTrackProperties(this.header),this.header.hasOwnProperty("clickToHighlight")){let e=this.header.clickToHighlight;this.config.clickToHighlight=e,this.config.samplesClickedToHighlight={}}if(this.header.hasOwnProperty("highlight")){this.config.highlightSamples={};let e=this.header.highlight;Array.isArray(e)||(e=[e]);for(let t of e){const e=t.split(";");2===e.length&&(this.config.highlightSamples[e[0]]=e[1])}}}}menuItemList(){return $t.numericDataMenuItems(this.trackView)}async getFeatures(e,t,i){const n=await this.featureSource.getFeatures({chr:e,start:0,end:Number.MAX_SAFE_INTEGER,visibilityWindow:this.visibilityWindow});let r,s;for(let e=1;et&&(r=e-1),void 0===s&&n[e].start>i){s=e+1;break}return void 0===r&&(r=0),void 0===s&&(s=n.length),n.slice(r,s)}draw(e){const{features:t,context:i,bpPerPixel:n,bpStart:r,pixelWidth:s,pixelHeight:o}=e,a=e=>(this.dataRange.max-e)/(this.dataRange.max-this.dataRange.min)*o,l=function(e){let t=Math.floor((e-r)/n);return isNaN(t)&&console.warn("isNaN(x). feature start "+Xt(e)+" bp start "+Xt(r)),t};if(t&&t.length>0&&(void 0===this.dataRange.min&&(this.dataRange.min=0),this.dataRange.max>this.dataRange.min)){const e=this.config.highlightSamples,n=this.config.onlyHandleClicksForHighlightedSamples,r=this.config.clickToHighlight;let s=-1,o={},h=[],c=[];this.clickDetectorCache={};for(let d of t){const t=l(d.start),u=l(d.end),f=s>=0?l(s):t;if(!isNaN(t)&&!isNaN(u)){this.clickDetectorCache[t]=[],this.clickDetectorCache[u]=[];for(let s=0;s=1){const s=o[l],c=a(s),d=e&&e[l];d?h.push([f,c,t,g,d]):r&&l in this.config.samplesClickedToHighlight?h.push([f,c,t,g,this.config.samplesClickedToHighlight[l]]):Co.strokeLine(i,f,c,t,g,{strokeStyle:"#D9D9D9"}),n&&!(l in e)||this.clickDetectorCache[t].push([f,c,t,g,l,d||"gray"])}if(u-t>=1){const s=e&&e[l];s?c.push([t,g,u,g,s]):r&&l in this.config.samplesClickedToHighlight?c.push([t,g,u,g,this.config.samplesClickedToHighlight[l]]):Co.strokeLine(i,t,g,u,g,{strokeStyle:"gray"}),n&&!(l in e)||this.clickDetectorCache[u].push([t,g,u,g,l,s||"gray"])}o[l]=p}s=d.end}}for(let e of h)Co.strokeLine(i,e[0],e[1],e[2],e[3],{strokeStyle:e[4],lineWidth:1.3});for(let e of c)Co.strokeLine(i,e[0],e[1],e[2],e[3],{strokeStyle:e[4],lineWidth:2})}(e=>{if(this.config.hasOwnProperty("guideLines"))for(let t of this.config.guideLines)if(t.hasOwnProperty("color")&&t.hasOwnProperty("y")&&t.hasOwnProperty("dotted")){let i=a(t.y),n={strokeStyle:t.color,strokeWidth:2};t.dotted?Co.dashedLine(e.context,0,i,e.pixelWidth,i,5,n):Co.strokeLine(e.context,0,i,e.pixelWidth,i,n)}})(e)}doAutoscale(e){let t,i;return e.length>0?(t=Number.MAX_VALUE,i=-Number.MAX_VALUE,e.forEach((function(e){t=Math.min(t,...e.values),i=Math.max(i,...e.values)})),t-=.01,i+=.01):(t=0,i=100),{min:t,max:i}}clickedFeatures(e){const t=e.canvasX,i=e.canvasY;let n=null;for(n of Object.keys(this.clickDetectorCache))if(n=parseInt(n),n>=t)break;if(n){let e=Number.MAX_VALUE,o=[];const a=this.clickDetectorCache[n];for(let n of a){const r=n[0],s=n[2];if(ts)return[];const a=n[1],l=n[3];if(iMath.max(a,l)+10)continue;const h=nf(t,i,r,a,s,l);h0?l/h:0}class rf extends Il{constructor(e,t){super(e,t),e.height||(this.height=300),this.arcOrientation=!1,this.theta=Math.PI/2,"bp"===e.format?this.featureSource=new of(e,t.genome):this.featureSource=new zh(e,t.genome)}async getFeatures(e,t,i){const n=this.visibilityWindow;return this.featureSource.getFeatures({chr:e,start:t,end:i,visibilityWindow:n})}draw(e){const t=e.context,i=Math.PI/2,n=e.pixelWidth,r=e.pixelHeight;e.viewportWidth;const s=e.bpPerPixel,o=e.bpStart,a=s,l=this.arcOrientation;Co.fillRect(t,0,e.pixelTop,n,r,{fillStyle:"rgb(255, 255, 255)"});const h=e.features;if(h){sf(h,1);for(let e of h)if(e.startLeft){let n=Math.round((e.startLeft-o)/a),r=Math.round((e.startRight-o)/a),s=Math.round((e.endLeft-o)/a),h=Math.round((e.endRight-o)/a);t.fillStyle=e.color,t.strokeStyle=e.color,t.beginPath();let c=(n+h)/2,d=(h-n)/2,u=this.height,f=Math.PI+(Math.PI/2-i),p=2*Math.PI-(Math.PI/2-i);l?(u=0,t.arc(c,u,d,p,f),t.lineTo(h,u)):(t.arc(c,u,d,f,p),t.lineTo(s,u));const g=(r+s)/2,m=(s-r)/2,b=u;l?(t.arc(g,b,m,f,p,!0),t.lineTo(s,b)):(t.arc(g,b,m,p,f,!0),t.lineTo(n,b)),t.stroke(),t.fill(),e.drawState={x1:c,y1:u,r1:d,x2:g,y2:b,r2:m,sa:f,ea:p}}else{let n=Math.round((e.start-o)/a),r=Math.round((e.end-o)/a);t.strokeStyle=e.color,t.beginPath();let s=(n+r)/2,h=(r-n)/2,c=this.height,d=Math.PI+(Math.PI/2-i),u=2*Math.PI-(Math.PI/2-i);l?(c=0,t.arc(s,c,h,u,d)):t.arc(s,c,h,d,u),t.stroke(),e.drawState={x1:s,y1:c,r1:h,sa:d,ea:u}}}}clickedFeatures(e){const t=super.clickedFeatures(e),i=[];sf(t,-1);for(let n of t){const t=n.drawState,r=e.canvasX-t.x1,s=e.canvasY-t.y1,o=Math.sqrt(r*r+s*s),a=t.r1+3;let l,h;if(void 0===t.x2)l=o,h=t.r1-3;else{const i=e.canvasX-t.x2,n=e.canvasY-t.y2;l=Math.sqrt(i*i+n*n),h=t.r2-3}if(oh){i.push(n);break}}return i}popupData(e,t){if(void 0===t&&(t=this.clickedFeatures(e)),t&&t.length>0)return this.extractPopupData(t[0],this.getGenomeId())}menuItemList(){var e=this;return[{name:"Toggle arc direction",click:function(){e.arcOrientation=!e.arcOrientation,e.trackView.repaintViews()}}]}}function sf(e,t){e.sort((function(e,i){const n=void 0===e.score?-Number.MAX_VALUE:e.score,r=void 0===i.score?-Number.MAX_VALUE:i.score;return(void 0===t?1:t)*(n-r)}))}class of{constructor(e,t){this.config=e,this.genome=t}async getFeatures({chr:e,start:t,end:i,bpPerPixel:n,visibilityWindow:r}){const s=this.genome;if(this.featureCache)return this.featureCache.queryFeatures(e,t,i);{const n=To(this.config),r=await ro.loadString(this.config.url,n);return this.featureCache=new po(function(e){if(!e)return null;const t=ch(e);let i,n=!0;const r=[],s=[],o=[];for(;void 0!==(i=t.nextLine());){const e=i.split("\t");if(n&&i.startsWith("color:")){const t="rgb("+e[1]+","+e[2]+","+e[3]+")";r.push(t),e.length>4&&s.push(e[4])}else{n=!1;const t=e[0],i=Number.parseInt(e[1])-1,l=Number.parseInt(e[2])-1,h=Number.parseInt(e[3]),c=Number.parseInt(e[4]);var a=Number.parseInt(e[5]);const d=r[a];let u;u=i<=c?{chr:t,startLeft:Math.min(i,l),startRight:Math.max(i,l),endLeft:Math.min(h,c),endRight:Math.max(h,c),color:d,score:a}:{chr:t,startLeft:Math.min(h,c),startRight:Math.max(h,c),endLeft:Math.min(i,l),endRight:Math.max(i,l),color:d,score:a},u.start=u.startLeft,u.end=u.endRight,s.length>a&&(u.description=s[a]),o.push(u)}}return o}(r),s),this.featureCache.queryFeatures(e,t,i)}}}class af{constructor(e){this.browser=e,this.type="ideogram",this.height=16,this.order=Number.MIN_SAFE_INTEGER,this.disableButtons=!0,this.ignoreTrackMenu=!0}async getFeatures(e,t,i){return[]}computePixelHeight(e){return this.height}draw({context:e,referenceFrame:t,pixelWidth:i,pixelHeight:n}){const r=t.chr,s=t.genome.getChromosome(r);if(void 0===s||i<=0||n<=0||"all"===r.toLowerCase())return;!function({ctx:e,chr:t,referenceFrame:i,genome:n,width:r,height:s,stainColors:o}){const a=1,l=.5*a,h=0;if(void 0===n)return;Co.fillRect(e,0,0,r,s,{fillStyle:Hs.greyScale(255)});const c=n.getCytobands(t);if(c){const t=h+s/2,i=[],n=[];if(0===c.length)return;const d=r/c[c.length-1].end;e.beginPath(),Co.roundRect(e,l,l+h,r-2*l,s-2*l,(s-2*l)/2,0,1),e.clip();for(let r=0;r0&&h[h.length-1].end&&(l=Math.max(l,h[h.length-1].end),s.bpLength=l),o{hf.getColor(e)}));const cf={};class df extends Il{constructor(e,t){super(e,t)}init(e){super.init(e),this.type=e.type||"junctions",e._featureSource?(this.featureSource=e._featureSource,delete e._featureSource):this.featureSource=e.featureSource?e.featureSource:uc(e,this.browser.genome),this.margin=void 0===e.margin?10:e.margin,this.height||(this.height=100),void 0===e.colorByNumReadsThreshold&&(e.colorByNumReadsThreshold=5)}async postInit(){if("function"!=typeof this.featureSource.getHeader||(this.header=await this.featureSource.getHeader(),!this.disposed))return this.header&&this.setTrackProperties(this.header),void 0===this.visibilityWindow&&"function"==typeof this.featureSource.defaultVisibilityWindow&&(this.visibilityWindow=await this.featureSource.defaultVisibilityWindow()),this}get supportsWholeGenome(){return!1}async getFeatures(e,t,i,n){const r=this.visibilityWindow;return this.featureSource.getFeatures({chr:e,start:t,end:i,bpPerPixel:n,visibilityWindow:r})}computePixelHeight(e){return this.height}draw(e){const t=e.features,i=e.context,n=e.bpPerPixel,r=e.bpStart,s=e.pixelWidth,o=e.pixelHeight,a=r+s*n+1;if(this.config.isMergedTrack||Co.fillRect(i,0,e.pixelTop,s,o,{fillStyle:"rgb(255, 255, 255)"}),t){cf.referenceFrame=e.viewport.referenceFrame,cf.referenceFrameStart=cf.referenceFrame.start,cf.referenceFrameEnd=cf.referenceFrameStart+cf.referenceFrame.toBP(e.viewport.getWidth()),cf.featureZoomOutTracker={};for(let e of t)if(!(e.enda)break;this.renderJunction(e,r,n,o,i)}}else console.log("No feature list")}renderJunction(e,t,i,n,r){e.isVisible=!1;const s=Math.round((e.start-t)/i),o=Math.round((e.end-t)/i),a=(s+o)/2;if(o-s<=3){if(a in cf.featureZoomOutTracker)return;cf.featureZoomOutTracker[a]=!0}if(this.config.hideAnnotatedJunctions&&"true"===e.attributes.annotated_junction)return;if(this.config.hideUnannotatedJunctions&&"false"===e.attributes.annotated_junction)return;if(this.config.hideMotifs&&this.config.hideMotifs.includes(e.attributes.motif))return;if(this.config.hideStrand===e.strand)return;if(this.config.minJunctionEndsVisible){let t=0;if(e.start>=cf.referenceFrameStart&&e.start<=cf.referenceFrameEnd&&(t+=1),e.end>=cf.referenceFrameStart&&e.end<=cf.referenceFrameEnd&&(t+=1),t0&&h/c>this.config.maxFractionMultiMappedReads)return;if(e.attributes.maximum_spliced_alignment_overhang&&parseInt(e.attributes.maximum_spliced_alignment_overhang)this.config.maxSamplesWithThisJunction)return;if(e.attributes.num_samples_total&&(e.attributes.percent_samples_with_this_junction=100*d/Number(e.attributes.num_samples_total),this.config.minPercentSamplesWithThisJunction&&(e.attributes.percent_samples_with_this_junctionthis.config.maxPercentSamplesWithThisJunction)))return}const u=this.margin,f=this.height,p=u+.5*f;let g=u;const m=u+f-10,b=(s+a)/2,w=(a+o)/2;let v,y,_=1;e.attributes.line_width?_=Number(e.attributes.line_width):(void 0===this.config.thicknessBasedOn||"numUniqueReads"===this.config.thicknessBasedOn?_=l:"numReads"===this.config.thicknessBasedOn?_=c:"numSamplesWithThisJunction"===this.config.thicknessBasedOn&&void 0!==d&&(_=d),_=1+Math.log(_+1)/Math.log(12)),void 0===this.config.bounceHeightBasedOn||"random"===this.config.bounceHeightBasedOn?v=(e.start+e.end)%7:"distance"===this.config.bounceHeightBasedOn?v=6*(e.end-e.start)/(cf.referenceFrameEnd-cf.referenceFrameStart):"thickness"===this.config.bounceHeightBasedOn&&(v=2*_),g+=f*Math.max(7-v,0)/10,y=e.attributes.color?e.attributes.color:void 0===this.config.colorBy||"numUniqueReads"===this.config.colorBy?l>this.config.colorByNumReadsThreshold?"blue":"#AAAAAA":"numReads"===this.config.colorBy?c>this.config.colorByNumReadsThreshold?"blue":"#AAAAAA":"isAnnotatedJunction"===this.config.colorBy?"true"===e.attributes.annotated_junction?"#b0b0ec":"orange":"strand"===this.config.colorBy?"+"===e.strand?"#b0b0ec":"#ecb0b0":"motif"===this.config.colorBy?hf.getColor(e.attributes.motif):"#AAAAAA";let x="";e.attributes.label?x=e.attributes.label.replace(/_/g," "):void 0===this.config.labelWith||"uniqueReadCount"===this.config.labelWith?x=l:"totalReadCount"===this.config.labelWith?x=c:"numSamplesWithThisJunction"===this.config.labelWith?void 0!==d&&(x=d):"percentSamplesWithThisJunction"===this.config.labelWith?void 0!==e.attributes.percent_samples_with_this_junction&&(x=e.attributes.percent_samples_with_this_junction.toFixed(0)+"%"):"motif"===this.config.labelWith&&void 0!==e.attributes.motif&&(x+=e.attributes.motif),"uniqueReadCount"===this.config.labelWithInParen?x+=" ("+l+")":"totalReadCount"===this.config.labelWithInParen?x+=" ("+c+")":"multiMappedReadCount"===this.config.labelWithInParen?h>0&&(x+=" (+"+h+")"):"numSamplesWithThisJunction"===this.config.labelWithInParen?void 0!==d&&(x+=" ("+d+")"):"percentSamplesWithThisJunction"===this.config.labelWithInParen?void 0!==e.attributes.percent_samples_with_this_junction&&(x+=" ("+e.attributes.percent_samples_with_this_junction.toFixed(0)+"%)"):"motif"===this.config.labelWithInParen&&void 0!==e.attributes.motif&&(x+=` ${e.attributes.motif}`),e.isVisible=!0,r.beginPath(),r.moveTo(s,m),r.bezierCurveTo(b,g,w,g,o,m),r.lineWidth=_,r.strokeStyle=y,r.stroke();const k=(e,t,i,n)=>{e.beginPath(),e.moveTo(t,i),e.lineTo(t-n/2,i-n),e.lineTo(t+n/2,i-n),e.lineTo(t,i),e.closePath(),e.fill()};if(e.attributes.left_shape||e.attributes.right_shape){r.fillStyle=y;const t=r.lineWidth>2?10:7;e.attributes.left_shape&&k(r,s,m,t),e.attributes.right_shape&&k(r,o,m,t)}r.fillText(x,a-r.measureText(x).width/2,(7*g+p)/8)}clickedFeatures(e){return super.clickedFeatures(e).filter((function(e){return e.isVisible&&e.attributes}))}popupData(e,t){void 0===t&&(t=this.clickedFeatures(e));const i=e.genomicLocation,n=[];for(let e of t){const t="function"==typeof e.popupData?e.popupData(i):this.extractPopupData(e._f||e,this.getGenomeId());t&&(n.length>0&&n.push("

"),Array.prototype.push.apply(n,t))}return n}dispose(){this.trackView=void 0}}class uf{constructor(e){var t;this.config=e,this.url=(t=e.path||e.url).includes("//www.dropbox.com")?t.replace("//www.dropbox.com","//dl.dropboxusercontent.com"):t.startsWith("ftp://ftp.ncbi.nlm.nih.gov")?t.replace("ftp://","https://"):t}async read(e,t){const i=this.config.headers||{};if(void 0!==e&&t){const n="bytes="+e+"-"+(e+t-1);i.Range=n}let n=this.url.slice();if(this.config.oauthToken){const e=async function(e){return"function"==typeof e?await Promise.resolve(e()):e}(this.config.oauthToken);i.Authorization=`Bearer ${e}`}this.config.apiKey&&(n=function(e,t,i){const n=e.includes("?")?"&":"?";return e+n+t+"="+i}(n,"key",this.config.apiKey));const r=await fetch(n,{method:"GET",headers:i,redirect:"follow",mode:"cors"}),s=r.status;if(s>=400){const e=Error(r.statusText);throw e.code=s,e}return r.arrayBuffer()}}class ff{constructor(e){this.file=e.file,this.fetchSize=e.fetchSize||16e3,this.maxSize=e.maxSize||1e6,this.buffers=[]}async read(e,t){let i=this.buffers.filter((i=>i.overlaps(e,e+t)));for(let n of i)if(n.contains(e,e+t))return n.slice(e,e+t);if(0===i.length){let i=Math.max(t,this.fetchSize);this.buffers.sort(((e,t)=>e.start-t.start));const n=function(e,t,i){let n=i-1,r=e.length;for(;1+n>1);t(e[i])?r=i:n=i}return r}(this.buffers,(t=>t.start>e),0);ne.start-t.start));const n=[];let r=e;for(let e of i){if(rr){const e=r,t=s-e,i=await this.file.read(e,t),o=new pf(e,i);n.push(o)}const o=n[0].start,a=function(e){const t=e.reduce(((e,t)=>e+t.byteLength),0),i=new Uint8Array(t);let n=0;for(let t of e)i.set(new Uint8Array(t),n),n+=t.byteLength;return i.buffer}(n.map((e=>e.buffer))),l=new pf(o,a),h=new Set(i);return this.buffers=this.buffers.filter((e=>!h.has(e))),this.addBuffer(l),l.slice(e,e+t)}}addBuffer(e){const t=this.buffers.reduce(((e,t)=>e+t.size),0)+e.size;if(t>this.maxSize){const e=t-this.maxSize;this.buffers.sort(((e,t)=>e.creationTime-t.creationTime));let i,n=0;for(i=0;ie));i++);this.buffers=ithis.buffer.byteLength)throw Error("buffer bounds error");return this.buffer.slice(e-this.start,t-this.start)}get end(){return this.start+this.buffer.byteLength}get size(){return this.buffer.byteLength}contains(e,t){return e>=this.start&&t<=this.end}overlaps(e,t){return e>this.start&&ethis.start&&t=!@]?(i|u|f)(\d*)/);i=parseInt(s||4,10),t="get"+_f[r]+(8*i).toFixed()}return[t,n,i]}var kf=new class{constructor(){this.big_endian=function(){const e=new Uint8Array(4);return!((new Uint32Array(e.buffer)[0]=1)&e[0])}(),this.getters={s:"getUint8",b:"getInt8",B:"getUint8",h:"getInt16",H:"getUint16",i:"getInt32",I:"getUint32",l:"getInt32",L:"getUint32",q:"getInt64",Q:"getUint64",f:"getFloat32",d:"getFloat64"},this.byte_lengths={s:1,b:1,B:1,h:2,H:2,i:4,I:4,l:4,L:4,q:8,Q:8,f:4,d:8};let e=Object.keys(this.byte_lengths).join("");this.fmt_size_regex="(\\d*)(["+e+"])"}calcsize(e){for(var t,i=0,n=new RegExp(this.fmt_size_regex,"g");null!==(t=n.exec(e));){let e=parseInt(t[1]||1,10),n=t[2];i+=e*this.byte_lengths[n]}return i}_is_big_endian(e){return!/^)/.test(e)||this.big_endian)}async unpack_from_async(e,t,i){i=Number(i||0);const n=this.calcsize(e),r=await t.slice(i,i+n);let s=0;for(var o,a=new Cf(r),l=[],h=this._is_big_endian(e),c=new RegExp(this.fmt_size_regex,"g");null!==(o=c.exec(e));){let e=parseInt(o[1]||1,10),t=o[2],i=this.getters[t],n=this.byte_lengths[t];if("s"==t)l.push((new TextDecoder).decode(r.slice(s,s+e))),s+=e;else for(var d=0;de+(t<<8*i)),0);return o}var Ef=class{constructor(e,t){this.buf=e,this.offset=t,this.dtype=this.determine_dtype()}async determine_dtype(){let e=await mf(Mf,this.buf,this.offset);this.offset+=Tf;let t=15&e.get("class_and_version");if(t==Rf)return this._determine_dtype_fixed_point(e);if(t==Lf)return this._determine_dtype_floating_point(e);if(t==If)throw"Time datatype class not supported.";if(t==Bf)return this._determine_dtype_string(e);if(t==Ff)throw"Bitfield datatype class not supported.";if(t==Nf)return{datatype_class:Nf,size:e.get("size")};if(t==Of)return this._determine_dtype_compound(e);if(t==Pf)return["REFERENCE",e.get("size")];if(t==Df)return this.determine_dtype();if(t==Hf)throw"Array datatype class not supported.";if(t==zf){let t=this._determine_dtype_vlen(e);if("VLEN_SEQUENCE"==t[0]){t=["VLEN_SEQUENCE",this.determine_dtype()]}return t}throw"Invalid datatype class "+t}_determine_dtype_fixed_point(e){let t=e.get("size");if(![1,2,4,8].includes(t))throw"Unsupported datatype size";var i;var n;return i=(8&e.get("class_bit_field_0"))>0?"i":"u",n=0==(1&e.get("class_bit_field_0"))?"<":">",this.offset+=4,n+i+t.toFixed()}_determine_dtype_floating_point(e){let t=e.get("size");if(![1,2,4,8].includes(t))throw"Unsupported datatype size";var i;return i=0==(1&e.get("class_bit_field_0"))?"<":">",this.offset+=12,i+"f"+t.toFixed()}_determine_dtype_string(e){return"S"+e.get("size").toFixed()}_determine_dtype_vlen(e){return 1!=(1&e.get("class_bit_field_0"))?["VLEN_SEQUENCE",0,0]:["VLEN_STRING",e.get("class_bit_field_0")>>4,1&e.get("class_bit_field_1")]}_determine_dtype_compound(e){throw"Compound type not yet implemented!"}},Mf=new Map([["class_and_version","B"],["class_bit_field_0","B"],["class_bit_field_1","B"],["class_bit_field_2","B"],["size","I"]]),Tf=vf(Mf);vf(new Map([["offset","I"],["dimensionality","B"],["reserved_0","B"],["reserved_1","B"],["reserved_2","B"],["permutation","I"],["reserved_3","I"],["dim_size_1","I"],["dim_size_2","I"],["dim_size_3","I"],["dim_size_4","I"]]));var Rf=0,Lf=1,If=2,Bf=3,Ff=4,Nf=5,Of=6,Pf=7,Df=8,zf=9,Hf=10;function Vf(e){let t=e.length;for(;--t>=0;)e[t]=0}Vf(new Array(576)),Vf(new Array(60)),Vf(new Array(512)),Vf(new Array(256)),Vf(new Array(29)),Vf(new Array(30));var Uf=(e,t,i,n)=>{let r=65535&e|0,s=e>>>16&65535|0,o=0;for(;0!==i;){o=i>2e3?2e3:i,i-=o;do{r=r+t[n++]|0,s=s+r|0}while(--o);r%=65521,s%=65521}return r|s<<16|0},qf=new Uint32Array((()=>{let e,t=[];for(var i=0;i<256;i++){e=i;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[i]=e}return t})()),jf=(e,t,i,n)=>{const r=qf,s=n+i;e^=-1;for(let i=n;i>>8^r[255&(e^t[i])];return-1^e},$f={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"},Wf={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_MEM_ERROR:-4,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},Gf=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Zf={assign:function(e){const t=Array.prototype.slice.call(arguments,1);for(;t.length;){const i=t.shift();if(i){if("object"!=typeof i)throw new TypeError(i+"must be non-object");for(const t in i)Gf(i,t)&&(e[t]=i[t])}}return e},flattenChunks:e=>{let t=0;for(let i=0,n=e.length;i=252?6:iw>=248?5:iw>=240?4:iw>=224?3:iw>=192?2:1;Xf[254]=Xf[254]=1;var Yf={string2buf:e=>{if("function"==typeof TextEncoder&&TextEncoder.prototype.encode)return(new TextEncoder).encode(e);let t,i,n,r,s,o=e.length,a=0;for(r=0;r>>6,t[s++]=128|63&i):i<65536?(t[s++]=224|i>>>12,t[s++]=128|i>>>6&63,t[s++]=128|63&i):(t[s++]=240|i>>>18,t[s++]=128|i>>>12&63,t[s++]=128|i>>>6&63,t[s++]=128|63&i);return t},buf2string:(e,t)=>{const i=t||e.length;if("function"==typeof TextDecoder&&TextDecoder.prototype.decode)return(new TextDecoder).decode(e.subarray(0,t));let n,r;const s=new Array(2*i);for(r=0,n=0;n4)s[r++]=65533,n+=o-1;else{for(t&=2===o?31:3===o?15:7;o>1&&n1?s[r++]=65533:t<65536?s[r++]=t:(t-=65536,s[r++]=55296|t>>10&1023,s[r++]=56320|1023&t)}}return((e,t)=>{if(t<65534&&e.subarray&&Qf)return String.fromCharCode.apply(null,e.length===t?e:e.subarray(0,t));let i="";for(let n=0;n{(t=t||e.length)>e.length&&(t=e.length);let i=t-1;for(;i>=0&&128==(192&e[i]);)i--;return i<0||0===i?t:i+Xf[e[i]]>t?i:t}};var Kf=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},Jf=function(e,t){let i,n,r,s,o,a,l,h,c,d,u,f,p,g,m,b,w,v,y,_,x,k,C,S;const A=e.state;i=e.next_in,C=e.input,n=i+(e.avail_in-5),r=e.next_out,S=e.output,s=r-(t-e.avail_out),o=r+(e.avail_out-257),a=A.dmax,l=A.wsize,h=A.whave,c=A.wnext,d=A.window,u=A.hold,f=A.bits,p=A.lencode,g=A.distcode,m=(1<>>24,u>>>=v,f-=v,v=w>>>16&255,0===v)S[r++]=65535&w;else{if(!(16&v)){if(0==(64&v)){w=p[(65535&w)+(u&(1<>>=v,f-=v),f<15&&(u+=C[i++]<>>24,u>>>=v,f-=v,v=w>>>16&255,!(16&v)){if(0==(64&v)){w=g[(65535&w)+(u&(1<a){e.msg="invalid distance too far back",A.mode=30;break e}if(u>>>=v,f-=v,v=r-s,_>v){if(v=_-v,v>h&&A.sane){e.msg="invalid distance too far back",A.mode=30;break e}if(x=0,k=d,0===c){if(x+=l-v,v2;)S[r++]=k[x++],S[r++]=k[x++],S[r++]=k[x++],y-=3;y&&(S[r++]=k[x++],y>1&&(S[r++]=k[x++]))}else{x=r-_;do{S[r++]=S[x++],S[r++]=S[x++],S[r++]=S[x++],y-=3}while(y>2);y&&(S[r++]=S[x++],y>1&&(S[r++]=S[x++]))}break}}break}}while(i>3,i-=y,f-=y<<3,u&=(1<{const l=a.bits;let h,c,d,u,f,p,g=0,m=0,b=0,w=0,v=0,y=0,_=0,x=0,k=0,C=0,S=null,A=0;const E=new Uint16Array(16),M=new Uint16Array(16);let T,R,L,I=null,B=0;for(g=0;g<=ep;g++)E[g]=0;for(m=0;m=1&&0===E[w];w--);if(v>w&&(v=w),0===w)return r[s++]=20971520,r[s++]=20971520,a.bits=1,0;for(b=1;b0&&(0===e||1!==w))return-1;for(M[1]=0,g=1;g852||2===e&&k>592)return 1;for(;;){T=g-_,o[m]p?(R=I[B+o[m]],L=S[A+o[m]]):(R=96,L=0),h=1<>_)+c]=T<<24|R<<16|L|0}while(0!==c);for(h=1<>=1;if(0!==h?(C&=h-1,C+=h):C=0,m++,0==--E[g]){if(g===w)break;g=t[i+o[m]]}if(g>v&&(C&u)!==d){for(0===_&&(_=v),f+=b,y=g-_,x=1<852||2===e&&k>592)return 1;d=C&u,r[d]=v<<24|y<<16|f-s|0}}return 0!==C&&(r[f+C]=g-_<<24|64<<16|0),a.bits=v,0},{Z_FINISH:op,Z_BLOCK:ap,Z_TREES:lp,Z_OK:hp,Z_STREAM_END:cp,Z_NEED_DICT:dp,Z_STREAM_ERROR:up,Z_DATA_ERROR:fp,Z_MEM_ERROR:pp,Z_BUF_ERROR:gp,Z_DEFLATED:mp}=Wf,bp=12,wp=30,vp=e=>(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24);function yp(){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 Uint16Array(320),this.work=new Uint16Array(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}var _p,xp,kp=e=>{if(!e||!e.state)return up;const t=e.state;return e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=1,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new Int32Array(852),t.distcode=t.distdyn=new Int32Array(592),t.sane=1,t.back=-1,hp},Cp=e=>{if(!e||!e.state)return up;const t=e.state;return t.wsize=0,t.whave=0,t.wnext=0,kp(e)},Sp=(e,t)=>{let i;if(!e||!e.state)return up;const n=e.state;return t<0?(i=0,t=-t):(i=1+(t>>4),t<48&&(t&=15)),t&&(t<8||t>15)?up:(null!==n.window&&n.wbits!==t&&(n.window=null),n.wrap=i,n.wbits=t,Cp(e))},Ap=(e,t)=>{if(!e)return up;const i=new yp;e.state=i,i.window=null;const n=Sp(e,t);return n!==hp&&(e.state=null),n},Ep=!0,Mp=e=>{if(Ep){_p=new Int32Array(512),xp=new Int32Array(32);let t=0;for(;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(sp(1,e.lens,0,288,_p,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;sp(2,e.lens,0,32,xp,0,e.work,{bits:5}),Ep=!1}e.lencode=_p,e.lenbits=9,e.distcode=xp,e.distbits=5},Tp=(e,t,i,n)=>{let r;const s=e.state;return null===s.window&&(s.wsize=1<=s.wsize?(s.window.set(t.subarray(i-s.wsize,i),0),s.wnext=0,s.whave=s.wsize):(r=s.wsize-s.wnext,r>n&&(r=n),s.window.set(t.subarray(i-n,i-n+r),s.wnext),(n-=r)?(s.window.set(t.subarray(i-n,i),0),s.wnext=n,s.whave=s.wsize):(s.wnext+=r,s.wnext===s.wsize&&(s.wnext=0),s.whave{let i,n,r,s,o,a,l,h,c,d,u,f,p,g,m,b,w,v,y,_,x,k,C=0;const S=new Uint8Array(4);let A,E;const M=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);if(!e||!e.state||!e.output||!e.input&&0!==e.avail_in)return up;i=e.state,i.mode===bp&&(i.mode=13),o=e.next_out,r=e.output,l=e.avail_out,s=e.next_in,n=e.input,a=e.avail_in,h=i.hold,c=i.bits,d=a,u=l,k=hp;e:for(;;)switch(i.mode){case 1:if(0===i.wrap){i.mode=13;break}for(;c<16;){if(0===a)break e;a--,h+=n[s++]<>>8&255,i.check=jf(i.check,S,2,0),h=0,c=0,i.mode=2;break}if(i.flags=0,i.head&&(i.head.done=!1),!(1&i.wrap)||(((255&h)<<8)+(h>>8))%31){e.msg="incorrect header check",i.mode=wp;break}if((15&h)!==mp){e.msg="unknown compression method",i.mode=wp;break}if(h>>>=4,c-=4,x=8+(15&h),0===i.wbits)i.wbits=x;else if(x>i.wbits){e.msg="invalid window size",i.mode=wp;break}i.dmax=1<>8&1),512&i.flags&&(S[0]=255&h,S[1]=h>>>8&255,i.check=jf(i.check,S,2,0)),h=0,c=0,i.mode=3;case 3:for(;c<32;){if(0===a)break e;a--,h+=n[s++]<>>8&255,S[2]=h>>>16&255,S[3]=h>>>24&255,i.check=jf(i.check,S,4,0)),h=0,c=0,i.mode=4;case 4:for(;c<16;){if(0===a)break e;a--,h+=n[s++]<>8),512&i.flags&&(S[0]=255&h,S[1]=h>>>8&255,i.check=jf(i.check,S,2,0)),h=0,c=0,i.mode=5;case 5:if(1024&i.flags){for(;c<16;){if(0===a)break e;a--,h+=n[s++]<>>8&255,i.check=jf(i.check,S,2,0)),h=0,c=0}else i.head&&(i.head.extra=null);i.mode=6;case 6:if(1024&i.flags&&(f=i.length,f>a&&(f=a),f&&(i.head&&(x=i.head.extra_len-i.length,i.head.extra||(i.head.extra=new Uint8Array(i.head.extra_len)),i.head.extra.set(n.subarray(s,s+f),x)),512&i.flags&&(i.check=jf(i.check,n,f,s)),a-=f,s+=f,i.length-=f),i.length))break e;i.length=0,i.mode=7;case 7:if(2048&i.flags){if(0===a)break e;f=0;do{x=n[s+f++],i.head&&x&&i.length<65536&&(i.head.name+=String.fromCharCode(x))}while(x&&f>9&1,i.head.done=!0),e.adler=i.check=0,i.mode=bp;break;case 10:for(;c<32;){if(0===a)break e;a--,h+=n[s++]<>>=7&c,c-=7&c,i.mode=27;break}for(;c<3;){if(0===a)break e;a--,h+=n[s++]<>>=1,c-=1,3&h){case 0:i.mode=14;break;case 1:if(Mp(i),i.mode=20,t===lp){h>>>=2,c-=2;break e}break;case 2:i.mode=17;break;case 3:e.msg="invalid block type",i.mode=wp}h>>>=2,c-=2;break;case 14:for(h>>>=7&c,c-=7&c;c<32;){if(0===a)break e;a--,h+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",i.mode=wp;break}if(i.length=65535&h,h=0,c=0,i.mode=15,t===lp)break e;case 15:i.mode=16;case 16:if(f=i.length,f){if(f>a&&(f=a),f>l&&(f=l),0===f)break e;r.set(n.subarray(s,s+f),o),a-=f,s+=f,l-=f,o+=f,i.length-=f;break}i.mode=bp;break;case 17:for(;c<14;){if(0===a)break e;a--,h+=n[s++]<>>=5,c-=5,i.ndist=1+(31&h),h>>>=5,c-=5,i.ncode=4+(15&h),h>>>=4,c-=4,i.nlen>286||i.ndist>30){e.msg="too many length or distance symbols",i.mode=wp;break}i.have=0,i.mode=18;case 18:for(;i.have>>=3,c-=3}for(;i.have<19;)i.lens[M[i.have++]]=0;if(i.lencode=i.lendyn,i.lenbits=7,A={bits:i.lenbits},k=sp(0,i.lens,0,19,i.lencode,0,i.work,A),i.lenbits=A.bits,k){e.msg="invalid code lengths set",i.mode=wp;break}i.have=0,i.mode=19;case 19:for(;i.have>>24,b=C>>>16&255,w=65535&C,!(m<=c);){if(0===a)break e;a--,h+=n[s++]<>>=m,c-=m,i.lens[i.have++]=w;else{if(16===w){for(E=m+2;c>>=m,c-=m,0===i.have){e.msg="invalid bit length repeat",i.mode=wp;break}x=i.lens[i.have-1],f=3+(3&h),h>>>=2,c-=2}else if(17===w){for(E=m+3;c>>=m,c-=m,x=0,f=3+(7&h),h>>>=3,c-=3}else{for(E=m+7;c>>=m,c-=m,x=0,f=11+(127&h),h>>>=7,c-=7}if(i.have+f>i.nlen+i.ndist){e.msg="invalid bit length repeat",i.mode=wp;break}for(;f--;)i.lens[i.have++]=x}}if(i.mode===wp)break;if(0===i.lens[256]){e.msg="invalid code -- missing end-of-block",i.mode=wp;break}if(i.lenbits=9,A={bits:i.lenbits},k=sp(1,i.lens,0,i.nlen,i.lencode,0,i.work,A),i.lenbits=A.bits,k){e.msg="invalid literal/lengths set",i.mode=wp;break}if(i.distbits=6,i.distcode=i.distdyn,A={bits:i.distbits},k=sp(2,i.lens,i.nlen,i.ndist,i.distcode,0,i.work,A),i.distbits=A.bits,k){e.msg="invalid distances set",i.mode=wp;break}if(i.mode=20,t===lp)break e;case 20:i.mode=21;case 21:if(a>=6&&l>=258){e.next_out=o,e.avail_out=l,e.next_in=s,e.avail_in=a,i.hold=h,i.bits=c,Jf(e,u),o=e.next_out,r=e.output,l=e.avail_out,s=e.next_in,n=e.input,a=e.avail_in,h=i.hold,c=i.bits,i.mode===bp&&(i.back=-1);break}for(i.back=0;C=i.lencode[h&(1<>>24,b=C>>>16&255,w=65535&C,!(m<=c);){if(0===a)break e;a--,h+=n[s++]<>v)],m=C>>>24,b=C>>>16&255,w=65535&C,!(v+m<=c);){if(0===a)break e;a--,h+=n[s++]<>>=v,c-=v,i.back+=v}if(h>>>=m,c-=m,i.back+=m,i.length=w,0===b){i.mode=26;break}if(32&b){i.back=-1,i.mode=bp;break}if(64&b){e.msg="invalid literal/length code",i.mode=wp;break}i.extra=15&b,i.mode=22;case 22:if(i.extra){for(E=i.extra;c>>=i.extra,c-=i.extra,i.back+=i.extra}i.was=i.length,i.mode=23;case 23:for(;C=i.distcode[h&(1<>>24,b=C>>>16&255,w=65535&C,!(m<=c);){if(0===a)break e;a--,h+=n[s++]<>v)],m=C>>>24,b=C>>>16&255,w=65535&C,!(v+m<=c);){if(0===a)break e;a--,h+=n[s++]<>>=v,c-=v,i.back+=v}if(h>>>=m,c-=m,i.back+=m,64&b){e.msg="invalid distance code",i.mode=wp;break}i.offset=w,i.extra=15&b,i.mode=24;case 24:if(i.extra){for(E=i.extra;c>>=i.extra,c-=i.extra,i.back+=i.extra}if(i.offset>i.dmax){e.msg="invalid distance too far back",i.mode=wp;break}i.mode=25;case 25:if(0===l)break e;if(f=u-l,i.offset>f){if(f=i.offset-f,f>i.whave&&i.sane){e.msg="invalid distance too far back",i.mode=wp;break}f>i.wnext?(f-=i.wnext,p=i.wsize-f):p=i.wnext-f,f>i.length&&(f=i.length),g=i.window}else g=r,p=o-i.offset,f=i.length;f>l&&(f=l),l-=f,i.length-=f;do{r[o++]=g[p++]}while(--f);0===i.length&&(i.mode=21);break;case 26:if(0===l)break e;r[o++]=i.length,l--,i.mode=21;break;case 27:if(i.wrap){for(;c<32;){if(0===a)break e;a--,h|=n[s++]<Ap(e,15),inflateInit2:Ap,inflate:Rp,inflateEnd:e=>{if(!e||!e.state)return up;let t=e.state;return t.window&&(t.window=null),e.state=null,hp},inflateGetHeader:(e,t)=>{if(!e||!e.state)return up;const i=e.state;return 0==(2&i.wrap)?up:(i.head=t,t.done=!1,hp)},inflateSetDictionary:(e,t)=>{const i=t.length;let n,r,s;return e&&e.state?(n=e.state,0!==n.wrap&&11!==n.mode?up:11===n.mode&&(r=1,r=Uf(r,t,i,0),r!==n.check)?fp:(s=Tp(e,t,i,i),s?(n.mode=31,pp):(n.havedict=1,hp))):up},inflateInfo:"pako inflate (from Nodeca project)"};var Ip=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1},Bp=Object.prototype.toString,{Z_NO_FLUSH:Fp,Z_FINISH:Np,Z_OK:Op,Z_STREAM_END:Pp,Z_NEED_DICT:Dp,Z_STREAM_ERROR:zp,Z_DATA_ERROR:Hp,Z_MEM_ERROR:Vp}=Wf;function Up(e){this.options=Zf.assign({chunkSize:65536,windowBits:15,to:""},e||{});const t=this.options;t.raw&&t.windowBits>=0&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(t.windowBits>=0&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),t.windowBits>15&&t.windowBits<48&&0==(15&t.windowBits)&&(t.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new Kf,this.strm.avail_out=0;let i=Lp.inflateInit2(this.strm,t.windowBits);if(i!==Op)throw new Error($f[i]);if(this.header=new Ip,Lp.inflateGetHeader(this.strm,this.header),t.dictionary&&("string"==typeof t.dictionary?t.dictionary=Yf.string2buf(t.dictionary):"[object ArrayBuffer]"===Bp.call(t.dictionary)&&(t.dictionary=new Uint8Array(t.dictionary)),t.raw&&(i=Lp.inflateSetDictionary(this.strm,t.dictionary),i!==Op)))throw new Error($f[i])}function qp(e,t){const i=new Up(t);if(i.push(e),i.err)throw i.msg||$f[i.err];return i.result}Up.prototype.push=function(e,t){const i=this.strm,n=this.options.chunkSize,r=this.options.dictionary;let s,o,a;if(this.ended)return!1;for(o=t===~~t?t:!0===t?Np:Fp,"[object ArrayBuffer]"===Bp.call(e)?i.input=new Uint8Array(e):i.input=e,i.next_in=0,i.avail_in=i.input.length;;){for(0===i.avail_out&&(i.output=new Uint8Array(n),i.next_out=0,i.avail_out=n),s=Lp.inflate(i,o),s===Dp&&r&&(s=Lp.inflateSetDictionary(i,r),s===Op?s=Lp.inflate(i,o):s===Hp&&(s=Dp));i.avail_in>0&&s===Pp&&i.state.wrap>0&&0!==e[i.next_in];)Lp.inflateReset(i),s=Lp.inflate(i,o);switch(s){case zp:case Hp:case Dp:case Vp:return this.onEnd(s),this.ended=!0,!1}if(a=i.avail_out,i.next_out&&(0===i.avail_out||s===Pp))if("string"===this.options.to){let e=Yf.utf8border(i.output,i.next_out),t=i.next_out-e,r=Yf.buf2string(i.output,e);i.next_out=t,i.avail_out=n-t,t&&i.output.set(i.output.subarray(e,e+t),0),this.onData(r)}else this.onData(i.output.length===i.next_out?i.output:i.output.subarray(0,i.next_out));if(s!==Op||0!==a){if(s===Pp)return s=Lp.inflateEnd(this.strm),this.onEnd(s),this.ended=!0,!0;if(0===i.avail_in)break}}return!0},Up.prototype.onData=function(e){this.chunks.push(e)},Up.prototype.onEnd=function(e){e===Op&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=Zf.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg};var jp=function(e,t){return(t=t||{}).raw=!0,qp(e,t)},$p={Inflate:Up,inflate:qp,inflateRaw:jp,ungzip:qp,constants:Wf},{Inflate:Wp,inflate:Gp,inflateRaw:Zp,ungzip:Qp}=$p,Xp=Gp,Yp=Qp;var Kp=new Map([[1,function(e,t){let i=new Uint8Array(e);return Xp(i).buffer}],[2,function(e,t){let i=e.byteLength,n=new Uint8Array(i),r=Math.floor(i/t),s=new DataView(e);for(var o=0;oHH",e,i);if(l%=65535,r!=(a%=65535)||s!=l)throw'ValueError("fletcher32 checksum invalid")'}(e),e.slice(0,-4)}]]),Jp=class{constructor(e,t){this.fh=e,this.offset=t,this.depth=null}async init(){this.all_nodes=new Map,await this._read_root_node(),await this._read_children()}async _read_children(){let e=this.depth;for(;e>0;){for(var t of this.all_nodes.get(e))for(var i of t.get("addresses"))this._add_node(await this._read_node(i,e-1));e--}}async _read_root_node(){let e=await this._read_node(this.offset,null);this._add_node(e),this.depth=e.get("node_level")}_add_node(e){let t=e.get("node_level");this.all_nodes.has(t)?this.all_nodes.get(t).push(e):this.all_nodes.set(t,[e])}async _read_node(e,t){let i=await this._read_node_header(e,t);return i.set("keys",[]),i.set("addresses",[]),i}async _read_node_header(e){throw"NotImplementedError: must define _read_node_header in implementation class"}},eg=class extends Jp{B_LINK_NODE=new Map([["signature","4s"],["node_type","B"],["node_level","B"],["entries_used","H"],["left_sibling","Q"],["right_sibling","Q"]]);async _read_node_header(e,t){let i=await mf(this.B_LINK_NODE,this.fh,e);if(null!=t&&i.get("node_level")!=t)throw"node level does not match";return i}},tg=class extends eg{NODE_TYPE=0;constructor(e,t){super(e,t),this.ready=this.init()}async _read_node(e,t){let i=await this._read_node_header(e,t);e+=vf(this.B_LINK_NODE);let n=[],r=[],s=i.get("entries_used");for(var o=0;o=0&&v[x]>=e[x];x--)v[x]=0,w[x]=b[x],x>0&&(v[x-1]+=1,w[x-1]+=1);if(w.slice(0,-1).every((function(e,i){return e=0;o--){if(t&1<0?r*=n:r=n}}return s}_nrecords_max(e,t,i){return Math.floor((e-10-i)/(t+i))}_required_bytes(e){return Math.ceil(Sf(e)/8)}_int_format(e){return["0&&(c=(await kf.unpack_from_async(a,this.fh,i))[0],i+=r),l.push([n,h,c])}}return s.set("keys",a),s.set("addresses",l),s}async _read_node_header(e,t){let i=await mf(this.B_LINK_NODE,this.fh,e);return i.set("node_level",t),i}*iter_records(){for(let e of this.all_nodes.values())for(let t of e)for(let e of t.get("keys"))yield e}_parse_record(e){throw"NotImplementedError"}},rg=class extends ng{NODE_TYPE=5;async _parse_record(e,t,i){let n=(await kf.unpack_from_async("this._chunks))}get shape(){let e=this.find_msg_type(Xg)[0].get("offset_to_message");return async function(e,t){let i=(await kf.unpack_from_async("255&&(s=(await kf.unpack_from_async("0;e.set("optional",a);let l,h=(await kf.unpack_from_async("0&&(l=(await kf.unpack_from_async(`${s}s`,i,t))[0],t+=s),e.set("name",l);let c=await kf.unpack_from_async(`<${h}i`,i,t);t+=4*h,e.set("client_data_values",c),r.push(e)}}return this._filter_pipeline=r,this._filter_pipeline}find_msg_type(e){return this.msgs.filter((function(t){return t.get("type")==e}))}async get_attributes(){let e={},t=this.find_msg_type(nm);for(let i of t){let t=i.get("offset_to_message"),[n,r]=await this.unpack_attribute(t);e[n]=r}return e}async get_fillvalue(){var e,t=this.find_msg_type(Jg)[0].get("offset_to_message");let i=(await kf.unpack_from_async("=r&&([s,r]=a[++l],h=0);let t=await mf(Pg,e,s+h),i=s+h+Dg;if(t.set("offset_to_message",i),t.get("type")==rm){var[u,f]=await kf.unpack_from_async("=o-Hg){let e=l[++h];if(null==e)break;[r,o]=e,c=0}let t=await mf(zg,e,r+c),i=r+c+Hg+n;if(t.set("offset_to_message",i),t.get("type")==rm){var[d,u]=await kf.unpack_from_async("0,l=(4&n)>0;(8&n)>0?(r=(await kf.unpack_from_async("0)throw"Filter info size not supported on FractalHeap";if(t.get("btree_address_huge_objects")!=hg)throw"Huge objects not implemented in FractalHeap";t.set("btree_address_huge_objects",null),t.get("root_block_address")==hg&&t.set("root_block_address",null);let i=t.get("log2_maximum_heap_size"),n=this._min_size_nbits(i),r=new Map([["signature","4s"],["version","B"],["heap_header_adddress","Q"],["block_offset",`${n}B`]]);this.indirect_block_header=new Map(r),this.indirect_block_header_size=vf(r),2==(2&t.get("flags"))&&r.set("checksum","I"),this.direct_block_header=r,this.direct_block_header_size=vf(r);let s=t.get("maximum_direct_block_size");this._managed_object_offset_size=this._min_size_nbits(i);let o=Math.min(s,t.get("max_managed_object_size"));this._managed_object_length_size=this._min_size_integer(o);let a=t.get("starting_block_size"),l=t.get("table_width");if(!(a>0))throw"Starting block size == 0 not implemented";let h=Number(Math.floor(Math.log2(s)));wf(1n<0)for await(let e of this._iter_indirect_block(this.fh,f,p))u.push(e);else{let e=await this._read_direct_block(this.fh,f,a);u.push(e)}let g=u.reduce(((e,t)=>e+t.byteLength),0),m=new Uint8Array(g),b=0;u.forEach((e=>{m.set(new Uint8Array(e),b),b+=e.byteLength})),this.managed=m.buffer}async _read_direct_block(e,t,i){let n=await e.slice(t,t+i);return wf("FHDB"==bf(this.direct_block_header,n).get("signature")),n}get_data(e){let t=kf.unpack_from(">4&3,n=1;if(0==i){wf(0==t>>6);let i=this._managed_object_offset_size,r=Af(i,e,n);n+=i,i=this._managed_object_length_size;let s=Af(i,e,n);return this.managed.slice(r,r+s)}throw 1==i?"tiny objectID not supported in FractalHeap":2==i?"huge objectID not supported in FractalHeap":"unknown objectID type in FractalHeap"}_min_size_integer(e){return this._min_size_nbits(Sf(e))}_min_size_nbits(e){return Math.ceil(e/8)}async*_iter_indirect_block(e,t,i){let n=await mf(this.indirect_block_header,e,t);t+=this.indirect_block_header_size,wf("FHIB"==n.get("signature"));let r=n.get("block_offset").reduce(((e,t,i)=>e+(t<<8*i)),0);n.set("block_offset",r);let[s,o]=this._indirect_info(i),a=[];for(let i=0;i0&&(t+=8);let r=(2&n)>0?qg:Ug,s=await mf(r,e,t),o=new Map;for(let[e,t]of s.entries())o.set(e,t==Sg?null:t);return o}get is_dataset(){return this.find_msg_type(Xg).length>0}async get_data(){let e=this.find_msg_type(tm)[0].get("offset_to_message");var[t,i,n,r]=await this._get_data_message_properties(e);if(0==n)throw"Compact storage of DataObject not implemented";return 1==n?this._get_contiguous_data(r):2==n?this._get_chunked_data(e):void 0}async _get_data_message_properties(e){let t,i,n,[r,s,o]=await kf.unpack_from_async("=1&&r<=4),[r,t,i,n]}async _get_contiguous_data(e){let[t]=await kf.unpack_from_async("=!@\|]?(i|u|f|S)(\d*)/.test(n)){let[e,i,s]=xf(n),a=new Array(r);const l=await this.fh.slice(t,t+s*r);let h=new Cf(l);for(var o=0;o=1&&t<=3);var a="<"+(i-1).toFixed()+"I",l=await kf.unpack_from_async(a,this.fh,s);this._chunks=l,this._chunk_dims=i,this._chunk_address=o}}}};var Sg=kf.unpack_from("this.get(e)))}length(){return this.keys.length}_dereference(e){if(!e)throw"cannot deference null reference";let t=this.file._get_object_by_address(e);if(null==t)throw"reference not found in file";return t}async get(e){if("number"==typeof e)return this._dereference(e);var t=hm(e);if("/"==t)return this.file;if("."==t)return this;if(/^\//.test(t))return this.file.get(t.slice(1));if(""!=function(e){let t="/",i=e.lastIndexOf(t)+1,n=e.slice(0,i),r=new RegExp("^"+t+"+$"),s=new RegExp(t+"$");n&&!r.test(n)&&(n=n.replace(s,""));return n}(t))var[i,n]=t.split(/\/(.*)/);else var i=t,n=".";if(!(i in this._links))throw i+" not found in group";var r=hm(this.name+"/"+i);let s=this._links[i];if("string"==typeof s)try{return this.get(s)}catch(e){return null}var o=new Cg(this.file._fh,s);if(await o.ready,o.is_dataset){if("."!=n)throw r+" is a dataset, not a group";return new lm(r,o,this)}var a=new om(r,this);return await a.init(o),a.get(n)}visit(e){return this.visititems(((t,i)=>e(t)))}visititems(e){var t=this.name.length;/\/$/.test(this.name)||(t+=1);for(var i=this.values.slice();i;){let n=i.shift();1==i.length&&console.log(n);let r=e(n.name.slice(t),n);if(null!=r)return r;n instanceof om&&(i=i.concat(n.values))}return null}get attrs(){return null==this._attrs&&(this._attrs=this._dataobjects.get_attributes()),this._attrs}},am=class extends om{constructor(e,t,i){super("/",null),this.ready=this.init(e,t,i)}async init(e,t,i){var n=new class{constructor(e,t){this.ready=this.init(e,t)}async init(e,t){let i=await kf.unpack_from_async("{e._dataobjects.offset}))}},lm=class extends Array{constructor(e,t,i){super(),this.parent=i,this.file=i.file,this.name=e,this._dataobjects=t,this._attrs=null,this._astype=null}get value(){var e=this._dataobjects.get_data();return null==this._astype?this.getValue(e):e.astype(this._astype)}get shape(){return this._dataobjects.shape}get attrs(){return this._dataobjects.get_attributes()}get dtype(){return this._dataobjects.dtype}get fillvalue(){return this._dataobjects.get_fillvalue()}async to_array(){return function(e,t){const i=e.length,n=t.reduce(((e,t)=>e*t),1);i!==n&&console.warn(`shape product: ${n} does not match length of flattened array: ${i}`);let r=e;const s=t.slice(1).reverse();for(let e of s){const t=[],{length:i}=r;let n=0;for(;ne.substr(0,e.indexOf("\0")))):e}};function hm(e){return e.replace(/\/(\/)+/g,"/")}async function cm(e){var t;e.url&&("function"==typeof(t=e.url).slice&&"function"==typeof t.arrayBuffer)&&(e.file=e.url,e.url=void 0);const i=void 0!==e.url;let n=e.reader?e.reader:function(e){if(e.url)return new uf(e);if(e.path)return new NodeLocalFile(e);if(e.file)return new gf(e.file);throw Error("One of 'url', 'path (node only)', or 'file (browser only)' must be specified")}(e);const r=e.fetchSize||2e3,s=e.maxSize||2e5;i&&(n=new ff({file:n,fetchSize:r,maxSize:s}));const o=new dm(n),a=await async function(e){let t;if(e.indexReader)t=e.indexReader;else{if(e.index)return e.index;e.indexURL?t=new uf({url:e.indexURL}):e.indexPath?t=new NodeLocalFile({path:e.indexPath}):e.indexFile&&(t=new gf({file:e.indexFile}))}if(t){const e=await t.read(),i=(new TextDecoder).decode(e);return JSON.parse(i)}return}(e),l=e.indexOffset,h=function(e){if(e.url)return um(e.url);if(e.path)return um(e.path);if(e.file)return e.file.name}(e),c=new am(o,h,{index:a,indexOffset:l});return await c.ready,c}class dm{constructor(e){this.fileReader=e}async slice(e,t){return this.fileReader.read(e,t-e)}}function um(e){const t=e.lastIndexOf("/");return t>0?e.substring(t+1):e}class fm{constructor(e,t=1e5){this.h5_file=e,this.bin_size=t,this.h5_obj=void 0}async fetch(){return this.h5_obj||(this.h5_obj=await cm({url:this.h5_file,fetchSize:1e6,maxSize:2e8})),this.h5_obj}async get_keys(){return(await this.fetch()).keys}async get_rd_signal(e=this.bin_size){let t=await this.fetch(),i=t.keys,n=new pm(i),r=n.get_rd_bins(),s=n.get_snp_bins();this.available_bins=[...new Set(r,s)],this.available_bins.includes(e)||(e=this.available_bins.at(-1));const o=await t.get("rd_chromosomes");await o.dtype;let a=await o.value,l=await this.rd_stat(t,i,e);var h=[],c=[],d=[],u=[],f=[],p=[];for(let n of a){var g=`his_rd_p_${n}_${e}`;let r=await this.get_chr_signal(t,i,n,e,g,l);h=h.concat(r);var m=`his_rd_p_${n}_${e}_GC`;let s=await this.get_chr_signal(t,i,n,e,m,l);c=c.concat(s);let o=`his_rd_p_${n}_${e}_partition_GC_merge`,a=await this.get_chr_signal(t,i,n,e,o,l);d=d.concat(a);let b=await this.rd_call_combined(t,i,n,e,l);u=u.concat(b);let w=`snp_likelihood_${n}_${e}_mask`,v=await this.get_baf_signals(t,i,n,e,w);f=f.concat(v[0]),p=p.concat(v[1])}this.callers=[],0!=u.length&&this.callers.push("ReadDepth"),0!=u.length&&this.callers.push("2D");var b={},w={RD_Raw:h,RD_Raw_gc_coor:c,ReadDepth:d,"2D":u,BAF1:f,BAF2:p};return b[e]=w,b}decode_segments(e){let t=[],i=[];for(let n of e)4294967295==n?(t.push(i),i=[]):i.push(n);return t}async rd_call_combined(e,t,i,n,r){let s,o=[],a=`his_rd_p_${i}_${n}_partition_GC_mosaic_segments_2d`;if(t.includes(a)){const t=await e.get(a);let i=await t.value;s=this.decode_segments(i)}let l=`his_rd_p_${i}_${n}_partition_GC_mosaic_call_2d`;if(t.includes(l)){const t=await e.get(l);let a=await t.to_array();s.forEach(((e,t)=>{e.forEach(((e,s)=>{o.push({chr:i,start:e*n,end:(e+1)*n,value:a[0][t]/r[4]*2})}))}))}return o}async rd_stat(e,t,i){let n,r=`rd_stat_${i}_auto`;if(t.includes(r)){const t=await e.get(r);n=await t.value}return n}async get_chr_signal(e,t,i,n,r,s){let o=[];if(t.includes(r)){const t=await e.get(r);(await t.value).forEach(((e,t)=>{o.push({chr:i,start:t*n,end:(t+1)*n,value:e/s[4]*2})}))}return o}async get_baf_signals(e,t,i,n,r){let s=[],o=[];if(t.includes(r)){let t=await e.get(r);(await t.to_array()).forEach(((e,t)=>{let r=Math.max(...e);const a=e.indexOf(r);let l=Math.max(a/200,1-a/200);s.push({chr:i,start:t*n,end:(t+1)*n,value:-2*l}),.5!=l&&o.push({chr:i,start:t*n,end:(t+1)*n,value:-2*(1-l)})}))}return[s,o]}async get_baf_signals_v2(e,t,i,n,r){let s=[],o=[];if(t.includes(r)){let t=await e.get(r);(await t.to_array()).forEach(((e,t)=>{isNaN(e)||(s.push({chr:i,start:t*n,end:(t+1)*n,value:-2*(.5-e)}),.5!=e&&o.push({chr:i,start:t*n,end:(t+1)*n,value:-2*(.5+e)}))}))}return console.log(i,s,o),[s,o]}}class pm{constructor(e){this.signals=e}get_rd_bins(){let e=[];this.signals.forEach((t=>{let i=t.match(/^his_rd_p_(.*)_(\d+)$/);i&&e.push({chr:i[1],bin_size:i[2]})}));return[...new Set(e.map((e=>Number(e.bin_size))))]}get_snp_bins(){let e=[];this.signals.forEach((t=>{let i=t.match(/^snp_likelihood_(.*)_(\d+)_mask$/);i&&e.push({chr:i[1],bin_size:i[2]})}));return[...new Set(e.map((e=>Number(e.bin_size))))]}}function gm(e){let t=function(e){if(e.length<4)return e;let t,i,n,r,s,o;return t=e.slice().sort(((e,t)=>e-t)),t.length/4%1==0?(i=.5*(t[t.length/4]+t[t.length/4+1]),n=.5*(t[t.length*(3/4)]+t[t.length*(3/4)+1])):(i=t[Math.floor(t.length/4+1)],n=t[Math.ceil(t.length*(3/4)+1)]),r=n-i,s=n+1.5*r,o=i-1.5*r,t.filter((e=>e>=o&&e<=s))}(e);const i=t.length,n=t.reduce(((e,t)=>e+t))/i,r=Math.sqrt(t.map((e=>Math.pow(e-n,2))).reduce(((e,t)=>e+t))/i);return[n,r]}var mm={range_function:function(e,t,i){return Array(Math.ceil((t-e)/i)).fill(e).map(((e,t)=>e+t*i))},getDistParams:gm,linspace:function(e,t,i){if(void 0===i&&(i=Math.max(Math.round(t-e)+1,1)),i<2)return 1===i?[e]:[];var n=Array(i);for(let r=--i;r>=0;r--)n[r]=(r*t+(i-r)*e)/i;return n},GetFit:class{constructor(e){this.allBins=e}getValues(){return Object.values(this.allBins).reduce(((e,t)=>e.concat(t.filter((e=>e.binScore>0)).map((e=>e.binScore)))),[])}getMean(e){return e.reduce((function(e,t){return e+t}))/e.length}fit_data(){return gm(this.getValues())}histogram(e,t){const i=t[1]-t[0],n=[];e.forEach(((e,r)=>{t.forEach(((t,r)=>{if(n[t]||(n[t]={count:0}),t<=e&&e{r.push(e.count)})),r}}};function bm(e,t){return isNaN(e)||isNaN(t)||t<=0?NaN:0===e?.5:.5+.5*(wm(.5*t,.5,1)-wm(.5*t,.5,t/(t+e*e)))*Math.sign(e)}function wm(e,t,i){if(0==i)return 0;if(1==i)return 1;{let n=xm(e+t)-xm(e)-xm(t)+e*Math.log(i)+t*Math.log(1-i);return i<(e+1)/(e+t+2)?Math.exp(n)*vm(e,t,i)/e:1-Math.exp(n)*vm(t,e,1-i)/t}}function vm(e,t,i,n=1e3){let r=1,s=1,o=1,a=e+t,l=e+1,h=e-1,c=1-a*i/l;for(let d=0;d<=n;d++){let n=parseFloat(d+1),u=n+n,f=n*(t-n)*i/((h+u)*(e+u)),p=r+f*s,g=c+f*o;f=-(e+n)*(a+n)*i/((l+u)*(e+u));let m=g+f*c,b=r;if(s=p/m,o=g/m,r=(p+f*r)/m,c=1,Math.abs(r-b)<3e-7*Math.abs(r))return r}}function ym(e){if(0==e||1==e)return 1;return e*ym(e-1)}function _m(e){let t;var i=[75122.633153,80916.6278952,36308.2951477,8687.24529705,1168.92649479,83.8676043424,2.50662827511],n=0,r=1;if(0==e)t=1e99;else if(e%1==0)t=ym(e-1);else{for(let t=0;tt?e:t}))}function Sm(e,t,i,n){return t*Math.exp(-1*(e-i)**2/(2*n**2))/Math.sqrt(2*Math.PI)/n}function Am(e,t,i,n){return Math.exp(-1*(e-i)**2/(t**2+n**2))}function Em(e,t){let i;try{i=e.reduce(((e,i,n)=>e+Math.min(i,t[n])))}catch{return console.log("Failed to find likelihood overlap: ",e,t),0}return i}function Mm(e,t,i,n){return 0==t&&0==n?{nl:.5*(e+i),ne:0}:{nl:(e*n*n+i*t*t)/(t*t+n*n),ne:Math.sqrt(t*t*n*n/(t*t+n*n))}}function Tm(e,t){let i=parseInt(t*(e.length-1)),n=t*(e.length-1)-i;return iMath.floor(t/2)&&(n=t-1-n);const r=(t/2-n)/(t+1),s=Math.floor((t/2+n)/2),o=t-1-s;let a=e.slice(s,o+1).reduce(((e,t)=>e+t),0)/e.reduce(((e,t)=>e+t),0);return n===Math.floor(t/2)&&(a=1),{mean:r,p:a}}var Lm=class{constructor(e,t){this.wigFeatures=e,this.binSize=t}get_fit(){var e=new mm.GetFit(this.wigFeatures),[t,i]=e.fit_data();return{globalMean:t,globalStd:i}}async call_2d(e=null,t=null,i="both",n=.1,r=0,s=10,o=0){let a=this.get_fit();this.globalMean=a.globalMean,this.globalStd=a.globalStd;let l=null==e?.05*this.binSize/3e9:e,h=null==t?parseInt(this.binSize/1e4):t,c=[],d=[],u=[],f=[],p=[],g=[];for(const[e,t]of Object.entries(this.wigFeatures)){let e=[],i=[],s=[];t.forEach(((t,n)=>{t.hets_count>4&&t.dp_count>h&&(e.push([n]),i.push(t.binScore),s.push(t.likelihood_score),delete t.likelihood_score)}));let o=[];for(let e=1;eMath.sqrt(Math.sqrt(e)**2+this.globalStd**2+Math.pow(a[t]/2,2)))),m=[];for(let t=0;t0;){m=m.filter((e=>"number"==typeof e));let t=Cm(m);if(isNaN(t)&&console.log("NaN value",m),te*s[o+1][t])),r=n.reduce(((e,t)=>e+t))}catch{console.log(s),console.log("max_overlap:",t,o,m.length),console.log("likelihood: ",o,s[o],s[o+1]),console.log("nlh: ",r)}if(i[o]=a.nl,g[o]=a.ne,s[o]=n.map((function(e){return e/r})),e[o].push(...e[o+1]),i.splice(o+1,1),g.splice(o+1,1),e.splice(o+1,1),s.splice(o+1,1),m.splice(o,1),o0){let e=Am(i[o-1],g[o-1],i[o],g[o])*Em(s[o-1],s[o]);m[o-1]=e}}let b=-1;for(;;){m=[];for(let t=0;te*s[r+1][t])),a=n.reduce(((e,t)=>e+t));s[r]=n.map((function(e){return e/a})),e[r].push(...e[r+1]),e[r]=e[r].sort(((e,t)=>e-t)),i.splice(o,1),g.splice(o,1),e.splice(o,1),s.splice(o,1),o>=e.length&&(r+=1,o=r+1)}else o+=1,o>=e.length&&(r+=1,o=r+1)}if(b==e.length)break;b=e.length}e.forEach(((e,n)=>{let o=Rm(s[n]);e.length>1&&(e.forEach(((e,s)=>{d.push(t[e]),o.mean<=r&&c.push(t[e]),t[e].segment_score=i[n]})),u.push(i[n]),f.push(g[n]),p.push(s[n]))}))}let m=parseInt(1e3*(1-o));0==m&&(m=1);let b=mm.linspace(o,1,m),w={},v={};for(let e=10;e>-1;e--)for(let t=0;t1-t+t*e/2)),a=e/2;e>0?(n=.5-t/(t+s),r=b.map(((e,i)=>.5-(1-e+e*t)/(2-2*e+(t+s)*e)))):(n=0,r=b.map(((e,t)=>0*e)));for(let i=0;i{if(!isNaN(r[t])){let n=Sm(e*this.globalMean,1,u[i],f[i])*Tm(p[i],.5+r[t]);h+=n,n>c&&(c=n,d=b[t])}})),i in w?w[i].push([e,t,s,h/b.length,d]):w[i]=[e,t,s,h/b.length,d]}for(let e=0;ee[3]-t[3]));else if(w[e].sort(((e,t)=>e[3]-t[3])),"both"==i&&(v[e].sort(((e,t)=>e[3]-t[3])),v[e][0][3]>w[e][0][3])){let t=w[e].filter((t=>t[0]!=v[e][0][0]&&t[1]<=v[e][0][1]));w[e]=[v[e][0]].push(...t)}for(let e=0;e{var r={...e};1!=i&&(r.value=e[t]/i*2),n.push(r)}));return n}formatDataStructure_BAF(e,t=2){const i=[],n=[];for(const[t,r]of Object.entries(this.wigFeatures))r.forEach((t=>{var r={...t},s={...t};let o=t[e];.5!=o&&(s.value=-2*(1-o),n.push(s)),r.value=-2*o,i.push(r)}));return[i,n]}};function Im(e){for(var t=1,i=1,n=1*e,r=1;r<50;r++)t*=r,n+=(i*=-1)*Math.pow(e,2*r+1)/(t*(2*r+1));return 2*n/Math.sqrt(3.14159265358979)}function Bm(e,t,i,n,r){var s=new Om(i.slice(n,r));return 0==s.std&&(s.std=t>0?t*s.mean/e:1),Pm(e,s.mean,s.std,r-n)/(r-n)}function Fm(e,t,i,n,r){var s=new Om(i.slice(n,r));if(s.means&!h&l0&&(c=Bm(e,t,i,n-1,r)),r-n>2)var d=Bm(e,t,i,n+1,r),u=Bm(e,t,i,n,r-1);if(re+t))/e.length,this.std=Math.sqrt(e.reduce(((e,t)=>(t-this.mean)**2))/e.length)}}function Pm(e,t,i,n){0==i&&(i=1);var r=(e-t)/i*Math.sqrt(n);return 1-km.TdistributionCDF(Math.abs(r),n-1)}function Dm(e,t,i,n,r,s){0==t&&(t=1),0==r&&(r=1);var o=(e-n)/Math.sqrt(t**2/i+r**2/s),a=(t**2/i+r**2/s)**2*(i-1)*(s-1)/(t**4*(s-1)/i**2+r**4*(i-1)/s**2);return 1-km.TdistributionCDF(Math.abs(o),parseInt(a+.5))}var zm=class{constructor(e,t,i){this.rd=e,this.mean=t,this.std=i,this.bin_bands=[2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128]}get_rd_signal_bandwidth(e){var t=[];return e.forEach(((e,i)=>{var n=0;n=e>this.mean/4?this.mean/(this.std**2*e):4/this.std**2,t.push(n)})),t}meanShiftCaller(e=3){var t={};return Object.entries(this.rd).forEach((([i,n])=>{var r=new Array(n.length).fill(!1),s=new Array(n.length);for(let e=0;e{var o=r.map(((e,t)=>!e)),a=[];Object.entries(n).forEach((([e,t])=>{a.push(t.binScore)}));var l=[0],h=0;for(let e=0;e0&&(l.push(l[l.length-1]+h-1),h=0):h+=1;l.shift();for(let i=0;i=a.length||Math.abs(e-i)>=a.length)){var u=(i-e)*Math.exp(-.5*(i-e)**2/t**2)*Math.exp(-.5*(a[e]-a[i])**2*c[e]);d[e]+=u}for(var f=new Array,p=0;p=0&&f.push(p);f.push(d.length-1),f=f.concat(l).sort(((e,t)=>e-t)),f=Array.from(new Set(f));var g=0;for(p=0;pe+t))/m.length;a.fill(b,g,f[p]+1),g=f[p]+1}}p=0;for(var w=0;p.01&&f.push(p+1)}f.unshift(0),f.push(s.length),r=new Array(this.rd.length).fill(!1);for(p=1;p1))continue;y[0]=f[p-2];var _=[f[p],f[p]];if(!(p.01/genome_size*bin_size*(x+k))continue;if(Dm(S.mean,S.std,S.data.length,R.mean,R.std,R.data.length)>.01/genome_size*bin_size*(x+C))continue}if(Pm(this.mean,S.mean,S.std,S.data.length)>.05)continue;let e=a.slice(v[0],v[1]);var L=new Om(e);r.fill(!0,v[0],v[1]),s.fill(L.mean,v[0],v[1])}})),t[i]=s})),t}call_mean_shift(e=3){for(var t=new Array(this.rd.length).fill(!1),i=new Array(this.rd.length),n=0;n{var s=t.map(((e,t)=>!e)),o=[];s.forEach(((e,t)=>{e&&o.push(this.rd[t])}));for(var a=[0],l=0,h=0;h0&&(a.push(a[a.length-1]+l-1),l=0):l+=1;a.shift();for(var c=0;c=o.length||Math.abs(h-f)>=o.length)){var p=(f-h)*Math.exp(-.5*(f-h)**2/n**2)*Math.exp(-.5*(o[h]-o[f])**2*d[h]);u[h]+=p}var g=new Array;for(h=0;h=0&&g.push(h);g.push(u.length-1),g=g.concat(a).sort(((e,t)=>e-t)),g=Array.from(new Set(g));var m=0;for(h=0;he+t))/b.length;o.fill(w,m,g[h]+1),m=g[h]+1}}for(h=0,f=0;h.01&&g.push(h+1)}g.unshift(0),g.push(i.length),t=new Array(this.rd.length).fill(!1);for(h=1;h1){y[0]=g[h-2];var _=[g[h],g[h]];if(h3.483106931382794e-9*(x+k))continue;if(Dm(S.mean,S.std,S.data.length,R.mean,R.std,R.data.length)>3.483106931382794e-9*(x+C))continue}if(!(Pm(this.mean,S.mean,S.std,S.data.length)>.05)){var L=new Om(this.rd.slice(v[0],v[1]));t.fill(!0,v[0],v[1]),i.fill(L.mean,v[0],v[1])}}}}}})),i}cnv_calling(e=1e5){var t=(t=.25)*this.mean,i=this.mean-t,n=this.mean+t,r=2971e6,s=this.meanShiftCaller(),o={},a=[];return Object.entries(s).forEach((([l,h])=>{for(var c=!1;!c;){c=!0;var d=new Array(1).fill(0);for(let e=0;e.01&&d.push(e+1)}d.push(h.length);for(let e=0;e0&&(f=Math.abs(h[d[e]]-h[d[e-1]])),e{m.push(t.binScore)}));for(var b,w=new Array(h.length).fill(""),v=0,y=.05*e/r;vx+1){var k=Nm(this.mean,this.std,m,x,C,y);if(k){var C=k;w.fill("D",x,C)}}for(x=v;vn;)v+=1;(C=v)>x+1&&(k=Nm(this.mean,this.std,m,x,C,y))&&(C=k,w.fill("A",x,C)),v==_&&(v+=1)}for(v=0;vx+1&&(Fm(this.mean,this.std,m,x,C)<1682935038707506e-26&&w.fill(["d"]*(C-x),x,C),v-=1),v+=1}(v=0)=w.length););if(v>x){var A=new Om(S.slice(x,v));S.fill(A.mean,x,v)}va&&(s>0&&t.push({chr:i,start:o,end:a,value:r,bin:l,count:s}),r=0,o=n.start-n.start%this.binSize,a=o+this.binSize);const h=n.calls[9].info.DP;h&&(r+=Number.parseInt(h),s++)}s>0&&t.push({chr:i,start:o,end:a,value:r,bin:l,count:s})}return t}async computeReadDepth(){const e=Object.keys(this.allVariants);var t={};for(let n of e){const e=this.allVariants[n];var i;if(0!==e.length)for(let r of e){i=Math.max(Math.floor(r.start/this.rowBinSize),0),t[n]||(t[n]=[]),t[n][i]||(t[n][i]={chr:n,start:i*this.rowBinSize,end:(i+1)*this.rowBinSize,value:0,sum_score:0,count:0});const e=r.calls[9].info.DP;e&&(t[n][i].sum_score+=Number.parseInt(e),t[n][i].count++)}}var n={};for(let i of e){n[i]||(n[i]=[]);for(let e=0;e{t.forEach(((t,i)=>{t.partition_level=parseInt(s[e][i]),t.partition_call=parseInt(o[0][e][i])}))})),[this.formatDataStructure(e,"binScore",i),this.formatDataStructure(e,"partition_level",i),this.formatDataStructure(e,"partition_call",i),o[1]]}formatDataStructure(e,t,i=1){const n=[];for(const[r,s]of Object.entries(e))s.forEach((e=>{var r={...e};1!=i&&(r.value=e[t]/i*2),n.push(r)}));return n}async computeBAF_v2(){const e=Object.keys(this.allVariants),t={},i=[],n=[];for(let o of e){const e=this.allVariants[o];if(0===e.length)continue;var r;for(let a of e){if(r=Math.max(Math.floor(a.start/this.binSize),0),t[o]||(t[o]=[]),!t[o][r]){if(r>0){let e=r-1;if(t[o][e]){const r=this.get_max_min_score(t[o][e]);if(.5!=r.value){let e=Object.assign({},r);e.value=-2*(1-r.value),n.push(e)}r.value=-2*r.value,t[o][e]=r,i.push(t[o][e])}}t[o][r]={chr:o,start:r*this.binSize,end:(r+1)*this.binSize,value:0,count:0,likelihood_score:[],min_score:0}}const e=a.calls[9];let l=e.genotype,h=e.info.AD.split(","),c=h[0],d=h[1];if(0==l[0]&&1==l[1]||1==l[0]&&0==l[1]){if(0==t[o][r].likelihood_score.length)t[o][r].likelihood_score=mm.linspace(0,1,100).map(((e,t)=>Vm(c,d,e)));else{var s=0;t[o][r].likelihood_score=mm.linspace(0,1,100).map(((e,i)=>{var n=t[o][r].likelihood_score[i]*Vm(c,d,e);return s+=n,n})),t[o][r].likelihood_score=mm.linspace(0,1,100).map(((e,i)=>t[o][r].likelihood_score[i]/s))}t[o][r].count++}}const a=this.get_max_min_score(t[o][r]);if(.5!=a.value){let e=Object.assign({},a);e.value=-2*(1-a.value),n.push(e)}a.value=-2*a.value,t[o][r]=a,i.push(t[o][r])}return[i,n]}format_BAF_likelihood(e){const t=[];for(const[i,n]of Object.entries(e))n.forEach((e=>{var i={...e};.5!=e.value&&(i.value=1-e.value,t.push(i))}));return t}get_max_min_score(e){if(e.likelihood_score.length>0){const t=Math.max(...e.likelihood_score),i=e.likelihood_score.indexOf(t);e.value=Math.max(i/100,1-i/100),e.min_score=Math.min(i/100,1-i/100)}else e.score=0;return e}async getAllbins(){const e=await this.computeDepthFeatures();return new mm.GetFit(e).fit_data(),e}async read_rd_baf(e="ReadDepth"){const t=Object.keys(this.allVariants);var i={};for(let e of t){const t=this.allVariants[e];var n;if(0!==t.length)for(let r of t){n=Math.max(Math.floor(r.start/this.rowBinSize),0),i[e]||(i[e]=[]),i[e][n]||(i[e][n]={chr:e,start:n*this.rowBinSize,end:(n+1)*this.rowBinSize,dp_sum_score:0,dp_count:0,hets_count:0,hets:[]});const t=r.calls[9],s=t.info.DP;s&&(i[e][n].dp_sum_score+=Number.parseInt(s),i[e][n].dp_count++);let o=t.info.AD.split(","),a=t.genotype;if(0==a[0]&&1==a[1]||1==a[0]&&0==a[1]){i[e][n].hets_count++;let t=parseInt(o[0]),r=parseInt(o[1]);i[e][n].hets.push({ref:t,alt:r})}}}var r,s=this.adjust_bin_size(i);if("ReadDepth"==e){r=this.readDepthMeanshift(s);var o=this.formatDataStructure_BAF(s,"max_likelihood")}else if("2D"==e){let e=new Lm(s,this.binSize),t=await e.call_2d();r=[t.binScore,[],t.segment_score];o=e.formatDataStructure_BAF("max_likelihood")}return[r,o]}formatDataStructure_BAF(e,t,i=2){const n=[],r=[];for(const[i,s]of Object.entries(e))s.forEach((e=>{delete e.likelihood_score;var i={...e},s={...e};let o=e[t];.5!=o&&(s.value=-2*(1-o),r.push(s)),i.value=-2*o,n.push(i)}));return[n,r]}adjust_bin_size(e){const t=Object.keys(this.allVariants);var i={};for(let s of t){i[s]||(i[s]=[]);for(let t=0;t{if(0==i[s][t].likelihood_score.length)i[s][t].likelihood_score=mm.linspace(0,1,100).map(((t,i)=>Vm(e.ref,e.alt,t)));else{var r=0;i[s][t].likelihood_score=mm.linspace(0,1,100).map(((n,o)=>{var a=i[s][t].likelihood_score[o]*Vm(e.ref,e.alt,n);return r+=a,a})),i[s][t].likelihood_score=mm.linspace(0,1,100).map(((e,n)=>i[s][t].likelihood_score[n]/r))}}))}const a=this.get_max_min_score(i[s][t]);i[s][t].max_likelihood=a.value}}return i}}function Vm(e,t,i,n=!0){return i**e*(1-i)**t+i**t*(1-i)**e}class Um extends Il{constructor(e,t){super(e,t),this.featureType="numeric",this.paintAxis=ud,e.max||(this.defaultScale=!0,this.autoscale=!1),this.height=void 0!==e.height?e.height:250}async init(e){this.type="cnvpytor",this.graphType=e.graphType||"points",this.bin_size=e.bin_size||1e5,this.signal_name=e.signal_name||"rd_snp",this.cnv_caller=e.cnv_caller||"2D",this.colors=e.colors||["gray","black","green","blue"],super.init(e)}get supportsWholeGenome(){return!0}get_signals(){let e=[];return"rd_snp"==this.signal_name?e=["RD_Raw","RD_Raw_gc_coor",this.cnv_caller,"BAF1","BAF2"]:"rd"==this.signal_name?e=["RD_Raw","RD_Raw_gc_coor",this.cnv_caller]:"snp"==this.signal_name?e=["BAF1","BAF2"]:"cnh"==this.signal_name&&(e=[this.cnv_caller]),e}get_signal_colors(){return[{singal_name:"RD_Raw",color:this.colors[0]},{singal_name:"RD_Raw_gc_coor",color:this.colors[1]},{singal_name:"ReadDepth",color:this.colors[2]},{singal_name:"2D",color:this.colors[2]},{singal_name:"BAF1",color:this.colors[3]},{singal_name:"BAF2",color:this.colors[3]}]}async postInit(){if("vcf"==this.config.format){this.featureSource=uc(this.config,this.browser.genome),this.header=await this.getHeader();var e=this.featureSource.reader.features.reduce((function(e,t){return e[t.chr]=e[t.chr]||[],e[t.chr].push(t),e}),Object.create(null));const t=new Hm(e,this.bin_size);let i,n,r;this.wigFeatures_obj={},this.wigFeatures_obj[this.bin_size]={},"2D"==this.config.cnv_caller?(r=await t.read_rd_baf("2D"),i=r[0],n=r[1],this.wigFeatures_obj[this.bin_size]["2D"]=i[2],this.available_callers=["2D"]):(r=await t.read_rd_baf(),i=r[0],n=r[1],this.wigFeatures_obj[this.bin_size].ReadDepth=i[2],this.available_callers=["ReadDepth"]),this.wigFeatures_obj[this.bin_size].RD_Raw=i[0],this.wigFeatures_obj[this.bin_size].RD_Raw_gc_coor=i[1],this.wigFeatures_obj[this.bin_size].BAF1=n[0],this.wigFeatures_obj[this.bin_size].BAF2=n[1],this.available_bins=[this.bin_size],this.set_available_callers()}else this.cnvpytor_obj=new fm(this.config.url,this.bin_size),this.wigFeatures_obj=await this.cnvpytor_obj.get_rd_signal(this.bin_size),this.available_bins=this.cnvpytor_obj.available_bins,this.available_callers=this.cnvpytor_obj.callers,this.set_available_callers();this.tracks=[];const t=[];this.signals=this.get_signals(),this.signal_colors=this.get_signal_colors();for(let e in this.wigFeatures_obj)for(const[i,n]of Object.entries(this.wigFeatures_obj[e]))if(this.signals.includes(i)){let e={type:"wig",isMergedTrack:!0};e.features=n,e.name=i,e.color=this.signal_colors.filter((e=>e.singal_name===i)).map((e=>e.color));const r=await this.browser.createTrack(e);r?(r.autoscale=!1,this.tracks.push(r)):console.warn("Could not create track "+e),"function"==typeof r.postInit&&t.push(r.postInit())}this.flipAxis=!!this.config.flipAxis&&this.config.flipAxis,this.logScale=!!this.config.logScale&&this.config.logScale,this.autoscale=this.config.autoscale,this.autoscale||(this.dataRange={min:this.config.min||0,max:this.config.max});for(let e of this.tracks)e.autoscale=!1,e.dataRange=this.dataRange;return Promise.all(t)}set_available_callers(){this.available_callers.includes(this.cnv_caller)||(this.available_callers.length>0?this.cnv_caller=this.available_callers[0]:this.cnv_caller=null)}async getHeader(){if(!this.header){if("function"==typeof this.featureSource.getHeader){const e=await this.featureSource.getHeader();e&&(this.callSets=e.callSets||[]),this.header=e}this.sampleNames=this.callSets?this.callSets.map((e=>e.name)):[]}return this.header}get height(){return this._height}set height(e){if(this._height=e,this.tracks)for(let t of this.tracks)t.height=e,t.config.height=e}menuItemList(){let e=[];void 0!==this.flipAxis&&e.push({label:"Flip y-axis",click:()=>{this.flipAxis=!this.flipAxis,this.trackView.repaintViews()}}),e=e.concat($t.numericDataMenuItems(this.trackView)),e.push("
"),e.push("Bin Sizes");for(let t of this.available_bins){const i=jt(t,t===this.bin_size);e.push({object:ft(i),click:async()=>{this.bin_size=t,await this.recreate_tracks(t),this.clearCachedFeatures(),this.trackView.updateViews(),this.trackView.repaintViews()}})}e.push("
"),e.push("Signal Type");let t={rd_snp:"RD and BAF Likelihood",rd:"RD Signal",snp:"BAF Likelihood"};for(let i in t){const n=jt(t[i],i===this.signal_name);e.push({object:ft(n),click:async()=>{this.signal_name=i,await this.recreate_tracks(this.bin_size),this.clearCachedFeatures(),this.trackView.updateViews(),this.trackView.repaintViews()}})}e.push("
"),e.push("CNV caller");for(let t of this.available_callers){const i=jt(t,t===this.cnv_caller);e.push({object:ft(i),click:async()=>{this.cnv_caller=t,await this.recreate_tracks(this.bin_size),this.clearCachedFeatures(),this.trackView.updateViews(),this.trackView.repaintViews()}})}return e}async recreate_tracks(e){this.tracks=[];const t=[];e in this.wigFeatures_obj||(this.wigFeatures_obj={...this.wigFeatures_obj,...await this.cnvpytor_obj.get_rd_signal(e)}),this.signals=this.get_signals(),this.signal_colors=this.get_signal_colors();for(const[i,n]of Object.entries(this.wigFeatures_obj[e]))if(this.signals.includes(i)){let e={type:"wig",isMergedTrack:!0};e.features=n,e.name=i,e.color=this.signal_colors.filter((e=>e.singal_name===i)).map((e=>e.color));const r=await this.browser.createTrack(e);r?(r.autoscale=!1,this.tracks.push(r)):console.warn("Could not create track "+e),"function"==typeof r.postInit&&t.push(r.postInit())}this.flipAxis=!!this.config.flipAxis&&this.config.flipAxis,this.logScale=!!this.config.logScale&&this.config.logScale,this.autoscale=this.config.autoscale,this.autoscale||(this.dataRange={min:this.config.min||0,max:this.config.max});for(let e of this.tracks)e.autoscale=!1,e.dataRange=this.dataRange;return Promise.all(t)}async getFeatures(e,t,i,n){if(this.tracks){const r=this.tracks.map((r=>r.getFeatures(e,t,i,n)));return Promise.all(r)}}getScaleFactor(e,t,i,n){return n?i/(Math.log10(t+1)-(e<=0?0:Math.log10(e+1))):i/(t-e)}computeYPixelValue(e,t){return(this.flipAxis?e-this.dataRange.min:this.dataRange.max-e)*t}computeYPixelValueInLogScale(e,t){let i=this.dataRange.max,n=this.dataRange.min;return i<=0?0:(n<=-1&&(n=0),n=n<=0?0:Math.log10(n+1),i=Math.log10(i+1),e=Math.log10(e+1),(this.flipAxis?e-n:i-e)*t)}draw(e){const t=e.features;if(!t)return;if(this.defaultScale&&("rd_snp"==this.signal_name?this.dataRange={min:this.config.min||this.dataRange.min||-2,max:this.config.max||this.dataRange.max||6}:"rd"==this.signal_name?this.dataRange={min:this.config.min||this.dataRange.min||0,max:this.config.max||this.dataRange.max||6}:"snp"==this.signal_name&&(this.dataRange={min:this.config.min||this.dataRange.min||-2,max:this.config.max||this.dataRange.max||0})),this.autoscale&&(this.dataRange=function(e,t){let i=0,n=-Number.MAX_VALUE;for(let e of t)for(let t of e)void 0===t.value||Number.isNaN(t.value)||(i=Math.min(i,t.value),n=Math.max(n,t.value));return{min:i,max:n}}(e.referenceFrame.chr,t)),this.tracks)for(let i=0,n=this.tracks.length;ithis.logScale?this.computeYPixelValueInLogScale(e,i):this.computeYPixelValue(e,i);if(this.config.hasOwnProperty("guideLines"))for(let t of this.config.guideLines)if(t.hasOwnProperty("color")&&t.hasOwnProperty("y")&&t.hasOwnProperty("dotted")){let i=n(t.y),r={strokeStyle:t.color,strokeWidth:1};t.dotted?Co.dashedLine(e.context,0,i,e.pixelWidth,i,5,r):Co.strokeLine(e.context,0,i,e.pixelWidth,i,r)}}popupData(e,t){const i=t||e.viewport.cachedFeatures;if(i&&i.length===this.tracks.length){const t=[];for(let n=0;n0&&t.push("
"),t.push(`
${this.tracks[n].name}
`);const r=this.tracks[n].popupData(e,i[n]);t.push(...r)}return t}}}const qm=new Map([["ideogram",(e,t)=>new af(e,t)],["sequence",(e,t)=>new wa(e,t)],["feature",(e,t)=>new iu(e,t)],["seg",(e,t)=>new Ou(e,t)],["mut",(e,t)=>new Ou(e,t)],["maf",(e,t)=>new Ou(e,t)],["wig",(e,t)=>new Iu(e,t)],["merged",(e,t)=>new Du(e,t)],["alignment",(e,t)=>new du(e,t)],["interaction",(e,t)=>new Hu(e,t)],["interact",(e,t)=>new Hu(e,t)],["variant",(e,t)=>new Qu(e,t)],["eqtl",(e,t)=>new Yu(e,t)],["gwas",(e,t)=>new ef(e,t)],["arc",(e,t)=>new rf(e,t)],["gcnv",(e,t)=>new tf(e,t)],["junction",(e,t)=>new df(e,t)],["blat",(e,t)=>new lu(e,t)],["cnvpytor",(e,t)=>new Um(e,t)]]),jm=function(e,t){qm.set(e,t)};var $m=jm,Wm=function(e,t,i){let n;switch(e){case"annotation":case"genes":case"fusionjuncspan":case"snp":n="feature";break;case"seg":case"maf":case"mut":n="seg";break;case"junctions":case"splicejunctions":n="junction";break;default:n=e}return qm.has(n)?qm.get(n)(t,i):void 0};class Gm{constructor(e,t){const i=(new DOMParser).parseFromString(e,"text/xml");this.processRootNode(i,t);const n=i.getElementsByTagName("Resource"),r=i.getElementsByTagName("Track"),s=r&&r.length>0,o=[];this.tracks=o;const a=new Map;Array.from(n).forEach((function(e,t){var i={url:e.getAttribute("path"),indexURL:e.getAttribute("index"),order:t};a.set(i.url,i),s||o.push(i)})),s&&Array.from(r).forEach((function(e){const t=e.getElementsByTagName("Track");if(t&&t.length>0){const i={type:"merged",tracks:[]};Zm(e,i),o.push(i),Array.from(t).forEach((function(e){e.processed=!0;const t=e.getAttribute("id"),n=a.get(t);n&&(i.tracks.push(n),Zm(e,n),n.autoscale=!1,i.height=n.height)}))}else if(!e.processed){const t=e.getAttribute("id"),i=a.get(t);i&&(o.push(i),Zm(e,i))}}))}processRootNode(e,t){const i=e.getElementsByTagName("Session");!i||i.length;const n=i.item(0),r=n.getAttribute("genome"),s=n.getAttribute("locus"),o=n.getAttribute("ucscID");t&&t.hasOwnProperty(r)?this.genome=r:(this.reference={fastaURL:r},o&&(this.reference.id=o)),s&&(this.locus=s)}}function Zm(e,t){t.name=e.getAttribute("name");const i=e.getAttribute("color");i&&(t.color="rgb("+i+")");const n=e.getAttribute("altColor");i&&(t.altColor="rgb("+n+")");const r=e.getAttribute("height");r&&(t.height=parseInt(r));const s=e.getAttribute("autoScale");s&&(t.autoscale="true"===s);const o=e.getAttribute("autoscaleGroup");o&&(t.autoscaleGroup=o);const a=e.getAttribute("windowFunction");a&&(t.windowFunction=a);const l=e.getAttribute("visibilityWindow")||e.getAttribute("featureVisibilityWindow");l&&(t.visibilityWindow=l);const h=e.getAttribute("indexed");h&&(t.indexed="true"===h);const c=e.getAttribute("normalize");c&&(t.normalize="true"===c);const d=e.getElementsByTagName("DataRange");if(d.length>0){const e=d.item(0);t.min=Number(e.getAttribute("minimum")),t.max=Number(e.getAttribute("maximum")),t.logScale="LOG"===e.getAttribute("type")}}const Qm=Yt;class Xm{constructor(){this.attributes={},this.plinkLoaded=!1}async loadPlinkFile(e,t){t||(t={});var i=To(t);const n=await ro.loadString(e,i);var r=Qm(n);for(let e of r){var s=e.split(" ");this.attributes[s[1]]={familyId:s[0],fatherId:s[2],motherId:s[3],sex:s[4],phenotype:s[5]}}return this.plinkLoaded=!0,this}getAttributes(e){return this.attributes[e]}getAttributeNames(){return this.hasAttributes()?Object.keys(this.attributes[Object.keys(this.attributes)[0]]):[]}hasAttributes(){return Object.keys(this.attributes).length>0}}class Ym{constructor(e,t){this.geneColors={},this.gene=null,this.snp=null,this.genesCount=0,e&&(this.gene=e.toUpperCase(),this.geneColors[this.gene]=Km[this.genesCount++]),t&&(this.snp=t.toUpperCase())}addGene(e){this.geneColors[e.toUpperCase()]||(this.geneColors[e.toUpperCase()]=Km[this.genesCount++])}colorForGene(e){return this.geneColors[e.toUpperCase()]}}var Km=[];Km.push("rgb(228,26,28)"),Km.push("rgb(55,126,184)"),Km.push("rgb(77,175,74)"),Km.push("rgb(166,86,40)"),Km.push("rgb(152,78,163)"),Km.push("rgb(255,127,0)"),Km.push("rgb(247,129,191)"),Km.push("rgb(153,153,153)"),Km.push("rgb(255,255,51)"),Km.push("rgb(102, 194, 165"),Km.push("rgb(252, 141, 98"),Km.push("rgb(141, 160, 203"),Km.push("rgb(231, 138, 195"),Km.push("rgb(166, 216, 84"),Km.push("rgb(255, 217, 47"),Km.push("rgb(229, 196, 148"),Km.push("rgb(179, 179, 179"),Km.push("rgb( 141, 211, 199"),Km.push("rgb(255, 255, 179"),Km.push("rgb(190, 186, 218"),Km.push("rgb(251, 128, 114"),Km.push("rgb(128, 177, 211"),Km.push("rgb(253, 180, 98"),Km.push("rgb(179, 222, 105"),Km.push("rgb(252, 205, 229"),Km.push("rgb(217, 217, 217"),Km.push("rgb(188, 128, 189"),Km.push("rgb(204, 235, 197"),Km.push("rgb(255, 237, 111");class Jm{constructor(e,t,i,n,r){this.genome=e,this.chr=t,this.start=i,this.end=n,this.bpPerPixel=r,this.id=_t.guid()}extend(e){const t=Math.min(e.start,this.start),i=Math.max(e.end,this.end),n=(i-t)/(this.end-this.start);this.start=t,this.end=i,this.bpPerPixel*=n}calculateEnd(e){return this.start+this.bpPerPixel*e}calculateBPP(e,t){return(e-this.start)/t}set(e){this.chr=e.chr,this.start=e.start,this.bpPerPixel=e.bpPerPixel}toPixels(e){return e/this.bpPerPixel}toBP(e){return this.bpPerPixel*e}shiftPixels(e,t,i){const n=this.start,r=e*this.bpPerPixel;return this.start+=r,i&&this.clampStart(t),this.end=this.start+t*this.bpPerPixel,n!==this.start}clampStart(e){const t=this.genome.getChromosome(this.chr).bpStart||0;if(this.start=Math.max(t,this.start),e){const{bpLength:t}=this.genome.getChromosome(this.chr),i=t-e*this.bpPerPixel;this.start>i&&(this.start=i)}}async zoomWithScaleFactor(e,t,i,n){const r=void 0===n?this.start+this.toBP(i/2):n,{start:s,bpPerPixel:o}=this.start,{bpLength:a}=this.getChromosome(),l=t<1?e.minimumBases()/i:a/i;this.bpPerPixel=t<1?Math.max(this.bpPerPixel*t,l):Math.min(this.bpPerPixel*t,l);const h=this.bpPerPixel*i;this.start=r-.5*h,this.clampStart(i),this.end=this.start+h;(s!==this.start||o!==this.bpPerPixel)&&await e.updateViews(!0)}getChromosome(){return this.genome.getChromosome(this.chr)}getMultiLocusLabelBPLengthOnly(e){const t=" ",i=Math.floor(this.start)+1,n=Math.round(this.start+this.bpPerPixel*e);return`${t}${this.chr}${t}${Bo(n-i)}${t}`}getMultiLocusLabelLocusOnly(e){const t=" ",{chr:i,start:n,end:r}=this.getPresentationLocusComponents(e);return`${t}${i}:${n}-${r}${t}`}getMultiLocusLabel(e){const t=" ",{chr:i,start:n,end:r}=this.getPresentationLocusComponents(e),s=Math.floor(this.start)+1;return`${t}${i}:${n}-${r}${t}${t}(${Bo(Math.round(this.start+this.bpPerPixel*e)-s)})${t}`}getPresentationLocusComponents(e){if("all"===this.chr)return{chr:this.chr};{const t=Xt(Math.floor(this.start)+1),i=Xt(Math.round(this.start+this.bpPerPixel*e));return{chr:this.chr,start:t,end:i}}}getLocusString(){if("all"===this.chr)return"all";{const e=Xt(Math.floor(this.start)+1),t=Xt(Math.round(this.end));return`${this.chr}:${e}-${t}`}}description(e){console.log(` ${e||""} referenceFrame - ${this.chr} bpp ${this.bpPerPixel.toFixed(3)} start ${Xt(Math.round(this.start))} end ${Xt(Math.round(this.end))} `)}}const eb={timeout:5e3,type:"plain",url:"https://igv.org/genomes/locus.php?genome=$GENOME$&name=$FEATURE$",coords:0,chromosomeField:"chromosome",startField:"start",endField:"end",geneField:"gene",snpField:"snp"};async function tb(e,t){if(void 0===t||""===t.trim())return;(t&&"all"===t.trim().toLowerCase()||"*"===t)&&(t="all");const i=t.split(" ");let n=e.searchConfig||eb,r=[];const s=async i=>{let r=function(e,t){const i=t.split("\t");if(i.length>=3)try{const t=e.genome.getChromosomeName(i[0]),n=parseInt(i[1].replace(/,/g,""),10)-1,r=parseInt(i[2].replace(/,/g,""),10);if(!isNaN(n)&&!isNaN(r))return{chr:t,start:n,end:r}}catch(e){}const n=t.split(":"),r=n[0];if("all"===r&&e.genome.getChromosome(r))return{chr:r,start:0,end:e.genome.getChromosome(r).bpLength};if(void 0===e.genome.getChromosome(r))return;{const t={chr:e.genome.getChromosomeName(r),start:0,end:e.genome.getChromosome(r).bpLength};if(n.length>1){let i,r=n[1].split("-");if(r.length>2){if(!n[1].startsWith("-"))return;{const e=n[1].indexOf("-",1);if(e>0){r=[n[1].substring(0,e),n[1].substring(e+1)]}}}if(i=r[0].replace(/,/g,""),isNaN(i))return;if(t.start=parseInt(i,10)-1,t.end=t.start+1,1===r.length&&(t.start-=20,t.end+=20),2===r.length){if(i=r[1].replace(/,/g,""),isNaN(i))return;if(t.end=parseInt(i,10),t.start<0&&!e.isSoftclipped()){const e=-t.start;t.start+=e,t.end+=e}}}return t}}(e,i);if(!r){const n=e.genome.featureDB.get(i.toUpperCase());n&&(r={chr:n.chr,start:n.start,end:n.end,gene:n.name,locusSearchString:t})}if(!r&&e.config&&!1!==e.config.search)try{r=await async function(e,t,i){let n=i.url.replace("$FEATURE$",t.toUpperCase());n.indexOf("$GENOME$")>-1&&(n=n.replace("$GENOME$",e.genome.id?e.genome.id:"hg19"));const r=i.timeout?{timeout:i.timeout}:void 0,s=await ro.loadString(n,r),o=function(e,t,i){let n;n="plain"===i.type?function(e,t){const i=[],n=[];return Yt(t).forEach((function(e){""===e||i.push(e)})),i.forEach((function(t){var i,r,s,o,a=t.split("\t");a.length>=3&&(s=(r=a[1].split(":"))[1].split("-"),i=a[2].trim(),o={gene:a[0],chromosome:e.genome.getChromosomeName(r[0].trim()),start:parseInt(s[0].replace(/,/g,"")),end:parseInt(s[1].replace(/,/g,"")),type:"gtex"===i?"snp":"gene"},n.push(o))})),n}(e,t):JSON.parse(t);i.resultsField&&(n=n[i.resultsField]);if(n&&0!==n.length){const t=i.chromosomeField||"chromosome",r=i.startField||"start",s=i.endField||"end",o=i.coords||1;let a;a=Array.isArray(n)?n[0]:n,a.hasOwnProperty(t)&&a.hasOwnProperty(r)||console.error("Search service results must include chromosome and start fields: "+a);const l=a[t],h=e.genome.getChromosome(l);if(!h)return;const c=h.name;let d=a[r]-o,u=a[s];void 0===u&&(u=d+1);const f={chr:c,start:d,end:u},p=a.type?a.type:"gene";return i.geneField&&"gene"===p&&(f.gene=a[i.geneField]),i.snpField&&"snp"===p&&(f.snp=a[i.snpField]),f}return}(e,s,i);o&&(o.locusSearchString=t);return o}(e,i,n)}catch(e){throw console.error(e),Error("Search service currently unavailable.")}return r};for(let e of i){const t=await s(e);t&&(t.locusSearchString=e,r.push(t))}if(0===r.length){const e=await s(t);e&&(e.locusSearchString=t,r.push(e))}return 0===r.length?void 0:r}class ib{constructor(e){this.browser=e}navbarDidResize(e){this.updateNavbar(this.createResponsiveClassSchedule(e))}updateNavbar(e){this.browser.$toggle_button_container.removeClass(),this.browser.$toggle_button_container.addClass(e.$toggle_button_container),ft(this.browser.zoomWidget.zoomContainer).removeClass(),ft(this.browser.zoomWidget.zoomContainer).addClass(e.zoomContainer)}createResponsiveClassSchedule(e){let t={};const i=this.browser.isMultiLocusWholeGenomeView()||this.browser.referenceFrameList&&Ba.isWholeGenomeView(this.browser.referenceFrameList[0].chr);return i?this.browser.windowSizePanel.hide():this.browser.windowSizePanel.show(),e>990?(t.$toggle_button_container="igv-navbar-toggle-button-container",t.zoomContainer="igv-zoom-widget"):e>860?(t.$toggle_button_container="igv-navbar-toggle-button-container",t.zoomContainer="igv-zoom-widget-900"):e>540?(t.$toggle_button_container="igv-navbar-toggle-button-container-750",t.zoomContainer="igv-zoom-widget-900"):(t.$toggle_button_container="igv-navbar-toggle-button-container-750",t.zoomContainer="igv-zoom-widget-900",this.browser.windowSizePanel.hide()),i&&(t.zoomContainer="igv-zoom-widget-hidden"),t}}const nb=function(e,t){this.container=_t.div({class:"igv-chromosome-select-widget-container"}),t.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(),""!==this.select.value&&e.search(this.select.value)})),this.showAllChromosomes=!1!==e.config.showAllChromosomes};nb.prototype.show=function(){this.container.style.display="flex"},nb.prototype.hide=function(){this.container.style.display="none"},nb.prototype.update=function(e){const t=this.showAllChromosomes?e.chromosomeNames.slice():e.wgChromosomeNames.slice();e.showWholeGenomeView()&&(t.unshift("all"),t.unshift("")),this.select.innerHTML="";for(let e of t){const t=document.createElement("option");t.setAttribute("value",e),t.innerText=e,this.select.appendChild(t)}};class rb{constructor(e,t){this.container=_t.div({class:"igv-windowsize-panel-container"}),e.appendChild(this.container),t.on("locuschange",(e=>{this.updatePanel(e)})),this.browser=t}show(){this.container.style.display="block"}hide(){this.container.style.display="none"}updatePanel(e){const t=this.browser.calculateViewportWidth(this.browser.referenceFrameList.length);this.container.innerText=1===e.length?Bo(Math.round(t*e[0].bpPerPixel)):""}}class sb{constructor(e,t){this.browser=t,this.columnContainer=e,this.horizontalGuide=_t.div({class:"igv-cursor-guide-horizontal"}),e.appendChild(this.horizontalGuide),this.verticalGuide=_t.div({class:"igv-cursor-guide-vertical"}),e.appendChild(this.verticalGuide),this.addMouseHandler(t),this.setVisibility(t.config.showCursorGuide)}addMouseHandler(e){this.boundMouseMoveHandler=function(t){const{x:i,y:n}=_t.translateMouseCoordinates(t,this.columnContainer);this.horizontalGuide.style.top=`${n}px`;const r=function(e,t){for(;e.parentElement;){if(e.parentElement.classList.contains(t))return e.parentElement;e=e.parentElement}return}(document.elementFromPoint(t.clientX,t.clientY),"igv-viewport");if(r&&e.getRulerTrackView()){this.verticalGuide.style.left=`${i}px`;const n=e.root.querySelectorAll(".igv-column");let s;const o=r.parentElement;for(let e=0;e{e.cursorGuideVisible=!e.cursorGuideVisible,e.setCursorGuideVisibility(e.cursorGuideVisible),this.setButtonState(e.cursorGuideVisible)})),this.setButtonState(e.cursorGuideVisible),e.config.showCursorTrackingGuideButton?this.show():this.hide()}setButtonState(e){!0===e?this.button.classList.add("igv-navbar-button-clicked"):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 ab{constructor(e,t){this.browser=e,this.button=_t.div({class:"igv-navbar-button"}),t.appendChild(this.button),this.button.textContent="center line",this.button.addEventListener("click",(()=>{e.isCenterLineVisible=!e.isCenterLineVisible,e.setCenterLineVisibility(e.isCenterLineVisible),this.setButtonState(e.isCenterLineVisible)})),this.setButtonState(e.isCenterLineVisible),e.config.showCenterGuideButton?this.show():this.hide()}setButtonState(e){!0===e?this.button.classList.add("igv-navbar-button-clicked"):this.button.classList.remove("igv-navbar-button-clicked")}show(){this.isVisible=!0,this.button.style.display="block",this.setButtonState(this.browser.isCenterLineVisible)}hide(){this.isVisible=!1,this.button.style.display="none"}}class lb{constructor(e,t){this.button=_t.div({class:"igv-navbar-button"}),e.appendChild(this.button),this.button.textContent="track labels",this.button.addEventListener("click",(()=>{t.trackLabelsVisible=!t.trackLabelsVisible,this.setState(t.trackLabelsVisible),t.setTrackLabelVisibility(t.trackLabelsVisible)})),this.browser=t,this.setVisibility(t.config.showTrackLabelButton),this.setState(t.trackLabelsVisible)}setVisibility(e){!0===e?this.show():this.hide()}setState(e){!0===e?this.button.classList.add("igv-navbar-button-clicked"):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"}}class hb{constructor(e,t){this.button=_t.div({class:"igv-navbar-button"}),e.appendChild(this.button),this.button.innerText="Sample Names",this.setState(t.showSampleNames),this.setVisibility(t.showSampleNameButton),this.button.addEventListener("click",(()=>{t.showSampleNames=!t.showSampleNames,this.setState(t.showSampleNames);for(let{sampleNameViewport:e}of t.trackViews)!1===t.showSampleNames?e.hide():e.show();t.layoutChange()}))}setVisibility(e){!0===e?this.show():this.hide()}setState(e){!0===e?this.button.classList.add("igv-navbar-button-clicked"):this.button.classList.remove("igv-navbar-button-clicked")}hide(){this.button.style.display="none"}show(){this.button.style.display="block"}}let cb=23,db=0;const ub=function(e,t){this.browser=e,this.zoomContainer=_t.div({class:"igv-zoom-widget"}),t.appendChild(this.zoomContainer),this.zoomOutButton=_t.div(),this.zoomContainer.appendChild(this.zoomOutButton),this.zoomOutButton.appendChild(At.createIcon("minus-circle")),this.zoomOutButton.addEventListener("click",(()=>{e.zoomOut()}));const i=_t.div();this.zoomContainer.appendChild(i),this.slider=document.createElement("input"),this.slider.type="range",this.slider.min="0",this.slider.max=`${cb}`,i.appendChild(this.slider),this.slider.addEventListener("change",(t=>{t.preventDefault(),t.stopPropagation();const i=e.referenceFrameList[0],{bpLength:n}=i.genome.getChromosome(i.chr),{end:r,start:s}=i,o=r-s,a=n/Math.pow(2,t.target.valueAsNumber);e.zoomWithScaleFactor(a/o)})),this.zoomInButton=_t.div(),this.zoomContainer.appendChild(this.zoomInButton),this.zoomInButton.appendChild(At.createIcon("plus-circle")),this.zoomInButton.addEventListener("click",(()=>{e.zoomIn()})),e.on("locuschange",(e=>{this.browser.isMultiLocusMode()?this.disable():(this.enable(),this.update(e))}))};ub.prototype.update=function(e){const t=e[0],{bpLength:i}=t.genome.getChromosome(t.chr),{start:n,end:r}=t;cb=Math.ceil(Math.log2(i/this.browser.minimumBases())),this.slider.max=`${cb}`;const s=i/(r-n);db=Math.log2(s),this.slider.value=`${Math.round(db)}`},ub.prototype.enable=function(){this.slider.disabled=!1},ub.prototype.disable=function(){this.slider.disabled=!0},ub.prototype.hide=function(){this.zoomContainer.style.display="none"},ub.prototype.show=function(){this.zoomContainer.style.display="block"},ub.prototype.hideSlider=function(){this.slider.style.display="none"},ub.prototype.showSlider=function(){this.slider.style.display="block"};const fb=function(e,t){const i=_t.div({class:"igv-navbar-button"});e.append(i),i.textContent="Save SVG",i.addEventListener("click",(()=>t.saveSVGtoFile({})))},pb=(e,t)=>{const i=0===e?t.nextElementSibling:t.previousElementSibling;t.remove(),i.remove()},gb=e=>{const t=_t.div({class:"igv-column-shim"});Po(t,e);const i=_t.div({class:"igv-column"});return Po(i,t),i},mb=(e,t)=>{for(let i=0;i1&&i>0){Oo(_t.div({class:"igv-column-shim"}),n)}}};class bb{constructor(e,t,i){this.browser=e,this.referenceFrame=t,this.column=i,this.container=_t.div({class:"igv-center-line"}),i.appendChild(this.container),e.isCenterLineVisible?this.show():this.hide()}repaint(){if(this.referenceFrame){if(1/this.referenceFrame.bpPerPixel>1){const e=Math.floor(this.referenceFrame.toPixels(1));this.container.style.width=`${e}px`,this.container.classList.remove("igv-center-line-thin"),this.container.classList.add("igv-center-line-wide")}else this.container.style.width="1px",this.container.classList.remove("igv-center-line-wide"),this.container.classList.add("igv-center-line-thin")}}show(){this.isVisible=!0,this.container.style.display="block",this.repaint()}hide(){this.isVisible=!1,this.container.style.display="none"}resize(){this.repaint()}}const wb=Xt;class vb{constructor(e){this.browser=e,this.height=40,this.name="",this.id="ruler",this.disableButtons=!0,this.ignoreTrackMenu=!0,this.order=.01*Number.MIN_SAFE_INTEGER,this.removable=!1,this.type="ruler"}async getFeatures(e,t,i){return[]}computePixelHeight(e){return this.height}draw({context:e,referenceFrame:t,pixelWidth:i,pixelHeight:n,bpPerPixel:r,bpStart:s}){Ba.isWholeGenomeView(t.chr)?this.drawWholeGenome({context:e,pixelWidth:i,pixelHeight:n,bpPerPixel:r}):this.doDraw({context:e,referenceFrame:t,pixelWidth:i,pixelHeight:n,bpStart:s})}drawWholeGenome({context:e,pixelWidth:t,pixelHeight:i,bpPerPixel:n}){e.save(),Co.fillRect(e,0,0,t,i,{fillStyle:"white"});for(let t of this.browser.genome.wgChromosomeNames){let r=this.browser.genome.getCumulativeOffset(t),s=this.browser.genome.getChromosome(t).bpLength,o=Math.round(r/n),a=Math.round(s/n);this.renderChromosomeRect(e,o,0,a,i,t)}e.restore()}doDraw({context:e,referenceFrame:t,pixelWidth:i,pixelHeight:n,bpStart:r}){e.clearRect(0,0,i,n);const s=function(e,t){if(e<10)return new yb(1,"bp",1);const i=Math.floor(Math.log10(e));let n="bp",r=1;i>9?(n="gb",r=1e9):i>6?(n="mb",r=1e6):i>3&&(n="kb",r=1e3);const s=Math.pow(10,i-1),o=e/s,a=75,l=Math.pow(10,i-1),h=Math.pow(10,i)/2;return new yb(o0&&u+l<=d&&(Co.fillText(e,i,n,this.height-8),d=0),h>0&&Co.strokeLine(e,h,this.height-6,h,this.height-2),c=Math.floor((1+o)*s.majorTick);let f=h+(Math.round(t.toPixels(c-1-r+.5))-h)/2;f>0&&Co.strokeLine(e,f,this.height-6,f,this.height-2),++o,d+=a}while(he.measureText(o).width&&Co.fillText(e,o,t+n/2,i+r/2,{fillStyle:Hs.greyScale(68)})}get supportsWholeGenome(){return!0}dispose(){}}class yb{constructor(e,t,i){this.majorTick=e,this.minorTick=e/10,this.majorUnit=t,this.unitMultiplier=i}description(e){console.log((e||"")+" tick "+wb(this.majorTick)+" label width "+wb(this.labelWidthBP)+" multiplier "+this.unitMultiplier)}}const _b=function(e,t){this.button=_t.div({class:"igv-navbar-button"}),e.appendChild(this.button),this.button.textContent="circular view",this.button.addEventListener("click",(()=>{t.circularViewVisible=!t.circularViewVisible})),this.browser=t,this.setVisibility(t.config.showCircularViewButton),this.setState(t.circularViewVisible)};_b.prototype.setVisibility=function(e){!0===e?this.show():this.hide()},_b.prototype.setState=function(e){!0===e?this.button.classList.add("igv-navbar-button-clicked"):this.button.classList.remove("igv-navbar-button-clicked")},_b.prototype.show=function(){this.button.style.display="block",this.setState(this.browser.circularViewVisible)},_b.prototype.hide=function(){this.button.style.display="none"};const xb=function(e,t,i){const n=_t.div({class:"igv-navbar-button"});e.append(n),n.textContent=i.label,n.addEventListener("click",(()=>i.callback(t)))};class kb{constructor(e,t,i,n,r){this.browser=e,this.roiMenu=t,this.roiTable=i,this.top=n,this.roiSets=r||[],this.boundLocusChangeHandler=Cb.bind(this),e.on("locuschange",this.boundLocusChangeHandler)}async initialize(){this.roiSets.length>0&&(this.browser.showROITableButton=!0,this.browser.roiTableControl.setVisibility(this.browser.showROITableButton));const e=this.roiSets.map((e=>this.renderROISet({browser:this.browser,pixelTop:this.top,roiSet:e})));e.length>0&&await Promise.all(e);const t=await this.getTableRecords();this.roiTable.renderTable(t)}async loadROI(e,t){const i=Array.isArray(e)?e:[e];for(let e of i)this.roiSets.push(new gc(e,t));await this.initialize()}clearROIs(){this.roiTable.clearTable();const e=this.browser.columnContainer.querySelectorAll(".igv-roi-region");for(let t of e)t.remove();for(let e of this.roiSets)e.dispose();this.roiSets=[]}async getTableRecords(){const e=[];for(let t of this.roiSets){const i=t.isUserDefined?"":t.name||"",n=await t.getAllFeatures();for(let t of Object.keys(n))for(let r of n[t])e.push({setName:i,feature:r})}return e}presentTable(){this.roiTable.present()}async repaintTable(){const e=await this.getTableRecords();this.roiTable.renderTable(e)}dismissTable(){this.roiTable.dismiss()}async updateUserDefinedROISet(e){let t=await this.getUserDefinedROISet();void 0===t&&(t=this.initializeUserDefinedROISet()),t.addFeature(e),!1===this.browser.showROITableButton&&this.setROITableButtonVisibility(!0),await this.renderROISet({browser:this.browser,pixelTop:this.top,roiSet:t});const i=await this.getTableRecords();this.roiTable.renderTable(i)}setROITableButtonVisibility(e){this.browser.showROITableButton=e,this.browser.roiTableControl.setVisibility(this.browser.showROITableButton)}async renderAllROISets(){for(let e of this.roiSets)await this.renderROISet({browser:this.browser,pixelTop:this.top,roiSet:e})}async renderROISet({browser:e,pixelTop:t,roiSet:i}){const n=e.columnContainer.querySelectorAll(".igv-column");for(let r=0;ra)&&e.remove()}const c=await i.getFeatures(s,o,a);if(c)for(let h of c){const c=Sb(s,h.start,h.end),{x:d,width:u}=mc(Math.max(o,h.start),Math.min(a,h.end),o,l),f=n[r].querySelector(Ab(c));if(f)f.style.left=`${d}px`,f.style.width=`${u}px`;else{const s=this.createRegionElement(e.columnContainer,t,d,u,i,c,h.name);n[r].appendChild(s)}}}}createRegionElement(e,t,i,n,r,s,o){const a=_t.div({class:"igv-roi-region"});a.style.top=`${t}px`,a.style.left=`${i}px`,a.style.width=`${n}px`,a.style.backgroundColor=r.color,a.dataset.region=s;const l=_t.div();return a.appendChild(l),l.style.backgroundColor=r.headerColor,!0===r.isUserDefined?l.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation();const{x:i,y:n}=_t.translateMouseCoordinates(t,e);this.roiMenu.present(i,n,this,e,a)})):o?l.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),this.popover&&this.popover.dispose(),this.popover=new Vt(e,r.name),this.popover.presentContentWithEvent(t,o)})):l.style.pointerEvents="none",a}renderSVGContext(e,{deltaX:t,deltaY:i}){for(const n of document.querySelectorAll(".igv-roi-region")){const{x:r,y:s,width:o,height:a}=n.getBoundingClientRect();e.fillStyle=n.style.backgroundColor,e.fillRect(r+t,s+i,o,a);const l=n.querySelector("div"),{x:h,y:c,width:d,height:u}=l.getBoundingClientRect();e.fillStyle=l.style.backgroundColor,e.fillRect(h+t,c+i,d,u)}}async getUserDefinedROISet(){return this.roiSets.find((e=>!0===e.isUserDefined))}initializeUserDefinedROISet(){const e=new gc({isUserDefined:!0,features:[]},this.browser.genome);return this.roiSets.push(e),e}async deleteUserDefinedRegionWithKey(e,t){t.querySelectorAll(Ab(e)).forEach((e=>e.remove()));const i=await this.findUserDefinedRegionWithKey(e),n=await this.getUserDefinedROISet();n&&n.removeFeature(i);0===(await this.getTableRecords()).length&&(this.browser.roiTableControl.buttonHandler(!1),this.setROITableButtonVisibility(!1))}async findUserDefinedRegionWithKey(e){const{chr:t,start:i,end:n}=Eb(e),r=await this.getUserDefinedROISet();if(r){const e=await r.getFeatures(t,i,n);for(let r of e)if(r.chr===t&&r.start>=i&&r.end<=n)return r}}toJSON(){return this.roiSets.map((e=>e.toJSON()))}dispose(){this.browser.off("locuschange",this.boundLocusChangeHandler);const e=this.browser.columnContainer.querySelectorAll(".igv-roi-region");for(let t of e)t.remove();this.roiMenu&&this.roiMenu.dispose(),this.roiTable&&this.roiTable.dispose();for(let e of this.roiSets)e.dispose();for(let e of Object.keys(this))this[e]=void 0}}function Cb(){this.renderAllROISets()}function Sb(e,t,i){return`${e}-${t}-${i}`}function Ab(e){return`[data-region="${e}"]`}function Eb(e){let[t,i,n]=e.split("-");return i=parseInt(i),n=parseInt(n),{chr:t,start:i,end:n,locus:`${t}:${i}-${n}`,bedRecord:`${t}\t${i}\t${n}`}}class Mb extends nu{constructor(e){super(Object.assign({width:"512px"},e))}tableRowDOM(e){const t=_t.div({class:"igv-roi-table-row"}),{setName:i,feature:n}=e;t.dataset.region=Sb(n.chr,n.start,n.end);let r=[n.chr,Xt(n.start),Xt(n.end),n.name||"",i];4===this.columnFormat.length&&(r=r.slice(0,4));for(let e=0;ee.remove())),e.length>0){const t=e.sort(((e,t)=>e.feature.chr.localeCompare(t.feature.chr)||e.feature.start-t.feature.start||e.feature.end-t.feature.end));for(let e of t){const t=this.tableRowDOM(e);this.tableRowContainer.appendChild(t)}}}dispose(){document.removeEventListener("click",this.boundGotoButtonHandler),this.browser.roiTableControl.buttonHandler(!1),super.dispose()}static getColumnFormatConfiguration(e){return!0===e?[{label:"Chr",width:"20%"},{label:"Start",width:"15%"},{label:"End",width:"15%"},{label:"Description",width:"30%"},{label:"ROI Set",width:"20%"}]:[{label:"Chr",width:"25%"},{label:"Start",width:"20%"},{label:"End",width:"20%"},{label:"Description",width:"35%"}]}static gotoButtonHandler(e){e.stopPropagation();const t=this.tableDOM.querySelectorAll(".igv-roi-table-row-selected"),i=[];for(let e of t){const{locus:t}=Eb(e.dataset.region);i.push(t)}for(let e of this.tableDOM.querySelectorAll(".igv-roi-table-row"))e.classList.remove("igv-roi-table-row-selected");this.setTableRowSelectionState(!1),i.length>0&&this.browser.search(i.join(" "))}}class Tb{constructor(e,t){this.browser=e,this.container=_t.div({class:"igv-roi-menu-next-gen"}),t.appendChild(this.container);const i=_t.div();this.container.appendChild(i),Mt.attachDialogCloseHandlerWithParent(i,(()=>this.container.style.display="none")),this.body=_t.div(),this.container.appendChild(this.body),this.container.style.display="none"}async present(e,t,i,n,r){Rb(this.body);const s=await this.browser.roiManager.findUserDefinedRegionWithKey(r.dataset.region),o=_t.div();this.body.appendChild(o);const a="Description",l=s.name||a;o.innerText=l,o.setAttribute("title",l),a===l?o.classList.add("igv-roi-placeholder"):o.classList.remove("igv-roi-placeholder");const h=_t.div();this.body.appendChild(h),h.innerText="Set Description",h.addEventListener("click",(e=>{e.stopPropagation(),this.container.style.display="none";const t={label:"Description",value:s.name||"",callback:()=>{const e=this.browser.inputDialog.input.value||"";s.name=e.trim(),this.container.style.display="none",this.browser.roiManager.repaintTable()}};this.browser.inputDialog.present(t,e)}));const c=_t.div();this.body.appendChild(c),c.innerText="Delete Region",c.addEventListener("click",(e=>{e.stopPropagation(),this.container.style.display="none",this.browser.roiManager.deleteUserDefinedRegionWithKey(r.dataset.region,this.browser.columnContainer)})),this.container.style.left=`${e}px`,this.container.style.top=`${t}px`,this.container.style.display="flex"}async __present(e,t,i,n,r){Rb(this.container);const s=await this.browser.roiManager.findUserDefinedRegionWithKey(r.dataset.region);let o;o=_t.div({class:"igv-roi-menu-row-edit-description"}),this.container.appendChild(o),o.addEventListener("click",(e=>{e.stopPropagation()}));const a="description-input",l=document.createElement("label");o.appendChild(l),l.setAttribute("for",a),l.innerText="Description:";const h=document.createElement("input");o.appendChild(h),h.setAttribute("type","text"),h.setAttribute("name",a),h.setAttribute("placeholder",""),h.value=s.name||"",h.addEventListener("change",(async e=>{e.stopPropagation();(await this.browser.roiManager.findUserDefinedRegionWithKey(r.dataset.region)).name=h.value,h.blur(),this.container.style.display="none",await this.browser.roiManager.repaintTable()})),o=_t.div({class:"igv-roi-menu-row"}),o.innerText="Delete region",this.container.appendChild(o),o.addEventListener("click",(e=>{e.stopPropagation(),this.container.style.display="none",this.browser.roiManager.deleteUserDefinedRegionWithKey(r.dataset.region,this.browser.columnContainer)})),this.container.style.left=`${e}px`,this.container.style.top=`${t}px`,this.container.style.display="flex",n.addEventListener("click",(e=>{e.stopPropagation(),this.container.style.display="none"}))}dispose(){this.container.innerHTML=""}}function Rb(e){for(;e.firstChild;)e.removeChild(e.firstChild)}class Lb{constructor(e,t){this.name=e.name,this.featureSource=e.featureSource||uc(e,t),this.color=e.color||fc}async getFeatures(e,t,i){return this.featureSource.getFeatures({chr:e,start:t,end:i})}draw(e){const{context:t,bpPerPixel:i,bpStart:n,pixelTop:r,pixelHeight:s,pixelWidth:o,features:a}=e;if(!a)return;const l=n+o*i+1;for(let{start:e,end:o}of a){if(ol)break;const{x:a,width:h}=mc(e,o,n,i);Co.fillRect(t,a,r,h,s,{fillStyle:this.color})}}}class Ib{constructor(e,t){this.browser=t,this.button=_t.div({class:"igv-navbar-button"}),e.appendChild(this.button),this.button.textContent="ROI Table",this.button.addEventListener("click",(()=>{this.buttonHandler(!t.roiTableVisible)})),this.browser=t,this.setVisibility(t.showROITableButton),this.setState(t.roiTableVisible)}buttonHandler(e){this.browser.roiTableVisible=e,this.setState(this.browser.roiTableVisible),this.browser.setROITableVisibility(this.browser.roiTableVisible)}setVisibility(e){!0===e?this.show():this.hide()}setState(e){!0===e?this.button.classList.add("igv-navbar-button-clicked"):this.button.classList.remove("igv-navbar-button-clicked")}show(){this.button.style.display="block",this.setState(this.browser.roiTableVisible)}hide(){this.button.style.display="none"}}class Bb{constructor(e,t){this.config=e,this.guid=_t.guid(),this.namespace=".browser_"+this.guid,this.parent=t,this.root=_t.div({class:"igv-container"}),t.appendChild(this.root),this.alert=new Ao(this.root),this.columnContainer=_t.div({class:"igv-column-container"}),this.root.appendChild(this.columnContainer),this.menuPopup=new Au(this.columnContainer),this.initialize(e),this.trackViews=[],this.constants={dragThreshold:3,scrollThreshold:5,defaultColor:"rgb(0,0,150)",doubleClickDelay:e.doubleClickDelay||500},this.eventHandlers={},this.addMouseHandlers(),this.setControls(e)}initialize(e){e.gtex&&(Wd.gtexLoaded=!0),this.flanking=e.flanking,this.crossDomainProxy=e.crossDomainProxy,this.formats=e.formats,this.trackDefaults=e.trackDefaults,this.nucleotideColors=e.nucleotideColors||da;for(let e of Object.keys(this.nucleotideColors))this.nucleotideColors[e.toLowerCase()]=this.nucleotideColors[e];this.trackLabelsVisible=e.showTrackLabels,this.roiTableVisible=e.showROITable,this.showROITableButton=e.showROITableButton,this.isCenterLineVisible=e.showCenterGuide,this.cursorGuideVisible=e.showCursorGuide,this.showSampleNames=e.showSampleNames,this.showSampleNameButton=e.showSampleNameButton,this.sampleNameViewportWidth=e.sampleNameViewportWidth||200,e.search&&(this.searchConfig={type:"json",url:e.search.url,coords:void 0===e.search.coords?1:e.search.coords,chromosomeField:e.search.chromosomeField||"chromosome",startField:e.search.startField||"start",endField:e.search.endField||"end",geneField:e.search.geneField||"gene",snpField:e.search.snpField||"snp",resultsField:e.search.resultsField})}setControls(e){const t=this.createStandardControls(e);t.insertBefore(ft(this.columnContainer)),this.$navigation=t,!1===e.showControls&&t.hide()}createStandardControls(e){this.navbarManager=new ib(this);const t=ft("
",{class:"igv-navbar"});this.$navigation=t;const i=ft("
",{class:"igv-navbar-left-container"});t.append(i);const n=ft("
",{class:"igv-logo"});i.append(n);const r=ft('IGV; ');r.css("width","34px"),r.css("height","32px"),n.append(r),this.$current_genome=ft("
",{class:"igv-current-genome"}),i.append(this.$current_genome),this.$current_genome.text("");const s=ft("
",{class:"igv-navbar-genomic-location"});i.append(s),this.chromosomeSelectWidget=new nb(this,s.get(0)),void 0===e.showChromosomeWidget&&(e.showChromosomeWidget=!0),!0===e.showChromosomeWidget?this.chromosomeSelectWidget.show():this.chromosomeSelectWidget.hide();const o=ft("
",{class:"igv-locus-size-group"});s.append(o);const a=ft("
",{class:"igv-search-container"});o.append(a),this.$searchInput=ft("",{class:"igv-search-input",type:"text",placeholder:"Locus Search"}),a.append(this.$searchInput),this.$searchInput.change((()=>this.doSearch(this.$searchInput.val())));const l=_t.div({class:"igv-search-icon-container"});a.append(ft(l)),l.appendChild(At.createIcon("search")),l.addEventListener("click",(()=>this.doSearch(this.$searchInput.val()))),this.windowSizePanel=new rb(o.get(0),this);const h=ft("
",{class:"igv-navbar-right-container"});t.append(h);const c=ft('
');if(h.append(c),this.$toggle_button_container=c,this.cursorGuide=new sb(this.columnContainer,this),this.cursorGuideButton=new ob(this,c.get(0)),this.centerLineButton=new ab(this,c.get(0)),this.setTrackLabelVisibility(e.showTrackLabels),this.trackLabelControl=new lb(c.get(0),this),this.roiTableControl=new Ib(c.get(0),this),this.sampleNameControl=new hb(c.get(0),this),!0===e.showSVGButton&&(this.svgSaveControl=new fb(c.get(0),this)),e.customButtons)for(let t of e.customButtons)new xb(c.get(0),this,t);return this.zoomWidget=new ub(this,h.get(0)),!1===e.showNavigation&&this.$navigation.hide(),this.inputDialog=new Nt(this.root),this.inputDialog.container.id=`igv-input-dialog-${_t.guid()}`,this.dataRangeDialog=new Zt(this,ft(this.root)),this.dataRangeDialog.$container.get(0).id=`igv-data-range-dialog-${_t.guid()}`,this.genericColorPicker=new qt({parent:this.columnContainer,width:432}),this.genericColorPicker.container.id=`igv-track-color-picker-${_t.guid()}`,t}getSampleNameViewportWidth(){return!1===this.showSampleNames?0:this.sampleNameViewportWidth}isMultiLocusMode(){return this.referenceFrameList&&this.referenceFrameList.length>1}addTrackToFactory(e,t){$m(e,t)}isMultiLocusWholeGenomeView(){if(void 0===this.referenceFrameList||1===this.referenceFrameList.length)return!1;for(let e of this.referenceFrameList)if("all"===e.chr.toLowerCase())return!0;return!1}currentLoci(){const e=e=>`${e.chr}:${e.start+1}-${e.end}`;return void 0===this.referenceFrameList||0===this.referenceFrameList.length?"":1===this.referenceFrameList.length?e(this.referenceFrameList[0]):this.referenceFrameList.map((t=>e(t)))}toSVG(){const{y:e,width:t,height:i}=this.columnContainer.getBoundingClientRect(),n=new Ta({width:t,height:8e3,backdropColor:"white",multiLocusGap:0,viewbox:{x:0,y:0,width:t,height:8e3}}),r={deltaX:0,deltaY:-e};for(let e of this.trackViews)e.renderSVGContext(n,r);return this.roiManager.renderSVGContext(n,r),n.setHeight(i),n.getSerializedSvg(!0)}renderSVG(e){const t=this.toSVG();return e.empty(),e.append(t),t}saveSVGtoFile(e){let t=this.toSVG();e.$container&&(e.$container.empty(),e.$container.append(t));ii(e.filename||"igvjs.svg",URL.createObjectURL(new Blob([t],{type:"application/octet-stream"})))}async loadSession(e){let t;return this.roiSets=[],t=e.url||e.file?await async function(e){const t=e.url||e.file;if(e.url&&(e.url.startsWith("blob:")||e.url.startsWith("data:"))){const t=Bb.uncompressSession(e.url);return JSON.parse(t)}{let i=e.filename;if(i||(i=e.url?await Io(e.url):e.file.name),i.endsWith(".xml")){const e=Ba.KNOWN_GENOMES,i=await ro.loadString(t);return new Gm(i,e)}return i.endsWith(".json")?ro.loadJson(t):void 0}}(e):e,this.loadSessionObject(t)}async loadSessionObject(e){this.cleanHouseForSession(),this.showSampleNames=e.showSampleNames||!1,this.sampleNameControl.setState(!0===this.showSampleNames),e.sampleNameViewportWidth&&(this.sampleNameViewportWidth=e.sampleNameViewportWidth),No(this.columnContainer,"igv-axis-column"),No(this.columnContainer,"igv-sample-name-column"),No(this.columnContainer,"igv-scrollbar-column"),No(this.columnContainer,"igv-track-drag-column"),No(this.columnContainer,"igv-gear-menu-column");const t=e.reference||e.genome;if(!t)return void console.warn("No genome or reference object specified");const i=await Ba.expandReference(this.alert,t);await this.loadReference(i,e.locus),this.centerLineList=this.createCenterLineList(this.columnContainer);let n=0;if(!1!==e.showIdeogram){const e=new af(this);e.id="ideogram";const t=new Ru(this,this.columnContainer,e),{$viewport:i}=t.viewports[0];n=function(e){e="string"==typeof e?document.querySelector(e):e;const t=window.getComputedStyle(e),i=parseFloat(t.marginTop)+parseFloat(t.marginBottom),n=e.offsetHeight;return Math.ceil(i+n)}(i.get(0)),this.trackViews.push(t)}if(!1!==e.showRuler&&this.trackViews.push(new Ru(this,this.columnContainer,new vb(this))),e.gtexSelections)for(let t of this.referenceFrameList)for(let i of Object.keys(e.gtexSelections)){const n=e.gtexSelections[i].gene,r=e.gtexSelections[i].snp;t.selection=new Ym(n,r)}this.roiManager&&this.roiManager.dispose();const r=new Tb(this,this.columnContainer),s={browser:this,parent:this.columnContainer,headerTitle:"Regions of Interest",dismissHandler:()=>this.roiTableControl.buttonHandler(!1),gotoButtonHandler:Mb.gotoButtonHandler};if(e.roi){const t=e.roi.map((e=>new gc(e,this.genome))),i=t.filter((({name:e})=>void 0!==e&&e.length>0));s.columnFormat=Mb.getColumnFormatConfiguration(i.length>0);const o=new Mb(s);this.roiManager=new kb(this,r,o,n,t)}else{s.columnFormat=Mb.getColumnFormatConfiguration(!1);const e=new Mb(s);this.roiManager=new kb(this,r,e,n,void 0)}await this.roiManager.initialize();const o=i.tracks||[],a=e.tracks?o.concat(e.tracks):o;0===a.filter((e=>"sequence"===e.type&&!e.url&&!e.fastaURL)).length&&a.push({type:"sequence",order:ua});let l=1;for(let e of a)void 0===e.order&&(e.order=l++);await this.loadTrackList(a);for(let e of this.trackViews.filter((e=>"ruler"===e.track.type||"ideogram"===e.track.type)))e.updateViews();this.updateUIWithReferenceFrameList()}createCenterLineList(e){const t=e.querySelectorAll(".igv-center-line");for(let e=0;ee.remove())),this.trackViews=[],this.circularView&&this.circularView.clearChords()}updateNavbarDOMWithGenome(e){let t=e.id&&e.id.length<10?e.id:"";this.$current_genome.text(t),this.$current_genome.attr("title",e.id||""),this.chromosomeSelectWidget.update(e)}async loadGenome(e){const t=await Ba.expandReference(this.alert,e);await this.loadReference(t,void 0);const i=t.tracks||[];return 0===i.filter((e=>"sequence"===e.type)).length&&i.push({type:"sequence",order:ua}),await this.loadTrackList(i),await this.updateViews(),this.genome}updateUIWithReferenceFrameList(){const e=this.referenceFrameList;this.updateLocusSearchWidget();const t=this.isMultiLocusWholeGenomeView()||Ba.isWholeGenomeView(e[0].chr);this.navbarManager.navbarDidResize(this.$navigation.width(),t),Pb(this.trackViews,this.trackLabelsVisible),this.setCenterLineAndCenterLineButtonVisibility(!Ba.isWholeGenomeView(e[0].chr))}setTrackLabelVisibility(e){Pb(this.trackViews,e)}setROITableVisibility(e){!0===e?this.roiManager.presentTable():this.roiManager.dismissTable()}setCursorGuideVisibility(e){e?this.cursorGuide.show():this.cursorGuide.hide()}setCustomCursorGuideMouseHandler(e){this.cursorGuide.customMouseHandler=e}setCenterLineVisibility(e){for(let t of this.centerLineList)!0===e?(t.show(),t.repaint()):t.hide()}setCenterLineAndCenterLineButtonVisibility(e){for(let t of this.centerLineList){e&&t.isVisible?t.show():t.container.style.display="none"}e&&this.centerLineButton.isVisible?this.centerLineButton.show():this.centerLineButton.button.style.display="none"}async loadTrackList(e){const t=[];for(let i of e)t.push(this._loadTrack(i));const i=await Promise.all(t);return this.trackViews.filter((function(e){return e.track.autoscaleGroup})).length>0&&this.updateViews(),i}async loadTrack(e){const t=this._loadTrack(e);return e.autoscaleGroup&&(await t,this.updateViews()),t}async _loadTrack(e){Qt(e)&&(e=JSON.parse(e));try{const t=await this.createTrack(e);if(void 0===t)return;void 0===t.order&&(t.order=this.trackViews.length);const i=new Ru(this,this.columnContainer,t);if(this.trackViews.push(i),Pb(this.trackViews,this.trackLabelsVisible),this.reorderTracks(),this.fireEvent("trackorderchanged",[this.getTrackOrder()]),"function"==typeof t.postInit)try{i.startSpinner(),await t.postInit()}finally{i.stopSpinner()}return t.autoscaleGroup||(e.sync?await i.updateViews():i.updateViews()),"function"==typeof t.hasSamples&&t.hasSamples()&&!1!==this.config.showSampleNameButton&&this.sampleNameControl.show(),t}catch(t){const i={401:"Access unauthorized",403:"Access forbidden",404:"Not found"};console.error(t);let n=t.message||t.error||t.toString();i.hasOwnProperty(n)&&(n=i[n]),n+=": "+e.url,this.alert.present(new Error(n),void 0)}}async loadROI(e){await this.roiManager.loadROI(e,this.genome)}clearROIs(){this.roiManager.clearROIs()}async getUserDefinedROIs(){if(this.roiManager){const e=await this.roiManager.getUserDefinedROISet();if(void 0===e)return[];const t=await e.getAllFeatures(),i=[];for(let e of Object.values(t))i.push(...e);return i}return[]}getRulerTrackView(){const e=this.trackViews.filter((({track:e})=>"ruler"===e.id));return e.length>0?e[0]:void 0}async createTrack(e){let t=await async function(e){return"function"==typeof e?e():e} +/*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */(e.url||e.fastaURL);if(Qt(t)&&(t=t.trim()),t)if(e.format)e.format=e.format.toLowerCase();else if(e.fastaURL)e.format="fasta";else{let i=e.filename;i||(i=await Io(t));const n=jo(i);"tsv"===n?e.format=await Go(e):n?e.format=n:"htsget"===e.sourceType&&await Ih.inferFormat(e)}let i=e.type?e.type.toLowerCase():void 0;if(!i){if(i=Wo(e),"bedtype"===i){const t=uc(e,this.genome);e._featureSource=t;const n=await t.trackType();i=n||"annotation"}e.type=i}if(this.trackDefaults&&i){const t=this.trackDefaults[i];if(t)for(let i in t)t.hasOwnProperty(i)&&void 0===e[i]&&(e[i]=t[i])}const n=Wm(i,e,this);if(void 0!==n)return e.roi&&e.roi.length>0&&(n.roiSets=e.roi.map((e=>new Lb(e,this.genome)))),n;this.alert.present(new Error(`Error creating track. Could not determine track type for file: ${e.url||e}`),void 0)}reorderTracks(){this.trackViews.sort((function(e,t){const i=e=>"ideogram"===e.track.id?1:"ruler"===e.track.id?2:3,n=i(e),r=i(t);if(n===r){return(e.track.order||0)-(t.track.order||0)}return n-r}));for(let{axis:e,viewports:t,sampleNameViewport:i,outerScroll:n,dragHandle:r,gearContainer:s}of this.trackViews){e.remove();for(let{$viewport:e}of t)e.detach();i.viewport.remove(),n.remove(),r.remove(),s.remove()}const e=this.columnContainer.querySelectorAll(".igv-column");for(let{axis:t,viewports:i,sampleNameViewport:n,outerScroll:r,dragHandle:s,gearContainer:o}of this.trackViews){this.columnContainer.querySelector(".igv-axis-column").appendChild(t);for(let t=0;te.track&&e.track.name)).map((e=>e.track.name))}removeTrackByName(e){const t=this.trackViews.slice();for(let i of t)e===i.track.name&&this.removeTrack(i.track)}removeTrack(e){for(let t of this.trackViews)if(e===t.track){this._removeTrack(t.track);break}}_removeTrack(e){e.disposed||(this.trackViews.splice(this.trackViews.indexOf(e.trackView),1),this.fireEvent("trackremoved",[e]),this.fireEvent("trackorderchanged",[this.getTrackOrder()]),e.trackView&&e.trackView.dispose())}removeAllTracks(){const e=[];for(let t of this.trackViews)"ruler"!==t.track.id&&"ideogram"!==t.track.id?(this.fireEvent("trackremoved",[t.track]),t.dispose()):e.push(t);this.trackViews=e}findTracks(e,t){let i="function"==typeof e?t=>e(t.track):i=>t===i.track[e];return this.trackViews.filter(i).map((e=>e.track))}setTrackHeight(e){this.trackHeight=e,this.trackViews.forEach((function(t){t.setTrackHeight(e)}))}async visibilityChange(){this.layoutChange()}async layoutChange(){if(this.referenceFrameList.find((e=>e.bpPerPixel<0))){const e=this.calculateViewportWidth(this.referenceFrameList.length);for(let t of this.referenceFrameList)t.bpPerPixel=(t.end-t.start)/e}if(this.referenceFrameList){const e=this.isMultiLocusWholeGenomeView()||Ba.isWholeGenomeView(this.referenceFrameList[0].chr);this.navbarManager.navbarDidResize(this.$navigation.width(),e)}Fb.call(this),await this.updateViews()}async updateViews(){const e=this.trackViews;this.updateLocusSearchWidget();for(let e of this.centerLineList)e.repaint();if(this.dragObject)for(let t of e)await t.updateViews();else{const r={},s=[];for(let i of e){const e=i.track.autoscaleGroup;if(e){var t=r[e];t||(t=[],r[e]=t),t.push(i)}else s.push(i)}if(Object.entries(r).length>0){const e=Object.keys(r);for(let t of e){const e=r[t],s=[];for(let t of e)s.push(t.getInViewFeatures());const o=await Promise.all(s);var i,n=[];for(let e of o)n=n.concat(e);i=Ro(n);const a=[];for(let t of e)t.track.dataRange=i,t.track.autoscale=!1,a.push(t.updateViews());await Promise.all(a)}}await Promise.all(s.map((e=>e.updateViews())))}}repaintViews(){for(let e of this.trackViews)e.repaintViews()}updateLocusSearchWidget(){if(!this.referenceFrameList)return;const e=this.referenceFrameList,t=this.calculateViewportWidth(this.referenceFrameList.length);for(let i of e)i.end=i.start+i.bpPerPixel*t;this.chromosomeSelectWidget.select.value=1===e.length?this.referenceFrameList[0].chr:"";const i=this.referenceFrameList.map((e=>e.getLocusString())).join(" ");this.$searchInput.val(i),this.fireEvent("locuschange",[this.referenceFrameList])}calculateViewportWidth(e){let{width:t}=this.columnContainer.getBoundingClientRect();return t-=50+this.getSampleNameViewportWidth()+14+12+28,t-=5*(e-1),Math.floor(t/e)}getCenterLineXOffset(){let{width:e}=this.columnContainer.getBoundingClientRect();return e-=50+this.getSampleNameViewportWidth()+14+12+28,Math.floor(e/2+50)}minimumBases(){return this.config.minimumBases}zoomIn(){this.zoomWithScaleFactor(.5)}zoomOut(){this.zoomWithScaleFactor(2)}async zoomWithScaleFactor(e,t,i){if(!this.referenceFrameList)return;const n=this.calculateViewportWidth(this.referenceFrameList.length);let r=i?[i]:this.referenceFrameList;for(let i of r)i.zoomWithScaleFactor(this,e,n,t)}async addMultiLocusPanel(e,t,i,n){if(!this.referenceFrameList)return;const r=this.calculateViewportWidth(1+this.referenceFrameList.length),s=this.calculateViewportWidth(this.referenceFrameList.length)/this.calculateViewportWidth(1+this.referenceFrameList.length);for(let e of this.referenceFrameList)e.bpPerPixel*=s;const o=(i-t)/r,a=new Jm(this.genome,e,t,i,o),l=n?this.referenceFrameList.indexOf(n):this.referenceFrameList.length-1,h=1+l,{$viewport:c}=this.trackViews[0].viewports[l],d=gb(c.get(0).parentElement);if(h===this.referenceFrameList.length){this.referenceFrameList.push(a);for(let e of this.trackViews){const t=xu(e,d,a);e.viewports.push(t)}}else{this.referenceFrameList.splice(h,0,a);for(let e of this.trackViews){const t=xu(e,d,a);e.viewports.splice(h,0,t)}}this.centerLineList=this.createCenterLineList(this.columnContainer),Fb.call(this),await this.updateViews(!0)}async removeMultiLocusPanel(e){const t=this.referenceFrameList.indexOf(e),{$viewport:i}=this.trackViews[0].viewports[t];pb(t,i.parent().get(0));for(let{viewports:e}of this.trackViews)e[t].dispose(),e.splice(t,1);if(this.referenceFrameList.splice(t,1),1===this.referenceFrameList.length&&this.getRulerTrackView())for(let e of this.getRulerTrackView().viewports)e.dismissLocusLabel();const n=this.calculateViewportWidth(1+this.referenceFrameList.length)/this.calculateViewportWidth(this.referenceFrameList.length);await this.rescaleForMultiLocus(n)}async gotoMultilocusPanel(e){const t=this.referenceFrameList.indexOf(e);this.columnContainer.querySelectorAll(".igv-column").forEach(((e,i)=>{i===t||e.remove()})),this.columnContainer.querySelectorAll(".igv-column-shim").forEach((e=>e.remove()));for(let e of this.trackViews){const i=e.viewports[t];e.viewports.filter(((e,i)=>i!==t)).forEach((e=>e.dispose())),e.viewports=[i]}const i=this.calculateViewportWidth(1);e.bpPerPixel=(e.end-e.start)/i,this.referenceFrameList=[e],this.trackViews.forEach((({viewports:e})=>e.forEach((e=>e.setWidth(i))))),this.centerLineList=this.createCenterLineList(this.columnContainer),this.updateUIWithReferenceFrameList(),await this.updateViews(!0)}async rescaleForMultiLocus(e){const t=this.calculateViewportWidth(this.referenceFrameList.length);for(let t of this.referenceFrameList)t.bpPerPixel*=e;for(let{viewports:e}of this.trackViews)for(let i of e)i.setWidth(t);this.centerLineList=this.createCenterLineList(this.columnContainer),this.updateUIWithReferenceFrameList(),await this.updateViews()}async goto(e,t,i){await this.search(e+":"+t+"-"+i)}async doSearch(e,t){const i=await this.search(e,t);return i||this.alert.present(new Error(`Unrecognized locus: ${e} `)),i}async search(e,t){const i=await tb(this,e);if(i&&i.length>0){this.referenceFrameList=function(e,t,i,n,r,s){return e.map((e=>{if(i&&e.gene&&(e.start=Math.max(0,e.start-i),e.end+=i),!s){const i=t.getChromosome(e.chr);Lo(i.bpLength,e,n)}const o=new Jm(t,e.chr,e.start,e.end,(e.end-e.start)/r);return o.locusSearchString=e.locusSearchString,(e.gene||e.snp)&&(o.selection=new Ym(e.gene,e.snp)),o}))}(i,this.genome,this.flanking,this.minimumBases(),this.calculateViewportWidth(i.length),this.isSoftclipped());for(let e of this.trackViews)e.removeDOMFromColumnContainer();this.columnContainer.querySelectorAll(".igv-column-shim, .igv-column").forEach((e=>e.remove())),mb(this.columnContainer.querySelector(".igv-sample-name-column"),this.referenceFrameList.length),this.centerLineList=this.createCenterLineList(this.columnContainer);for(let e of this.trackViews)e.addDOMToColumnContainer(this,this.columnContainer,this.referenceFrameList);return this.updateUIWithReferenceFrameList(),t||await this.updateViews(),!0}return!1}async loadSampleInformation(e){var t=e;ti(e)&&(t=e.name),"fam"===t.substr(t.lastIndexOf(".")+1)&&(this.sampleInformation=await function(e,t){return(new Xm).loadPlinkFile(e,t)}(e))}on(e,t){this.eventHandlers[e]||(this.eventHandlers[e]=[]),this.eventHandlers[e].push(t)}un(e,t){this.off(e,t)}off(e,t){if(e)if(t){const i=this.eventHandlers[e];if(i&&0!==i.length){const n=i.indexOf(t);-1!==n&&this.eventHandlers[e].splice(n,1)}else console.warn("No handlers to remove for event: "+e)}else this.eventHandlers[e]=[];else this.eventHandlers={}}fireEvent(e,t,i){const n=this.eventHandlers[e];if(void 0===n||0===n.length)return;const r=i||window;return n.map((function(e){return e.apply(r,t)}))[0]}dispose(){this.removeMouseHandlers();for(let e of this.trackViews)e.dispose()}toJSON(){const e={version:"2.15.9"};if(void 0!==this.showSampleNames&&(e.showSampleNames=this.showSampleNames),200!==this.sampleNameViewportWidth&&(e.sampleNameViewportWidth=this.sampleNameViewportWidth),e.reference=this.genome.toJSON(),e.reference.fastaURL instanceof File)throw new Error(`Error. Sessions cannot include local file references ${e.reference.fastaURL.name}.`);if(e.reference.indexURL instanceof File)throw new Error(`Error. Sessions cannot include local file references ${e.reference.indexURL.name}.`);const t=[],i={};let n=!1,r=this.trackViews[0];for(let{referenceFrame:e}of r.viewports){const r=e.getLocusString();if(t.push(r),e.selection){const t={gene:e.selection.gene,snp:e.selection.snp};i[r]=t,n=!0}}e.locus=1===t.length?t[0]:t,n&&(e.gtexSelections=i),e.roi=this.roiManager.toJSON();const s=[],o=[];for(let{track:e}of this.trackViews)try{let t;t="function"==typeof e.getState?e.getState():e.config,t&&(t.browser&&delete t.browser,t.order=e.order,s.push(t))}catch(t){console.error(`Track: ${e.name}: ${t}`),o.push(`Track: ${e.name}: ${t}`)}if(o.length>0){let e=1,t="Errors encountered saving session:
";for(let i of o)t+=` (${e++}) ${i.toString()}
`;throw Error(t)}return e.tracks=s,e}compressedSession(){return function(e){const t=new Uint8Array(e.length);for(var i=0;i0?e.substring(0,t):e)+"?sessionURL=blob:"+this.compressedSession()}mouseDownOnViewport(e,t){var i;i=_t.pageCoordinates(e),this.vpMouseDown={viewport:t,lastMouseX:i.x,mouseDownX:i.x,lastMouseY:i.y,mouseDownY:i.y,referenceFrame:t.referenceFrame}}cancelTrackPan(){const e=this.dragObject;this.dragObject=void 0,this.isScrolling=!1,this.vpMouseDown=void 0,e&&e.viewport.referenceFrame.start!==e.start&&(this.updateViews(),this.fireEvent("trackdragend"))}isTrackPanning(){return this.dragObject}isSoftclipped(){return void 0!==this.trackViews.find((e=>!0===e.track.showSoftClips))}startTrackDrag(e){this.dragTrack=e}updateTrackDrag(e){if(e&&this.dragTrack){const t=this.dragTrack,i=this.trackViews.indexOf(e),n=this.trackViews.indexOf(t),r=this.trackViews;r[i]=t,r[n]=e;const s=this.trackViews[i].track.order;this.trackViews[n].track.order=s;const o=r.length;let a=s;if(i0;e--){const t=r[e].track;if(!(t.order>=a))break;t.order=Math.max(-Number.MAX_SAFE_INTEGER,a-1),a=t.order}this.reorderTracks()}}endTrackDrag(){this.dragTrack?(this.dragTrack=void 0,this.fireEvent("trackorderchanged",[this.getTrackOrder()])):this.dragTrack=void 0}addMouseHandlers(){this.addWindowResizeHandler(),this.addRootMouseUpHandler(),this.addRootMouseLeaveHandler(),this.addColumnContainerEventHandlers()}removeMouseHandlers(){this.removeWindowResizeHandler(),this.removeRootMouseUpHandler(),this.removeRootMouseLeaveHandler(),this.removeColumnContainerEventHandlers()}addWindowResizeHandler(){this.boundWindowResizeHandler=Fb.bind(this),window.addEventListener("resize",this.boundWindowResizeHandler)}removeWindowResizeHandler(){window.removeEventListener("resize",this.boundWindowResizeHandler)}addRootMouseUpHandler(){this.boundRootMouseUpHandler=Ob.bind(this),this.root.addEventListener("mouseup",this.boundRootMouseUpHandler)}removeRootMouseUpHandler(){this.root.removeEventListener("mouseup",this.boundRootMouseUpHandler)}addRootMouseLeaveHandler(){this.boundRootMouseLeaveHandler=Ob.bind(this),this.root.addEventListener("mouseleave",this.boundRootMouseLeaveHandler)}removeRootMouseLeaveHandler(){this.root.removeEventListener("mouseleave",this.boundRootMouseLeaveHandler)}addColumnContainerEventHandlers(){this.boundColumnContainerMouseMoveHandler=Nb.bind(this),this.boundColumnContainerTouchMoveHandler=Nb.bind(this),this.boundColumnContainerMouseLeaveHandler=Ob.bind(this),this.boundColumnContainerMouseUpHandler=Ob.bind(this),this.boundColumnContainerTouchEndHandler=Ob.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)}static uncompressSession(e){let t;if(e.indexOf("/gzip;base64")>0){t=Fs(e);let i="";for(let e of t)i+=String.fromCharCode(e);return i}return function(e){e=e.replace(/\./g,"+").replace(/_/g,"/").replace(/-/g,"=");const t=atob(e),i=[];for(let e=0;e{const r=e.data,s=r.mate;function o(e){e.chr=i.genome.getChromosomeName(e.refName);let t=!1;for(let n of i.referenceFrameList){const i=fd.fromLocusString(n.getLocusString());if(i.contains(e)){t=!0;break}if(i.overlaps(e)){n.extend(e),t=!0;break}}if(!t){const t=2e3,n=(e.start+e.end)/2;i.addMultiLocusPanel(e.chr,n-t,n+t)}}o(r),o(s)}})),this.circularViewControl=new _b(this.$toggle_button_container.get(0),this),this.circularView.setAssembly({name:this.genome.id,id:this.genome.id,chromosomes:Ud(this.genome)}),this.circularViewVisible=t,this.circularView}get circularViewVisible(){return void 0!==this.circularView&&this.circularView.visible}set circularViewVisible(e){this.circularView&&(this.circularView.visible=e,this.circularViewControl.setState(e))}}async function Fb(){if(!this.referenceFrameList)return;const e=this.calculateViewportWidth(this.referenceFrameList.length);for(let t of this.referenceFrameList){const i=this.referenceFrameList.indexOf(t),{chr:n,genome:r}=t,{bpLength:s}=r.getChromosome(t.chr),o=t.toBP(e);Ba.isWholeGenomeView(n)||o>s?t.bpPerPixel=s/e:t.end=t.start+t.toBP(e);for(let{viewports:t}of this.trackViews)t[i].setWidth(e)}this.updateUIWithReferenceFrameList(),await this.updateViews(!0)}function Nb(e){e.preventDefault();const{x:t,y:i}=_t.pageCoordinates(e);if(this.vpMouseDown){const{viewport:e,referenceFrame:n}=this.vpMouseDown,r=Math.abs(t-this.vpMouseDown.mouseDownX)>Math.abs(i-this.vpMouseDown.mouseDownY);if(!this.dragObject&&!this.isScrolling)if(r)this.vpMouseDown.mouseDownX&&Math.abs(t-this.vpMouseDown.mouseDownX)>this.constants.dragThreshold&&(this.dragObject={viewport:e,start:n.start});else if(this.vpMouseDown.mouseDownY&&Math.abs(i-this.vpMouseDown.mouseDownY)>this.constants.scrollThreshold){this.isScrolling=!0;const t=e.$viewport.height(),i=e.trackView.maxViewportContentHeight();this.vpMouseDown.r=t/i}if(this.dragObject){const i=!this.isSoftclipped();let r=this.vpMouseDown.lastMouseX-t;n.shiftPixels(r,e.$viewport.width(),i)&&this.updateViews(),this.fireEvent("trackdrag")}if(this.isScrolling){const t=this.vpMouseDown.r*(this.vpMouseDown.lastMouseY-i);e.trackView.moveScroller(t)}this.vpMouseDown.lastMouseX=t,this.vpMouseDown.lastMouseY=i}}function Ob(e){this.cancelTrackPan(),this.endTrackDrag()}function Pb(e,t){for(let{viewports:i}of e)for(let e of i)e.$trackLabel&&(0===i.indexOf(e)&&!0===t?e.$trackLabel.show():e.$trackLabel.hide())}let Db=[];const zb=ro.setApiKey;!function(){var e=document.createElement("style");e.setAttribute("type","text/css"),e.innerHTML='.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}\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}\n.igv-navbar .igv-navbar-left-container .igv-logo {\n width: 34px;\n height: 32px;\n margin-right: 8px;\n}\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}\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}\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}\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}\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}\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}\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}\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}\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}\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}\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}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div {\n margin-left: 0;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div:last-child {\n margin-left: 0;\n margin-right: 0;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-750 {\n display: none;\n}\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}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\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}\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}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n display: block;\n height: fit-content;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n display: block;\n width: 125px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n display: block;\n}\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}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\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}\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}\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}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n display: none;\n}\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\n.igv-navbar-button-clicked {\n color: white;\n background-color: #737373;\n}\n\n.igv-navbar-button:hover {\n cursor: pointer;\n}\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}\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\n.igv-zoom-in-notice {\n position: absolute;\n top: 10px;\n left: 50%;\n}\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\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\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: 12px;\n height: 12px;\n color: #666666;\n background-color: white;\n z-index: 1000;\n}\n.igv-multi-locus-close-button > svg {\n vertical-align: top;\n}\n\n.igv-multi-locus-close-button:hover {\n cursor: pointer;\n color: #434343;\n}\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: 12px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-multi-locus-ruler-label > div {\n font-family: "Open Sans", sans-serif;\n font-size: 12px;\n color: rgb(16, 16, 16);\n background-color: white;\n}\n.igv-multi-locus-ruler-label > div {\n cursor: pointer;\n}\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}\n.igv-multi-locus-ruler-label-square-dot > div:first-child {\n width: 14px;\n height: 14px;\n}\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: rgb(16, 16, 16);\n}\n\n.igv-ruler-sweeper {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 26px;\n bottom: 0;\n left: 0;\n width: 0;\n z-index: 99999;\n background-color: rgba(68, 134, 247, 0.25);\n}\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}\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\n.igv-track-label {\n position: absolute;\n left: 8px;\n top: 8px;\n width: auto;\n height: auto;\n max-width: 50%;\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\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n background-color: #e8e8e8;\n}\n\n.igv-track-label-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-top: 4px;\n}\n\n.igv-center-line {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\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\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\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\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\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\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}\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}\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}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\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}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\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}\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}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n font-size: 16px;\n}\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}\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}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\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}\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}\n.igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\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}\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}\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\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}\n.igv-menu-popup > div:not(:first-child) {\n width: 100%;\n}\n.igv-menu-popup > div:not(:first-child) > div {\n background: white;\n}\n.igv-menu-popup > div:not(:first-child) > div.context-menu {\n padding-left: 4px;\n padding-right: 4px;\n}\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}\n.igv-menu-popup > div:not(:first-child) > div:hover {\n background: #efefef;\n}\n\n.igv-menu-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 1px;\n padding-top: 1px;\n}\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}\n.igv-menu-popup-header div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-menu-popup-header div:hover {\n cursor: pointer;\n color: #444;\n}\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}\n.igv-menu-popup-check-container div {\n padding-top: 2px;\n padding-left: 8px;\n}\n.igv-menu-popup-check-container div:first-child {\n position: relative;\n width: 12px;\n height: 12px;\n}\n.igv-menu-popup-check-container div:first-child svg {\n position: absolute;\n width: 12px;\n height: 12px;\n}\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}\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}\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}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\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}\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: rgb(255, 255, 255);\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n}\n\n@keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n@-webkit-keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n.igv-roi-menu-next-gen {\n position: absolute;\n z-index: 512;\n font-family: "Open Sans", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background-color: white;\n width: 192px;\n border-radius: 4px;\n border-color: #7F7F7F;\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: stretch;\n}\n.igv-roi-menu-next-gen > div:first-child {\n height: 24px;\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}\n.igv-roi-menu-next-gen > div:first-child > div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-roi-menu-next-gen > div:first-child > div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-menu-next-gen > div:last-child {\n background-color: white;\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: 0;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n text-align: start;\n vertical-align: middle;\n}\n.igv-roi-menu-next-gen > div:last-child > div {\n height: 24px;\n padding-left: 4px;\n border-bottom-style: solid;\n border-bottom-width: thin;\n border-bottom-color: #7f7f7f;\n}\n.igv-roi-menu-next-gen > div:last-child > div:not(:first-child):hover {\n background-color: rgba(127, 127, 127, 0.1);\n}\n.igv-roi-menu-next-gen > div:last-child div:first-child {\n font-style: italic;\n text-align: center;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.igv-roi-menu-next-gen > div:last-child > div:last-child {\n border-bottom-width: 0;\n border-bottom-color: transparent;\n}\n\n.igv-roi-placeholder {\n font-style: normal;\n color: rgba(75, 75, 75, 0.6);\n}\n\n.igv-roi-table {\n position: absolute;\n z-index: 1024;\n width: min-content;\n max-width: 1600px;\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: 12px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n cursor: default;\n}\n.igv-roi-table > div {\n height: 24px;\n font-size: 14px;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n}\n.igv-roi-table > div:first-child {\n border-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-top-width: 0;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-roi-table > div:first-child > div:first-child {\n text-align: center;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n margin-left: 4px;\n margin-right: 4px;\n width: calc(100% - 4px - 12px);\n}\n.igv-roi-table > div:first-child > div:last-child {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7f7f7f;\n}\n.igv-roi-table > div:first-child > div:last-child > svg {\n display: block;\n}\n.igv-roi-table > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-table > .igv-roi-table-description {\n padding: 4px;\n margin-left: 4px;\n word-break: break-all;\n overflow-y: auto;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-goto-explainer {\n margin-top: 5px;\n margin-left: 4px;\n color: #7F7F7F;\n font-style: italic;\n height: 24px;\n border-top: solid lightgray;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-column-titles {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n padding-right: 16px;\n background-color: white;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: #7f7f7f;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container {\n overflow: auto;\n resize: both;\n max-width: 1600px;\n height: 360px;\n background-color: transparent;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: transparent;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row-hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n.igv-roi-table > div:last-child {\n height: 32px;\n line-height: 32px;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: transparent;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-width: 0;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n\n.igv-roi-table-row-selected {\n background-color: rgba(0, 0, 0, 0.125);\n}\n\n.igv-roi-table-button {\n cursor: pointer;\n height: 20px;\n user-select: none;\n line-height: 20px;\n text-align: center;\n vertical-align: middle;\n font-family: "Open Sans", sans-serif;\n font-size: 13px;\n font-weight: 400;\n color: black;\n padding-left: 6px;\n padding-right: 6px;\n background-color: rgb(239, 239, 239);\n border-color: black;\n border-style: solid;\n border-width: thin;\n border-radius: 3px;\n}\n\n.igv-roi-table-button:hover {\n font-weight: 400;\n background-color: rgba(0, 0, 0, 0.13);\n}\n\n.igv-roi-region {\n z-index: 64;\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n overflow: visible;\n margin-top: 44px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-region > div {\n position: relative;\n width: 100%;\n height: 8px;\n pointer-events: auto;\n}\n\n.igv-roi-menu {\n position: absolute;\n z-index: 1024;\n width: 144px;\n border-color: #7f7f7f;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: "Open Sans", sans-serif;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-menu > div:not(:last-child) {\n border-bottom-color: rgba(128, 128, 128, 0.5);\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-roi-menu > div:first-child {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-top-color: transparent;\n border-top-style: solid;\n border-top-width: 0;\n}\n.igv-roi-menu > 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: 0;\n}\n\n.igv-roi-menu-row {\n height: 24px;\n padding-left: 8px;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n background-color: white;\n}\n\n.igv-roi-menu-row-edit-description {\n width: -webkit-fill-available;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n background-color: white;\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-menu-row-edit-description > label {\n margin-left: 2px;\n margin-bottom: 0;\n display: block;\n width: -webkit-fill-available;\n}\n.igv-roi-menu-row-edit-description > input {\n display: block;\n margin-left: 2px;\n margin-right: 2px;\n margin-bottom: 1px;\n width: -webkit-fill-available;\n}\n\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\n.igv-viewport {\n position: relative;\n margin-top: 5px;\n line-height: 1;\n overflow-x: hidden;\n overflow-y: hidden;\n}\n\n.igv-viewport-content {\n position: relative;\n width: 100%;\n}\n.igv-viewport-content > canvas {\n position: relative;\n display: block;\n}\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\n.igv-column-shim {\n width: 1px;\n margin-left: 2px;\n margin-right: 2px;\n background-color: #545453;\n}\n\n.igv-column {\n position: relative;\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\n.igv-axis-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 width: 50px;\n}\n.igv-axis-column > div {\n margin-top: 5px;\n width: 100%;\n}\n\n.igv-sample-name-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\n.igv-scrollbar-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 width: 14px;\n}\n.igv-scrollbar-column > div {\n position: relative;\n margin-top: 5px;\n width: 14px;\n}\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}\n.igv-scrollbar-column > div > div:hover {\n background-color: #c4c4c4;\n}\n\n.igv-track-drag-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 width: 12px;\n background-color: white;\n}\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}\n.igv-track-drag-column .igv-track-drag-handle-hover {\n background-color: #787878;\n}\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\n.igv-gear-menu-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 width: 28px;\n}\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}\n.igv-gear-menu-column > div > div {\n position: relative;\n margin-top: 4px;\n width: 16px;\n height: 16px;\n color: #7F7F7F;\n}\n.igv-gear-menu-column > div > div:hover {\n cursor: pointer;\n color: #444;\n}\n\n/*# sourceMappingURL=dom.css.map */\n',document.head.append(e)}();const Hb=ro.oauth;var Vb={TrackUtils:Qo,IGVGraphics:Co,MenuUtils:$t,DataRangeDialog:Zt,createTrack:async function(e,t){return await Bb.prototype.createTrack.call(t,e)},createBrowser:async function(e,t){void 0===t&&(t={}),Ba.KNOWN_GENOMES||await Ba.initializeGenomes(t),function(e){void 0===e.minimumBases&&(e.minimumBases=40);void 0===e.showIdeogram&&(e.showIdeogram=!0);void 0===e.showCircularView&&(e.showCircularView=!1);void 0===e.showCircularViewButton&&(e.showCircularViewButton=!1);void 0===e.showTrackLabelButton&&(e.showTrackLabelButton=!0);void 0===e.showTrackLabels&&(e.showTrackLabels=!0);void 0===e.showROITableButton&&(e.showROITableButton=!1);void 0===e.showROITable&&(e.showROITable=!1);void 0===e.showCursorTrackingGuideButton&&(e.showCursorTrackingGuideButton=!0);void 0===e.showCursorGuide&&(e.showCursorGuide=e.showCursorTrackingGuide||!1);void 0===e.showCenterGuideButton&&(e.showCenterGuideButton=!0);void 0===e.showCenterGuide&&(e.showCenterGuide=!1);void 0===e.showSampleNames&&(e.showSampleNames=!1);void 0===e.showSVGButton&&(e.showSVGButton=!0);void 0===e.showControls&&(e.showControls=!0);void 0===e.showNavigation&&(e.showNavigation=!0);void 0===e.showRuler&&(e.showRuler=!0);void 0===e.flanking&&(e.flanking=1e3);void 0===e.pairsSupported&&(e.pairsSupported=!0);e.tracks||(e.tracks=[])}(t),t.queryParametersSupported&&function(e){var t,i,n,r,s,o,a,l,h;let c,d,u;if(a=window.location.href,s={},t=a.indexOf("?"),i=a.lastIndexOf("#"),t>=0)for(i<0&&(i=a.length),n=t+1;nt&&(i.indexURL=d[t]),u&&u.length>t&&(i.name=u[t]),e.tracks.push(i)}}}(t),t.apiKey&&ro.setApiKey(t.apiKey),t.oauthToken&&ro.setOauthToken(t.oauthToken),t.clientId&&!Xs()&&await async function(e){if(!google.accounts.oauth2.initTokenClient)throw new Error("Google accounts token client not loaded (https://accounts.google.com/gsi/client)");if(Xs())throw new Error("Google client is already initialized");const t={client_id:e.client_id,scope:e.scope||"https://www.googleapis.com/auth/userinfo.profile",state:e.state||"igv",error:e=>{throw new Error(e.type)},hint:e.hint,hosted_domain:e.hosted_domain},i=google.accounts.oauth2.initTokenClient(t);google.igv={tokenClient:i,apiKey:e.apiKey}}({clientId:t.clientId,apiKey:t.apiKey,scope:"https://www.googleapis.com/auth/userinfo.profile"});const i=new Bb(t,e);return Db.push(i),t.sessionURL?await i.loadSession({url:t.sessionURL}):await i.loadSessionObject(t),i.navbarManager.navbarDidResize(i.$navigation.width()),i},removeBrowser:function(e){e.dispose(),e.root.remove(),Db=Db.filter((t=>t!==e))},removeAllBrowsers:function(){for(let e of Db)e.dispose(),e.root.remove();Db=[]},visibilityChange:async function(){for(let e of Db)await e.visibilityChange()},setGoogleOauthToken:function(e){return ro.setOauthToken(e)},setOauthToken:function(e,t){return ro.setOauthToken(e,t)},oauth:Hb,version:La,setApiKey:zb,registerFileFormats:Zo};return Vb})); +//# sourceMappingURL=igv.min.js.map