diff --git a/public/cy_custom.js b/public/cy_custom.js new file mode 100644 index 0000000..a1efed8 --- /dev/null +++ b/public/cy_custom.js @@ -0,0 +1,65 @@ +(function () { + const EXTREME_ZOOM_MIN = 1e-4; + const EXTREME_ZOOM_MAX = 1e4; + + function applyCustomZoomHandling(cy) { + let zoomFramePending = false; + + if (!cy || typeof cy.zoom !== 'function') { + console.warn('๐Ÿšซ ์œ ํšจํ•˜์ง€ ์•Š์€ Cytoscape ์ธ์Šคํ„ด์Šค์ž…๋‹ˆ๋‹ค.'); + return; + } + + cy.userZoomingEnabled(false); // Cytoscape ๊ธฐ๋ณธ ์คŒ ๋น„ํ™œ์„ฑํ™” + + const container = cy.container(); + if (!container) { + console.warn('๐Ÿšซ Cytoscape container๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.'); + return; + } + + container.addEventListener( + 'wheel', + (event) => { + event.preventDefault(); + + if (zoomFramePending) return; + zoomFramePending = true; + + requestAnimationFrame(() => { + const currentZoom = cy.zoom(); + + const clampedDelta = Math.max(-100, Math.min(100, event.deltaY)); + const zoomFactor = 1 + (clampedDelta > 0 ? -0.05 : 0.05); + const nextZoom = currentZoom * zoomFactor; + + const isValid = + isFinite(nextZoom) && + nextZoom >= EXTREME_ZOOM_MIN && + nextZoom <= EXTREME_ZOOM_MAX; + + if (isValid) { + const rect = container.getBoundingClientRect(); + const renderedPosition = { + x: event.clientX - rect.left, + y: event.clientY - rect.top + }; + cy.zoom({ + level: nextZoom, + renderedPosition + }); + } else { + console.warn("๐Ÿšซ ํœ  ์คŒ ๋น„์ •์ƒ ๊ฐ’ ์ฐจ๋‹จ:", nextZoom); + } + + zoomFramePending = false; + }); + }, + { passive: false } + ); + } + + // โœ… ์ „์—ญ(window)์œผ๋กœ ํ•จ์ˆ˜ ๋“ฑ๋ก + window.applyCustomZoomHandling = applyCustomZoomHandling; + })(); + \ No newline at end of file diff --git a/public/igv.css b/public/igv.css new file mode 100644 index 0000000..8f89c40 --- /dev/null +++ b/public/igv.css @@ -0,0 +1,28 @@ +/* IGV blue lines & UI custom style (๊ธฐ๋ณธ ํŒŒ๋ž€์ƒ‰: #3579f6) */ +.igv-blue-line { + background: #3579f6 !important; + width: 2px !important; + z-index: 1000; +} +.igv-blue-fill { + background: rgba(53, 121, 246, 0.18) !important; + z-index: 999; +} +.igv-control-btn { + background: #f5faff; + color: #3579f6; + border: 1.5px solid #3579f6; + border-radius: 5px; + padding: 6px 16px; + font-size: 15px; + cursor: pointer; + transition: background 0.2s, border 0.2s, color 0.2s; +} +.igv-control-btn:hover { + background: #3579f6; + color: #fff; + border: 2px solid #2456a6; +} +.igv-blue-line.selected { + box-shadow: 0 0 8px #3579f6; +} \ No newline at end of file diff --git a/public/igv.js b/public/igv.js new file mode 100644 index 0000000..e58285f --- /dev/null +++ b/public/igv.js @@ -0,0 +1,67298 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.igv = factory()); +}(this, (function () { 'use strict'; + + /*! + * jQuery JavaScript Library v3.3.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2018-01-20T17:24Z + */ + + var arr = []; + var document$2 = window.document; + var getProto = Object.getPrototypeOf; + var slice = arr.slice; + var concat = arr.concat; + var push = arr.push; + var indexOf$1 = arr.indexOf; + var class2type = {}; + var toString$1 = 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$2; + var i, + script = doc.createElement("script"); + script.text = code; + + if (node) { + for (i in preservedScriptAttributes) { + if (node[i]) { + script[i] = node[i]; + } + } + } + + doc.head.appendChild(script).parentNode.removeChild(script); + } + + function toType(obj) { + if (obj == null) { + return obj + ""; + } // Support: Android <=2.3 only (functionish RegExp) + + + return typeof obj === "object" || typeof obj === "function" ? class2type[toString$1.call(obj)] || "object" : typeof obj; + } // global Symbol + // Defining this global in .eslintrc.json would create a danger of using the global + // unguarded in another place, it seems safer to define global only for this module + + + var version$2 = "3.3.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector", + // Define a local copy of jQuery + jQuery = function (selector, context) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init(selector, context); + }, + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + + jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version$2, + constructor: jQuery, + // The default length of a jQuery object is 0 + length: 0, + toArray: function () { + return slice.call(this); + }, + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function (num) { + // Return all the elements in a clean array + if (num == null) { + return slice.call(this); + } // Return just the one element from the set + + + return num < 0 ? this[num + this.length] : this[num]; + }, + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function (elems) { + // Build a new jQuery matched element set + var ret = jQuery.merge(this.constructor(), elems); // Add the old object onto the stack (as a reference) + + ret.prevObject = this; // Return the newly-formed element set + + return ret; + }, + // Execute a callback for every element in the matched set. + each: function (callback) { + return jQuery.each(this, callback); + }, + map: function (callback) { + return this.pushStack(jQuery.map(this, function (elem, i) { + return callback.call(elem, i, elem); + })); + }, + slice: function () { + return this.pushStack(slice.apply(this, arguments)); + }, + first: function () { + return this.eq(0); + }, + last: function () { + return this.eq(-1); + }, + eq: function (i) { + var len = this.length, + j = +i + (i < 0 ? len : 0); + return this.pushStack(j >= 0 && j < len ? [this[j]] : []); + }, + end: function () { + return this.prevObject || this.constructor(); + }, + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice + }; + + jQuery.extend = jQuery.fn.extend = function () { + var options, + name, + src, + copy, + copyIsArray, + clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; // Handle a deep copy situation + + if (typeof target === "boolean") { + deep = target; // Skip the boolean and the target + + target = arguments[i] || {}; + i++; + } // Handle case when target is a string or something (possible in deep copy) + + + if (typeof target !== "object" && !isFunction(target)) { + target = {}; + } // Extend jQuery itself if only one argument is passed + + + if (i === length) { + target = this; + i--; + } + + for (; i < length; i++) { + // Only deal with non-null/undefined values + if ((options = arguments[i]) != null) { + // Extend the base object + for (name in options) { + src = target[name]; + copy = options[name]; // Prevent never-ending loop + + if (target === copy) { + continue; + } // Recurse if we're merging plain objects or arrays + + + if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { + if (copyIsArray) { + copyIsArray = false; + clone = src && Array.isArray(src) ? src : []; + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } // Never move original objects, clone them + + + target[name] = jQuery.extend(deep, clone, copy); // Don't bring in undefined values + } else if (copy !== undefined) { + target[name] = copy; + } + } + } + } // Return the modified object + + + return target; + }; + + jQuery.extend({ + // Unique for each copy of jQuery on the page + expando: "jQuery" + (version$2 + Math.random()).replace(/\D/g, ""), + // Assume jQuery is ready without the ready module + isReady: true, + error: function (msg) { + throw new Error(msg); + }, + noop: function () {}, + isPlainObject: function (obj) { + var proto, Ctor; // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + + if (!obj || toString$1.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$1.call(arr, elem, i); + }, + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function (first, second) { + var len = +second.length, + j = 0, + i = first.length; + + for (; j < len; j++) { + first[i++] = second[j]; + } + + first.length = i; + return first; + }, + grep: function (elems, callback, invert) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; // Go through the array, only saving the items + // that pass the validator function + + for (; i < length; i++) { + callbackInverse = !callback(elems[i], i); + + if (callbackInverse !== callbackExpect) { + matches.push(elems[i]); + } + } + + return matches; + }, + // arg is for internal usage only + map: function (elems, callback, arg) { + var length, + value, + i = 0, + ret = []; // Go through the array, translating each of the items to their new values + + if (isArrayLike(elems)) { + length = elems.length; + + for (; i < length; i++) { + value = callback(elems[i], i, arg); + + if (value != null) { + ret.push(value); + } + } // Go through every key on the object, + + } else { + for (i in elems) { + value = callback(elems[i], i, arg); + + if (value != null) { + ret.push(value); + } + } + } // Flatten any nested arrays + + + return concat.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) && (excess = tokenize(unquoted, true)) && (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] || (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" && ((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$1.call(qualifier, elem) > -1 !== not; + }); + } // Filtered directly for both simple and complex selectors + + + return jQuery.filter(qualifier, elements, not); + } + + jQuery.filter = function (expr, elems, not) { + var elem = elems[0]; + + if (not) { + expr = ":not(" + expr + ")"; + } + + if (elems.length === 1 && elem.nodeType === 1) { + return jQuery.find.matchesSelector(elem, expr) ? [elem] : []; + } + + return jQuery.find.matches(expr, jQuery.grep(elems, function (elem) { + return elem.nodeType === 1; + })); + }; + + jQuery.fn.extend({ + find: function (selector) { + var i, + ret, + len = this.length, + self = this; + + if (typeof selector !== "string") { + return this.pushStack(jQuery(selector).filter(function () { + for (i = 0; i < len; i++) { + if (jQuery.contains(self[i], this)) { + return true; + } + } + })); + } + + ret = this.pushStack([]); + + for (i = 0; i < len; i++) { + jQuery.find(selector, self[i], ret); + } + + return len > 1 ? jQuery.uniqueSort(ret) : ret; + }, + filter: function (selector) { + return this.pushStack(winnow(this, selector || [], false)); + }, + not: function (selector) { + return this.pushStack(winnow(this, selector || [], true)); + }, + is: function (selector) { + return !!winnow(this, // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test(selector) ? jQuery(selector) : selector || [], false).length; + } + }); // Initialize a jQuery object + // A central reference to the root jQuery(document) + + var rootjQuery, + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + init$2 = jQuery.fn.init = function (selector, context, root) { + var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) + + if (!selector) { + return this; + } // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + + + root = root || rootjQuery; // Handle HTML strings + + if (typeof selector === "string") { + if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [null, selector, null]; + } else { + match = rquickExpr.exec(selector); + } // Match html or make sure no context is specified for #id + + + if (match && (match[1] || !context)) { + // HANDLE: $(html) -> $(array) + if (match[1]) { + context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + + jQuery.merge(this, jQuery.parseHTML(match[1], context && context.nodeType ? context.ownerDocument || context : document$2, true)); // HANDLE: $(html, props) + + if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { + for (match in context) { + // Properties of context are called as methods if possible + if (isFunction(this[match])) { + this[match](context[match]); // ...and otherwise set as attributes + } else { + this.attr(match, context[match]); + } + } + } + + return this; // HANDLE: $(#id) + } else { + elem = document$2.getElementById(match[2]); + + if (elem) { + // Inject the element directly into the jQuery object + this[0] = elem; + this.length = 1; + } + + return this; + } // HANDLE: $(expr, $(...)) + + } else if (!context || context.jquery) { + return (context || root).find(selector); // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor(context).find(selector); + } // HANDLE: $(DOMElement) + + } else if (selector.nodeType) { + this[0] = selector; + this.length = 1; + return this; // HANDLE: $(function) + // Shortcut for document ready + } else if (isFunction(selector)) { + return root.ready !== undefined ? root.ready(selector) : // Execute immediately if ready is not present + selector(jQuery); + } + + return jQuery.makeArray(selector, this); + }; // Give the init function the jQuery prototype for later instantiation + + + init$2.prototype = jQuery.fn; // Initialize central reference + + rootjQuery = jQuery(document$2); + var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + jQuery.fn.extend({ + has: function (target) { + var targets = jQuery(target, this), + l = targets.length; + return this.filter(function () { + var i = 0; + + for (; i < l; i++) { + if (jQuery.contains(this, targets[i])) { + return true; + } + } + }); + }, + closest: function (selectors, context) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery(selectors); // Positional selectors never match, since there's no _selection_ context + + if (!rneedsContext.test(selectors)) { + for (; i < l; i++) { + for (cur = this[i]; cur && cur !== context; cur = cur.parentNode) { + // Always skip document fragments + if (cur.nodeType < 11 && (targets ? targets.index(cur) > -1 : // Don't pass non-elements to Sizzle + cur.nodeType === 1 && jQuery.find.matchesSelector(cur, selectors))) { + matched.push(cur); + break; + } + } + } + } + + return this.pushStack(matched.length > 1 ? jQuery.uniqueSort(matched) : matched); + }, + // Determine the position of an element within the set + index: function (elem) { + // No argument, return index in parent + if (!elem) { + return this[0] && this[0].parentNode ? this.first().prevAll().length : -1; + } // Index in selector + + + if (typeof elem === "string") { + return indexOf$1.call(jQuery(elem), this[0]); + } // Locate the position of the desired element + + + return indexOf$1.call(this, // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem); + }, + add: function (selector, context) { + return this.pushStack(jQuery.uniqueSort(jQuery.merge(this.get(), jQuery(selector, context)))); + }, + addBack: function (selector) { + return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector)); + } + }); + + function sibling(cur, dir) { + while ((cur = cur[dir]) && cur.nodeType !== 1) {} + + return cur; + } + + jQuery.each({ + parent: function (elem) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function (elem) { + return dir(elem, "parentNode"); + }, + parentsUntil: function (elem, i, until) { + return dir(elem, "parentNode", until); + }, + next: function (elem) { + return sibling(elem, "nextSibling"); + }, + prev: function (elem) { + return sibling(elem, "previousSibling"); + }, + nextAll: function (elem) { + return dir(elem, "nextSibling"); + }, + prevAll: function (elem) { + return dir(elem, "previousSibling"); + }, + nextUntil: function (elem, i, until) { + return dir(elem, "nextSibling", until); + }, + prevUntil: function (elem, i, until) { + return dir(elem, "previousSibling", until); + }, + siblings: function (elem) { + return siblings((elem.parentNode || {}).firstChild, elem); + }, + children: function (elem) { + return siblings(elem.firstChild); + }, + contents: function (elem) { + if (nodeName(elem, "iframe")) { + return elem.contentDocument; + } // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + + + if (nodeName(elem, "template")) { + elem = elem.content || elem; + } + + return jQuery.merge([], elem.childNodes); + } + }, function (name, fn) { + jQuery.fn[name] = function (until, selector) { + var matched = jQuery.map(this, fn, until); + + if (name.slice(-5) !== "Until") { + selector = until; + } + + if (selector && typeof selector === "string") { + matched = jQuery.filter(selector, matched); + } + + if (this.length > 1) { + // Remove duplicates + if (!guaranteedUnique[name]) { + jQuery.uniqueSort(matched); + } // Reverse order for parents* and prev-derivatives + + + if (rparentsprev.test(name)) { + matched.reverse(); + } + } + + return this.pushStack(matched); + }; + }); + var rnothtmlwhite = /[^\x20\t\r\n\f]+/g; // Convert String-formatted options into Object-formatted ones + + function createOptions(options) { + var object = {}; + jQuery.each(options.match(rnothtmlwhite) || [], function (_, flag) { + object[flag] = true; + }); + return object; + } + /* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ + + + jQuery.Callbacks = function (options) { + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? createOptions(options) : jQuery.extend({}, options); + + var // Flag to know if list is currently firing + firing, + // Last fire value for non-forgettable lists + memory, + // Flag to know if list was already fired + fired, + // Flag to prevent firing + locked, + // Actual callback list + list = [], + // Queue of execution data for repeatable lists + queue = [], + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + // Fire callbacks + fire = function () { + // Enforce single-firing + locked = locked || options.once; // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + + fired = firing = true; + + for (; queue.length; firingIndex = -1) { + memory = queue.shift(); + + while (++firingIndex < list.length) { + // Run callback and check for early termination + if (list[firingIndex].apply(memory[0], memory[1]) === false && options.stopOnFalse) { + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } // Forget the data if we're done with it + + + if (!options.memory) { + memory = false; + } + + firing = false; // Clean up if we're done firing for good + + if (locked) { + // Keep an empty list if we have data for future add calls + if (memory) { + list = []; // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function () { + if (list) { + // If we have memory from a past run, we should fire after adding + if (memory && !firing) { + firingIndex = list.length - 1; + queue.push(memory); + } + + (function add(args) { + jQuery.each(args, function (_, arg) { + if (isFunction(arg)) { + if (!options.unique || !self.has(arg)) { + list.push(arg); + } + } else if (arg && arg.length && toType(arg) !== "string") { + // Inspect recursively + add(arg); + } + }); + })(arguments); + + if (memory && !firing) { + fire(); + } + } + + return this; + }, + // Remove a callback from the list + remove: function () { + jQuery.each(arguments, function (_, arg) { + var index; + + while ((index = jQuery.inArray(arg, list, index)) > -1) { + list.splice(index, 1); // Handle firing indexes + + if (index <= firingIndex) { + firingIndex--; + } + } + }); + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function (fn) { + return fn ? jQuery.inArray(fn, list) > -1 : list.length > 0; + }, + // Remove all callbacks from the list + empty: function () { + if (list) { + list = []; + } + + return this; + }, + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function () { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function () { + return !list; + }, + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function () { + locked = queue = []; + + if (!memory && !firing) { + list = memory = ""; + } + + return this; + }, + locked: function () { + return !!locked; + }, + // Call all callbacks with the given context and arguments + fireWith: function (context, args) { + if (!locked) { + args = args || []; + args = [context, args.slice ? args.slice() : args]; + queue.push(args); + + if (!firing) { + fire(); + } + } + + return this; + }, + // Call all the callbacks with the given arguments + fire: function () { + self.fireWith(this, arguments); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function () { + return !!fired; + } + }; + + return self; + }; + + function Identity(v) { + return v; + } + + function Thrower(ex) { + throw ex; + } + + function adoptValue(value, resolve, reject, noValue) { + var method; + + try { + // Check for promise aspect first to privilege synchronous behavior + if (value && isFunction(method = value.promise)) { + method.call(value).done(resolve).fail(reject); // Other thenables + } else if (value && isFunction(method = value.then)) { + method.call(value, resolve, reject); // Other non-thenables + } else { + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply(undefined, [value].slice(noValue)); + } // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + + } catch (value) { + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply(undefined, [value]); + } + } + + jQuery.extend({ + Deferred: function (func) { + var tuples = [// action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + ["notify", "progress", jQuery.Callbacks("memory"), jQuery.Callbacks("memory"), 2], ["resolve", "done", jQuery.Callbacks("once memory"), jQuery.Callbacks("once memory"), 0, "resolved"], ["reject", "fail", jQuery.Callbacks("once memory"), jQuery.Callbacks("once memory"), 1, "rejected"]], + state = "pending", + promise = { + state: function () { + return state; + }, + always: function () { + deferred.done(arguments).fail(arguments); + return this; + }, + "catch": function (fn) { + return promise.then(null, fn); + }, + // Keep pipe for back-compat + pipe: function () { + 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 && (typeof returned === "object" || typeof returned === "function") && returned.then; // Handle a returned thenable + + if (isFunction(then)) { + // Special processors (notify) just wait for resolution + if (special) { + then.call(returned, resolve(maxDepth, deferred, Identity, special), resolve(maxDepth, deferred, Thrower, special)); // Normal processors (resolve) also hook into progress + } else { + // ...and disregard older resolution values + maxDepth++; + then.call(returned, resolve(maxDepth, deferred, Identity, special), resolve(maxDepth, deferred, Thrower, special), resolve(maxDepth, deferred, Identity, deferred.notifyWith)); + } // Handle all other returned values + + } else { + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if (handler !== Identity) { + that = undefined; + args = [returned]; + } // Process the value(s) + // Default process is resolve + + + (special || deferred.resolveWith)(that, args); + } + }, + // Only normal processors (resolve) catch and reject exceptions + process = special ? mightThrow : function () { + try { + mightThrow(); + } catch (e) { + if (jQuery.Deferred.exceptionHook) { + jQuery.Deferred.exceptionHook(e, process.stackTrace); + } // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + + + if (depth + 1 >= maxDepth) { + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if (handler !== Thrower) { + that = undefined; + args = [e]; + } + + deferred.rejectWith(that, args); + } + } + }; // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + + + if (depth) { + process(); + } else { + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if (jQuery.Deferred.getStackHook) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + + window.setTimeout(process); + } + }; + } + + return jQuery.Deferred(function (newDefer) { + // progress_handlers.add( ... ) + tuples[0][3].add(resolve(0, newDefer, isFunction(onProgress) ? onProgress : Identity, newDefer.notifyWith)); // fulfilled_handlers.add( ... ) + + tuples[1][3].add(resolve(0, newDefer, isFunction(onFulfilled) ? onFulfilled : Identity)); // rejected_handlers.add( ... ) + + tuples[2][3].add(resolve(0, newDefer, isFunction(onRejected) ? onRejected : Thrower)); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function (obj) { + return obj != null ? jQuery.extend(obj, promise) : promise; + } + }, + deferred = {}; // Add list-specific methods + + jQuery.each(tuples, function (i, tuple) { + var list = tuple[2], + stateString = tuple[5]; // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + + promise[tuple[1]] = list.add; // Handle state + + if (stateString) { + list.add(function () { + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[3 - i][2].disable, // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[3 - i][3].disable, // progress_callbacks.lock + tuples[0][2].lock, // progress_handlers.lock + tuples[0][3].lock); + } // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + + + list.add(tuple[3].fire); // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + + deferred[tuple[0]] = function () { + deferred[tuple[0] + "With"](this === deferred ? undefined : this, arguments); + return this; + }; // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + + + deferred[tuple[0] + "With"] = list.fireWith; + }); // Make the deferred a promise + + promise.promise(deferred); // Call given func if any + + if (func) { + func.call(deferred, deferred); + } // All done! + + + return deferred; + }, + // Deferred helper + when: function (singleValue) { + var // count of uncompleted subordinates + remaining = arguments.length, + // count of unprocessed arguments + i = remaining, + // subordinate fulfillment data + resolveContexts = Array(i), + resolveValues = slice.call(arguments), + // the master Deferred + master = jQuery.Deferred(), + // subordinate callback factory + updateFunc = function (i) { + return function (value) { + resolveContexts[i] = this; + resolveValues[i] = arguments.length > 1 ? slice.call(arguments) : value; + + if (! --remaining) { + master.resolveWith(resolveContexts, resolveValues); + } + }; + }; // Single- and empty arguments are adopted like Promise.resolve + + + if (remaining <= 1) { + adoptValue(singleValue, master.done(updateFunc(i)).resolve, master.reject, !remaining); // Use .then() to unwrap secondary thenables (cf. gh-3000) + + if (master.state() === "pending" || isFunction(resolveValues[i] && resolveValues[i].then)) { + return master.then(); + } + } // Multiple arguments are aggregated like Promise.all array elements + + + while (i--) { + adoptValue(resolveValues[i], updateFunc(i), master.reject); + } + + return master.promise(); + } + }); // These usually indicate a programmer mistake during development, + // warn about them ASAP rather than swallowing them by default. + + var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + + jQuery.Deferred.exceptionHook = function (error, stack) { + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if (window.console && window.console.warn && error && rerrorNames.test(error.name)) { + window.console.warn("jQuery.Deferred exception: " + error.message, error.stack, stack); + } + }; + + jQuery.readyException = function (error) { + window.setTimeout(function () { + throw error; + }); + }; // The deferred used on DOM ready + + + var readyList = jQuery.Deferred(); + + jQuery.fn.ready = function (fn) { + readyList.then(fn) // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch(function (error) { + jQuery.readyException(error); + }); + return this; + }; + + jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + // Handle when the DOM is ready + ready: function (wait) { + // Abort if there are pending holds or we're already ready + if (wait === true ? --jQuery.readyWait : jQuery.isReady) { + return; + } // Remember that the DOM is ready + + + jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be + + if (wait !== true && --jQuery.readyWait > 0) { + return; + } // If there are functions bound, to execute + + + readyList.resolveWith(document$2, [jQuery]); + } + }); + jQuery.ready.then = readyList.then; // The ready event handler and self cleanup method + + function completed() { + document$2.removeEventListener("DOMContentLoaded", completed); + window.removeEventListener("load", completed); + jQuery.ready(); + } // Catch cases where $(document).ready() is called + // after the browser event has already occurred. + // Support: IE <=9 - 10 only + // Older IE sometimes signals "interactive" too soon + + + if (document$2.readyState === "complete" || document$2.readyState !== "loading" && !document$2.documentElement.doScroll) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout(jQuery.ready); + } else { + // Use the handy event callback + document$2.addEventListener("DOMContentLoaded", completed); // A fallback to window.onload, that will always work + + window.addEventListener("load", completed); + } // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + + + var access = function (elems, fn, key, value, chainable, emptyGet, raw) { + var i = 0, + len = elems.length, + bulk = key == null; // Sets many values + + if (toType(key) === "object") { + chainable = true; + + for (i in key) { + access(elems, fn, i, key[i], true, emptyGet, raw); + } // Sets one value + + } else if (value !== undefined) { + chainable = true; + + if (!isFunction(value)) { + raw = true; + } + + if (bulk) { + // Bulk operations run against the entire set + if (raw) { + fn.call(elems, value); + fn = null; // ...except when executing function values + } else { + bulk = fn; + + fn = function (elem, key, value) { + return bulk.call(jQuery(elem), value); + }; + } + } + + if (fn) { + for (; i < len; i++) { + fn(elems[i], key, raw ? value : value.call(elems[i], i, fn(elems[i], key))); + } + } + } + + if (chainable) { + return elems; + } // Gets + + + if (bulk) { + return fn.call(elems); + } + + return len ? fn(elems[0], key) : emptyGet; + }; // Matches dashed string for camelizing + + + var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; // Used by camelCase as callback to replace() + + function fcamelCase(all, letter) { + return letter.toUpperCase(); + } // Convert dashed to camelCase; used by the css and data modules + // Support: IE <=9 - 11, Edge 12 - 15 + // Microsoft forgot to hump their vendor prefix (#9572) + + + function camelCase(string) { + return string.replace(rmsPrefix, "ms-").replace(rdashAlpha, fcamelCase); + } + + var acceptData = function (owner) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !+owner.nodeType; + }; + + function Data() { + this.expando = jQuery.expando + Data.uid++; + } + + Data.uid = 1; + Data.prototype = { + cache: function (owner) { + // Check if the owner object already has a cache + var value = owner[this.expando]; // If not, create one + + if (!value) { + value = {}; // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + + if (acceptData(owner)) { + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if (owner.nodeType) { + owner[this.expando] = value; // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty(owner, this.expando, { + value: value, + configurable: true + }); + } + } + } + + return value; + }, + set: function (owner, data, value) { + var prop, + cache = this.cache(owner); // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + + if (typeof data === "string") { + cache[camelCase(data)] = value; // Handle: [ owner, { properties } ] args + } else { + // Copy the properties one-by-one to the cache object + for (prop in data) { + cache[camelCase(prop)] = data[prop]; + } + } + + return cache; + }, + get: function (owner, key) { + return key === undefined ? this.cache(owner) : // Always use camelCase key (gh-2257) + owner[this.expando] && owner[this.expando][camelCase(key)]; + }, + access: function (owner, key, value) { + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if (key === undefined || key && typeof key === "string" && value === undefined) { + return this.get(owner, key); + } // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + + + this.set(owner, key, value); // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + + return value !== undefined ? value : key; + }, + remove: function (owner, key) { + var i, + cache = owner[this.expando]; + + if (cache === undefined) { + return; + } + + if (key !== undefined) { + // Support array or space separated string of keys + if (Array.isArray(key)) { + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map(camelCase); + } else { + key = camelCase(key); // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + + key = key in cache ? [key] : key.match(rnothtmlwhite) || []; + } + + i = key.length; + + while (i--) { + delete cache[key[i]]; + } + } // Remove the expando if there's no more data + + + if (key === undefined || jQuery.isEmptyObject(cache)) { + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if (owner.nodeType) { + owner[this.expando] = undefined; + } else { + delete owner[this.expando]; + } + } + }, + hasData: function (owner) { + var cache = owner[this.expando]; + return cache !== undefined && !jQuery.isEmptyObject(cache); + } + }; + var dataPriv = new Data(); + var dataUser = new Data(); // Implementation Summary + // + // 1. Enforce API surface and semantic compatibility with 1.9.x branch + // 2. Improve the module's maintainability by reducing the storage + // paths to a single mechanism. + // 3. Use the same single mechanism to support "private" and "user" data. + // 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) + // 5. Avoid exposing implementation details on user objects (eg. expando properties) + // 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + + var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + + function getData(data) { + if (data === "true") { + return true; + } + + if (data === "false") { + return false; + } + + if (data === "null") { + return null; + } // Only convert to a number if it doesn't change the string + + + if (data === +data + "") { + return +data; + } + + if (rbrace.test(data)) { + return JSON.parse(data); + } + + return data; + } + + function dataAttr(elem, key, data) { + var name; // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + + if (data === undefined && elem.nodeType === 1) { + name = "data-" + key.replace(rmultiDash, "-$&").toLowerCase(); + data = elem.getAttribute(name); + + if (typeof data === "string") { + try { + data = getData(data); + } catch (e) {} // Make sure we set the data so it isn't changed later + + + dataUser.set(elem, key, data); + } else { + data = undefined; + } + } + + return data; + } + + jQuery.extend({ + hasData: function (elem) { + return dataUser.hasData(elem) || dataPriv.hasData(elem); + }, + data: function (elem, name, data) { + return dataUser.access(elem, name, data); + }, + removeData: function (elem, name) { + dataUser.remove(elem, name); + }, + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function (elem, name, data) { + return dataPriv.access(elem, name, data); + }, + _removeData: function (elem, name) { + dataPriv.remove(elem, name); + } + }); + jQuery.fn.extend({ + data: function (key, value) { + var i, + name, + data, + elem = this[0], + attrs = elem && elem.attributes; // Gets all values + + if (key === undefined) { + if (this.length) { + data = dataUser.get(elem); + + if (elem.nodeType === 1 && !dataPriv.get(elem, "hasDataAttrs")) { + i = attrs.length; + + while (i--) { + // Support: IE 11 only + // The attrs elements can be null (#14894) + if (attrs[i]) { + name = attrs[i].name; + + if (name.indexOf("data-") === 0) { + name = camelCase(name.slice(5)); + dataAttr(elem, name, data[name]); + } + } + } + + dataPriv.set(elem, "hasDataAttrs", true); + } + } + + return data; + } // Sets multiple values + + + if (typeof key === "object") { + return this.each(function () { + dataUser.set(this, key); + }); + } + + return access(this, function (value) { + var data; // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + + if (elem && value === undefined) { + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get(elem, key); + + if (data !== undefined) { + return data; + } // Attempt to "discover" the data in + // HTML5 custom data-* attrs + + + data = dataAttr(elem, key); + + if (data !== undefined) { + return data; + } // We tried really hard, but the data doesn't exist. + + + return; + } // Set the data... + + + this.each(function () { + // We always store the camelCased key + dataUser.set(this, key, value); + }); + }, null, value, arguments.length > 1, null, true); + }, + removeData: function (key) { + return this.each(function () { + dataUser.remove(this, key); + }); + } + }); + jQuery.extend({ + queue: function (elem, type, data) { + var queue; + + if (elem) { + type = (type || "fx") + "queue"; + queue = dataPriv.get(elem, type); // Speed up dequeue by getting out quickly if this is just a lookup + + if (data) { + if (!queue || Array.isArray(data)) { + queue = dataPriv.access(elem, type, jQuery.makeArray(data)); + } else { + queue.push(data); + } + } + + return queue || []; + } + }, + dequeue: function (elem, type) { + type = type || "fx"; + + var queue = jQuery.queue(elem, type), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks(elem, type), + next = function () { + jQuery.dequeue(elem, type); + }; // If the fx queue is dequeued, always remove the progress sentinel + + + if (fn === "inprogress") { + fn = queue.shift(); + startLength--; + } + + if (fn) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if (type === "fx") { + queue.unshift("inprogress"); + } // Clear up the last queue stop function + + + delete hooks.stop; + fn.call(elem, next, hooks); + } + + if (!startLength && hooks) { + hooks.empty.fire(); + } + }, + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function (elem, type) { + var key = type + "queueHooks"; + return dataPriv.get(elem, key) || dataPriv.access(elem, key, { + empty: jQuery.Callbacks("once memory").add(function () { + dataPriv.remove(elem, [type + "queue", key]); + }) + }); + } + }); + jQuery.fn.extend({ + queue: function (type, data) { + var setter = 2; + + if (typeof type !== "string") { + data = type; + type = "fx"; + setter--; + } + + if (arguments.length < setter) { + return jQuery.queue(this[0], type); + } + + return data === undefined ? this : this.each(function () { + var queue = jQuery.queue(this, type, data); // Ensure a hooks for this queue + + jQuery._queueHooks(this, type); + + if (type === "fx" && queue[0] !== "inprogress") { + jQuery.dequeue(this, type); + } + }); + }, + dequeue: function (type) { + return this.each(function () { + jQuery.dequeue(this, type); + }); + }, + clearQueue: function (type) { + return this.queue(type || "fx", []); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function (type, obj) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function () { + if (! --count) { + defer.resolveWith(elements, [elements]); + } + }; + + if (typeof type !== "string") { + obj = type; + type = undefined; + } + + type = type || "fx"; + + while (i--) { + tmp = dataPriv.get(elements[i], type + "queueHooks"); + + if (tmp && tmp.empty) { + count++; + tmp.empty.add(resolve); + } + } + + resolve(); + return defer.promise(obj); + } + }); + var pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source; + var rcssNum = new RegExp("^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i"); + var cssExpand = ["Top", "Right", "Bottom", "Left"]; + + var isHiddenWithinTree = function (elem, el) { + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; // Inline style trumps all + + return elem.style.display === "none" || elem.style.display === "" && // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + jQuery.contains(elem.ownerDocument, elem) && jQuery.css(elem, "display") === "none"; + }; + + var swap = function (elem, options, callback, args) { + var ret, + name, + old = {}; // Remember the old values, and insert the new ones + + for (name in options) { + old[name] = elem.style[name]; + elem.style[name] = options[name]; + } + + ret = callback.apply(elem, args || []); // Revert the old values + + for (name in options) { + elem.style[name] = old[name]; + } + + return ret; + }; + + function adjustCSS(elem, prop, valueParts, tween) { + var adjusted, + scale, + maxIterations = 20, + currentValue = tween ? function () { + return tween.cur(); + } : function () { + return jQuery.css(elem, prop, ""); + }, + initial = currentValue(), + unit = valueParts && valueParts[3] || (jQuery.cssNumber[prop] ? "" : "px"), + // Starting value computation is required for potential unit mismatches + initialInUnit = (jQuery.cssNumber[prop] || unit !== "px" && +initial) && rcssNum.exec(jQuery.css(elem, prop)); + + if (initialInUnit && initialInUnit[3] !== unit) { + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; // Trust units reported by jQuery.css + + unit = unit || initialInUnit[3]; // Iteratively approximate from a nonzero starting point + + initialInUnit = +initial || 1; + + while (maxIterations--) { + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style(elem, prop, initialInUnit + unit); + + if ((1 - scale) * (1 - (scale = currentValue() / initial || 0.5)) <= 0) { + maxIterations = 0; + } + + initialInUnit = initialInUnit / scale; + } + + initialInUnit = initialInUnit * 2; + jQuery.style(elem, prop, initialInUnit + unit); // Make sure we update the tween properties later on + + valueParts = valueParts || []; + } + + if (valueParts) { + initialInUnit = +initialInUnit || +initial || 0; // Apply relative offset (+=/-=) if specified + + adjusted = valueParts[1] ? initialInUnit + (valueParts[1] + 1) * valueParts[2] : +valueParts[2]; + + if (tween) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + + return adjusted; + } + + var defaultDisplayMap = {}; + + function getDefaultDisplay(elem) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[nodeName]; + + if (display) { + return display; + } + + temp = doc.body.appendChild(doc.createElement(nodeName)); + display = jQuery.css(temp, "display"); + temp.parentNode.removeChild(temp); + + if (display === "none") { + display = "block"; + } + + defaultDisplayMap[nodeName] = display; + return display; + } + + function showHide(elements, show) { + var display, + elem, + values = [], + index = 0, + length = elements.length; // Determine new display value for elements that need to change + + for (; index < length; index++) { + elem = elements[index]; + + if (!elem.style) { + continue; + } + + display = elem.style.display; + + if (show) { + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if (display === "none") { + values[index] = dataPriv.get(elem, "display") || null; + + if (!values[index]) { + elem.style.display = ""; + } + } + + if (elem.style.display === "" && isHiddenWithinTree(elem)) { + values[index] = getDefaultDisplay(elem); + } + } else { + if (display !== "none") { + values[index] = "none"; // Remember what we're overwriting + + dataPriv.set(elem, "display", display); + } + } + } // Set the display of the elements in a second loop to avoid constant reflow + + + for (index = 0; index < length; index++) { + if (values[index] != null) { + elements[index].style.display = values[index]; + } + } + + return elements; + } + + jQuery.fn.extend({ + show: function () { + return showHide(this, true); + }, + hide: function () { + return showHide(this); + }, + toggle: function (state) { + if (typeof state === "boolean") { + return state ? this.show() : this.hide(); + } + + return this.each(function () { + if (isHiddenWithinTree(this)) { + jQuery(this).show(); + } else { + jQuery(this).hide(); + } + }); + } + }); + var rcheckableType = /^(?:checkbox|radio)$/i; + var rtagName = /<([a-z][^\/\0>\x20\t\r\n\f]+)/i; + var rscriptType = /^$|^module$|\/(?:java|ecma)script/i; // We have to close these tags to support XHTML (#13200) + + var wrapMap = { + // Support: IE <=9 only + option: [1, ""], + // 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$2.createDocumentFragment(), + div = fragment.appendChild(document$2.createElement("div")), + input = document$2.createElement("input"); // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + + input.setAttribute("type", "radio"); + input.setAttribute("checked", "checked"); + input.setAttribute("name", "t"); + div.appendChild(input); // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + + support.checkClone = div.cloneNode(true).cloneNode(true).lastChild.checked; // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode(true).lastChild.defaultValue; + })(); + + var documentElement = document$2.documentElement; + var rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + + function returnTrue() { + return true; + } + + function returnFalse() { + return false; + } // Support: IE <=9 only + // See #13393 for more info + + + function safeActiveElement() { + try { + return document$2.activeElement; + } catch (err) {} + } + + function on(elem, types, selector, data, fn, one) { + var origFn, type; // Types can be a map of types/handlers + + if (typeof types === "object") { + // ( types-Object, selector, data ) + if (typeof selector !== "string") { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + + for (type in types) { + on(elem, type, selector, data, types[type], one); + } + + return elem; + } + + if (data == null && fn == null) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if (fn == null) { + if (typeof selector === "string") { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + + if (fn === false) { + fn = returnFalse; + } else if (!fn) { + return elem; + } + + if (one === 1) { + origFn = fn; + + fn = function (event) { + // Can use an empty set, since event contains the info + jQuery().off(event); + return origFn.apply(this, arguments); + }; // Use same guid so caller can remove using origFn + + + fn.guid = origFn.guid || (origFn.guid = jQuery.guid++); + } + + return elem.each(function () { + jQuery.event.add(this, types, fn, data, selector); + }); + } + /* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ + + + jQuery.event = { + global: {}, + add: function (elem, types, handler, data, selector) { + var handleObjIn, + eventHandle, + tmp, + events, + t, + handleObj, + special, + handlers, + type, + namespaces, + origType, + elemData = dataPriv.get(elem); // Don't attach events to noData or text/comment nodes (but allow plain objects) + + if (!elemData) { + return; + } // Caller can pass in an object of custom data in lieu of the handler + + + if (handler.handler) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + + + if (selector) { + jQuery.find.matchesSelector(documentElement, selector); + } // Make sure that the handler has a unique ID, used to find/remove it later + + + if (!handler.guid) { + handler.guid = jQuery.guid++; + } // Init the element's event structure and main handler, if this is the first + + + if (!(events = elemData.events)) { + events = elemData.events = {}; + } + + if (!(eventHandle = elemData.handle)) { + eventHandle = elemData.handle = function (e) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply(elem, arguments) : undefined; + }; + } // Handle multiple events separated by a space + + + types = (types || "").match(rnothtmlwhite) || [""]; + t = types.length; + + while (t--) { + tmp = rtypenamespace.exec(types[t]) || []; + type = origType = tmp[1]; + namespaces = (tmp[2] || "").split(".").sort(); // There *must* be a type, no attaching namespace-only handlers + + if (!type) { + continue; + } // If event changes its type, use the special event handlers for the changed type + + + special = jQuery.event.special[type] || {}; // If selector defined, determine special event api type, otherwise given type + + type = (selector ? special.delegateType : special.bindType) || type; // Update special based on newly reset type + + special = jQuery.event.special[type] || {}; // handleObj is passed to all event handlers + + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test(selector), + namespace: namespaces.join(".") + }, handleObjIn); // Init the event handler queue if we're the first + + if (!(handlers = events[type])) { + handlers = events[type] = []; + handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false + + if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) { + if (elem.addEventListener) { + elem.addEventListener(type, eventHandle); + } + } + } + + if (special.add) { + special.add.call(elem, handleObj); + + if (!handleObj.handler.guid) { + handleObj.handler.guid = handler.guid; + } + } // Add to the element's handler list, delegates in front + + + if (selector) { + handlers.splice(handlers.delegateCount++, 0, handleObj); + } else { + handlers.push(handleObj); + } // Keep track of which events have ever been used, for event optimization + + + jQuery.event.global[type] = true; + } + }, + // Detach an event or set of events from an element + remove: function (elem, types, handler, selector, mappedTypes) { + var j, + origCount, + tmp, + events, + t, + handleObj, + special, + handlers, + type, + namespaces, + origType, + elemData = dataPriv.hasData(elem) && dataPriv.get(elem); + + if (!elemData || !(events = elemData.events)) { + return; + } // Once for each type.namespace in types; type may be omitted + + + types = (types || "").match(rnothtmlwhite) || [""]; + t = types.length; + + while (t--) { + tmp = rtypenamespace.exec(types[t]) || []; + type = origType = tmp[1]; + namespaces = (tmp[2] || "").split(".").sort(); // Unbind all events (on this namespace, if provided) for the element + + if (!type) { + for (type in events) { + jQuery.event.remove(elem, type + types[t], handler, selector, true); + } + + continue; + } + + special = jQuery.event.special[type] || {}; + type = (selector ? special.delegateType : special.bindType) || type; + handlers = events[type] || []; + tmp = tmp[2] && new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)"); // Remove matching events + + origCount = j = handlers.length; + + while (j--) { + handleObj = handlers[j]; + + if ((mappedTypes || origType === handleObj.origType) && (!handler || handler.guid === handleObj.guid) && (!tmp || tmp.test(handleObj.namespace)) && (!selector || selector === handleObj.selector || selector === "**" && handleObj.selector)) { + handlers.splice(j, 1); + + if (handleObj.selector) { + handlers.delegateCount--; + } + + if (special.remove) { + special.remove.call(elem, handleObj); + } + } + } // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + + + if (origCount && !handlers.length) { + if (!special.teardown || special.teardown.call(elem, namespaces, elemData.handle) === false) { + jQuery.removeEvent(elem, type, elemData.handle); + } + + delete events[type]; + } + } // Remove data and the expando if it's no longer used + + + if (jQuery.isEmptyObject(events)) { + dataPriv.remove(elem, "handle events"); + } + }, + dispatch: function (nativeEvent) { + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix(nativeEvent); + var i, + j, + ret, + matched, + handleObj, + handlerQueue, + args = new Array(arguments.length), + handlers = (dataPriv.get(this, "events") || {})[event.type] || [], + special = jQuery.event.special[event.type] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event + + args[0] = event; + + for (i = 1; i < arguments.length; i++) { + args[i] = arguments[i]; + } + + event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired + + if (special.preDispatch && special.preDispatch.call(this, event) === false) { + return; + } // Determine handlers + + + handlerQueue = jQuery.event.handlers.call(this, event, handlers); // Run delegates first; they may want to stop propagation beneath us + + i = 0; + + while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) { + event.currentTarget = matched.elem; + j = 0; + + while ((handleObj = matched.handlers[j++]) && !event.isImmediatePropagationStopped()) { + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if (!event.rnamespace || event.rnamespace.test(handleObj.namespace)) { + event.handleObj = handleObj; + event.data = handleObj.data; + ret = ((jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler).apply(matched.elem, args); + + if (ret !== undefined) { + if ((event.result = ret) === false) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } // Call the postDispatch hook for the mapped type + + + if (special.postDispatch) { + special.postDispatch.call(this, event); + } + + return event.result; + }, + handlers: function (event, handlers) { + var i, + handleObj, + sel, + matchedHandlers, + matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; // Find delegate handlers + + if (delegateCount && // Support: IE <=9 + // Black-hole SVG 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$2.createElement("div"), + div = document$2.createElement("div"); // Finish early in limited (non-browser) environments + + if (!div.style) { + return; + } // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + + + div.style.backgroundClip = "content-box"; + div.cloneNode(true).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + jQuery.extend(support, { + boxSizingReliable: function () { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function () { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function () { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function () { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function () { + computeStyleTests(); + return scrollboxSizeVal; + } + }); + })(); + + function curCSS(elem, name, computed) { + var width, + minWidth, + maxWidth, + ret, + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + computed = computed || getStyles(elem); // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + + if (computed) { + ret = computed.getPropertyValue(name) || computed[name]; + + if (ret === "" && !jQuery.contains(elem.ownerDocument, elem)) { + ret = jQuery.style(elem, name); + } // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + + + if (!support.pixelBoxStyles() && rnumnonpx.test(ret) && rboxStyle.test(name)) { + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; // Put in the new values to get a computed value out + + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; // Revert the changed values + + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : ret; + } + + function addGetHookIf(conditionFn, hookFn) { + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function () { + if (conditionFn()) { + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } // Hook needed; redefine it so that the support test is not executed again. + + + return (this.get = hookFn).apply(this, arguments); + } + }; + } + + var // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { + position: "absolute", + visibility: "hidden", + display: "block" + }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + cssPrefixes = ["Webkit", "Moz", "ms"], + emptyStyle = document$2.createElement("div").style; // Return a css property mapped to a potentially vendor prefixed property + + function vendorPropName(name) { + // Shortcut for names that are not vendor prefixed + if (name in emptyStyle) { + return name; + } // Check for vendor prefixed names + + + var capName = name[0].toUpperCase() + name.slice(1), + i = cssPrefixes.length; + + while (i--) { + name = cssPrefixes[i] + capName; + + if (name in emptyStyle) { + return name; + } + } + } // Return a property mapped along what jQuery.cssProps suggests or to + // a vendor prefixed property. + + + function finalPropName(name) { + var ret = jQuery.cssProps[name]; + + if (!ret) { + ret = jQuery.cssProps[name] = vendorPropName(name) || name; + } + + return ret; + } + + function setPositiveNumber(elem, value, subtract) { + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec(value); + return matches ? // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max(0, matches[2] - (subtract || 0)) + (matches[3] || "px") : value; + } + + function boxModelAdjustment(elem, dimension, box, isBorderBox, styles, computedVal) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; // Adjustment may not be necessary + + if (box === (isBorderBox ? "border" : "content")) { + return 0; + } + + for (; i < 4; i += 2) { + // Both box models exclude margin + if (box === "margin") { + delta += jQuery.css(elem, box + cssExpand[i], true, styles); + } // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + + + if (!isBorderBox) { + // Add padding + delta += jQuery.css(elem, "padding" + cssExpand[i], true, styles); // For "border" or "margin", add border + + if (box !== "padding") { + delta += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles); // But still keep track of it otherwise + } else { + extra += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles); + } // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + + } else { + // For "content", subtract padding + if (box === "content") { + delta -= jQuery.css(elem, "padding" + cssExpand[i], true, styles); + } // For "content" or "padding", subtract border + + + if (box !== "margin") { + delta -= jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles); + } + } + } // Account for positive content-box scroll gutter when requested by providing computedVal + + + if (!isBorderBox && computedVal >= 0) { + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max(0, Math.ceil(elem["offset" + dimension[0].toUpperCase() + dimension.slice(1)] - computedVal - delta - extra - 0.5)); + } + + return delta; + } + + function getWidthOrHeight(elem, dimension, extra) { + // Start with computed style + var styles = getStyles(elem), + val = curCSS(elem, dimension, styles), + isBorderBox = jQuery.css(elem, "boxSizing", false, styles) === "border-box", + valueIsBorderBox = isBorderBox; // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + + if (rnumnonpx.test(val)) { + if (!extra) { + return val; + } + + val = "auto"; + } // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + + + valueIsBorderBox = valueIsBorderBox && (support.boxSizingReliable() || val === elem.style[dimension]); // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + + if (val === "auto" || !parseFloat(val) && jQuery.css(elem, "display", false, styles) === "inline") { + val = elem["offset" + dimension[0].toUpperCase() + dimension.slice(1)]; // offsetWidth/offsetHeight provide border-box values + + valueIsBorderBox = true; + } // Normalize "" and auto + + + val = parseFloat(val) || 0; // Adjust for the element's box model + + return val + boxModelAdjustment(elem, dimension, extra || (isBorderBox ? "border" : "content"), valueIsBorderBox, styles, // Provide the current computed size to request scroll gutter calculation (gh-3589) + val) + "px"; + } + + jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function (elem, computed) { + if (computed) { + // We should always get a number back from opacity + var ret = curCSS(elem, "opacity"); + return ret === "" ? "1" : ret; + } + } + } + }, + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + // Get and set the style property on a DOM Node + style: function (elem, name, value, extra) { + // Don't set styles on text and comment nodes + if (!elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style) { + return; + } // Make sure that we're working with the right name + + + var ret, + type, + hooks, + origName = camelCase(name), + isCustomProp = rcustomProp.test(name), + style = elem.style; // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + + if (!isCustomProp) { + name = finalPropName(origName); + } // Gets hook for the prefixed version, then unprefixed version + + + hooks = jQuery.cssHooks[name] || jQuery.cssHooks[origName]; // Check if we're setting a value + + if (value !== undefined) { + type = typeof value; // Convert "+=" or "-=" to relative numbers (#7345) + + if (type === "string" && (ret = rcssNum.exec(value)) && ret[1]) { + value = adjustCSS(elem, name, ret); // Fixes bug #9237 + + type = "number"; + } // Make sure that null and NaN values aren't set (#7116) + + + if (value == null || value !== value) { + return; + } // If a number was passed in, add the unit (except for certain CSS properties) + + + if (type === "number") { + value += ret && ret[3] || (jQuery.cssNumber[origName] ? "" : "px"); + } // background-* props affect original clone's values + + + if (!support.clearCloneStyle && value === "" && name.indexOf("background") === 0) { + style[name] = "inherit"; + } // If a hook was provided, use that value, otherwise just set the specified value + + + if (!hooks || !("set" in hooks) || (value = hooks.set(elem, value, extra)) !== undefined) { + if (isCustomProp) { + style.setProperty(name, value); + } else { + style[name] = value; + } + } + } else { + // If a hook was provided get the non-computed value from there + if (hooks && "get" in hooks && (ret = hooks.get(elem, false, extra)) !== undefined) { + return ret; + } // Otherwise just get the value from the style object + + + return style[name]; + } + }, + css: function (elem, name, extra, styles) { + var val, + num, + hooks, + origName = camelCase(name), + isCustomProp = rcustomProp.test(name); // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + + if (!isCustomProp) { + name = finalPropName(origName); + } // Try prefixed name followed by the unprefixed name + + + hooks = jQuery.cssHooks[name] || jQuery.cssHooks[origName]; // If a hook was provided get the computed value from there + + if (hooks && "get" in hooks) { + val = hooks.get(elem, true, extra); + } // Otherwise, if a way to get the computed value exists, use that + + + if (val === undefined) { + val = curCSS(elem, name, styles); + } // Convert "normal" to computed value + + + if (val === "normal" && name in cssNormalTransform) { + val = cssNormalTransform[name]; + } // Make numeric if forced or a qualifier was provided and val looks numeric + + + if (extra === "" || extra) { + num = parseFloat(val); + return extra === true || isFinite(num) ? num || 0 : val; + } + + return val; + } + }); + jQuery.each(["height", "width"], function (i, dimension) { + jQuery.cssHooks[dimension] = { + get: function (elem, computed, extra) { + if (computed) { + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test(jQuery.css(elem, "display")) && (!elem.getClientRects().length || !elem.getBoundingClientRect().width) ? swap(elem, cssShow, function () { + return getWidthOrHeight(elem, dimension, extra); + }) : getWidthOrHeight(elem, dimension, extra); + } + }, + set: function (elem, value, extra) { + var matches, + styles = getStyles(elem), + isBorderBox = jQuery.css(elem, "boxSizing", false, styles) === "border-box", + subtract = extra && boxModelAdjustment(elem, dimension, extra, isBorderBox, styles); // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + + if (isBorderBox && support.scrollboxSize() === styles.position) { + subtract -= Math.ceil(elem["offset" + dimension[0].toUpperCase() + dimension.slice(1)] - parseFloat(styles[dimension]) - boxModelAdjustment(elem, dimension, "border", false, styles) - 0.5); + } // Convert to pixels if value adjustment is needed + + + if (subtract && (matches = rcssNum.exec(value)) && (matches[3] || "px") !== "px") { + elem.style[dimension] = value; + value = jQuery.css(elem, dimension); + } + + return setPositiveNumber(elem, value, subtract); + } + }; + }); + jQuery.cssHooks.marginLeft = addGetHookIf(support.reliableMarginLeft, function (elem, computed) { + if (computed) { + return (parseFloat(curCSS(elem, "marginLeft")) || elem.getBoundingClientRect().left - swap(elem, { + marginLeft: 0 + }, function () { + return elem.getBoundingClientRect().left; + })) + "px"; + } + }); // These hooks are used by animate to expand properties + + jQuery.each({ + margin: "", + padding: "", + border: "Width" + }, function (prefix, suffix) { + jQuery.cssHooks[prefix + suffix] = { + expand: function (value) { + var i = 0, + expanded = {}, + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [value]; + + for (; i < 4; i++) { + expanded[prefix + cssExpand[i] + suffix] = parts[i] || parts[i - 2] || parts[0]; + } + + return expanded; + } + }; + + if (prefix !== "margin") { + jQuery.cssHooks[prefix + suffix].set = setPositiveNumber; + } + }); + jQuery.fn.extend({ + css: function (name, value) { + return access(this, function (elem, name, value) { + var styles, + len, + map = {}, + i = 0; + + if (Array.isArray(name)) { + styles = getStyles(elem); + len = name.length; + + for (; i < len; i++) { + map[name[i]] = jQuery.css(elem, name[i], false, styles); + } + + return map; + } + + return value !== undefined ? jQuery.style(elem, name, value) : jQuery.css(elem, name); + }, name, value, arguments.length > 1); + } + }); // Based off of the plugin by Clint Helfers, with permission. + // https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ + + jQuery.fn.delay = function (time, type) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + return this.queue(type, function (next, hooks) { + var timeout = window.setTimeout(next, time); + + hooks.stop = function () { + window.clearTimeout(timeout); + }; + }); + }; + + (function () { + var input = document$2.createElement("input"), + select = document$2.createElement("select"), + opt = select.appendChild(document$2.createElement("option")); + input.type = "checkbox"; // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + + support.checkOn = input.value !== ""; // Support: IE <=11 only + // Must access selectedIndex to make default options select + + support.optSelected = opt.selected; // Support: IE <=11 only + // An input loses its value after becoming a radio + + input = document$2.createElement("input"); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; + })(); + + var boolHook, + attrHandle = jQuery.expr.attrHandle; + jQuery.fn.extend({ + attr: function (name, value) { + return access(this, jQuery.attr, name, value, arguments.length > 1); + }, + removeAttr: function (name) { + return this.each(function () { + jQuery.removeAttr(this, name); + }); + } + }); + jQuery.extend({ + attr: function (elem, name, value) { + var ret, + hooks, + nType = elem.nodeType; // Don't get/set attributes on text, comment and attribute nodes + + if (nType === 3 || nType === 8 || nType === 2) { + return; + } // Fallback to prop when attributes are not supported + + + if (typeof elem.getAttribute === "undefined") { + return jQuery.prop(elem, name, value); + } // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + + + if (nType !== 1 || !jQuery.isXMLDoc(elem)) { + hooks = jQuery.attrHooks[name.toLowerCase()] || (jQuery.expr.match.bool.test(name) ? boolHook : undefined); + } + + if (value !== undefined) { + if (value === null) { + jQuery.removeAttr(elem, name); + return; + } + + if (hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== undefined) { + return ret; + } + + elem.setAttribute(name, value + ""); + return value; + } + + if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) { + return ret; + } + + ret = jQuery.find.attr(elem, name); // Non-existent attributes return null, we normalize to undefined + + return ret == null ? undefined : ret; + }, + attrHooks: { + type: { + set: function (elem, value) { + if (!support.radioValue && value === "radio" && nodeName(elem, "input")) { + var val = elem.value; + elem.setAttribute("type", value); + + if (val) { + elem.value = val; + } + + return value; + } + } + } + }, + removeAttr: function (elem, value) { + var name, + i = 0, + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match(rnothtmlwhite); + + if (attrNames && elem.nodeType === 1) { + while (name = attrNames[i++]) { + elem.removeAttribute(name); + } + } + } + }); // Hooks for boolean attributes + + boolHook = { + set: function (elem, value, name) { + if (value === false) { + // Remove boolean attributes when set to false + jQuery.removeAttr(elem, name); + } else { + elem.setAttribute(name, name); + } + + return name; + } + }; + jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g), function (i, name) { + var getter = attrHandle[name] || jQuery.find.attr; + + attrHandle[name] = function (elem, name, isXML) { + var ret, + handle, + lowercaseName = name.toLowerCase(); + + if (!isXML) { + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[lowercaseName]; + attrHandle[lowercaseName] = ret; + ret = getter(elem, name, isXML) != null ? lowercaseName : null; + attrHandle[lowercaseName] = handle; + } + + return ret; + }; + }); + var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + jQuery.fn.extend({ + prop: function (name, value) { + return access(this, jQuery.prop, name, value, arguments.length > 1); + }, + removeProp: function (name) { + return this.each(function () { + delete this[jQuery.propFix[name] || name]; + }); + } + }); + jQuery.extend({ + prop: function (elem, name, value) { + var ret, + hooks, + nType = elem.nodeType; // Don't get/set properties on text, comment and attribute nodes + + if (nType === 3 || nType === 8 || nType === 2) { + return; + } + + if (nType !== 1 || !jQuery.isXMLDoc(elem)) { + // Fix name and attach hooks + name = jQuery.propFix[name] || name; + hooks = jQuery.propHooks[name]; + } + + if (value !== undefined) { + if (hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== undefined) { + return ret; + } + + return elem[name] = value; + } + + if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) { + return ret; + } + + return elem[name]; + }, + propHooks: { + tabIndex: { + get: function (elem) { + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr(elem, "tabindex"); + + if (tabindex) { + return parseInt(tabindex, 10); + } + + if (rfocusable.test(elem.nodeName) || rclickable.test(elem.nodeName) && elem.href) { + return 0; + } + + return -1; + } + } + }, + propFix: { + "for": "htmlFor", + "class": "className" + } + }); // Support: IE <=11 only + // Accessing the selectedIndex property + // forces the browser to respect setting selected + // on the option + // The getter ensures a default option is selected + // when in an optgroup + // eslint rule "no-unused-expressions" is disabled for this code + // since it considers such accessions noop + + if (!support.optSelected) { + jQuery.propHooks.selected = { + get: function (elem) { + /* eslint no-unused-expressions: "off" */ + var parent = elem.parentNode; + + if (parent && parent.parentNode) { + parent.parentNode.selectedIndex; + } + + return null; + }, + set: function (elem) { + /* eslint no-unused-expressions: "off" */ + var parent = elem.parentNode; + + if (parent) { + parent.selectedIndex; + + if (parent.parentNode) { + parent.parentNode.selectedIndex; + } + } + } + }; + } + + jQuery.each(["tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable"], function () { + jQuery.propFix[this.toLowerCase()] = this; + }); // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + + function stripAndCollapse(value) { + var tokens = value.match(rnothtmlwhite) || []; + return tokens.join(" "); + } + + function getClass(elem) { + return elem.getAttribute && elem.getAttribute("class") || ""; + } + + function classesToArray(value) { + if (Array.isArray(value)) { + return value; + } + + if (typeof value === "string") { + return value.match(rnothtmlwhite) || []; + } + + return []; + } + + jQuery.fn.extend({ + addClass: function (value) { + var classes, + elem, + cur, + curValue, + clazz, + j, + finalValue, + i = 0; + + if (isFunction(value)) { + return this.each(function (j) { + jQuery(this).addClass(value.call(this, j, getClass(this))); + }); + } + + classes = classesToArray(value); + + if (classes.length) { + while (elem = this[i++]) { + curValue = getClass(elem); + cur = elem.nodeType === 1 && " " + stripAndCollapse(curValue) + " "; + + if (cur) { + j = 0; + + while (clazz = classes[j++]) { + if (cur.indexOf(" " + clazz + " ") < 0) { + cur += clazz + " "; + } + } // Only assign if different to avoid unneeded rendering. + + + finalValue = stripAndCollapse(cur); + + if (curValue !== finalValue) { + elem.setAttribute("class", finalValue); + } + } + } + } + + return this; + }, + removeClass: function (value) { + var classes, + elem, + cur, + curValue, + clazz, + j, + finalValue, + i = 0; + + if (isFunction(value)) { + return this.each(function (j) { + jQuery(this).removeClass(value.call(this, j, getClass(this))); + }); + } + + if (!arguments.length) { + return this.attr("class", ""); + } + + classes = classesToArray(value); + + if (classes.length) { + while (elem = this[i++]) { + curValue = getClass(elem); // This expression is here for better compressibility (see addClass) + + cur = elem.nodeType === 1 && " " + stripAndCollapse(curValue) + " "; + + if (cur) { + j = 0; + + while (clazz = classes[j++]) { + // Remove *all* instances + while (cur.indexOf(" " + clazz + " ") > -1) { + cur = cur.replace(" " + clazz + " ", " "); + } + } // Only assign if different to avoid unneeded rendering. + + + finalValue = stripAndCollapse(cur); + + if (curValue !== finalValue) { + elem.setAttribute("class", finalValue); + } + } + } + } + + return this; + }, + toggleClass: function (value, stateVal) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray(value); + + if (typeof stateVal === "boolean" && isValidValue) { + return stateVal ? this.addClass(value) : this.removeClass(value); + } + + if (isFunction(value)) { + return this.each(function (i) { + jQuery(this).toggleClass(value.call(this, i, getClass(this), stateVal), stateVal); + }); + } + + return this.each(function () { + var className, i, self, classNames; + + if (isValidValue) { + // Toggle individual class names + i = 0; + self = jQuery(this); + classNames = classesToArray(value); + + while (className = classNames[i++]) { + // Check each className given, space separated list + if (self.hasClass(className)) { + self.removeClass(className); + } else { + self.addClass(className); + } + } // Toggle whole class name + + } else if (value === undefined || type === "boolean") { + className = getClass(this); + + if (className) { + // Store className if set + dataPriv.set(this, "__className__", className); + } // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + + + if (this.setAttribute) { + this.setAttribute("class", className || value === false ? "" : dataPriv.get(this, "__className__") || ""); + } + } + }); + }, + hasClass: function (selector) { + var className, + elem, + i = 0; + className = " " + selector + " "; + + while (elem = this[i++]) { + if (elem.nodeType === 1 && (" " + stripAndCollapse(getClass(elem)) + " ").indexOf(className) > -1) { + return true; + } + } + + return false; + } + }); + var rreturn = /\r/g; + jQuery.fn.extend({ + val: function (value) { + var hooks, + ret, + valueIsFunction, + elem = this[0]; + + if (!arguments.length) { + if (elem) { + hooks = jQuery.valHooks[elem.type] || jQuery.valHooks[elem.nodeName.toLowerCase()]; + + if (hooks && "get" in hooks && (ret = hooks.get(elem, "value")) !== undefined) { + return ret; + } + + ret = elem.value; // Handle most common string cases + + if (typeof ret === "string") { + return ret.replace(rreturn, ""); + } // Handle cases where value is null/undef or number + + + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction(value); + return this.each(function (i) { + var val; + + if (this.nodeType !== 1) { + return; + } + + if (valueIsFunction) { + val = value.call(this, i, jQuery(this).val()); + } else { + val = value; + } // Treat null/undefined as ""; convert numbers to string + + + if (val == null) { + val = ""; + } else if (typeof val === "number") { + val += ""; + } else if (Array.isArray(val)) { + val = jQuery.map(val, function (value) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[this.type] || jQuery.valHooks[this.nodeName.toLowerCase()]; // If set returns undefined, fall back to normal setting + + if (!hooks || !("set" in hooks) || hooks.set(this, val, "value") === undefined) { + this.value = val; + } + }); + } + }); + jQuery.extend({ + valHooks: { + option: { + get: function (elem) { + var val = jQuery.find.attr(elem, "value"); + return val != null ? val : // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse(jQuery.text(elem)); + } + }, + select: { + get: function (elem) { + var value, + option, + i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if (index < 0) { + i = max; + } else { + i = one ? index : 0; + } // Loop through all the selected options + + + for (; i < max; i++) { + option = options[i]; // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + + if ((option.selected || i === index) && // Don't return options that are disabled or in a disabled optgroup + !option.disabled && (!option.parentNode.disabled || !nodeName(option.parentNode, "optgroup"))) { + // Get the specific value for the option + value = jQuery(option).val(); // We don't need an array for one selects + + if (one) { + return value; + } // Multi-Selects return an array + + + values.push(value); + } + } + + return values; + }, + set: function (elem, value) { + var optionSet, + option, + options = elem.options, + values = jQuery.makeArray(value), + i = options.length; + + while (i--) { + option = options[i]; + /* eslint-disable no-cond-assign */ + + if (option.selected = jQuery.inArray(jQuery.valHooks.option.get(option), values) > -1) { + optionSet = true; + } + /* eslint-enable no-cond-assign */ + + } // Force browsers to behave consistently when non-matching value is set + + + if (!optionSet) { + elem.selectedIndex = -1; + } + + return values; + } + } + } + }); // Radios and checkboxes getter/setter + + jQuery.each(["radio", "checkbox"], function () { + jQuery.valHooks[this] = { + set: function (elem, value) { + if (Array.isArray(value)) { + return elem.checked = jQuery.inArray(jQuery(elem).val(), value) > -1; + } + } + }; + + if (!support.checkOn) { + jQuery.valHooks[this].get = function (elem) { + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } + }); // Return jQuery for attributes-only inclusion + + support.focusin = "onfocusin" in window; + + var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function (e) { + e.stopPropagation(); + }; + + jQuery.extend(jQuery.event, { + trigger: function (event, data, elem, onlyHandlers) { + var i, + cur, + tmp, + bubbleType, + ontype, + handle, + special, + lastElement, + eventPath = [elem || document$2], + type = hasOwn.call(event, "type") ? event.type : event, + namespaces = hasOwn.call(event, "namespace") ? event.namespace.split(".") : []; + cur = lastElement = tmp = elem = elem || document$2; // Don't do events on text and comment nodes + + if (elem.nodeType === 3 || elem.nodeType === 8) { + return; + } // focus/blur morphs to focusin/out; ensure we're not firing them right now + + + if (rfocusMorph.test(type + jQuery.event.triggered)) { + return; + } + + if (type.indexOf(".") > -1) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string + + event = event[jQuery.expando] ? event : new jQuery.Event(type, typeof event === "object" && event); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.rnamespace = event.namespace ? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; // Clean up the event in case it is being reused + + event.result = undefined; + + if (!event.target) { + event.target = elem; + } // Clone any incoming data and prepend the event, creating the handler arg list + + + data = data == null ? [event] : jQuery.makeArray(data, [event]); // Allow special events to draw outside the lines + + special = jQuery.event.special[type] || {}; + + if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) { + return; + } // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + + + if (!onlyHandlers && !special.noBubble && !isWindow(elem)) { + bubbleType = special.delegateType || type; + + if (!rfocusMorph.test(bubbleType + type)) { + cur = cur.parentNode; + } + + for (; cur; cur = cur.parentNode) { + eventPath.push(cur); + tmp = cur; + } // Only add window if we got to document (e.g., not plain obj or detached DOM) + + + if (tmp === (elem.ownerDocument || document$2)) { + eventPath.push(tmp.defaultView || tmp.parentWindow || window); + } + } // Fire handlers on the event path + + + i = 0; + + while ((cur = eventPath[i++]) && !event.isPropagationStopped()) { + lastElement = cur; + event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler + + handle = (dataPriv.get(cur, "events") || {})[event.type] && dataPriv.get(cur, "handle"); + + if (handle) { + handle.apply(cur, data); + } // Native handler + + + handle = ontype && cur[ontype]; + + if (handle && handle.apply && acceptData(cur)) { + event.result = handle.apply(cur, data); + + if (event.result === false) { + event.preventDefault(); + } + } + } + + event.type = type; // If nobody prevented the default action, do it now + + if (!onlyHandlers && !event.isDefaultPrevented()) { + if ((!special._default || special._default.apply(eventPath.pop(), data) === false) && acceptData(elem)) { + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if (ontype && isFunction(elem[type]) && !isWindow(elem)) { + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ontype]; + + if (tmp) { + elem[ontype] = null; + } // Prevent re-triggering of the same event, since we already bubbled it above + + + jQuery.event.triggered = type; + + if (event.isPropagationStopped()) { + lastElement.addEventListener(type, stopPropagationCallback); + } + + elem[type](); + + if (event.isPropagationStopped()) { + lastElement.removeEventListener(type, stopPropagationCallback); + } + + jQuery.event.triggered = undefined; + + if (tmp) { + elem[ontype] = tmp; + } + } + } + } + + return event.result; + }, + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function (type, elem, event) { + var e = jQuery.extend(new jQuery.Event(), event, { + type: type, + isSimulated: true + }); + jQuery.event.trigger(e, null, elem); + } + }); + jQuery.fn.extend({ + trigger: function (type, data) { + return this.each(function () { + jQuery.event.trigger(type, data, this); + }); + }, + triggerHandler: function (type, data) { + var elem = this[0]; + + if (elem) { + return jQuery.event.trigger(type, data, elem, true); + } + } + }); // Support: Firefox <=44 + // Firefox doesn't have focus(in | out) events + // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 + // + // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 + // focus(in | out) events fire after focus & blur events, + // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order + // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 + + if (!support.focusin) { + jQuery.each({ + focus: "focusin", + blur: "focusout" + }, function (orig, fix) { + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function (event) { + jQuery.event.simulate(fix, event.target, jQuery.event.fix(event)); + }; + + jQuery.event.special[fix] = { + setup: function () { + var doc = this.ownerDocument || this, + attaches = dataPriv.access(doc, fix); + + if (!attaches) { + doc.addEventListener(orig, handler, true); + } + + dataPriv.access(doc, fix, (attaches || 0) + 1); + }, + teardown: function () { + var doc = this.ownerDocument || this, + attaches = dataPriv.access(doc, fix) - 1; + + if (!attaches) { + doc.removeEventListener(orig, handler, true); + dataPriv.remove(doc, fix); + } else { + dataPriv.access(doc, fix, attaches); + } + } + }; + }); + } + + var rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + + function buildParams(prefix, obj, traditional, add) { + var name; + + if (Array.isArray(obj)) { + // Serialize array item. + jQuery.each(obj, function (i, v) { + if (traditional || rbracket.test(prefix)) { + // Treat each array item as a scalar. + add(prefix, v); + } else { + // Item is non-scalar (array or object), encode its numeric index. + buildParams(prefix + "[" + (typeof v === "object" && v != null ? i : "") + "]", v, traditional, add); + } + }); + } else if (!traditional && toType(obj) === "object") { + // Serialize object item. + for (name in obj) { + buildParams(prefix + "[" + name + "]", obj[name], traditional, add); + } + } else { + // Serialize scalar item. + add(prefix, obj); + } + } // Serialize an array of form elements or a set of + // key/values into a query string + + + jQuery.param = function (a, traditional) { + var prefix, + s = [], + add = function (key, valueOrFunction) { + // If value is a function, invoke it and use its return value + var value = isFunction(valueOrFunction) ? valueOrFunction() : valueOrFunction; + s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value == null ? "" : value); + }; // If an array was passed in, assume that it is an array of form elements. + + + if (Array.isArray(a) || a.jquery && !jQuery.isPlainObject(a)) { + // Serialize the form elements + jQuery.each(a, function () { + add(this.name, this.value); + }); + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for (prefix in a) { + buildParams(prefix, a[prefix], traditional, add); + } + } // Return the resulting serialization + + + return s.join("&"); + }; + + jQuery.fn.extend({ + serialize: function () { + return jQuery.param(this.serializeArray()); + }, + serializeArray: function () { + return this.map(function () { + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop(this, "elements"); + return elements ? jQuery.makeArray(elements) : this; + }).filter(function () { + var type = this.type; // Use .is( ":disabled" ) so that fieldset[disabled] works + + return this.name && !jQuery(this).is(":disabled") && rsubmittable.test(this.nodeName) && !rsubmitterTypes.test(type) && (this.checked || !rcheckableType.test(type)); + }).map(function (i, elem) { + var val = jQuery(this).val(); + + if (val == null) { + return null; + } + + if (Array.isArray(val)) { + return jQuery.map(val, function (val) { + return { + name: elem.name, + value: val.replace(rCRLF, "\r\n") + }; + }); + } + + return { + name: elem.name, + value: val.replace(rCRLF, "\r\n") + }; + }).get(); + } + }); + jQuery.fn.extend({ + wrapAll: function (html) { + var wrap; + + if (this[0]) { + if (isFunction(html)) { + html = html.call(this[0]); + } // The elements to wrap the target around + + + wrap = jQuery(html, this[0].ownerDocument).eq(0).clone(true); + + if (this[0].parentNode) { + wrap.insertBefore(this[0]); + } + + wrap.map(function () { + var elem = this; + + while (elem.firstElementChild) { + elem = elem.firstElementChild; + } + + return elem; + }).append(this); + } + + return this; + }, + wrapInner: function (html) { + if (isFunction(html)) { + return this.each(function (i) { + jQuery(this).wrapInner(html.call(this, i)); + }); + } + + return this.each(function () { + var self = jQuery(this), + contents = self.contents(); + + if (contents.length) { + contents.wrapAll(html); + } else { + self.append(html); + } + }); + }, + wrap: function (html) { + var htmlIsFunction = isFunction(html); + return this.each(function (i) { + jQuery(this).wrapAll(htmlIsFunction ? html.call(this, i) : html); + }); + }, + unwrap: function (selector) { + this.parent(selector).not("body").each(function () { + jQuery(this).replaceWith(this.childNodes); + }); + return this; + } + }); + + jQuery.expr.pseudos.hidden = function (elem) { + return !jQuery.expr.pseudos.visible(elem); + }; + + jQuery.expr.pseudos.visible = function (elem) { + return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length); + }; // Support: Safari 8 only + // In Safari 8 documents created via document.implementation.createHTMLDocument + // collapse sibling forms: the second one becomes a child of the first one. + // Because of that, this security measure has to be disabled in Safari 8. + // https://bugs.webkit.org/show_bug.cgi?id=137337 + + + support.createHTMLDocument = function () { + var body = document$2.implementation.createHTMLDocument("").body; + body.innerHTML = "
"; + return body.childNodes.length === 2; + }(); // Argument "data" should be string of html + // context (optional): If specified, the fragment will be created in this context, + // defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + + + jQuery.parseHTML = function (data, context, keepScripts) { + if (typeof data !== "string") { + return []; + } + + if (typeof context === "boolean") { + keepScripts = context; + context = false; + } + + var base, parsed, scripts; + + if (!context) { + // Stop scripts or inline event handlers from being executed immediately + // by using document.implementation + if (support.createHTMLDocument) { + context = document$2.implementation.createHTMLDocument(""); // Set the base href for the created document + // so any parsed elements with URLs + // are based on the document's URL (gh-2965) + + base = context.createElement("base"); + base.href = document$2.location.href; + context.head.appendChild(base); + } else { + context = document$2; + } + } + + parsed = rsingleTag.exec(data); + scripts = !keepScripts && []; // Single tag + + if (parsed) { + return [context.createElement(parsed[1])]; + } + + parsed = buildFragment([data], context, scripts); + + if (scripts && scripts.length) { + jQuery(scripts).remove(); + } + + return jQuery.merge([], parsed.childNodes); + }; + + jQuery.offset = { + setOffset: function (elem, options, i) { + var curPosition, + curLeft, + curCSSTop, + curTop, + curOffset, + curCSSLeft, + calculatePosition, + position = jQuery.css(elem, "position"), + curElem = jQuery(elem), + props = {}; // Set position first, in-case top/left are set even on static elem + + if (position === "static") { + elem.style.position = "relative"; + } + + curOffset = curElem.offset(); + curCSSTop = jQuery.css(elem, "top"); + curCSSLeft = jQuery.css(elem, "left"); + calculatePosition = (position === "absolute" || position === "fixed") && (curCSSTop + curCSSLeft).indexOf("auto") > -1; // Need to be able to calculate position if either + // top or left is auto and position is either absolute or fixed + + if (calculatePosition) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + } else { + curTop = parseFloat(curCSSTop) || 0; + curLeft = parseFloat(curCSSLeft) || 0; + } + + if (isFunction(options)) { + // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) + options = options.call(elem, i, jQuery.extend({}, curOffset)); + } + + if (options.top != null) { + props.top = options.top - curOffset.top + curTop; + } + + if (options.left != null) { + props.left = options.left - curOffset.left + curLeft; + } + + if ("using" in options) { + options.using.call(elem, props); + } else { + curElem.css(props); + } + } + }; + jQuery.fn.extend({ + // offset() relates an element's border box to the document origin + offset: function (options) { + // Preserve chaining for setter + if (arguments.length) { + return options === undefined ? this : this.each(function (i) { + jQuery.offset.setOffset(this, options, i); + }); + } + + var rect, + win, + elem = this[0]; + + if (!elem) { + return; + } // Return zeros for disconnected and hidden (display: none) elements (gh-2310) + // Support: IE <=11 only + // Running getBoundingClientRect on a + // disconnected node in IE throws an error + + + if (!elem.getClientRects().length) { + return { + top: 0, + left: 0 + }; + } // Get document-relative position by adding viewport scroll to viewport-relative gBCR + + + rect = elem.getBoundingClientRect(); + win = elem.ownerDocument.defaultView; + return { + top: rect.top + win.pageYOffset, + left: rect.left + win.pageXOffset + }; + }, + // position() relates an element's margin box to its offset parent's padding box + // This corresponds to the behavior of CSS absolute positioning + position: function () { + if (!this[0]) { + return; + } + + var offsetParent, + offset, + doc, + elem = this[0], + parentOffset = { + top: 0, + left: 0 + }; // position:fixed elements are offset from the viewport, which itself always has zero offset + + if (jQuery.css(elem, "position") === "fixed") { + // Assume position:fixed implies availability of getBoundingClientRect + offset = elem.getBoundingClientRect(); + } else { + offset = this.offset(); // Account for the *real* offset parent, which can be the document or its root element + // when a statically positioned element is identified + + doc = elem.ownerDocument; + offsetParent = elem.offsetParent || doc.documentElement; + + while (offsetParent && (offsetParent === doc.body || offsetParent === doc.documentElement) && jQuery.css(offsetParent, "position") === "static") { + offsetParent = offsetParent.parentNode; + } + + if (offsetParent && offsetParent !== elem && offsetParent.nodeType === 1) { + // Incorporate borders into its offset, since they are outside its content origin + parentOffset = jQuery(offsetParent).offset(); + parentOffset.top += jQuery.css(offsetParent, "borderTopWidth", true); + parentOffset.left += jQuery.css(offsetParent, "borderLeftWidth", true); + } + } // Subtract parent offsets and element margins + + + return { + top: offset.top - parentOffset.top - jQuery.css(elem, "marginTop", true), + left: offset.left - parentOffset.left - jQuery.css(elem, "marginLeft", true) + }; + }, + // This method will return documentElement in the following cases: + // 1) For the element inside the iframe without offsetParent, this method will return + // documentElement of the parent window + // 2) For the hidden or detached element + // 3) For body or html element, i.e. in case of the html node - it will return itself + // + // but those exceptions were never presented as a real life use-cases + // and might be considered as more preferable results. + // + // This logic, however, is not guaranteed and can change at any point in the future + offsetParent: function () { + return this.map(function () { + var offsetParent = this.offsetParent; + + while (offsetParent && jQuery.css(offsetParent, "position") === "static") { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || documentElement; + }); + } + }); // Create scrollLeft and scrollTop methods + + jQuery.each({ + scrollLeft: "pageXOffset", + scrollTop: "pageYOffset" + }, function (method, prop) { + var top = "pageYOffset" === prop; + + jQuery.fn[method] = function (val) { + return access(this, function (elem, method, val) { + // Coalesce documents and windows + var win; + + if (isWindow(elem)) { + win = elem; + } else if (elem.nodeType === 9) { + win = elem.defaultView; + } + + if (val === undefined) { + return win ? win[prop] : elem[method]; + } + + if (win) { + win.scrollTo(!top ? val : win.pageXOffset, top ? val : win.pageYOffset); + } else { + elem[method] = val; + } + }, method, val, arguments.length); + }; + }); // Support: Safari <=7 - 9.1, Chrome <=37 - 49 + // Add the top/left cssHooks using jQuery.fn.position + // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 + // Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347 + // getComputedStyle returns percent when specified for top/left/bottom/right; + // rather than make the css module depend on the offset module, just check for it here + + jQuery.each(["top", "left"], function (i, prop) { + jQuery.cssHooks[prop] = addGetHookIf(support.pixelPosition, function (elem, computed) { + if (computed) { + computed = curCSS(elem, prop); // If curCSS returns percentage, fallback to offset + + return rnumnonpx.test(computed) ? jQuery(elem).position()[prop] + "px" : computed; + } + }); + }); // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods + + jQuery.each({ + Height: "height", + Width: "width" + }, function (name, type) { + jQuery.each({ + padding: "inner" + name, + content: type, + "": "outer" + name + }, function (defaultExtra, funcName) { + // Margin is only for outerHeight, outerWidth + jQuery.fn[funcName] = function (margin, value) { + var chainable = arguments.length && (defaultExtra || typeof margin !== "boolean"), + extra = defaultExtra || (margin === true || value === true ? "margin" : "border"); + return access(this, function (elem, type, value) { + var doc; + + if (isWindow(elem)) { + // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) + return funcName.indexOf("outer") === 0 ? elem["inner" + name] : elem.document.documentElement["client" + name]; + } // Get document width or height + + + if (elem.nodeType === 9) { + doc = elem.documentElement; // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], + // whichever is greatest + + return Math.max(elem.body["scroll" + name], doc["scroll" + name], elem.body["offset" + name], doc["offset" + name], doc["client" + name]); + } + + return value === undefined ? // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css(elem, type, extra) : // Set width or height on the element + jQuery.style(elem, type, value, extra); + }, type, chainable ? margin : undefined, chainable); + }; + }); + }); + jQuery.each(("blur focus focusin focusout resize scroll click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup contextmenu").split(" "), function (i, name) { + // Handle event binding + jQuery.fn[name] = function (data, fn) { + return arguments.length > 0 ? this.on(name, null, data, fn) : this.trigger(name); + }; + }); + jQuery.fn.extend({ + hover: function (fnOver, fnOut) { + return this.mouseenter(fnOver).mouseleave(fnOut || fnOver); + } + }); + jQuery.fn.extend({ + bind: function (types, data, fn) { + return this.on(types, null, data, fn); + }, + unbind: function (types, fn) { + return this.off(types, null, fn); + }, + delegate: function (selector, types, data, fn) { + return this.on(types, selector, data, fn); + }, + undelegate: function (selector, types, fn) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off(selector, "**") : this.off(types, selector || "**", fn); + } + }); // Bind a function to a context, optionally partially applying any + // arguments. + // jQuery.proxy is deprecated to promote standards (specifically Function#bind) + // However, it is not slated for removal any time soon + + jQuery.proxy = function (fn, context) { + var tmp, args, proxy; + + if (typeof context === "string") { + tmp = fn[context]; + context = fn; + fn = tmp; + } // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + + + if (!isFunction(fn)) { + return undefined; + } // Simulated bind + + + args = slice.call(arguments, 2); + + proxy = function () { + return fn.apply(context || this, args.concat(slice.call(arguments))); + }; // Set the guid of unique handler to the same of original handler, so it can be removed + + + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + return proxy; + }; + + jQuery.holdReady = function (hold) { + if (hold) { + jQuery.readyWait++; + } else { + jQuery.ready(true); + } + }; + + jQuery.isArray = Array.isArray; + jQuery.parseJSON = JSON.parse; + jQuery.nodeName = nodeName; + jQuery.isFunction = isFunction; + jQuery.isWindow = isWindow; + jQuery.camelCase = camelCase; + jQuery.type = toType; + jQuery.now = Date.now; + + jQuery.isNumeric = function (obj) { + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type(obj); + return (type === "number" || type === "string") && // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN(obj - parseFloat(obj)); + }; + + const $ = jQuery; + + function div$1(options) { + return create$1("div", options); + } + + function create$1(tag, options) { + const elem = document.createElement(tag); + + if (options) { + if (options.class) { + elem.classList.add(options.class); + } + + if (options.id) { + elem.id = options.id; + } + + if (options.style) { + applyStyle$1(elem, options.style); + } + } + + return elem; + } + + function hide$1(elem) { + const cssStyle = getComputedStyle(elem); + + if (cssStyle.display !== "none") { + elem._initialDisplay = cssStyle.display; + } + + elem.style.display = "none"; + } + + function offset$1(elem) { + // Return zeros for disconnected and hidden (display: none) elements (gh-2310) + // Support: IE <=11 only + // Running getBoundingClientRect on a + // disconnected node in IE throws an error + if (!elem.getClientRects().length) { + return { + top: 0, + left: 0 + }; + } // Get document-relative position by adding viewport scroll to viewport-relative gBCR + + + const rect = elem.getBoundingClientRect(); + const win = elem.ownerDocument.defaultView; + return { + top: rect.top + win.pageYOffset, + left: rect.left + win.pageXOffset + }; + } + + function pageCoordinates$1(e) { + if (e.type.startsWith("touch")) { + const touch = e.touches[0]; + return { + x: touch.pageX, + y: touch.pageY + }; + } else { + return { + x: e.pageX, + y: e.pageY + }; + } + } + + const relativeDOMBBox = (parentElement, childElement) => { + const { + x: x_p, + y: y_p, + width: width_p, + height: height_p + } = parentElement.getBoundingClientRect(); + const { + x: x_c, + y: y_c, + width: width_c, + height: height_c + } = childElement.getBoundingClientRect(); + return { + x: x_c - x_p, + y: y_c - y_p, + width: width_c, + height: height_c + }; + }; + + function applyStyle$1(elem, style) { + for (let key of Object.keys(style)) { + elem.style[key] = style[key]; + } + } + + function guid() { + return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4); + } + + let getMouseXY$1 = (domElement, { + clientX, + clientY + }) => { + // DOMRect object with eight properties: left, top, right, bottom, x, y, width, height + const { + left, + top, + width, + height + } = domElement.getBoundingClientRect(); + const x = clientX - left; + const y = clientY - top; + return { + x, + y, + xNormalized: x / width, + yNormalized: y / height, + width, + height + }; + }; + /** + * Translate the mouse coordinates for the event to the coordinates for the given target element + * @param event + * @param domElement + * @returns {{x: number, y: number}} + */ + + + function translateMouseCoordinates$1(event, domElement) { + const { + clientX, + clientY + } = event; + return getMouseXY$1(domElement, { + clientX, + clientY + }); + } + + function createIcon$2(name, color) { + return iconMarkup$1(name, color); + } + + function iconMarkup$1(name, color) { + color = color || "currentColor"; + let icon = icons$1[name]; + + if (!icon) { + console.error(`No icon named: ${name}`); + icon = icons$1["question"]; + } + + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.setAttributeNS(null, 'viewBox', '0 0 ' + icon[0] + ' ' + icon[1]); + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttributeNS(null, 'fill', color); + path.setAttributeNS(null, 'd', icon[4]); + svg.appendChild(path); + return svg; + } + + const icons$1 = { + "check": [512, 512, [], "f00c", "M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"], + "cog": [512, 512, [], "f013", "M444.788 291.1l42.616 24.599c4.867 2.809 7.126 8.618 5.459 13.985-11.07 35.642-29.97 67.842-54.689 94.586a12.016 12.016 0 0 1-14.832 2.254l-42.584-24.595a191.577 191.577 0 0 1-60.759 35.13v49.182a12.01 12.01 0 0 1-9.377 11.718c-34.956 7.85-72.499 8.256-109.219.007-5.49-1.233-9.403-6.096-9.403-11.723v-49.184a191.555 191.555 0 0 1-60.759-35.13l-42.584 24.595a12.016 12.016 0 0 1-14.832-2.254c-24.718-26.744-43.619-58.944-54.689-94.586-1.667-5.366.592-11.175 5.459-13.985L67.212 291.1a193.48 193.48 0 0 1 0-70.199l-42.616-24.599c-4.867-2.809-7.126-8.618-5.459-13.985 11.07-35.642 29.97-67.842 54.689-94.586a12.016 12.016 0 0 1 14.832-2.254l42.584 24.595a191.577 191.577 0 0 1 60.759-35.13V25.759a12.01 12.01 0 0 1 9.377-11.718c34.956-7.85 72.499-8.256 109.219-.007 5.49 1.233 9.403 6.096 9.403 11.723v49.184a191.555 191.555 0 0 1 60.759 35.13l42.584-24.595a12.016 12.016 0 0 1 14.832 2.254c24.718 26.744 43.619 58.944 54.689 94.586 1.667 5.366-.592 11.175-5.459 13.985L444.788 220.9a193.485 193.485 0 0 1 0 70.2zM336 256c0-44.112-35.888-80-80-80s-80 35.888-80 80 35.888 80 80 80 80-35.888 80-80z"], + "exclamation": [192, 512, [], "f12a", "M176 432c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zM25.26 25.199l13.6 272C39.499 309.972 50.041 320 62.83 320h66.34c12.789 0 23.331-10.028 23.97-22.801l13.6-272C167.425 11.49 156.496 0 142.77 0H49.23C35.504 0 24.575 11.49 25.26 25.199z"], + "exclamation-circle": [512, 512, [], "f06a", "M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"], + "exclamation-triangle": [576, 512, [], "f071", "M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"], + "minus": [448, 512, [], "f068", "M424 318.2c13.3 0 24-10.7 24-24v-76.4c0-13.3-10.7-24-24-24H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h400z"], + "minus-circle": [512, 512, [], "f056", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zM124 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H124z"], + "minus-square": [448, 512, [], "f146", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zM92 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H92z"], + "plus": [448, 512, [], "f067", "M448 294.2v-76.4c0-13.3-10.7-24-24-24H286.2V56c0-13.3-10.7-24-24-24h-76.4c-13.3 0-24 10.7-24 24v137.8H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h137.8V456c0 13.3 10.7 24 24 24h76.4c13.3 0 24-10.7 24-24V318.2H424c13.3 0 24-10.7 24-24z"], + "plus-circle": [512, 512, [], "f055", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"], + "plus-square": [448, 512, [], "f0fe", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"], + "question": [384, 512, [], "f128", "M202.021 0C122.202 0 70.503 32.703 29.914 91.026c-7.363 10.58-5.093 25.086 5.178 32.874l43.138 32.709c10.373 7.865 25.132 6.026 33.253-4.148 25.049-31.381 43.63-49.449 82.757-49.449 30.764 0 68.816 19.799 68.816 49.631 0 22.552-18.617 34.134-48.993 51.164-35.423 19.86-82.299 44.576-82.299 106.405V320c0 13.255 10.745 24 24 24h72.471c13.255 0 24-10.745 24-24v-5.773c0-42.86 125.268-44.645 125.268-160.627C377.504 66.256 286.902 0 202.021 0zM192 373.459c-38.196 0-69.271 31.075-69.271 69.271 0 38.195 31.075 69.27 69.271 69.27s69.271-31.075 69.271-69.271-31.075-69.27-69.271-69.27z"], + "save": [448, 512, [], "f0c7", "M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM224 416c-35.346 0-64-28.654-64-64 0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64zm96-304.52V212c0 6.627-5.373 12-12 12H76c-6.627 0-12-5.373-12-12V108c0-6.627 5.373-12 12-12h228.52c3.183 0 6.235 1.264 8.485 3.515l3.48 3.48A11.996 11.996 0 0 1 320 111.48z"], + "search": [512, 512, [], "f002", "M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"], + "share": [512, 512, [], "f064", "M503.691 189.836L327.687 37.851C312.281 24.546 288 35.347 288 56.015v80.053C127.371 137.907 0 170.1 0 322.326c0 61.441 39.581 122.309 83.333 154.132 13.653 9.931 33.111-2.533 28.077-18.631C66.066 312.814 132.917 274.316 288 272.085V360c0 20.7 24.3 31.453 39.687 18.164l176.004-152c11.071-9.562 11.086-26.753 0-36.328z"], + "spinner": [512, 512, [], "f110", "M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z"], + "square": [448, 512, [], "f0c8", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z"], + "square-full": [512, 512, [], "f45c", "M512 512H0V0h512v512z"], + "times": [384, 512, [], "f00d", "M323.1 441l53.9-53.9c9.4-9.4 9.4-24.5 0-33.9L279.8 256l97.2-97.2c9.4-9.4 9.4-24.5 0-33.9L323.1 71c-9.4-9.4-24.5-9.4-33.9 0L192 168.2 94.8 71c-9.4-9.4-24.5-9.4-33.9 0L7 124.9c-9.4 9.4-9.4 24.5 0 33.9l97.2 97.2L7 353.2c-9.4 9.4-9.4 24.5 0 33.9L60.9 441c9.4 9.4 24.5 9.4 33.9 0l97.2-97.2 97.2 97.2c9.3 9.3 24.5 9.3 33.9 0z"], + "times-circle": [512, 512, [], "f057", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"], + "wrench": [512, 512, [], "f0ad", "M481.156 200c9.3 0 15.12 10.155 10.325 18.124C466.295 259.992 420.419 288 368 288c-79.222 0-143.501-63.974-143.997-143.079C223.505 65.469 288.548-.001 368.002 0c52.362.001 98.196 27.949 123.4 69.743C496.24 77.766 490.523 88 481.154 88H376l-40 56 40 56h105.156zm-171.649 93.003L109.255 493.255c-24.994 24.993-65.515 24.994-90.51 0-24.993-24.994-24.993-65.516 0-90.51L218.991 202.5c16.16 41.197 49.303 74.335 90.516 90.503zM104 432c0-13.255-10.745-24-24-24s-24 10.745-24 24 10.745 24 24 24 24-10.745 24-24z"] + }; + + function attachDialogCloseHandlerWithParent$1(parent, closeHandler) { + var container = document.createElement("div"); + parent.appendChild(container); + container.appendChild(createIcon$2("times")); + container.addEventListener('click', function (e) { + e.preventDefault(); + e.stopPropagation(); + closeHandler(); + }); + } + + /** + * @fileoverview Zlib namespace. Zlib รฃยยฎรคยปโ€ขรฆยงหœรฃยยซรฆยบโ€“รฆโ€น รฃยโ€”รฃยลธรฅล“ยงรงยธยฎรฃยยฏ Zlib.Deflate รฃยยงรฅยฎลธรจยฃโ€ฆ + * รฃยโ€ขรฃโ€šล’รฃยยฆรฃยโ€žรฃโ€šโ€น. รฃยโ€œรฃโ€šล’รฃยยฏ Inflate รฃยยจรฃยยฎรฅโ€ฆยฑรฅยญหœรฃโ€šโ€™รจโ‚ฌฦ’รฆโ€ฆยฎรฃยโ€”รฃยยฆรฃยโ€žรฃโ€šโ€นรงโ€šยบ. + */ + const ZLIB_STREAM_RAW_INFLATE_BUFFER_SIZE$1 = 65000; + var Zlib$1 = { + Huffman: {}, + Util: {}, + CRC32: {} + }; + /** + * Compression Method + * @enum {number} + */ + + Zlib$1.CompressionMethod = { + DEFLATE: 8, + RESERVED: 15 + }; + /** + * @param {Object=} opt_params options. + * @constructor + */ + + Zlib$1.Zip = function (opt_params) { + opt_params = opt_params || {}; + /** @type {Array.<{ + * buffer: !(Array.|Uint8Array), + * option: Object, + * compressed: boolean, + * encrypted: boolean, + * size: number, + * crc32: number + * }>} */ + + this.files = []; + /** @type {(Array.|Uint8Array)} */ + + this.comment = opt_params['comment']; + /** @type {(Array.|Uint8Array)} */ + + this.password; + }; + /** + * @enum {number} + */ + + + Zlib$1.Zip.CompressionMethod = { + STORE: 0, + DEFLATE: 8 + }; + /** + * @enum {number} + */ + + Zlib$1.Zip.OperatingSystem = { + MSDOS: 0, + UNIX: 3, + MACINTOSH: 7 + }; + /** + * @enum {number} + */ + + Zlib$1.Zip.Flags = { + ENCRYPT: 0x0001, + DESCRIPTOR: 0x0008, + UTF8: 0x0800 + }; + /** + * @type {Array.} + * @const + */ + + Zlib$1.Zip.FileHeaderSignature = [0x50, 0x4b, 0x01, 0x02]; + /** + * @type {Array.} + * @const + */ + + Zlib$1.Zip.LocalFileHeaderSignature = [0x50, 0x4b, 0x03, 0x04]; + /** + * @type {Array.} + * @const + */ + + Zlib$1.Zip.CentralDirectorySignature = [0x50, 0x4b, 0x05, 0x06]; + /** + * @param {Array.|Uint8Array} input + * @param {Object=} opt_params options. + */ + + Zlib$1.Zip.prototype.addFile = function (input, opt_params) { + opt_params = opt_params || {}; + /** @type {string} */ + + opt_params['filename']; + /** @type {boolean} */ + + var compressed; + /** @type {number} */ + + var size = input.length; + /** @type {number} */ + + var crc32 = 0; + + if (input instanceof Array) { + input = new Uint8Array(input); + } // default + + + if (typeof opt_params['compressionMethod'] !== 'number') { + opt_params['compressionMethod'] = Zlib$1.Zip.CompressionMethod.DEFLATE; + } // รฃยยรฃยยฎรฅ ยดรฃยยงรฅล“ยงรงยธยฎรฃยโ„ขรฃโ€šโ€นรฅ ยดรฅยห† + + + if (opt_params['compress']) { + switch (opt_params['compressionMethod']) { + case Zlib$1.Zip.CompressionMethod.STORE: + break; + + case Zlib$1.Zip.CompressionMethod.DEFLATE: + crc32 = Zlib$1.CRC32.calc(input); + input = this.deflateWithOption(input, opt_params); + compressed = true; + break; + + default: + throw new Error('unknown compression method:' + opt_params['compressionMethod']); + } + } + + this.files.push({ + buffer: input, + option: opt_params, + compressed: compressed, + encrypted: false, + size: size, + crc32: crc32 + }); + }; + /** + * @param {(Array.|Uint8Array)} password + */ + + + Zlib$1.Zip.prototype.setPassword = function (password) { + this.password = password; + }; + + Zlib$1.Zip.prototype.compress = function () { + /** @type {Array.<{ + * buffer: !(Array.|Uint8Array), + * option: Object, + * compressed: boolean, + * encrypted: boolean, + * size: number, + * crc32: number + * }>} */ + var files = this.files; + /** @type {{ + * buffer: !(Array.|Uint8Array), + * option: Object, + * compressed: boolean, + * encrypted: boolean, + * size: number, + * crc32: number + * }} */ + + var file; + /** @type {!(Array.|Uint8Array)} */ + + var output; + /** @type {number} */ + + var op1; + /** @type {number} */ + + var op2; + /** @type {number} */ + + var op3; + /** @type {number} */ + + var localFileSize = 0; + /** @type {number} */ + + var centralDirectorySize = 0; + /** @type {number} */ + + var endOfCentralDirectorySize; + /** @type {number} */ + + var offset; + /** @type {number} */ + + var needVersion; + /** @type {number} */ + + var flags; + /** @type {Zlib.Zip.CompressionMethod} */ + + var compressionMethod; + /** @type {Date} */ + + var date; + /** @type {number} */ + + var crc32; + /** @type {number} */ + + var size; + /** @type {number} */ + + var plainSize; + /** @type {number} */ + + var filenameLength; + /** @type {number} */ + + var extraFieldLength; + /** @type {number} */ + + var commentLength; + /** @type {(Array.|Uint8Array)} */ + + var filename; + /** @type {(Array.|Uint8Array)} */ + + var extraField; + /** @type {(Array.|Uint8Array)} */ + + var comment; + /** @type {(Array.|Uint8Array)} */ + + var buffer; + /** @type {*} */ + + var tmp; + /** @type {Array.|Uint32Array|Object} */ + + var key; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + /** @type {number} */ + + var j; + /** @type {number} */ + + var jl; // รฃฦ’โ€ขรฃโ€šยกรฃโ€šยครฃฦ’ยซรฃยยฎรฅล“ยงรงยธยฎ + + for (i = 0, il = files.length; i < il; ++i) { + file = files[i]; + filenameLength = file.option['filename'] ? file.option['filename'].length : 0; + extraFieldLength = file.option['extraField'] ? file.option['extraField'].length : 0; + commentLength = file.option['comment'] ? file.option['comment'].length : 0; // รฅล“ยงรงยธยฎรฃยโ€ขรฃโ€šล’รฃยยฆรฃยโ€žรฃยยชรฃยโ€นรฃยยฃรฃยลธรฃโ€šโ€ฐรฅล“ยงรงยธยฎ + + if (!file.compressed) { + // รฅล“ยงรงยธยฎรฅโ€ฐยรฃยยซ CRC32 รฃยยฎรจยจห†รงยฎโ€”รฃโ€šโ€™รฃยโ€”รฃยยฆรฃยล รฃยย + file.crc32 = Zlib$1.CRC32.calc(file.buffer); + + switch (file.option['compressionMethod']) { + case Zlib$1.Zip.CompressionMethod.STORE: + break; + + case Zlib$1.Zip.CompressionMethod.DEFLATE: + file.buffer = this.deflateWithOption(file.buffer, file.option); + file.compressed = true; + break; + + default: + throw new Error('unknown compression method:' + file.option['compressionMethod']); + } + } // encryption + + + if (file.option['password'] !== void 0 || this.password !== void 0) { + // init encryption + key = this.createEncryptionKey(file.option['password'] || this.password); // add header + + buffer = file.buffer; + + { + tmp = new Uint8Array(buffer.length + 12); + tmp.set(buffer, 12); + buffer = tmp; + } + + for (j = 0; j < 12; ++j) { + buffer[j] = this.encode(key, i === 11 ? file.crc32 & 0xff : Math.random() * 256 | 0); + } // data encryption + + + for (jl = buffer.length; j < jl; ++j) { + buffer[j] = this.encode(key, buffer[j]); + } + + file.buffer = buffer; + } // รฅยฟโ€ฆรจยฆยรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šยตรฃโ€šยครฃโ€šยบรฃยยฎรจยจห†รงยฎโ€” + + + localFileSize += // local file header + 30 + filenameLength + // file data + file.buffer.length; + centralDirectorySize += // file header + 46 + filenameLength + commentLength; + } // end of central directory + + + endOfCentralDirectorySize = 22 + (this.comment ? this.comment.length : 0); + output = new (Uint8Array )(localFileSize + centralDirectorySize + endOfCentralDirectorySize); + op1 = 0; + op2 = localFileSize; + op3 = op2 + centralDirectorySize; // รฃฦ’โ€ขรฃโ€šยกรฃโ€šยครฃฦ’ยซรฃยยฎรฅล“ยงรงยธยฎ + + for (i = 0, il = files.length; i < il; ++i) { + file = files[i]; + filenameLength = file.option['filename'] ? file.option['filename'].length : 0; + extraFieldLength = 0; // TODO + + commentLength = file.option['comment'] ? file.option['comment'].length : 0; //------------------------------------------------------------------------- + // local file header & file header + //------------------------------------------------------------------------- + + offset = op1; // signature + // local file header + + output[op1++] = Zlib$1.Zip.LocalFileHeaderSignature[0]; + output[op1++] = Zlib$1.Zip.LocalFileHeaderSignature[1]; + output[op1++] = Zlib$1.Zip.LocalFileHeaderSignature[2]; + output[op1++] = Zlib$1.Zip.LocalFileHeaderSignature[3]; // file header + + output[op2++] = Zlib$1.Zip.FileHeaderSignature[0]; + output[op2++] = Zlib$1.Zip.FileHeaderSignature[1]; + output[op2++] = Zlib$1.Zip.FileHeaderSignature[2]; + output[op2++] = Zlib$1.Zip.FileHeaderSignature[3]; // compressor info + + needVersion = 20; + output[op2++] = needVersion & 0xff; + output[op2++] = + /** @type {Zlib.Zip.OperatingSystem} */ + file.option['os'] || Zlib$1.Zip.OperatingSystem.MSDOS; // need version + + output[op1++] = output[op2++] = needVersion & 0xff; + output[op1++] = output[op2++] = needVersion >> 8 & 0xff; // general purpose bit flag + + flags = 0; + + if (file.option['password'] || this.password) { + flags |= Zlib$1.Zip.Flags.ENCRYPT; + } + + output[op1++] = output[op2++] = flags & 0xff; + output[op1++] = output[op2++] = flags >> 8 & 0xff; // compression method + + compressionMethod = file.option['compressionMethod']; + output[op1++] = output[op2++] = compressionMethod & 0xff; + output[op1++] = output[op2++] = compressionMethod >> 8 & 0xff; // date + + date = + /** @type {(Date|undefined)} */ + file.option['date'] || new Date(); + output[op1++] = output[op2++] = (date.getMinutes() & 0x7) << 5 | (date.getSeconds() / 2 | 0); + output[op1++] = output[op2++] = date.getHours() << 3 | date.getMinutes() >> 3; // + + output[op1++] = output[op2++] = (date.getMonth() + 1 & 0x7) << 5 | date.getDate(); + output[op1++] = output[op2++] = (date.getFullYear() - 1980 & 0x7f) << 1 | date.getMonth() + 1 >> 3; // CRC-32 + + crc32 = file.crc32; + output[op1++] = output[op2++] = crc32 & 0xff; + output[op1++] = output[op2++] = crc32 >> 8 & 0xff; + output[op1++] = output[op2++] = crc32 >> 16 & 0xff; + output[op1++] = output[op2++] = crc32 >> 24 & 0xff; // compressed size + + size = file.buffer.length; + output[op1++] = output[op2++] = size & 0xff; + output[op1++] = output[op2++] = size >> 8 & 0xff; + output[op1++] = output[op2++] = size >> 16 & 0xff; + output[op1++] = output[op2++] = size >> 24 & 0xff; // uncompressed size + + plainSize = file.size; + output[op1++] = output[op2++] = plainSize & 0xff; + output[op1++] = output[op2++] = plainSize >> 8 & 0xff; + output[op1++] = output[op2++] = plainSize >> 16 & 0xff; + output[op1++] = output[op2++] = plainSize >> 24 & 0xff; // filename length + + output[op1++] = output[op2++] = filenameLength & 0xff; + output[op1++] = output[op2++] = filenameLength >> 8 & 0xff; // extra field length + + output[op1++] = output[op2++] = extraFieldLength & 0xff; + output[op1++] = output[op2++] = extraFieldLength >> 8 & 0xff; // file comment length + + output[op2++] = commentLength & 0xff; + output[op2++] = commentLength >> 8 & 0xff; // disk number start + + output[op2++] = 0; + output[op2++] = 0; // internal file attributes + + output[op2++] = 0; + output[op2++] = 0; // external file attributes + + output[op2++] = 0; + output[op2++] = 0; + output[op2++] = 0; + output[op2++] = 0; // relative offset of local header + + output[op2++] = offset & 0xff; + output[op2++] = offset >> 8 & 0xff; + output[op2++] = offset >> 16 & 0xff; + output[op2++] = offset >> 24 & 0xff; // filename + + filename = file.option['filename']; + + if (filename) { + { + output.set(filename, op1); + output.set(filename, op2); + op1 += filenameLength; + op2 += filenameLength; + } + } // extra field + + + extraField = file.option['extraField']; + + if (extraField) { + { + output.set(extraField, op1); + output.set(extraField, op2); + op1 += extraFieldLength; + op2 += extraFieldLength; + } + } // comment + + + comment = file.option['comment']; + + if (comment) { + { + output.set(comment, op2); + op2 += commentLength; + } + } //------------------------------------------------------------------------- + // file data + //------------------------------------------------------------------------- + + + { + output.set(file.buffer, op1); + op1 += file.buffer.length; + } + } //------------------------------------------------------------------------- + // end of central directory + //------------------------------------------------------------------------- + // signature + + + output[op3++] = Zlib$1.Zip.CentralDirectorySignature[0]; + output[op3++] = Zlib$1.Zip.CentralDirectorySignature[1]; + output[op3++] = Zlib$1.Zip.CentralDirectorySignature[2]; + output[op3++] = Zlib$1.Zip.CentralDirectorySignature[3]; // number of this disk + + output[op3++] = 0; + output[op3++] = 0; // number of the disk with the start of the central directory + + output[op3++] = 0; + output[op3++] = 0; // total number of entries in the central directory on this disk + + output[op3++] = il & 0xff; + output[op3++] = il >> 8 & 0xff; // total number of entries in the central directory + + output[op3++] = il & 0xff; + output[op3++] = il >> 8 & 0xff; // size of the central directory + + output[op3++] = centralDirectorySize & 0xff; + output[op3++] = centralDirectorySize >> 8 & 0xff; + output[op3++] = centralDirectorySize >> 16 & 0xff; + output[op3++] = centralDirectorySize >> 24 & 0xff; // offset of start of central directory with respect to the starting disk number + + output[op3++] = localFileSize & 0xff; + output[op3++] = localFileSize >> 8 & 0xff; + output[op3++] = localFileSize >> 16 & 0xff; + output[op3++] = localFileSize >> 24 & 0xff; // .ZIP file comment length + + commentLength = this.comment ? this.comment.length : 0; + output[op3++] = commentLength & 0xff; + output[op3++] = commentLength >> 8 & 0xff; // .ZIP file comment + + if (this.comment) { + { + output.set(this.comment, op3); + op3 += commentLength; + } + } + + return output; + }; + /** + * @param {!(Array.|Uint8Array)} input + * @param {Object=} opt_params options. + * @return {!(Array.|Uint8Array)} + */ + + + Zlib$1.Zip.prototype.deflateWithOption = function (input, opt_params) { + /** @type {Zlib.RawDeflate} */ + var deflator = new Zlib$1.RawDeflate(input, opt_params['deflateOption']); + return deflator.compress(); + }; + /** + * @param {(Array.|Uint32Array)} key + * @return {number} + */ + + + Zlib$1.Zip.prototype.getByte = function (key) { + /** @type {number} */ + var tmp = key[2] & 0xffff | 2; + return tmp * (tmp ^ 1) >> 8 & 0xff; + }; + /** + * @param {(Array.|Uint32Array|Object)} key + * @param {number} n + * @return {number} + */ + + + Zlib$1.Zip.prototype.encode = function (key, n) { + /** @type {number} */ + var tmp = this.getByte(key); + this.updateKeys(key, n); + return tmp ^ n; + }; + /** + * @param {(Array.|Uint32Array)} key + * @param {number} n + */ + + + Zlib$1.Zip.prototype.updateKeys = function (key, n) { + key[0] = Zlib$1.CRC32.single(key[0], n); + key[1] = (((key[1] + (key[0] & 0xff)) * 20173 >>> 0) * 6681 >>> 0) + 1 >>> 0; + key[2] = Zlib$1.CRC32.single(key[2], key[1] >>> 24); + }; + /** + * @param {(Array.|Uint8Array)} password + * @return {!(Array.|Uint32Array|Object)} + */ + + + Zlib$1.Zip.prototype.createEncryptionKey = function (password) { + /** @type {!(Array.|Uint32Array)} */ + var key = [305419896, 591751049, 878082192]; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + + { + key = new Uint32Array(key); + } + + for (i = 0, il = password.length; i < il; ++i) { + this.updateKeys(key, password[i] & 0xff); + } + + return key; + }; + /** + * build huffman table from length list. + * @param {!(Array.|Uint8Array)} lengths length list. + * @return {!Array} huffman table. + */ + + + Zlib$1.Huffman.buildHuffmanTable = function (lengths) { + /** @type {number} length list size. */ + var listSize = lengths.length; + /** @type {number} max code length for table size. */ + + var maxCodeLength = 0; + /** @type {number} min code length for table size. */ + + var minCodeLength = Number.POSITIVE_INFINITY; + /** @type {number} table size. */ + + var size; + /** @type {!(Array|Uint8Array)} huffman code table. */ + + var table; + /** @type {number} bit length. */ + + var bitLength; + /** @type {number} huffman code. */ + + var code; + /** + * รฃโ€šยตรฃโ€šยครฃโ€šยบรฃยล’ 2^maxlength รฅโ‚ฌโ€นรฃยยฎรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃโ€šโ€™รฅลธโ€นรฃโ€šยรฃโ€šโ€นรฃยลธรฃโ€šยรฃยยฎรฃโ€šยนรฃโ€šยญรฃฦ’ฦ’รฃฦ’โ€”รฉโ€ขยท. + * @type {number} skip length for table filling. + */ + + var skip; + /** @type {number} reversed code. */ + + var reversed; + /** @type {number} reverse temp. */ + + var rtemp; + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limit. */ + + var il; + /** @type {number} loop counter. */ + + var j; + /** @type {number} table value. */ + + var value; // Math.max รฃยยฏรฉยโ€ฆรฃยโ€žรฃยยฎรฃยยงรฆล“โ‚ฌรฉโ€ขยทรฃยยฎรฅโ‚ฌยครฃยยฏ for-loop รฃยยงรฅยโ€“รฅยพโ€”รฃยโ„ขรฃโ€šโ€น + + for (i = 0, il = listSize; i < il; ++i) { + if (lengths[i] > maxCodeLength) { + maxCodeLength = lengths[i]; + } + + if (lengths[i] < minCodeLength) { + minCodeLength = lengths[i]; + } + } + + size = 1 << maxCodeLength; + table = new (Uint32Array )(size); // รฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฃยยฎรงลธยญรฃยโ€žรฉ โ€ รฃยโ€นรฃโ€šโ€ฐรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃโ€šโ€™รฅโ€ฐยฒรฃโ€šล รฅยฝโ€œรฃยยฆรฃโ€šโ€น + + for (bitLength = 1, code = 0, skip = 2; bitLength <= maxCodeLength;) { + for (i = 0; i < listSize; ++i) { + if (lengths[i] === bitLength) { + // รฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยชรฃฦ’ยผรฃฦ’โ‚ฌรฃฦ’ยผรฃยล’รฉโ‚ฌโ€ รฃยยซรฃยยชรฃโ€šโ€นรฃยลธรฃโ€šยรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฅห†โ€ รคยธยฆรฃยยณรฃโ€šโ€™รฅยยรจยปยขรฃยโ„ขรฃโ€šโ€น + for (reversed = 0, rtemp = code, j = 0; j < bitLength; ++j) { + reversed = reversed << 1 | rtemp & 1; + rtemp >>= 1; + } // รฆล“โ‚ฌรฅยคยงรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฃโ€šโ€™รฃโ€šโ€šรฃยยจรฃยยซรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃโ€šโ€™รคยฝล“รฃโ€šโ€นรฃยลธรฃโ€šยรฃโ‚ฌย + // รฆล“โ‚ฌรฅยคยงรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรคยปยฅรฅยคโ€“รฃยยงรฃยยฏ 0 / 1 รฃยยฉรฃยยกรฃโ€šโ€ฐรฃยยงรฃโ€šโ€šรจโ€ฐยฏรฃยโ€žรงยฎโ€กรฆโ€ฐโ‚ฌรฃยล’รฃยยงรฃยยรฃโ€šโ€น + // รฃยยรฃยยฎรฃยยฉรฃยยกรฃโ€šโ€ฐรฃยยงรฃโ€šโ€šรจโ€ฐยฏรฃยโ€žรฅ ยดรฆโ€ฐโ‚ฌรฃยยฏรฅยล’รฃยหœรฅโ‚ฌยครฃยยงรฅลธโ€นรฃโ€šยรฃโ€šโ€นรฃยโ€œรฃยยจรฃยยง + // รฆล“ยฌรฆยยฅรฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรคยปยฅรคยธล รฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฆโ€ขยฐรฅยโ€“รฅยพโ€”รฃยโ€”รฃยยฆรฃโ€šโ€šรฅโ€ขยรฉยกล’รฃยล’รจยตยทรฃยโ€œรฃโ€šโ€ฐรฃยยชรฃยโ€žรฃโ€šห†รฃยโ€ รฃยยซรฃยโ„ขรฃโ€šโ€น + + + value = bitLength << 16 | i; + + for (j = reversed; j < size; j += skip) { + table[j] = value; + } + + ++code; + } + } // รฆยฌยกรฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฃยยธ + + + ++bitLength; + code <<= 1; + skip <<= 1; + } + + return [table, maxCodeLength, minCodeLength]; + }; //----------------------------------------------------------------------------- + + /** @define {number} buffer block size. */ + + + var ZLIB_RAW_INFLATE_BUFFER_SIZE$1 = 0x8000; // [ 0x8000 >= ZLIB_BUFFER_BLOCK_SIZE ] + //----------------------------------------------------------------------------- + + var buildHuffmanTable$1 = Zlib$1.Huffman.buildHuffmanTable; + /** + * @constructor + * @param {!(Uint8Array|Array.)} input input buffer. + * @param {Object} opt_params option parameter. + * + * opt_params รฃยยฏรคยปยฅรคยธโ€นรฃยยฎรฃฦ’โ€”รฃฦ’ยญรฃฦ’โ€˜รฃฦ’โ€ รฃโ€šยฃรฃโ€šโ€™รฆล’โ€กรฅยฎลกรฃยโ„ขรฃโ€šโ€นรคยบโ€นรฃยล’รฃยยงรฃยยรฃยยพรฃยโ„ขรฃโ‚ฌโ€š + * - index: input buffer รฃยยฎ deflate รฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃฦ’ล รฃยยฎรฉโ€“โ€นรฅยงโ€นรคยฝยรงยฝยฎ. + * - blockSize: รฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃโ€šยตรฃโ€šยครฃโ€šยบ. + * - bufferType: Zlib.RawInflate.BufferType รฃยยฎรฅโ‚ฌยครฃยยซรฃโ€šห†รฃยยฃรฃยยฆรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรงยฎยกรงยโ€ รฆโ€“ยนรฆยณโ€ขรฃโ€šโ€™รฆล’โ€กรฅยฎลกรฃยโ„ขรฃโ€šโ€น. + * - resize: รงยขยบรคยฟยรฃยโ€”รฃยลธรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยล’รฅยฎลธรฉลกโ€บรฃยยฎรฅยคยงรฃยยรฃยโ€ขรฃโ€šห†รฃโ€šล รฅยคยงรฃยยรฃยโ€นรฃยยฃรฃยลธรฅ ยดรฅยห†รฃยยซรฅห†โ€กรฃโ€šล รจยฉยฐรฃโ€šยรฃโ€šโ€น. + */ + + Zlib$1.RawInflate = function (input, opt_params) { + /** @type {!(Array.|Uint8Array)} inflated buffer */ + this.buffer; + /** @type {!Array.<(Array.|Uint8Array)>} */ + + this.blocks = []; + /** @type {number} block size. */ + + this.bufferSize = ZLIB_RAW_INFLATE_BUFFER_SIZE$1; + /** @type {!number} total output buffer pointer. */ + + this.totalpos = 0; + /** @type {!number} input buffer pointer. */ + + this.ip = 0; + /** @type {!number} bit stream reader buffer. */ + + this.bitsbuf = 0; + /** @type {!number} bit stream reader buffer size. */ + + this.bitsbuflen = 0; + /** @type {!(Array.|Uint8Array)} input buffer. */ + + this.input = new Uint8Array(input) ; + /** @type {!(Uint8Array|Array.)} output buffer. */ + + this.output; + /** @type {!number} output buffer pointer. */ + + this.op; + /** @type {boolean} is final block flag. */ + + this.bfinal = false; + /** @type {Zlib.RawInflate.BufferType} buffer management. */ + + this.bufferType = Zlib$1.RawInflate.BufferType.ADAPTIVE; + /** @type {boolean} resize flag for memory size optimization. */ + + this.resize = false; // option parameters + + if (opt_params || !(opt_params = {})) { + if (opt_params['index']) { + this.ip = opt_params['index']; + } + + if (opt_params['bufferSize']) { + this.bufferSize = opt_params['bufferSize']; + } + + if (opt_params['bufferType']) { + this.bufferType = opt_params['bufferType']; + } + + if (opt_params['resize']) { + this.resize = opt_params['resize']; + } + } // initialize + + + switch (this.bufferType) { + case Zlib$1.RawInflate.BufferType.BLOCK: + this.op = Zlib$1.RawInflate.MaxBackwardLength; + this.output = new (Uint8Array )(Zlib$1.RawInflate.MaxBackwardLength + this.bufferSize + Zlib$1.RawInflate.MaxCopyLength); + break; + + case Zlib$1.RawInflate.BufferType.ADAPTIVE: + this.op = 0; + this.output = new (Uint8Array )(this.bufferSize); + break; + + default: + throw new Error('invalid inflate mode'); + } + }; + /** + * @enum {number} + */ + + + Zlib$1.RawInflate.BufferType = { + BLOCK: 0, + ADAPTIVE: 1 + }; + /** + * decompress. + * @return {!(Uint8Array|Array.)} inflated buffer. + */ + + Zlib$1.RawInflate.prototype.decompress = function () { + while (!this.bfinal) { + this.parseBlock(); + } + + switch (this.bufferType) { + case Zlib$1.RawInflate.BufferType.BLOCK: + return this.concatBufferBlock(); + + case Zlib$1.RawInflate.BufferType.ADAPTIVE: + return this.concatBufferDynamic(); + + default: + throw new Error('invalid inflate mode'); + } + }; + /** + * @const + * @type {number} max backward length for LZ77. + */ + + + Zlib$1.RawInflate.MaxBackwardLength = 32768; + /** + * @const + * @type {number} max copy length for LZ77. + */ + + Zlib$1.RawInflate.MaxCopyLength = 258; + /** + * huffman order + * @const + * @type {!(Array.|Uint8Array)} + */ + + Zlib$1.RawInflate.Order = function (table) { + return new Uint16Array(table) ; + }([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); + /** + * huffman length code table. + * @const + * @type {!(Array.|Uint16Array)} + */ + + + Zlib$1.RawInflate.LengthCodeTable = function (table) { + return new Uint16Array(table) ; + }([0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000d, 0x000f, 0x0011, 0x0013, 0x0017, 0x001b, 0x001f, 0x0023, 0x002b, 0x0033, 0x003b, 0x0043, 0x0053, 0x0063, 0x0073, 0x0083, 0x00a3, 0x00c3, 0x00e3, 0x0102, 0x0102, 0x0102]); + /** + * huffman length extra-bits table. + * @const + * @type {!(Array.|Uint8Array)} + */ + + + Zlib$1.RawInflate.LengthExtraTable = function (table) { + return new Uint8Array(table) ; + }([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, 0, 0]); + /** + * huffman dist code table. + * @const + * @type {!(Array.|Uint16Array)} + */ + + + Zlib$1.RawInflate.DistCodeTable = function (table) { + return new Uint16Array(table) ; + }([0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d, 0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1, 0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01, 0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001]); + /** + * huffman dist extra-bits table. + * @const + * @type {!(Array.|Uint8Array)} + */ + + + Zlib$1.RawInflate.DistExtraTable = function (table) { + return new Uint8Array(table) ; + }([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]); + /** + * fixed huffman length code table + * @const + * @type {!Array} + */ + + + Zlib$1.RawInflate.FixedLiteralLengthTable = function (table) { + return table; + }(function () { + var lengths = new (Uint8Array )(288); + var i, il; + + for (i = 0, il = lengths.length; i < il; ++i) { + lengths[i] = i <= 143 ? 8 : i <= 255 ? 9 : i <= 279 ? 7 : 8; + } + + return buildHuffmanTable$1(lengths); + }()); + /** + * fixed huffman distance code table + * @const + * @type {!Array} + */ + + + Zlib$1.RawInflate.FixedDistanceTable = function (table) { + return table; + }(function () { + var lengths = new (Uint8Array )(30); + var i, il; + + for (i = 0, il = lengths.length; i < il; ++i) { + lengths[i] = 5; + } + + return buildHuffmanTable$1(lengths); + }()); + /** + * parse deflated block. + */ + + + Zlib$1.RawInflate.prototype.parseBlock = function () { + /** @type {number} header */ + var hdr = this.readBits(3); // BFINAL + + if (hdr & 0x1) { + this.bfinal = true; + } // BTYPE + + + hdr >>>= 1; + + switch (hdr) { + // uncompressed + case 0: + this.parseUncompressedBlock(); + break; + // fixed huffman + + case 1: + this.parseFixedHuffmanBlock(); + break; + // dynamic huffman + + case 2: + this.parseDynamicHuffmanBlock(); + break; + // reserved or other + + default: + throw new Error('unknown BTYPE: ' + hdr); + } + }; + /** + * read inflate bits + * @param {number} length bits length. + * @return {number} read bits. + */ + + + Zlib$1.RawInflate.prototype.readBits = function (length) { + var bitsbuf = this.bitsbuf; + var bitsbuflen = this.bitsbuflen; + var input = this.input; + var ip = this.ip; + /** @type {number} */ + + var inputLength = input.length; + /** @type {number} input and output byte. */ + + var octet; // input byte + + if (ip + (length - bitsbuflen + 7 >> 3) >= inputLength) { + throw new Error('input buffer is broken'); + } // not enough buffer + + + while (bitsbuflen < length) { + bitsbuf |= input[ip++] << bitsbuflen; + bitsbuflen += 8; + } // output byte + + + octet = bitsbuf & (1 << length) - 1; + bitsbuf >>>= length; + bitsbuflen -= length; + this.bitsbuf = bitsbuf; + this.bitsbuflen = bitsbuflen; + this.ip = ip; + return octet; + }; + /** + * read huffman code using table + * @param {!(Array.|Uint8Array|Uint16Array)} table huffman code table. + * @return {number} huffman code. + */ + + + Zlib$1.RawInflate.prototype.readCodeByTable = function (table) { + var bitsbuf = this.bitsbuf; + var bitsbuflen = this.bitsbuflen; + var input = this.input; + var ip = this.ip; + /** @type {number} */ + + var inputLength = input.length; + /** @type {!(Array.|Uint8Array)} huffman code table */ + + var codeTable = table[0]; + /** @type {number} */ + + var maxCodeLength = table[1]; + /** @type {number} code length & code (16bit, 16bit) */ + + var codeWithLength; + /** @type {number} code bits length */ + + var codeLength; // not enough buffer + + while (bitsbuflen < maxCodeLength) { + if (ip >= inputLength) { + break; + } + + bitsbuf |= input[ip++] << bitsbuflen; + bitsbuflen += 8; + } // read max length + + + codeWithLength = codeTable[bitsbuf & (1 << maxCodeLength) - 1]; + codeLength = codeWithLength >>> 16; + + if (codeLength > bitsbuflen) { + throw new Error('invalid code length: ' + codeLength); + } + + this.bitsbuf = bitsbuf >> codeLength; + this.bitsbuflen = bitsbuflen - codeLength; + this.ip = ip; + return codeWithLength & 0xffff; + }; + /** + * parse uncompressed block. + */ + + + Zlib$1.RawInflate.prototype.parseUncompressedBlock = function () { + var input = this.input; + var ip = this.ip; + var output = this.output; + var op = this.op; + /** @type {number} */ + + var inputLength = input.length; + /** @type {number} block length */ + + var len; + /** @type {number} number for check block length */ + + var nlen; + /** @type {number} output buffer length */ + + var olength = output.length; + /** @type {number} copy counter */ + + var preCopy; // skip buffered header bits + + this.bitsbuf = 0; + this.bitsbuflen = 0; // len + + if (ip + 1 >= inputLength) { + throw new Error('invalid uncompressed block header: LEN'); + } + + len = input[ip++] | input[ip++] << 8; // nlen + + if (ip + 1 >= inputLength) { + throw new Error('invalid uncompressed block header: NLEN'); + } + + nlen = input[ip++] | input[ip++] << 8; // check len & nlen + + if (len === ~nlen) { + throw new Error('invalid uncompressed block header: length verify'); + } // check size + + + if (ip + len > input.length) { + throw new Error('input buffer is broken'); + } // expand buffer + + + switch (this.bufferType) { + case Zlib$1.RawInflate.BufferType.BLOCK: + // pre copy + while (op + len > output.length) { + preCopy = olength - op; + len -= preCopy; + + { + output.set(input.subarray(ip, ip + preCopy), op); + op += preCopy; + ip += preCopy; + } + + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + + break; + + case Zlib$1.RawInflate.BufferType.ADAPTIVE: + while (op + len > output.length) { + output = this.expandBufferAdaptive({ + fixRatio: 2 + }); + } + + break; + + default: + throw new Error('invalid inflate mode'); + } // copy + + + { + output.set(input.subarray(ip, ip + len), op); + op += len; + ip += len; + } + + this.ip = ip; + this.op = op; + this.output = output; + }; + /** + * parse fixed huffman block. + */ + + + Zlib$1.RawInflate.prototype.parseFixedHuffmanBlock = function () { + switch (this.bufferType) { + case Zlib$1.RawInflate.BufferType.ADAPTIVE: + this.decodeHuffmanAdaptive(Zlib$1.RawInflate.FixedLiteralLengthTable, Zlib$1.RawInflate.FixedDistanceTable); + break; + + case Zlib$1.RawInflate.BufferType.BLOCK: + this.decodeHuffmanBlock(Zlib$1.RawInflate.FixedLiteralLengthTable, Zlib$1.RawInflate.FixedDistanceTable); + break; + + default: + throw new Error('invalid inflate mode'); + } + }; + /** + * parse dynamic huffman block. + */ + + + Zlib$1.RawInflate.prototype.parseDynamicHuffmanBlock = function () { + /** @type {number} number of literal and length codes. */ + var hlit = this.readBits(5) + 257; + /** @type {number} number of distance codes. */ + + var hdist = this.readBits(5) + 1; + /** @type {number} number of code lengths. */ + + var hclen = this.readBits(4) + 4; + /** @type {!(Uint8Array|Array.)} code lengths. */ + + var codeLengths = new (Uint8Array )(Zlib$1.RawInflate.Order.length); + /** @type {!Array} code lengths table. */ + + var codeLengthsTable; + /** @type {!(Uint8Array|Array.)} literal and length code table. */ + + var litlenTable; + /** @type {!(Uint8Array|Array.)} distance code table. */ + + var distTable; + /** @type {!(Uint8Array|Array.)} code length table. */ + + var lengthTable; + /** @type {number} */ + + var code; + /** @type {number} */ + + var prev; + /** @type {number} */ + + var repeat; + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limit. */ + + var il; // decode code lengths + + for (i = 0; i < hclen; ++i) { + codeLengths[Zlib$1.RawInflate.Order[i]] = this.readBits(3); + } + + + codeLengthsTable = buildHuffmanTable$1(codeLengths); + lengthTable = new (Uint8Array )(hlit + hdist); + + for (i = 0, il = hlit + hdist; i < il;) { + code = this.readCodeByTable(codeLengthsTable); + + switch (code) { + case 16: + repeat = 3 + this.readBits(2); + + while (repeat--) { + lengthTable[i++] = prev; + } + + break; + + case 17: + repeat = 3 + this.readBits(3); + + while (repeat--) { + lengthTable[i++] = 0; + } + + prev = 0; + break; + + case 18: + repeat = 11 + this.readBits(7); + + while (repeat--) { + lengthTable[i++] = 0; + } + + prev = 0; + break; + + default: + lengthTable[i++] = code; + prev = code; + break; + } + } + + litlenTable = buildHuffmanTable$1(lengthTable.subarray(0, hlit)) ; + distTable = buildHuffmanTable$1(lengthTable.subarray(hlit)) ; + + switch (this.bufferType) { + case Zlib$1.RawInflate.BufferType.ADAPTIVE: + this.decodeHuffmanAdaptive(litlenTable, distTable); + break; + + case Zlib$1.RawInflate.BufferType.BLOCK: + this.decodeHuffmanBlock(litlenTable, distTable); + break; + + default: + throw new Error('invalid inflate mode'); + } + }; + /** + * decode huffman code + * @param {!(Array.|Uint16Array)} litlen literal and length code table. + * @param {!(Array.|Uint8Array)} dist distination code table. + */ + + + Zlib$1.RawInflate.prototype.decodeHuffmanBlock = function (litlen, dist) { + var output = this.output; + var op = this.op; + this.currentLitlenTable = litlen; + /** @type {number} output position limit. */ + + var olength = output.length - Zlib$1.RawInflate.MaxCopyLength; + /** @type {number} huffman code. */ + + var code; + /** @type {number} table index. */ + + var ti; + /** @type {number} huffman code distination. */ + + var codeDist; + /** @type {number} huffman code length. */ + + var codeLength; + var lengthCodeTable = Zlib$1.RawInflate.LengthCodeTable; + var lengthExtraTable = Zlib$1.RawInflate.LengthExtraTable; + var distCodeTable = Zlib$1.RawInflate.DistCodeTable; + var distExtraTable = Zlib$1.RawInflate.DistExtraTable; + + while ((code = this.readCodeByTable(litlen)) !== 256) { + // literal + if (code < 256) { + if (op >= olength) { + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + + output[op++] = code; + continue; + } // length code + + + ti = code - 257; + codeLength = lengthCodeTable[ti]; + + if (lengthExtraTable[ti] > 0) { + codeLength += this.readBits(lengthExtraTable[ti]); + } // dist code + + + code = this.readCodeByTable(dist); + codeDist = distCodeTable[code]; + + if (distExtraTable[code] > 0) { + codeDist += this.readBits(distExtraTable[code]); + } // lz77 decode + + + if (op >= olength) { + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + + while (codeLength--) { + output[op] = output[op++ - codeDist]; + } + } + + while (this.bitsbuflen >= 8) { + this.bitsbuflen -= 8; + this.ip--; + } + + this.op = op; + }; + /** + * decode huffman code (adaptive) + * @param {!(Array.|Uint16Array)} litlen literal and length code table. + * @param {!(Array.|Uint8Array)} dist distination code table. + */ + + + Zlib$1.RawInflate.prototype.decodeHuffmanAdaptive = function (litlen, dist) { + var output = this.output; + var op = this.op; + this.currentLitlenTable = litlen; + /** @type {number} output position limit. */ + + var olength = output.length; + /** @type {number} huffman code. */ + + var code; + /** @type {number} table index. */ + + var ti; + /** @type {number} huffman code distination. */ + + var codeDist; + /** @type {number} huffman code length. */ + + var codeLength; + var lengthCodeTable = Zlib$1.RawInflate.LengthCodeTable; + var lengthExtraTable = Zlib$1.RawInflate.LengthExtraTable; + var distCodeTable = Zlib$1.RawInflate.DistCodeTable; + var distExtraTable = Zlib$1.RawInflate.DistExtraTable; + + while ((code = this.readCodeByTable(litlen)) !== 256) { + // literal + if (code < 256) { + if (op >= olength) { + output = this.expandBufferAdaptive(); + olength = output.length; + } + + output[op++] = code; + continue; + } // length code + + + ti = code - 257; + codeLength = lengthCodeTable[ti]; + + if (lengthExtraTable[ti] > 0) { + codeLength += this.readBits(lengthExtraTable[ti]); + } // dist code + + + code = this.readCodeByTable(dist); + codeDist = distCodeTable[code]; + + if (distExtraTable[code] > 0) { + codeDist += this.readBits(distExtraTable[code]); + } // lz77 decode + + + if (op + codeLength > olength) { + output = this.expandBufferAdaptive(); + olength = output.length; + } + + while (codeLength--) { + output[op] = output[op++ - codeDist]; + } + } + + while (this.bitsbuflen >= 8) { + this.bitsbuflen -= 8; + this.ip--; + } + + this.op = op; + }; + /** + * expand output buffer. + * @param {Object=} opt_param option parameters. + * @return {!(Array.|Uint8Array)} output buffer. + */ + + + Zlib$1.RawInflate.prototype.expandBufferBlock = function (opt_param) { + /** @type {!(Array.|Uint8Array)} store buffer. */ + var buffer = new (Uint8Array )(this.op - Zlib$1.RawInflate.MaxBackwardLength); + /** @type {number} backward base point */ + + var backward = this.op - Zlib$1.RawInflate.MaxBackwardLength; + var output = this.output; // copy to output buffer + + { + buffer.set(output.subarray(Zlib$1.RawInflate.MaxBackwardLength, buffer.length)); + } + + this.blocks.push(buffer); + this.totalpos += buffer.length; // copy to backward buffer + + { + output.set(output.subarray(backward, backward + Zlib$1.RawInflate.MaxBackwardLength)); + } + + this.op = Zlib$1.RawInflate.MaxBackwardLength; + return output; + }; + /** + * expand output buffer. (adaptive) + * @param {Object=} opt_param option parameters. + * @return {!(Array.|Uint8Array)} output buffer pointer. + */ + + + Zlib$1.RawInflate.prototype.expandBufferAdaptive = function (opt_param) { + /** @type {!(Array.|Uint8Array)} store buffer. */ + var buffer; + /** @type {number} expantion ratio. */ + + var ratio = this.input.length / this.ip + 1 | 0; + /** @type {number} maximum number of huffman code. */ + + var maxHuffCode; + /** @type {number} new output buffer size. */ + + var newSize; + /** @type {number} max inflate size. */ + + var maxInflateSize; + var input = this.input; + var output = this.output; + + if (opt_param) { + if (typeof opt_param.fixRatio === 'number') { + ratio = opt_param.fixRatio; + } + + if (typeof opt_param.addRatio === 'number') { + ratio += opt_param.addRatio; + } + } // calculate new buffer size + + + if (ratio < 2) { + maxHuffCode = (input.length - this.ip) / this.currentLitlenTable[2]; + maxInflateSize = maxHuffCode / 2 * 258 | 0; + newSize = maxInflateSize < output.length ? output.length + maxInflateSize : output.length << 1; + } else { + newSize = output.length * ratio; + } // buffer expantion + + + { + buffer = new Uint8Array(newSize); + buffer.set(output); + } + + this.output = buffer; + return this.output; + }; + /** + * concat output buffer. + * @return {!(Array.|Uint8Array)} output buffer. + */ + + + Zlib$1.RawInflate.prototype.concatBufferBlock = function () { + /** @type {number} buffer pointer. */ + var pos = 0; + /** @type {number} buffer pointer. */ + + var limit = this.totalpos + (this.op - Zlib$1.RawInflate.MaxBackwardLength); + /** @type {!(Array.|Uint8Array)} output block array. */ + + var output = this.output; + /** @type {!Array} blocks array. */ + + var blocks = this.blocks; + /** @type {!(Array.|Uint8Array)} output block array. */ + + var block; + /** @type {!(Array.|Uint8Array)} output buffer. */ + + var buffer = new (Uint8Array )(limit); + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limiter. */ + + var il; + /** @type {number} loop counter. */ + + var j; + /** @type {number} loop limiter. */ + + var jl; // single buffer + + if (blocks.length === 0) { + return this.output.subarray(Zlib$1.RawInflate.MaxBackwardLength, this.op) ; + } // copy to buffer + + + for (i = 0, il = blocks.length; i < il; ++i) { + block = blocks[i]; + + for (j = 0, jl = block.length; j < jl; ++j) { + buffer[pos++] = block[j]; + } + } // current buffer + + + for (i = Zlib$1.RawInflate.MaxBackwardLength, il = this.op; i < il; ++i) { + buffer[pos++] = output[i]; + } + + this.blocks = []; + this.buffer = buffer; + return this.buffer; + }; + /** + * concat output buffer. (dynamic) + * @return {!(Array.|Uint8Array)} output buffer. + */ + + + Zlib$1.RawInflate.prototype.concatBufferDynamic = function () { + /** @type {Array.|Uint8Array} output buffer. */ + var buffer; + var op = this.op; + + { + if (this.resize) { + buffer = new Uint8Array(op); + buffer.set(this.output.subarray(0, op)); + } else { + buffer = this.output.subarray(0, op); + } + } + + this.buffer = buffer; + return this.buffer; + }; + + var buildHuffmanTable$1 = Zlib$1.Huffman.buildHuffmanTable; + /** + * @param {!(Uint8Array|Array.)} input input buffer. + * @param {number} ip input buffer pointer. + * @param {number=} opt_buffersize buffer block size. + * @constructor + */ + + Zlib$1.RawInflateStream = function (input, ip, opt_buffersize) { + /** @type {!Array.<(Array|Uint8Array)>} */ + this.blocks = []; + /** @type {number} block size. */ + + this.bufferSize = opt_buffersize ? opt_buffersize : ZLIB_STREAM_RAW_INFLATE_BUFFER_SIZE$1; + /** @type {!number} total output buffer pointer. */ + + this.totalpos = 0; + /** @type {!number} input buffer pointer. */ + + this.ip = ip === void 0 ? 0 : ip; + /** @type {!number} bit stream reader buffer. */ + + this.bitsbuf = 0; + /** @type {!number} bit stream reader buffer size. */ + + this.bitsbuflen = 0; + /** @type {!(Array|Uint8Array)} input buffer. */ + + this.input = new Uint8Array(input) ; + /** @type {!(Uint8Array|Array)} output buffer. */ + + this.output = new (Uint8Array )(this.bufferSize); + /** @type {!number} output buffer pointer. */ + + this.op = 0; + /** @type {boolean} is final block flag. */ + + this.bfinal = false; + /** @type {number} uncompressed block length. */ + + this.blockLength; + /** @type {boolean} resize flag for memory size optimization. */ + + this.resize = false; + /** @type {Array} */ + + this.litlenTable; + /** @type {Array} */ + + this.distTable; + /** @type {number} */ + + this.sp = 0; // stream pointer + + /** @type {Zlib.RawInflateStream.Status} */ + + this.status = Zlib$1.RawInflateStream.Status.INITIALIZED; // + // backup + // + + /** @type {!number} */ + + this.ip_; + /** @type {!number} */ + + this.bitsbuflen_; + /** @type {!number} */ + + this.bitsbuf_; + }; + /** + * @enum {number} + */ + + + Zlib$1.RawInflateStream.BlockType = { + UNCOMPRESSED: 0, + FIXED: 1, + DYNAMIC: 2 + }; + /** + * @enum {number} + */ + + Zlib$1.RawInflateStream.Status = { + INITIALIZED: 0, + BLOCK_HEADER_START: 1, + BLOCK_HEADER_END: 2, + BLOCK_BODY_START: 3, + BLOCK_BODY_END: 4, + DECODE_BLOCK_START: 5, + DECODE_BLOCK_END: 6 + }; + /** + * decompress. + * @return {!(Uint8Array|Array)} inflated buffer. + */ + + Zlib$1.RawInflateStream.prototype.decompress = function (newInput, ip) { + /** @type {boolean} */ + var stop = false; + + if (newInput !== void 0) { + this.input = newInput; + } + + if (ip !== void 0) { + this.ip = ip; + } // decompress + + + while (!stop) { + switch (this.status) { + // block header + case Zlib$1.RawInflateStream.Status.INITIALIZED: + case Zlib$1.RawInflateStream.Status.BLOCK_HEADER_START: + if (this.readBlockHeader() < 0) { + stop = true; + } + + break; + // block body + + case Zlib$1.RawInflateStream.Status.BLOCK_HEADER_END: + /* FALLTHROUGH */ + + case Zlib$1.RawInflateStream.Status.BLOCK_BODY_START: + switch (this.currentBlockType) { + case Zlib$1.RawInflateStream.BlockType.UNCOMPRESSED: + if (this.readUncompressedBlockHeader() < 0) { + stop = true; + } + + break; + + case Zlib$1.RawInflateStream.BlockType.FIXED: + if (this.parseFixedHuffmanBlock() < 0) { + stop = true; + } + + break; + + case Zlib$1.RawInflateStream.BlockType.DYNAMIC: + if (this.parseDynamicHuffmanBlock() < 0) { + stop = true; + } + + break; + } + + break; + // decode data + + case Zlib$1.RawInflateStream.Status.BLOCK_BODY_END: + case Zlib$1.RawInflateStream.Status.DECODE_BLOCK_START: + switch (this.currentBlockType) { + case Zlib$1.RawInflateStream.BlockType.UNCOMPRESSED: + if (this.parseUncompressedBlock() < 0) { + stop = true; + } + + break; + + case Zlib$1.RawInflateStream.BlockType.FIXED: + /* FALLTHROUGH */ + + case Zlib$1.RawInflateStream.BlockType.DYNAMIC: + if (this.decodeHuffman() < 0) { + stop = true; + } + + break; + } + + break; + + case Zlib$1.RawInflateStream.Status.DECODE_BLOCK_END: + if (this.bfinal) { + stop = true; + } else { + this.status = Zlib$1.RawInflateStream.Status.INITIALIZED; + } + + break; + } + } + + return this.concatBuffer(); + }; + /** + * @const + * @type {number} max backward length for LZ77. + */ + + + Zlib$1.RawInflateStream.MaxBackwardLength = 32768; + /** + * @const + * @type {number} max copy length for LZ77. + */ + + Zlib$1.RawInflateStream.MaxCopyLength = 258; + /** + * huffman order + * @const + * @type {!(Array.|Uint8Array)} + */ + + Zlib$1.RawInflateStream.Order = function (table) { + return new Uint16Array(table) ; + }([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); + /** + * huffman length code table. + * @const + * @type {!(Array.|Uint16Array)} + */ + + + Zlib$1.RawInflateStream.LengthCodeTable = function (table) { + return new Uint16Array(table) ; + }([0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000d, 0x000f, 0x0011, 0x0013, 0x0017, 0x001b, 0x001f, 0x0023, 0x002b, 0x0033, 0x003b, 0x0043, 0x0053, 0x0063, 0x0073, 0x0083, 0x00a3, 0x00c3, 0x00e3, 0x0102, 0x0102, 0x0102]); + /** + * huffman length extra-bits table. + * @const + * @type {!(Array.|Uint8Array)} + */ + + + Zlib$1.RawInflateStream.LengthExtraTable = function (table) { + return new Uint8Array(table) ; + }([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, 0, 0]); + /** + * huffman dist code table. + * @const + * @type {!(Array.|Uint16Array)} + */ + + + Zlib$1.RawInflateStream.DistCodeTable = function (table) { + return new Uint16Array(table) ; + }([0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d, 0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1, 0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01, 0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001]); + /** + * huffman dist extra-bits table. + * @const + * @type {!(Array.|Uint8Array)} + */ + + + Zlib$1.RawInflateStream.DistExtraTable = function (table) { + return new Uint8Array(table) ; + }([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]); + /** + * fixed huffman length code table + * @const + * @type {!Array} + */ + + + Zlib$1.RawInflateStream.FixedLiteralLengthTable = function (table) { + return table; + }(function () { + var lengths = new (Uint8Array )(288); + var i, il; + + for (i = 0, il = lengths.length; i < il; ++i) { + lengths[i] = i <= 143 ? 8 : i <= 255 ? 9 : i <= 279 ? 7 : 8; + } + + return buildHuffmanTable$1(lengths); + }()); + /** + * fixed huffman distance code table + * @const + * @type {!Array} + */ + + + Zlib$1.RawInflateStream.FixedDistanceTable = function (table) { + return table; + }(function () { + var lengths = new (Uint8Array )(30); + var i, il; + + for (i = 0, il = lengths.length; i < il; ++i) { + lengths[i] = 5; + } + + return buildHuffmanTable$1(lengths); + }()); + /** + * parse deflated block. + */ + + + Zlib$1.RawInflateStream.prototype.readBlockHeader = function () { + /** @type {number} header */ + var hdr; + this.status = Zlib$1.RawInflateStream.Status.BLOCK_HEADER_START; + this.save_(); + + if ((hdr = this.readBits(3)) < 0) { + this.restore_(); + return -1; + } // BFINAL + + + if (hdr & 0x1) { + this.bfinal = true; + } // BTYPE + + + hdr >>>= 1; + + switch (hdr) { + case 0: + // uncompressed + this.currentBlockType = Zlib$1.RawInflateStream.BlockType.UNCOMPRESSED; + break; + + case 1: + // fixed huffman + this.currentBlockType = Zlib$1.RawInflateStream.BlockType.FIXED; + break; + + case 2: + // dynamic huffman + this.currentBlockType = Zlib$1.RawInflateStream.BlockType.DYNAMIC; + break; + + default: + // reserved or other + throw new Error('unknown BTYPE: ' + hdr); + } + + this.status = Zlib$1.RawInflateStream.Status.BLOCK_HEADER_END; + }; + /** + * read inflate bits + * @param {number} length bits length. + * @return {number} read bits. + */ + + + Zlib$1.RawInflateStream.prototype.readBits = function (length) { + var bitsbuf = this.bitsbuf; + var bitsbuflen = this.bitsbuflen; + var input = this.input; + var ip = this.ip; + /** @type {number} input and output byte. */ + + var octet; // not enough buffer + + while (bitsbuflen < length) { + // input byte + if (input.length <= ip) { + return -1; + } + + octet = input[ip++]; // concat octet + + bitsbuf |= octet << bitsbuflen; + bitsbuflen += 8; + } // output byte + + + octet = bitsbuf & (1 << length) - 1; + bitsbuf >>>= length; + bitsbuflen -= length; + this.bitsbuf = bitsbuf; + this.bitsbuflen = bitsbuflen; + this.ip = ip; + return octet; + }; + /** + * read huffman code using table + * @param {Array} table huffman code table. + * @return {number} huffman code. + */ + + + Zlib$1.RawInflateStream.prototype.readCodeByTable = function (table) { + var bitsbuf = this.bitsbuf; + var bitsbuflen = this.bitsbuflen; + var input = this.input; + var ip = this.ip; + /** @type {!(Array|Uint8Array)} huffman code table */ + + var codeTable = table[0]; + /** @type {number} */ + + var maxCodeLength = table[1]; + /** @type {number} input byte */ + + var octet; + /** @type {number} code length & code (16bit, 16bit) */ + + var codeWithLength; + /** @type {number} code bits length */ + + var codeLength; // not enough buffer + + while (bitsbuflen < maxCodeLength) { + if (input.length <= ip) { + return -1; + } + + octet = input[ip++]; + bitsbuf |= octet << bitsbuflen; + bitsbuflen += 8; + } // read max length + + + codeWithLength = codeTable[bitsbuf & (1 << maxCodeLength) - 1]; + codeLength = codeWithLength >>> 16; + + if (codeLength > bitsbuflen) { + throw new Error('invalid code length: ' + codeLength); + } + + this.bitsbuf = bitsbuf >> codeLength; + this.bitsbuflen = bitsbuflen - codeLength; + this.ip = ip; + return codeWithLength & 0xffff; + }; + /** + * read uncompressed block header + */ + + + Zlib$1.RawInflateStream.prototype.readUncompressedBlockHeader = function () { + /** @type {number} block length */ + var len; + /** @type {number} number for check block length */ + + var nlen; + var input = this.input; + var ip = this.ip; + this.status = Zlib$1.RawInflateStream.Status.BLOCK_BODY_START; + + if (ip + 4 >= input.length) { + return -1; + } + + len = input[ip++] | input[ip++] << 8; + nlen = input[ip++] | input[ip++] << 8; // check len & nlen + + if (len === ~nlen) { + throw new Error('invalid uncompressed block header: length verify'); + } // skip buffered header bits + + + this.bitsbuf = 0; + this.bitsbuflen = 0; + this.ip = ip; + this.blockLength = len; + this.status = Zlib$1.RawInflateStream.Status.BLOCK_BODY_END; + }; + /** + * parse uncompressed block. + */ + + + Zlib$1.RawInflateStream.prototype.parseUncompressedBlock = function () { + var input = this.input; + var ip = this.ip; + var output = this.output; + var op = this.op; + var len = this.blockLength; + this.status = Zlib$1.RawInflateStream.Status.DECODE_BLOCK_START; // copy + // XXX: รฃยยจรฃโ€šล รฃยโ€šรฃยห†รฃยลกรงยด รงโ€บยดรฃยยซรฃโ€šยณรฃฦ’โ€รฃฦ’ยผ + + while (len--) { + if (op === output.length) { + output = this.expandBuffer({ + fixRatio: 2 + }); + } // not enough input buffer + + + if (ip >= input.length) { + this.ip = ip; + this.op = op; + this.blockLength = len + 1; // รฃโ€šยณรฃฦ’โ€รฃฦ’ยผรฃยโ€”รฃยยฆรฃยยชรฃยโ€žรฃยยฎรฃยยงรฆห†ยปรฃยโ„ข + + return -1; + } + + output[op++] = input[ip++]; + } + + if (len < 0) { + this.status = Zlib$1.RawInflateStream.Status.DECODE_BLOCK_END; + } + + this.ip = ip; + this.op = op; + return 0; + }; + /** + * parse fixed huffman block. + */ + + + Zlib$1.RawInflateStream.prototype.parseFixedHuffmanBlock = function () { + this.status = Zlib$1.RawInflateStream.Status.BLOCK_BODY_START; + this.litlenTable = Zlib$1.RawInflateStream.FixedLiteralLengthTable; + this.distTable = Zlib$1.RawInflateStream.FixedDistanceTable; + this.status = Zlib$1.RawInflateStream.Status.BLOCK_BODY_END; + return 0; + }; + /** + * รฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†รฃยยฎรฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃโ€šยญรฃโ€šยนรฃฦ’ห†รฃโ€šโ€™รฅห†ยฅรฃยยฎรฃฦ’โ€”รฃฦ’ยญรฃฦ’โ€˜รฃฦ’โ€ รฃโ€šยฃรฃยยซรฉโ‚ฌโ‚ฌรฉยยฟรฃยโ„ขรฃโ€šโ€น. + * @private + */ + + + Zlib$1.RawInflateStream.prototype.save_ = function () { + this.ip_ = this.ip; + this.bitsbuflen_ = this.bitsbuflen; + this.bitsbuf_ = this.bitsbuf; + }; + /** + * รฅห†ยฅรฃยยฎรฃฦ’โ€”รฃฦ’ยญรฃฦ’โ€˜รฃฦ’โ€ รฃโ€šยฃรฃยยซรฉโ‚ฌโ‚ฌรฉยยฟรฃยโ€”รฃยลธรฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃโ€šยญรฃโ€šยนรฃฦ’ห†รฃโ€šโ€™รฅยพยฉรฅโ€ฆฦ’รฃยโ„ขรฃโ€šโ€น. + * @private + */ + + + Zlib$1.RawInflateStream.prototype.restore_ = function () { + this.ip = this.ip_; + this.bitsbuflen = this.bitsbuflen_; + this.bitsbuf = this.bitsbuf_; + }; + /** + * parse dynamic huffman block. + */ + + + Zlib$1.RawInflateStream.prototype.parseDynamicHuffmanBlock = function () { + /** @type {number} number of literal and length codes. */ + var hlit; + /** @type {number} number of distance codes. */ + + var hdist; + /** @type {number} number of code lengths. */ + + var hclen; + /** @type {!(Uint8Array|Array)} code lengths. */ + + var codeLengths = new (Uint8Array )(Zlib$1.RawInflateStream.Order.length); + /** @type {!Array} code lengths table. */ + + var codeLengthsTable; + this.status = Zlib$1.RawInflateStream.Status.BLOCK_BODY_START; + this.save_(); + hlit = this.readBits(5) + 257; + hdist = this.readBits(5) + 1; + hclen = this.readBits(4) + 4; + + if (hlit < 0 || hdist < 0 || hclen < 0) { + this.restore_(); + return -1; + } + + try { + parseDynamicHuffmanBlockImpl.call(this); + } catch (e) { + this.restore_(); + return -1; + } + + function parseDynamicHuffmanBlockImpl() { + /** @type {number} */ + var bits; + var code; + var prev = 0; + var repeat; + /** @type {!(Uint8Array|Array.)} code length table. */ + + var lengthTable; + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limit. */ + + var il; // decode code lengths + + for (i = 0; i < hclen; ++i) { + if ((bits = this.readBits(3)) < 0) { + throw new Error('not enough input'); + } + + codeLengths[Zlib$1.RawInflateStream.Order[i]] = bits; + } // decode length table + + + codeLengthsTable = buildHuffmanTable$1(codeLengths); + lengthTable = new (Uint8Array )(hlit + hdist); + + for (i = 0, il = hlit + hdist; i < il;) { + code = this.readCodeByTable(codeLengthsTable); + + if (code < 0) { + throw new Error('not enough input'); + } + + switch (code) { + case 16: + if ((bits = this.readBits(2)) < 0) { + throw new Error('not enough input'); + } + + repeat = 3 + bits; + + while (repeat--) { + lengthTable[i++] = prev; + } + + break; + + case 17: + if ((bits = this.readBits(3)) < 0) { + throw new Error('not enough input'); + } + + repeat = 3 + bits; + + while (repeat--) { + lengthTable[i++] = 0; + } + + prev = 0; + break; + + case 18: + if ((bits = this.readBits(7)) < 0) { + throw new Error('not enough input'); + } + + repeat = 11 + bits; + + while (repeat--) { + lengthTable[i++] = 0; + } + + prev = 0; + break; + + default: + lengthTable[i++] = code; + prev = code; + break; + } + } // literal and length code + this.litlenTable = buildHuffmanTable$1(lengthTable.subarray(0, hlit)) ; + this.distTable = buildHuffmanTable$1(lengthTable.subarray(hlit)) ; + } + + this.status = Zlib$1.RawInflateStream.Status.BLOCK_BODY_END; + return 0; + }; + /** + * decode huffman code (dynamic) + * @return {(number|undefined)} -1 is error. + */ + + + Zlib$1.RawInflateStream.prototype.decodeHuffman = function () { + var output = this.output; + var op = this.op; + /** @type {number} huffman code. */ + + var code; + /** @type {number} table index. */ + + var ti; + /** @type {number} huffman code distination. */ + + var codeDist; + /** @type {number} huffman code length. */ + + var codeLength; + var litlen = this.litlenTable; + var dist = this.distTable; + var olength = output.length; + var bits; + this.status = Zlib$1.RawInflateStream.Status.DECODE_BLOCK_START; + + while (true) { + this.save_(); + code = this.readCodeByTable(litlen); + + if (code < 0) { + this.op = op; + this.restore_(); + return -1; + } + + if (code === 256) { + break; + } // literal + + + if (code < 256) { + if (op === olength) { + output = this.expandBuffer(); + olength = output.length; + } + + output[op++] = code; + continue; + } // length code + + + ti = code - 257; + codeLength = Zlib$1.RawInflateStream.LengthCodeTable[ti]; + + if (Zlib$1.RawInflateStream.LengthExtraTable[ti] > 0) { + bits = this.readBits(Zlib$1.RawInflateStream.LengthExtraTable[ti]); + + if (bits < 0) { + this.op = op; + this.restore_(); + return -1; + } + + codeLength += bits; + } // dist code + + + code = this.readCodeByTable(dist); + + if (code < 0) { + this.op = op; + this.restore_(); + return -1; + } + + codeDist = Zlib$1.RawInflateStream.DistCodeTable[code]; + + if (Zlib$1.RawInflateStream.DistExtraTable[code] > 0) { + bits = this.readBits(Zlib$1.RawInflateStream.DistExtraTable[code]); + + if (bits < 0) { + this.op = op; + this.restore_(); + return -1; + } + + codeDist += bits; + } // lz77 decode + + + if (op + codeLength >= olength) { + output = this.expandBuffer(); + olength = output.length; + } + + while (codeLength--) { + output[op] = output[op++ - codeDist]; + } // break + + + if (this.ip === this.input.length) { + this.op = op; + return -1; + } + } + + while (this.bitsbuflen >= 8) { + this.bitsbuflen -= 8; + this.ip--; + } + + this.op = op; + this.status = Zlib$1.RawInflateStream.Status.DECODE_BLOCK_END; + }; + /** + * expand output buffer. (dynamic) + * @param {Object=} opt_param option parameters. + * @return {!(Array|Uint8Array)} output buffer pointer. + */ + + + Zlib$1.RawInflateStream.prototype.expandBuffer = function (opt_param) { + /** @type {!(Array|Uint8Array)} store buffer. */ + var buffer; + /** @type {number} expantion ratio. */ + + var ratio = this.input.length / this.ip + 1 | 0; + /** @type {number} maximum number of huffman code. */ + + var maxHuffCode; + /** @type {number} new output buffer size. */ + + var newSize; + /** @type {number} max inflate size. */ + + var maxInflateSize; + var input = this.input; + var output = this.output; + + if (opt_param) { + if (typeof opt_param.fixRatio === 'number') { + ratio = opt_param.fixRatio; + } + + if (typeof opt_param.addRatio === 'number') { + ratio += opt_param.addRatio; + } + } // calculate new buffer size + + + if (ratio < 2) { + maxHuffCode = (input.length - this.ip) / this.litlenTable[2]; + maxInflateSize = maxHuffCode / 2 * 258 | 0; + newSize = maxInflateSize < output.length ? output.length + maxInflateSize : output.length << 1; + } else { + newSize = output.length * ratio; + } // buffer expantion + + + { + buffer = new Uint8Array(newSize); + buffer.set(output); + } + + this.output = buffer; + return this.output; + }; + /** + * concat output buffer. (dynamic) + * @return {!(Array|Uint8Array)} output buffer. + */ + + + Zlib$1.RawInflateStream.prototype.concatBuffer = function () { + /** @type {!(Array|Uint8Array)} output buffer. */ + var buffer; + /** @type {number} */ + + var op = this.op; + /** @type {Uint8Array} */ + + var tmp; + + if (this.resize) { + { + buffer = new Uint8Array(this.output.subarray(this.sp, op)); + } + } else { + buffer = this.output.subarray(this.sp, op) ; + } + + this.sp = op; // compaction + + if (op > Zlib$1.RawInflateStream.MaxBackwardLength + this.bufferSize) { + this.op = this.sp = Zlib$1.RawInflateStream.MaxBackwardLength; + + { + tmp = this.output; + this.output = new Uint8Array(this.bufferSize + Zlib$1.RawInflateStream.MaxBackwardLength); + this.output.set(tmp.subarray(op - Zlib$1.RawInflateStream.MaxBackwardLength, op)); + } + } + + return buffer; + }; + /** + * @constructor + * @param {!(Uint8Array|Array)} input deflated buffer. + * @param {Object=} opt_params option parameters. + * + * opt_params รฃยยฏรคยปยฅรคยธโ€นรฃยยฎรฃฦ’โ€”รฃฦ’ยญรฃฦ’โ€˜รฃฦ’โ€ รฃโ€šยฃรฃโ€šโ€™รฆล’โ€กรฅยฎลกรฃยโ„ขรฃโ€šโ€นรคยบโ€นรฃยล’รฃยยงรฃยยรฃยยพรฃยโ„ขรฃโ‚ฌโ€š + * - index: input buffer รฃยยฎ deflate รฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃฦ’ล รฃยยฎรฉโ€“โ€นรฅยงโ€นรคยฝยรงยฝยฎ. + * - blockSize: รฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃโ€šยตรฃโ€šยครฃโ€šยบ. + * - verify: รคยผยธรฅยผยตรฃยล’รงยตโ€šรฃโ€šยรฃยยฃรฃยลธรฅยพล’ adler-32 checksum รฃยยฎรฆยคล“รจยจยผรฃโ€šโ€™รจยกล’รฃยโ€ รฃยโ€น. + * - bufferType: Zlib.Inflate.BufferType รฃยยฎรฅโ‚ฌยครฃยยซรฃโ€šห†รฃยยฃรฃยยฆรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรงยฎยกรงยโ€ รฆโ€“ยนรฆยณโ€ขรฃโ€šโ€™รฆล’โ€กรฅยฎลกรฃยโ„ขรฃโ€šโ€น. + * Zlib.Inflate.BufferType รฃยยฏ Zlib.RawInflate.BufferType รฃยยฎรฃโ€šยจรฃโ€šยครฃฦ’ยชรฃโ€šยขรฃโ€šยน. + */ + + + Zlib$1.Inflate = function (input, opt_params) { + /** @type {number} */ + + var cmf; + /** @type {number} */ + + var flg; + /** @type {!(Uint8Array|Array)} */ + + this.input = input; + /** @type {number} */ + + this.ip = 0; + /** @type {Zlib.RawInflate} */ + + this.rawinflate; + /** @type {(boolean|undefined)} verify flag. */ + + this.verify; // option parameters + + if (opt_params || !(opt_params = {})) { + if (opt_params['index']) { + this.ip = opt_params['index']; + } + + if (opt_params['verify']) { + this.verify = opt_params['verify']; + } + } // Compression Method and Flags + + + cmf = input[this.ip++]; + flg = input[this.ip++]; // compression method + + switch (cmf & 0x0f) { + case Zlib$1.CompressionMethod.DEFLATE: + this.method = Zlib$1.CompressionMethod.DEFLATE; + break; + + default: + throw new Error('unsupported compression method'); + } // fcheck + + + if (((cmf << 8) + flg) % 31 !== 0) { + throw new Error('invalid fcheck flag:' + ((cmf << 8) + flg) % 31); + } // fdict (not supported) + + + if (flg & 0x20) { + throw new Error('fdict flag is not supported'); + } // RawInflate + + + this.rawinflate = new Zlib$1.RawInflate(input, { + 'index': this.ip, + 'bufferSize': opt_params['bufferSize'], + 'bufferType': opt_params['bufferType'], + 'resize': opt_params['resize'] + }); + }; + /** + * @enum {number} + */ + + + Zlib$1.Inflate.BufferType = Zlib$1.RawInflate.BufferType; + /** + * decompress. + * @return {!(Uint8Array|Array)} inflated buffer. + */ + + Zlib$1.Inflate.prototype.decompress = function () { + /** @type {!(Array|Uint8Array)} input buffer. */ + var input = this.input; + /** @type {!(Uint8Array|Array)} inflated buffer. */ + + var buffer; + /** @type {number} adler-32 checksum */ + + var adler32; + buffer = this.rawinflate.decompress(); + this.ip = this.rawinflate.ip; // verify adler-32 + + if (this.verify) { + adler32 = (input[this.ip++] << 24 | input[this.ip++] << 16 | input[this.ip++] << 8 | input[this.ip++]) >>> 0; + + if (adler32 !== Zlib$1.Adler32(buffer)) { + throw new Error('invalid adler-32 checksum'); + } + } + + return buffer; + }; + /* vim:set expandtab ts=2 sw=2 tw=80: */ + + /** + * @param {!(Uint8Array|Array)} input deflated buffer. + * @constructor + */ + + + Zlib$1.InflateStream = function (input) { + /** @type {!(Uint8Array|Array)} */ + this.input = input === void 0 ? new (Uint8Array )() : input; + /** @type {number} */ + + this.ip = 0; + /** @type {Zlib.RawInflateStream} */ + + this.rawinflate = new Zlib$1.RawInflateStream(this.input, this.ip); + /** @type {Zlib.CompressionMethod} */ + + this.method; + /** @type {!(Array|Uint8Array)} */ + + this.output = this.rawinflate.output; + }; + /** + * decompress. + * @return {!(Uint8Array|Array)} inflated buffer. + */ + + + Zlib$1.InflateStream.prototype.decompress = function (input) { + /** @type {!(Uint8Array|Array)} inflated buffer. */ + var buffer; + // XXX Array, Uint8Array รฃยยฎรฃฦ’ยรฃโ€šยงรฃฦ’ฦ’รฃโ€šยฏรฃโ€šโ€™รจยกล’รฃยโ€ รฃยโ€นรงยขยบรจยชยรฃยโ„ขรฃโ€šโ€น + + if (input !== void 0) { + { + var tmp = new Uint8Array(this.input.length + input.length); + tmp.set(this.input, 0); + tmp.set(input, this.input.length); + this.input = tmp; + } + } + + if (this.method === void 0) { + if (this.readHeader() < 0) { + return new (Uint8Array )(); + } + } + + buffer = this.rawinflate.decompress(this.input, this.ip); + + if (this.rawinflate.ip !== 0) { + this.input = this.input.subarray(this.rawinflate.ip) ; + this.ip = 0; + } // verify adler-32 + + /* + if (this.verify) { + adler32 = + input[this.ip++] << 24 | input[this.ip++] << 16 | + input[this.ip++] << 8 | input[this.ip++]; + if (adler32 !== Zlib.Adler32(buffer)) { + throw new Error('invalid adler-32 checksum'); + } + } + */ + + + return buffer; + }; + + Zlib$1.InflateStream.prototype.readHeader = function () { + var ip = this.ip; + var input = this.input; // Compression Method and Flags + + var cmf = input[ip++]; + var flg = input[ip++]; + + if (cmf === void 0 || flg === void 0) { + return -1; + } // compression method + + + switch (cmf & 0x0f) { + case Zlib$1.CompressionMethod.DEFLATE: + this.method = Zlib$1.CompressionMethod.DEFLATE; + break; + + default: + throw new Error('unsupported compression method'); + } // fcheck + + + if (((cmf << 8) + flg) % 31 !== 0) { + throw new Error('invalid fcheck flag:' + ((cmf << 8) + flg) % 31); + } // fdict (not supported) + + + if (flg & 0x20) { + throw new Error('fdict flag is not supported'); + } + + this.ip = ip; + }; + /** + * @fileoverview GZIP (RFC1952) รฅยฑโ€ขรฉโ€“โ€นรฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃฦ’ล รฅยฎลธรจยฃโ€ฆ. + */ + + /** + * @constructor + * @param {!(Array|Uint8Array)} input input buffer. + * @param {Object=} opt_params option parameters. + */ + + + Zlib$1.Gunzip = function (input, opt_params) { + /** @type {!(Array.|Uint8Array)} input buffer. */ + this.input = input; + /** @type {number} input buffer pointer. */ + + this.ip = 0; + /** @type {Array.} */ + + this.member = []; + /** @type {boolean} */ + + this.decompressed = false; + }; + /** + * @return {Array.} + */ + + + Zlib$1.Gunzip.prototype.getMembers = function () { + if (!this.decompressed) { + this.decompress(); + } + + return this.member.slice(); + }; + /** + * inflate gzip data. + * @return {!(Array.|Uint8Array)} inflated buffer. + */ + + + Zlib$1.Gunzip.prototype.decompress = function () { + /** @type {number} input length. */ + var il = this.input.length; + + while (this.ip < il) { + this.decodeMember(); + } + + this.decompressed = true; + return this.concatMember(); + }; + /** + * decode gzip member. + */ + + + Zlib$1.Gunzip.prototype.decodeMember = function () { + /** @type {Zlib.GunzipMember} */ + var member = new Zlib$1.GunzipMember(); + /** @type {number} */ + + var isize; + /** @type {Zlib.RawInflate} RawInflate implementation. */ + + var rawinflate; + /** @type {!(Array.|Uint8Array)} inflated data. */ + + var inflated; + /** @type {number} inflate size */ + + var inflen; + /** @type {number} character code */ + + var c; + /** @type {number} character index in string. */ + + var ci; + /** @type {Array.} character array. */ + + var str; + /** @type {number} modification time. */ + + var mtime; + /** @type {number} */ + + var crc32; + var input = this.input; + var ip = this.ip; + member.id1 = input[ip++]; + member.id2 = input[ip++]; // check signature + + if (member.id1 !== 0x1f || member.id2 !== 0x8b) { + throw new Error('invalid file signature:' + member.id1 + ',' + member.id2); + } // check compression method + + + member.cm = input[ip++]; + + switch (member.cm) { + case 8: + /* XXX: use Zlib const */ + break; + + default: + throw new Error('unknown compression method: ' + member.cm); + } // flags + + + member.flg = input[ip++]; // modification time + + mtime = input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24; + member.mtime = new Date(mtime * 1000); // extra flags + + member.xfl = input[ip++]; // operating system + + member.os = input[ip++]; // extra + + if ((member.flg & Zlib$1.Gzip.FlagsMask.FEXTRA) > 0) { + member.xlen = input[ip++] | input[ip++] << 8; + ip = this.decodeSubField(ip, member.xlen); + } // fname + + + if ((member.flg & Zlib$1.Gzip.FlagsMask.FNAME) > 0) { + for (str = [], ci = 0; (c = input[ip++]) > 0;) { + str[ci++] = String.fromCharCode(c); + } + + member.name = str.join(''); + } // fcomment + + + if ((member.flg & Zlib$1.Gzip.FlagsMask.FCOMMENT) > 0) { + for (str = [], ci = 0; (c = input[ip++]) > 0;) { + str[ci++] = String.fromCharCode(c); + } + + member.comment = str.join(''); + } // fhcrc + + + if ((member.flg & Zlib$1.Gzip.FlagsMask.FHCRC) > 0) { + member.crc16 = Zlib$1.CRC32.calc(input, 0, ip) & 0xffff; + + if (member.crc16 !== (input[ip++] | input[ip++] << 8)) { + throw new Error('invalid header crc16'); + } + } // isize รฃโ€šโ€™รคยบโ€นรฅโ€ฐยรฃยยซรฅยโ€“รฅยพโ€”รฃยโ„ขรฃโ€šโ€นรฃยยจรฅยฑโ€ขรฉโ€“โ€นรฅยพล’รฃยยฎรฃโ€šยตรฃโ€šยครฃโ€šยบรฃยล’รฅห†โ€ รฃยโ€นรฃโ€šโ€นรฃยลธรฃโ€šยรฃโ‚ฌย + // inflateรฅโ€กยฆรงยโ€ รฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šยตรฃโ€šยครฃโ€šยบรฃยล’รคยบโ€นรฅโ€ฐยรฃยยซรฅห†โ€ รฃยโ€นรฃโ€šล รฃโ‚ฌยรฉยซหœรฉโ‚ฌลธรฃยยซรฃยยชรฃโ€šโ€น + + + isize = input[input.length - 4] | input[input.length - 3] << 8 | input[input.length - 2] << 16 | input[input.length - 1] << 24; // isize รฃยยฎรฅยฆยฅรฅยฝโ€œรฆโ‚ฌยงรฃฦ’ยรฃโ€šยงรฃฦ’ฦ’รฃโ€šยฏ + // รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยงรฃยยฏรฆล“โ‚ฌรฅยฐย 2-bit รฃยยฎรฃยลธรฃโ€šยรฃโ‚ฌยรฆล“โ‚ฌรฅยคยงรฃยยง 1/4 รฃยยซรฃยยชรฃโ€šโ€น + // LZ77 รงยฌยฆรฅยยทรฃยยงรฃยยฏ รฉโ€ขยทรฃยโ€ขรฃยยจรจยทยรฉโ€บยข 2-Byte รฃยยงรฆล“โ‚ฌรฅยคยง 258-Byte รฃโ€šโ€™รจยกยจรงยยพรฃยยงรฃยยรฃโ€šโ€นรฃยลธรฃโ€šยรฃโ‚ฌย + // 1/128 รฃยยซรฃยยชรฃโ€šโ€นรฃยยจรฃยโ„ขรฃโ€šโ€น + // รฃยโ€œรฃยโ€œรฃยโ€นรฃโ€šโ€ฐรฅโ€ฆยฅรฅล โ€บรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรฆยฎโ€นรฃโ€šล รฃยล’ isize รฃยยฎ 512 รฅโ‚ฌยรคยปยฅรคยธล รฃย รฃยยฃรฃยลธรฃโ€šโ€ฐ + // รฃโ€šยตรฃโ€šยครฃโ€šยบรฆล’โ€กรฅยฎลกรฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรงยขยบรคยฟยรฃยยฏรจยกล’รฃโ€šยรฃยยชรฃยโ€žรคยบโ€นรฃยยจรฃยโ„ขรฃโ€šโ€น + + if (input.length - ip - + /* CRC-32 */ + 4 - + /* ISIZE */ + 4 < isize * 512) { + inflen = isize; + } // compressed block + + + rawinflate = new Zlib$1.RawInflate(input, { + 'index': ip, + 'bufferSize': inflen + }); + member.data = inflated = rawinflate.decompress(); + ip = rawinflate.ip; // crc32 + + member.crc32 = crc32 = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; + + if (Zlib$1.CRC32.calc(inflated) !== crc32) { + throw new Error('invalid CRC-32 checksum: 0x' + Zlib$1.CRC32.calc(inflated).toString(16) + ' / 0x' + crc32.toString(16)); + } // input size + + + member.isize = isize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; + + if ((inflated.length & 0xffffffff) !== isize) { + throw new Error('invalid input size: ' + (inflated.length & 0xffffffff) + ' / ' + isize); + } + + this.member.push(member); + this.ip = ip; + }; + /** + * รฃโ€šยตรฃฦ’โ€“รฃฦ’โ€ขรฃโ€šยฃรฃฦ’ยผรฃฦ’ยซรฃฦ’โ€ฐรฃยยฎรฃฦ’โ€กรฃโ€šยณรฃฦ’ยผรฃฦ’โ€ฐ + * XXX: รงยยพรฅล“ยจรฃยยฏรคยฝโ€ขรฃโ€šโ€šรฃยโ€บรฃยลกรฃโ€šยนรฃโ€šยญรฃฦ’ฦ’รฃฦ’โ€”รฃยโ„ขรฃโ€šโ€น + */ + + + Zlib$1.Gunzip.prototype.decodeSubField = function (ip, length) { + return ip + length; + }; + /** + * @return {!(Array.|Uint8Array)} + */ + + + Zlib$1.Gunzip.prototype.concatMember = function () { + /** @type {Array.} */ + var member = this.member; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + /** @type {number} */ + + var p = 0; + /** @type {number} */ + + var size = 0; + /** @type {!(Array.|Uint8Array)} */ + + var buffer; + + for (i = 0, il = member.length; i < il; ++i) { + size += member[i].data.length; + } + + { + buffer = new Uint8Array(size); + + for (i = 0; i < il; ++i) { + buffer.set(member[i].data, p); + p += member[i].data.length; + } + } + + return buffer; + }; + /** + * @constructor + */ + + + Zlib$1.GunzipMember = function () { + /** @type {number} signature first byte. */ + this.id1; + /** @type {number} signature second byte. */ + + this.id2; + /** @type {number} compression method. */ + + this.cm; + /** @type {number} flags. */ + + this.flg; + /** @type {Date} modification time. */ + + this.mtime; + /** @type {number} extra flags. */ + + this.xfl; + /** @type {number} operating system number. */ + + this.os; + /** @type {number} CRC-16 value for FHCRC flag. */ + + this.crc16; + /** @type {number} extra length. */ + + this.xlen; + /** @type {number} CRC-32 value for verification. */ + + this.crc32; + /** @type {number} input size modulo 32 value. */ + + this.isize; + /** @type {string} filename. */ + + this.name; + /** @type {string} comment. */ + + this.comment; + /** @type {!(Uint8Array|Array.)} */ + + this.data; + }; + + Zlib$1.GunzipMember.prototype.getName = function () { + return this.name; + }; + + Zlib$1.GunzipMember.prototype.getData = function () { + return this.data; + }; + + Zlib$1.GunzipMember.prototype.getMtime = function () { + return this.mtime; + }; + /** + * @fileoverview GZIP (RFC1952) รฅยฎลธรจยฃโ€ฆ. + */ + + /** + * @constructor + * @param {!(Array|Uint8Array)} input input buffer. + * @param {Object=} opt_params option parameters. + */ + + + Zlib$1.Gzip = function (input, opt_params) { + /** @type {!(Array.|Uint8Array)} input buffer. */ + this.input = input; + /** @type {number} input buffer pointer. */ + + this.ip = 0; + /** @type {!(Array.|Uint8Array)} output buffer. */ + + this.output; + /** @type {number} output buffer. */ + + this.op = 0; + /** @type {!Object} flags option flags. */ + + this.flags = {}; + /** @type {!string} filename. */ + + this.filename; + /** @type {!string} comment. */ + + this.comment; + /** @type {!Object} deflate options. */ + + this.deflateOptions; // option parameters + + if (opt_params) { + if (opt_params['flags']) { + this.flags = opt_params['flags']; + } + + if (typeof opt_params['filename'] === 'string') { + this.filename = opt_params['filename']; + } + + if (typeof opt_params['comment'] === 'string') { + this.comment = opt_params['comment']; + } + + if (opt_params['deflateOptions']) { + this.deflateOptions = opt_params['deflateOptions']; + } + } + + if (!this.deflateOptions) { + this.deflateOptions = {}; + } + }; + /** + * @type {number} + * @const + */ + + + Zlib$1.Gzip.DefaultBufferSize = 0x8000; + /** + * encode gzip members. + * @return {!(Array|Uint8Array)} gzip binary array. + */ + + Zlib$1.Gzip.prototype.compress = function () { + /** @type {number} flags. */ + var flg; + /** @type {number} modification time. */ + + var mtime; + /** @type {number} CRC-16 value for FHCRC flag. */ + + var crc16; + /** @type {number} CRC-32 value for verification. */ + + var crc32; + /** @type {!Zlib.RawDeflate} raw deflate object. */ + + var rawdeflate; + /** @type {number} character code */ + + var c; + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limiter. */ + + var il; + /** @type {!(Array|Uint8Array)} output buffer. */ + + var output = new (Uint8Array )(Zlib$1.Gzip.DefaultBufferSize); + /** @type {number} output buffer pointer. */ + + var op = 0; + var input = this.input; + var ip = this.ip; + var filename = this.filename; + var comment = this.comment; // check signature + + output[op++] = 0x1f; + output[op++] = 0x8b; // check compression method + + output[op++] = 8; + /* XXX: use Zlib const */ + // flags + + flg = 0; + if (this.flags['fname']) flg |= Zlib$1.Gzip.FlagsMask.FNAME; + if (this.flags['fcomment']) flg |= Zlib$1.Gzip.FlagsMask.FCOMMENT; + if (this.flags['fhcrc']) flg |= Zlib$1.Gzip.FlagsMask.FHCRC; // XXX: FTEXT + // XXX: FEXTRA + + output[op++] = flg; // modification time + + mtime = (Date.now ? Date.now() : +new Date()) / 1000 | 0; + output[op++] = mtime & 0xff; + output[op++] = mtime >>> 8 & 0xff; + output[op++] = mtime >>> 16 & 0xff; + output[op++] = mtime >>> 24 & 0xff; // extra flags + + output[op++] = 0; // operating system + + output[op++] = Zlib$1.Gzip.OperatingSystem.UNKNOWN; // extra + + /* NOP */ + // fname + + if (this.flags['fname'] !== void 0) { + for (i = 0, il = filename.length; i < il; ++i) { + c = filename.charCodeAt(i); + + if (c > 0xff) { + output[op++] = c >>> 8 & 0xff; + } + + output[op++] = c & 0xff; + } + + output[op++] = 0; // null termination + } // fcomment + + + if (this.flags['comment']) { + for (i = 0, il = comment.length; i < il; ++i) { + c = comment.charCodeAt(i); + + if (c > 0xff) { + output[op++] = c >>> 8 & 0xff; + } + + output[op++] = c & 0xff; + } + + output[op++] = 0; // null termination + } // fhcrc + + + if (this.flags['fhcrc']) { + crc16 = Zlib$1.CRC32.calc(output, 0, op) & 0xffff; + output[op++] = crc16 & 0xff; + output[op++] = crc16 >>> 8 & 0xff; + } // add compress option + + + this.deflateOptions['outputBuffer'] = output; + this.deflateOptions['outputIndex'] = op; // compress + + rawdeflate = new Zlib$1.RawDeflate(input, this.deflateOptions); + output = rawdeflate.compress(); + op = rawdeflate.op; // expand buffer + + { + if (op + 8 > output.buffer.byteLength) { + this.output = new Uint8Array(op + 8); + this.output.set(new Uint8Array(output.buffer)); + output = this.output; + } else { + output = new Uint8Array(output.buffer); + } + } // crc32 + + + crc32 = Zlib$1.CRC32.calc(input); + output[op++] = crc32 & 0xff; + output[op++] = crc32 >>> 8 & 0xff; + output[op++] = crc32 >>> 16 & 0xff; + output[op++] = crc32 >>> 24 & 0xff; // input size + + il = input.length; + output[op++] = il & 0xff; + output[op++] = il >>> 8 & 0xff; + output[op++] = il >>> 16 & 0xff; + output[op++] = il >>> 24 & 0xff; + this.ip = ip; + + if (op < output.length) { + this.output = output = output.subarray(0, op); + } + + return output; + }; + /** @enum {number} */ + + + Zlib$1.Gzip.OperatingSystem = { + FAT: 0, + AMIGA: 1, + VMS: 2, + UNIX: 3, + VM_CMS: 4, + ATARI_TOS: 5, + HPFS: 6, + MACINTOSH: 7, + Z_SYSTEM: 8, + CP_M: 9, + TOPS_20: 10, + NTFS: 11, + QDOS: 12, + ACORN_RISCOS: 13, + UNKNOWN: 255 + }; + /** @enum {number} */ + + Zlib$1.Gzip.FlagsMask = { + FTEXT: 0x01, + FHCRC: 0x02, + FEXTRA: 0x04, + FNAME: 0x08, + FCOMMENT: 0x10 + }; + /** + * @fileoverview Heap Sort รฅยฎลธรจยฃโ€ฆ. รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“รฃยยงรคยฝยฟรงโ€ยจรฃยโ„ขรฃโ€šโ€น. + */ + + /** + * รฃโ€šยซรฃโ€šยนรฃโ€šยฟรฃฦ’ รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยงรคยฝยฟรงโ€ยจรฃยโ„ขรฃโ€šโ€นรฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฅยฎลธรจยฃโ€ฆ + * @param {number} length รฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฃโ€šยตรฃโ€šยครฃโ€šยบ. + * @constructor + */ + + Zlib$1.Heap = function (length) { + this.buffer = new (Uint16Array )(length * 2); + this.length = 0; + }; + /** + * รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index รฅยโ€“รฅยพโ€” + * @param {number} index รฅยญยรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index. + * @return {number} รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index. + * + */ + + + Zlib$1.Heap.prototype.getParent = function (index) { + return ((index - 2) / 4 | 0) * 2; + }; + /** + * รฅยญยรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index รฅยโ€“รฅยพโ€” + * @param {number} index รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index. + * @return {number} รฅยญยรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index. + */ + + + Zlib$1.Heap.prototype.getChild = function (index) { + return 2 * index + 2; + }; + /** + * Heap รฃยยซรฅโ‚ฌยครฃโ€šโ€™รจยฟยฝรฅล  รฃยโ„ขรฃโ€šโ€น + * @param {number} index รฃโ€šยญรฃฦ’ยผ index. + * @param {number} value รฅโ‚ฌยค. + * @return {number} รงยยพรฅล“ยจรฃยยฎรฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฉโ€ขยท. + */ + + + Zlib$1.Heap.prototype.push = function (index, value) { + var current, + parent, + heap = this.buffer, + swap; + current = this.length; + heap[this.length++] = value; + heap[this.length++] = index; // รฃฦ’ยซรฃฦ’ยผรฃฦ’ห†รฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยซรฃยลธรฃยยฉรฃโ€šล รงยโ‚ฌรฃยยรฃยยพรฃยยงรฅโ€ฆยฅรฃโ€šล’รฆโ€บยฟรฃยห†รฃโ€šโ€™รจยฉยฆรฃยยฟรฃโ€šโ€น + + while (current > 0) { + parent = this.getParent(current); // รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยจรฆยฏโ€รจยผฦ’รฃยโ€”รฃยยฆรจยฆยชรฃยยฎรฆโ€“ยนรฃยล’รฅยฐยรฃยโ€ขรฃยโ€˜รฃโ€šล’รฃยยฐรฅโ€ฆยฅรฃโ€šล’รฆโ€บยฟรฃยห†รฃโ€šโ€น + + if (heap[current] > heap[parent]) { + swap = heap[current]; + heap[current] = heap[parent]; + heap[parent] = swap; + swap = heap[current + 1]; + heap[current + 1] = heap[parent + 1]; + heap[parent + 1] = swap; + current = parent; // รฅโ€ฆยฅรฃโ€šล’รฆโ€บยฟรฃยห†รฃยล’รฅยฟโ€ฆรจยฆยรฃยยชรฃยยรฃยยชรฃยยฃรฃยลธรฃโ€šโ€ฐรฃยยรฃยโ€œรฃยยงรฆล ล“รฃยโ€˜รฃโ€šโ€น + } else { + break; + } + } + + return this.length; + }; + /** + * Heapรฃยโ€นรฃโ€šโ€ฐรคยธโ‚ฌรงโ€ขยชรฅยคยงรฃยยรฃยโ€žรฅโ‚ฌยครฃโ€šโ€™รจยฟโ€รฃยโ„ข + * @return {{index: number, value: number, length: number}} {index: รฃโ€šยญรฃฦ’ยผindex, + * value: รฅโ‚ฌยค, length: รฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฉโ€ขยท} รฃยยฎ Object. + */ + + + Zlib$1.Heap.prototype.pop = function () { + var index, + value, + heap = this.buffer, + swap, + current, + parent; + value = heap[0]; + index = heap[1]; // รฅยพล’รฃโ€šยรฃยโ€นรฃโ€šโ€ฐรฅโ‚ฌยครฃโ€šโ€™รฅยโ€“รฃโ€šโ€น + + this.length -= 2; + heap[0] = heap[this.length]; + heap[1] = heap[this.length + 1]; + parent = 0; // รฃฦ’ยซรฃฦ’ยผรฃฦ’ห†รฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยโ€นรฃโ€šโ€ฐรคยธโ€นรฃยล’รฃยยฃรฃยยฆรฃยโ€žรฃยย + + while (true) { + current = this.getChild(parent); // รงยฏโ€žรฅโ€บยฒรฃฦ’ยรฃโ€šยงรฃฦ’ฦ’รฃโ€šยฏ + + if (current >= this.length) { + break; + } // รฉลกยฃรฃยยฎรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยจรฆยฏโ€รจยผฦ’รฃยโ€”รฃยยฆรฃโ‚ฌยรฉลกยฃรฃยยฎรฆโ€“ยนรฃยล’รฅโ‚ฌยครฃยล’รฅยคยงรฃยยรฃยโ€˜รฃโ€šล’รฃยยฐรฉลกยฃรฃโ€šโ€™รงยยพรฅล“ยจรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยจรฃยโ€”รฃยยฆรฉยยธรฆล ลพ + + + if (current + 2 < this.length && heap[current + 2] > heap[current]) { + current += 2; + } // รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยจรฆยฏโ€รจยผฦ’รฃยโ€”รฃยยฆรจยฆยชรฃยยฎรฆโ€“ยนรฃยล’รฅยฐยรฃยโ€ขรฃยโ€žรฅ ยดรฅยห†รฃยยฏรฅโ€ฆยฅรฃโ€šล’รฆโ€บยฟรฃยห†รฃโ€šโ€น + + + if (heap[current] > heap[parent]) { + swap = heap[parent]; + heap[parent] = heap[current]; + heap[current] = swap; + swap = heap[parent + 1]; + heap[parent + 1] = heap[current + 1]; + heap[current + 1] = swap; + } else { + break; + } + + parent = current; + } + + return { + index: index, + value: value, + length: this.length + }; + }; + /* vim:set expandtab ts=2 sw=2 tw=80: */ + + /** + * @fileoverview Deflate (RFC1951) รงยฌยฆรฅยยทรฅล’โ€“รฃโ€šยขรฃฦ’ยซรฃโ€šยดรฃฦ’ยชรฃโ€šยบรฃฦ’ รฅยฎลธรจยฃโ€ฆ. + */ + + /** + * Raw Deflate รฅยฎลธรจยฃโ€ฆ + * + * @constructor + * @param {!(Array.|Uint8Array)} input รงยฌยฆรฅยยทรฅล’โ€“รฃยโ„ขรฃโ€šโ€นรฅยฏยพรจยฑยกรฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยก. + * @param {Object=} opt_params option parameters. + * + * typed array รฃยล’รคยฝยฟรงโ€ยจรฅยยฏรจฦ’ยฝรฃยยชรฃยยจรฃยยรฃโ‚ฌยoutputBuffer รฃยล’ Array รฃยยฏรจโ€กยชรฅโ€นโ€ขรงลกโ€žรฃยยซ Uint8Array รฃยยซ + * รฅยคโ€ฐรฆยโ€บรฃยโ€ขรฃโ€šล’รฃยยพรฃยโ„ข. + * รฅห†ยฅรฃยยฎรฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†รฃยยซรฃยยชรฃโ€šโ€นรฃยลธรฃโ€šยรฅโ€กยบรฅล โ€บรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šโ€™รฅยโ€šรงโ€ฆยงรฃยโ€”รฃยยฆรฃยโ€žรฃโ€šโ€นรฅยคโ€ฐรฆโ€ขยฐรฃยยชรฃยยฉรฃยยฏ + * รฆโ€บยดรฆโ€“ยฐรฃยโ„ขรฃโ€šโ€นรฅยฟโ€ฆรจยฆยรฃยล’รฃยโ€šรฃโ€šล รฃยยพรฃยโ„ข. + */ + + + Zlib$1.RawDeflate = function (input, opt_params) { + /** @type {Zlib.RawDeflate.CompressionType} */ + this.compressionType = Zlib$1.RawDeflate.CompressionType.DYNAMIC; + /** @type {number} */ + + this.lazy = 0; + /** @type {!(Array.|Uint32Array)} */ + + this.freqsLitLen; + /** @type {!(Array.|Uint32Array)} */ + + this.freqsDist; + /** @type {!(Array.|Uint8Array)} */ + + this.input = input instanceof Array ? new Uint8Array(input) : input; + /** @type {!(Array.|Uint8Array)} output output buffer. */ + + this.output; + /** @type {number} pos output buffer position. */ + + this.op = 0; // option parameters + + if (opt_params) { + if (opt_params['lazy']) { + this.lazy = opt_params['lazy']; + } + + if (typeof opt_params['compressionType'] === 'number') { + this.compressionType = opt_params['compressionType']; + } + + if (opt_params['outputBuffer']) { + this.output = opt_params['outputBuffer'] instanceof Array ? new Uint8Array(opt_params['outputBuffer']) : opt_params['outputBuffer']; + } + + if (typeof opt_params['outputIndex'] === 'number') { + this.op = opt_params['outputIndex']; + } + } + + if (!this.output) { + this.output = new (Uint8Array )(0x8000); + } + }; + /** + * @enum {number} + */ + + + Zlib$1.RawDeflate.CompressionType = { + NONE: 0, + FIXED: 1, + DYNAMIC: 2, + RESERVED: 3 + }; + /** + * LZ77 รฃยยฎรฆล“โ‚ฌรฅยฐยรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฉโ€ขยท + * @const + * @type {number} + */ + + Zlib$1.RawDeflate.Lz77MinLength = 3; + /** + * LZ77 รฃยยฎรฆล“โ‚ฌรฅยคยงรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฉโ€ขยท + * @const + * @type {number} + */ + + Zlib$1.RawDeflate.Lz77MaxLength = 258; + /** + * LZ77 รฃยยฎรฃโ€šยฆรฃโ€šยฃรฃฦ’ยณรฃฦ’โ€ฐรฃโ€šยฆรฃโ€šยตรฃโ€šยครฃโ€šยบ + * @const + * @type {number} + */ + + Zlib$1.RawDeflate.WindowSize = 0x8000; + /** + * รฆล“โ‚ฌรฉโ€ขยทรฃยยฎรงยฌยฆรฅยยทรฉโ€ขยท + * @const + * @type {number} + */ + + Zlib$1.RawDeflate.MaxCodeLength = 16; + /** + * รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยฎรฆล“โ‚ฌรฅยคยงรฆโ€ขยฐรฅโ‚ฌยค + * @const + * @type {number} + */ + + Zlib$1.RawDeflate.HUFMAX = 286; + /** + * รฅโ€บยบรฅยฎลกรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยฎรงยฌยฆรฅยยทรฅล’โ€“รฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ + * @const + * @type {Array.>} + */ + + Zlib$1.RawDeflate.FixedHuffmanTable = function () { + var table = [], + i; + + for (i = 0; i < 288; i++) { + switch (true) { + case i <= 143: + table.push([i + 0x030, 8]); + break; + + case i <= 255: + table.push([i - 144 + 0x190, 9]); + break; + + case i <= 279: + table.push([i - 256 + 0x000, 7]); + break; + + case i <= 287: + table.push([i - 280 + 0x0C0, 8]); + break; + + default: + throw 'invalid literal: ' + i; + } + } + + return table; + }(); + /** + * DEFLATE รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยฎรคยฝล“รฆห†ย + * @return {!(Array.|Uint8Array)} รฅล“ยงรงยธยฎรฆยธห†รฃยยฟ byte array. + */ + + + Zlib$1.RawDeflate.prototype.compress = function () { + /** @type {!(Array.|Uint8Array)} */ + var blockArray; + /** @type {number} */ + + var position; + /** @type {number} */ + + var length; + var input = this.input; // compression + + switch (this.compressionType) { + case Zlib$1.RawDeflate.CompressionType.NONE: + // each 65535-Byte (length header: 16-bit) + for (position = 0, length = input.length; position < length;) { + blockArray = input.subarray(position, position + 0xffff) ; + position += blockArray.length; + this.makeNocompressBlock(blockArray, position === length); + } + + break; + + case Zlib$1.RawDeflate.CompressionType.FIXED: + this.output = this.makeFixedHuffmanBlock(input, true); + this.op = this.output.length; + break; + + case Zlib$1.RawDeflate.CompressionType.DYNAMIC: + this.output = this.makeDynamicHuffmanBlock(input, true); + this.op = this.output.length; + break; + + default: + throw 'invalid compression type'; + } + + return this.output; + }; + /** + * รฉยลพรฅล“ยงรงยธยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยฎรคยฝล“รฆห†ย + * @param {!(Array.|Uint8Array)} blockArray รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟ byte array. + * @param {!boolean} isFinalBlock รฆล“โ‚ฌรฅยพล’รฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยชรฃโ€šโ€ฐรฃยยฐtrue. + * @return {!(Array.|Uint8Array)} รฉยลพรฅล“ยงรงยธยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏ byte array. + */ + + + Zlib$1.RawDeflate.prototype.makeNocompressBlock = function (blockArray, isFinalBlock) { + /** @type {number} */ + var bfinal; + /** @type {Zlib.RawDeflate.CompressionType} */ + + var btype; + /** @type {number} */ + + var len; + /** @type {number} */ + + var nlen; + var output = this.output; + var op = this.op; // expand buffer + + { + output = new Uint8Array(this.output.buffer); + + while (output.length <= op + blockArray.length + 5) { + output = new Uint8Array(output.length << 1); + } + + output.set(this.output); + } // header + + + bfinal = isFinalBlock ? 1 : 0; + btype = Zlib$1.RawDeflate.CompressionType.NONE; + output[op++] = bfinal | btype << 1; // length + + len = blockArray.length; + nlen = ~len + 0x10000 & 0xffff; + output[op++] = len & 0xff; + output[op++] = len >>> 8 & 0xff; + output[op++] = nlen & 0xff; + output[op++] = nlen >>> 8 & 0xff; // copy buffer + + { + output.set(blockArray, op); + op += blockArray.length; + output = output.subarray(0, op); + } + + this.op = op; + this.output = output; + return output; + }; + /** + * รฅโ€บยบรฅยฎลกรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยฎรคยฝล“รฆห†ย + * @param {!(Array.|Uint8Array)} blockArray รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟ byte array. + * @param {!boolean} isFinalBlock รฆล“โ‚ฌรฅยพล’รฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยชรฃโ€šโ€ฐรฃยยฐtrue. + * @return {!(Array.|Uint8Array)} รฅโ€บยบรฅยฎลกรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏ byte array. + */ + + + Zlib$1.RawDeflate.prototype.makeFixedHuffmanBlock = function (blockArray, isFinalBlock) { + /** @type {Zlib.BitStream} */ + var stream = new Zlib$1.BitStream(new Uint8Array(this.output.buffer) , this.op); + /** @type {number} */ + + var bfinal; + /** @type {Zlib.RawDeflate.CompressionType} */ + + var btype; + /** @type {!(Array.|Uint16Array)} */ + + var data; // header + + bfinal = isFinalBlock ? 1 : 0; + btype = Zlib$1.RawDeflate.CompressionType.FIXED; + stream.writeBits(bfinal, 1, true); + stream.writeBits(btype, 2, true); + data = this.lz77(blockArray); + this.fixedHuffman(data, stream); + return stream.finish(); + }; + /** + * รฅโ€นโ€ขรงลกโ€žรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยฎรคยฝล“รฆห†ย + * @param {!(Array.|Uint8Array)} blockArray รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟ byte array. + * @param {!boolean} isFinalBlock รฆล“โ‚ฌรฅยพล’รฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยชรฃโ€šโ€ฐรฃยยฐtrue. + * @return {!(Array.|Uint8Array)} รฅโ€นโ€ขรงลกโ€žรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏ byte array. + */ + + + Zlib$1.RawDeflate.prototype.makeDynamicHuffmanBlock = function (blockArray, isFinalBlock) { + /** @type {Zlib.BitStream} */ + var stream = new Zlib$1.BitStream(new Uint8Array(this.output.buffer) , this.op); + /** @type {number} */ + + var bfinal; + /** @type {Zlib.RawDeflate.CompressionType} */ + + var btype; + /** @type {!(Array.|Uint16Array)} */ + + var data; + /** @type {number} */ + + var hlit; + /** @type {number} */ + + var hdist; + /** @type {number} */ + + var hclen; + /** @const @type {Array.} */ + + var hclenOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + /** @type {!(Array.|Uint8Array)} */ + + var litLenLengths; + /** @type {!(Array.|Uint16Array)} */ + + var litLenCodes; + /** @type {!(Array.|Uint8Array)} */ + + var distLengths; + /** @type {!(Array.|Uint16Array)} */ + + var distCodes; + /** @type {{ + * codes: !(Array.|Uint32Array), + * freqs: !(Array.|Uint8Array) + * }} */ + + var treeSymbols; + /** @type {!(Array.|Uint8Array)} */ + + var treeLengths; + /** @type {Array} */ + + var transLengths = new Array(19); + /** @type {!(Array.|Uint16Array)} */ + + var treeCodes; + /** @type {number} */ + + var code; + /** @type {number} */ + + var bitlen; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; // header + + bfinal = isFinalBlock ? 1 : 0; + btype = Zlib$1.RawDeflate.CompressionType.DYNAMIC; + stream.writeBits(bfinal, 1, true); + stream.writeBits(btype, 2, true); + data = this.lz77(blockArray); // รฃฦ’ยชรฃฦ’โ€ รฃฦ’ยฉรฃฦ’ยซรฃฦ’ยปรฉโ€ขยทรฃยโ€ข, รจยทยรฉโ€บยขรฃยยฎรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยจรงยฌยฆรฅยยทรฉโ€ขยทรฃยยฎรงยฎโ€”รฅโ€กยบ + + litLenLengths = this.getLengths_(this.freqsLitLen, 15); + litLenCodes = this.getCodesFromLengths_(litLenLengths); + distLengths = this.getLengths_(this.freqsDist, 7); + distCodes = this.getCodesFromLengths_(distLengths); // HLIT, HDIST รฃยยฎรฆยฑยบรฅยฎลก + + for (hlit = 286; hlit > 257 && litLenLengths[hlit - 1] === 0; hlit--) {} + + for (hdist = 30; hdist > 1 && distLengths[hdist - 1] === 0; hdist--) {} // HCLEN + + + treeSymbols = this.getTreeSymbols_(hlit, litLenLengths, hdist, distLengths); + treeLengths = this.getLengths_(treeSymbols.freqs, 7); + + for (i = 0; i < 19; i++) { + transLengths[i] = treeLengths[hclenOrder[i]]; + } + + for (hclen = 19; hclen > 4 && transLengths[hclen - 1] === 0; hclen--) {} + + treeCodes = this.getCodesFromLengths_(treeLengths); // รฅโ€กยบรฅล โ€บ + + stream.writeBits(hlit - 257, 5, true); + stream.writeBits(hdist - 1, 5, true); + stream.writeBits(hclen - 4, 4, true); + + for (i = 0; i < hclen; i++) { + stream.writeBits(transLengths[i], 3, true); + } // รฃฦ’โ€žรฃฦ’ยชรฃฦ’ยผรฃยยฎรฅโ€กยบรฅล โ€บ + + + for (i = 0, il = treeSymbols.codes.length; i < il; i++) { + code = treeSymbols.codes[i]; + stream.writeBits(treeCodes[code], treeLengths[code], true); // extra bits + + if (code >= 16) { + i++; + + switch (code) { + case 16: + bitlen = 2; + break; + + case 17: + bitlen = 3; + break; + + case 18: + bitlen = 7; + break; + + default: + throw 'invalid code: ' + code; + } + + stream.writeBits(treeSymbols.codes[i], bitlen, true); + } + } + + this.dynamicHuffman(data, [litLenCodes, litLenLengths], [distCodes, distLengths], stream); + return stream.finish(); + }; + /** + * รฅโ€นโ€ขรงลกโ€žรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“(รฃโ€šยซรฃโ€šยนรฃโ€šยฟรฃฦ’ รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ) + * @param {!(Array.|Uint16Array)} dataArray LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฆยธห†รฃยยฟ byte array. + * @param {!Zlib.BitStream} stream รฆโ€บยธรฃยยรจยพยผรฃยยฟรงโ€ยจรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ . + * @return {!Zlib.BitStream} รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“รฆยธห†รฃยยฟรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ รฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†. + */ + + + Zlib$1.RawDeflate.prototype.dynamicHuffman = function (dataArray, litLen, dist, stream) { + /** @type {number} */ + var index; + /** @type {number} */ + + var length; + /** @type {number} */ + + var literal; + /** @type {number} */ + + var code; + /** @type {number} */ + + var litLenCodes; + /** @type {number} */ + + var litLenLengths; + /** @type {number} */ + + var distCodes; + /** @type {number} */ + + var distLengths; + litLenCodes = litLen[0]; + litLenLengths = litLen[1]; + distCodes = dist[0]; + distLengths = dist[1]; // รงยฌยฆรฅยยทรฃโ€šโ€™ BitStream รฃยยซรฆโ€บยธรฃยยรจยพยผรฃโ€šโ€œรฃยยงรฃยโ€žรฃยย + + for (index = 0, length = dataArray.length; index < length; ++index) { + literal = dataArray[index]; // literal or length + + stream.writeBits(litLenCodes[literal], litLenLengths[literal], true); // รฉโ€ขยทรฃยโ€ขรฃฦ’ยปรจยทยรฉโ€บยขรงยฌยฆรฅยยท + + if (literal > 256) { + // length extra + stream.writeBits(dataArray[++index], dataArray[++index], true); // distance + + code = dataArray[++index]; + stream.writeBits(distCodes[code], distLengths[code], true); // distance extra + + stream.writeBits(dataArray[++index], dataArray[++index], true); // รงยตโ€šรงยซยฏ + } else if (literal === 256) { + break; + } + } + + return stream; + }; + /** + * รฅโ€บยบรฅยฎลกรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“ + * @param {!(Array.|Uint16Array)} dataArray LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฆยธห†รฃยยฟ byte array. + * @param {!Zlib.BitStream} stream รฆโ€บยธรฃยยรจยพยผรฃยยฟรงโ€ยจรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ . + * @return {!Zlib.BitStream} รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“รฆยธห†รฃยยฟรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ รฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†. + */ + + + Zlib$1.RawDeflate.prototype.fixedHuffman = function (dataArray, stream) { + /** @type {number} */ + var index; + /** @type {number} */ + + var length; + /** @type {number} */ + + var literal; // รงยฌยฆรฅยยทรฃโ€šโ€™ BitStream รฃยยซรฆโ€บยธรฃยยรจยพยผรฃโ€šโ€œรฃยยงรฃยโ€žรฃยย + + for (index = 0, length = dataArray.length; index < length; index++) { + literal = dataArray[index]; // รงยฌยฆรฅยยทรฃยยฎรฆโ€บยธรฃยยรจยพยผรฃยยฟ + + Zlib$1.BitStream.prototype.writeBits.apply(stream, Zlib$1.RawDeflate.FixedHuffmanTable[literal]); // รฉโ€ขยทรฃยโ€ขรฃฦ’ยปรจยทยรฉโ€บยขรงยฌยฆรฅยยท + + if (literal > 0x100) { + // length extra + stream.writeBits(dataArray[++index], dataArray[++index], true); // distance + + stream.writeBits(dataArray[++index], 5); // distance extra + + stream.writeBits(dataArray[++index], dataArray[++index], true); // รงยตโ€šรงยซยฏ + } else if (literal === 0x100) { + break; + } + } + + return stream; + }; + /** + * รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฆฦ’โ€ฆรฅ ยฑ + * @param {!number} length รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยโ€”รฃยลธรฉโ€ขยทรฃยโ€ข. + * @param {!number} backwardDistance รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรคยฝยรงยฝยฎรฃยยจรฃยยฎรจยทยรฉโ€บยข. + * @constructor + */ + + + Zlib$1.RawDeflate.Lz77Match = function (length, backwardDistance) { + /** @type {number} match length. */ + this.length = length; + /** @type {number} backward distance. */ + + this.backwardDistance = backwardDistance; + }; + /** + * รฉโ€ขยทรฃยโ€ขรงยฌยฆรฅยยทรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ. + * [รฃโ€šยณรฃฦ’ยผรฃฦ’โ€ฐ, รฆโ€นยกรฅยผยตรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†, รฆโ€นยกรฅยผยตรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยท] รฃยยฎรฉโ€ฆยรฅห†โ€”รฃยยจรฃยยชรฃยยฃรฃยยฆรฃยโ€žรฃโ€šโ€น. + * @const + * @type {!(Array.|Uint32Array)} + */ + + + Zlib$1.RawDeflate.Lz77Match.LengthCodeTable = function (table) { + return new Uint32Array(table) ; + }(function () { + /** @type {!Array} */ + var table = []; + /** @type {number} */ + + var i; + /** @type {!Array.} */ + + var c; + + for (i = 3; i <= 258; i++) { + c = code(i); + table[i] = c[2] << 24 | c[1] << 16 | c[0]; + } + /** + * @param {number} length lz77 length. + * @return {!Array.} lz77 codes. + */ + + + function code(length) { + switch (true) { + case length === 3: + return [257, length - 3, 0]; + + case length === 4: + return [258, length - 4, 0]; + + case length === 5: + return [259, length - 5, 0]; + + case length === 6: + return [260, length - 6, 0]; + + case length === 7: + return [261, length - 7, 0]; + + case length === 8: + return [262, length - 8, 0]; + + case length === 9: + return [263, length - 9, 0]; + + case length === 10: + return [264, length - 10, 0]; + + case length <= 12: + return [265, length - 11, 1]; + + case length <= 14: + return [266, length - 13, 1]; + + case length <= 16: + return [267, length - 15, 1]; + + case length <= 18: + return [268, length - 17, 1]; + + case length <= 22: + return [269, length - 19, 2]; + + case length <= 26: + return [270, length - 23, 2]; + + case length <= 30: + return [271, length - 27, 2]; + + case length <= 34: + return [272, length - 31, 2]; + + case length <= 42: + return [273, length - 35, 3]; + + case length <= 50: + return [274, length - 43, 3]; + + case length <= 58: + return [275, length - 51, 3]; + + case length <= 66: + return [276, length - 59, 3]; + + case length <= 82: + return [277, length - 67, 4]; + + case length <= 98: + return [278, length - 83, 4]; + + case length <= 114: + return [279, length - 99, 4]; + + case length <= 130: + return [280, length - 115, 4]; + + case length <= 162: + return [281, length - 131, 5]; + + case length <= 194: + return [282, length - 163, 5]; + + case length <= 226: + return [283, length - 195, 5]; + + case length <= 257: + return [284, length - 227, 5]; + + case length === 258: + return [285, length - 258, 0]; + + default: + throw 'invalid length: ' + length; + } + } + + return table; + }()); + /** + * รจยทยรฉโ€บยขรงยฌยฆรฅยยทรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ + * @param {!number} dist รจยทยรฉโ€บยข. + * @return {!Array.} รฃโ€šยณรฃฦ’ยผรฃฦ’โ€ฐรฃโ‚ฌยรฆโ€นยกรฅยผยตรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ‚ฌยรฆโ€นยกรฅยผยตรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฃยยฎรฉโ€ฆยรฅห†โ€”. + * @private + */ + + + Zlib$1.RawDeflate.Lz77Match.prototype.getDistanceCode_ = function (dist) { + /** @type {!Array.} distance code table. */ + var r; + + switch (true) { + case dist === 1: + r = [0, dist - 1, 0]; + break; + + case dist === 2: + r = [1, dist - 2, 0]; + break; + + case dist === 3: + r = [2, dist - 3, 0]; + break; + + case dist === 4: + r = [3, dist - 4, 0]; + break; + + case dist <= 6: + r = [4, dist - 5, 1]; + break; + + case dist <= 8: + r = [5, dist - 7, 1]; + break; + + case dist <= 12: + r = [6, dist - 9, 2]; + break; + + case dist <= 16: + r = [7, dist - 13, 2]; + break; + + case dist <= 24: + r = [8, dist - 17, 3]; + break; + + case dist <= 32: + r = [9, dist - 25, 3]; + break; + + case dist <= 48: + r = [10, dist - 33, 4]; + break; + + case dist <= 64: + r = [11, dist - 49, 4]; + break; + + case dist <= 96: + r = [12, dist - 65, 5]; + break; + + case dist <= 128: + r = [13, dist - 97, 5]; + break; + + case dist <= 192: + r = [14, dist - 129, 6]; + break; + + case dist <= 256: + r = [15, dist - 193, 6]; + break; + + case dist <= 384: + r = [16, dist - 257, 7]; + break; + + case dist <= 512: + r = [17, dist - 385, 7]; + break; + + case dist <= 768: + r = [18, dist - 513, 8]; + break; + + case dist <= 1024: + r = [19, dist - 769, 8]; + break; + + case dist <= 1536: + r = [20, dist - 1025, 9]; + break; + + case dist <= 2048: + r = [21, dist - 1537, 9]; + break; + + case dist <= 3072: + r = [22, dist - 2049, 10]; + break; + + case dist <= 4096: + r = [23, dist - 3073, 10]; + break; + + case dist <= 6144: + r = [24, dist - 4097, 11]; + break; + + case dist <= 8192: + r = [25, dist - 6145, 11]; + break; + + case dist <= 12288: + r = [26, dist - 8193, 12]; + break; + + case dist <= 16384: + r = [27, dist - 12289, 12]; + break; + + case dist <= 24576: + r = [28, dist - 16385, 13]; + break; + + case dist <= 32768: + r = [29, dist - 24577, 13]; + break; + + default: + throw 'invalid distance'; + } + + return r; + }; + /** + * รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฆฦ’โ€ฆรฅ ยฑรฃโ€šโ€™ LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฉโ€ฆยรฅห†โ€”รฃยยงรจยฟโ€รฃยโ„ข. + * รฃยยชรฃยล รฃโ‚ฌยรฃยโ€œรฃยโ€œรฃยยงรฃยยฏรคยปยฅรคยธโ€นรฃยยฎรฅโ€ โ€ฆรฉฦ’ยจรคยปโ€ขรฆยงหœรฃยยงรงยฌยฆรฅยยทรฅล’โ€“รฃยโ€”รฃยยฆรฃยโ€žรฃโ€šโ€น + * [ CODE, EXTRA-BIT-LEN, EXTRA, CODE, EXTRA-BIT-LEN, EXTRA ] + * @return {!Array.} LZ77 รงยฌยฆรฅยยทรฅล’โ€“ byte array. + */ + + + Zlib$1.RawDeflate.Lz77Match.prototype.toLz77Array = function () { + /** @type {number} */ + var length = this.length; + /** @type {number} */ + + var dist = this.backwardDistance; + /** @type {Array} */ + + var codeArray = []; + /** @type {number} */ + + var pos = 0; + /** @type {!Array.} */ + + var code; // length + + code = Zlib$1.RawDeflate.Lz77Match.LengthCodeTable[length]; + codeArray[pos++] = code & 0xffff; + codeArray[pos++] = code >> 16 & 0xff; + codeArray[pos++] = code >> 24; // distance + + code = this.getDistanceCode_(dist); + codeArray[pos++] = code[0]; + codeArray[pos++] = code[1]; + codeArray[pos++] = code[2]; + return codeArray; + }; + /** + * LZ77 รฅยฎลธรจยฃโ€ฆ + * @param {!(Array.|Uint8Array)} dataArray LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฃยโ„ขรฃโ€šโ€นรฃฦ’ยรฃโ€šยครฃฦ’ห†รฉโ€ฆยรฅห†โ€”. + * @return {!(Array.|Uint16Array)} LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฃยโ€”รฃยลธรฉโ€ฆยรฅห†โ€”. + */ + + + Zlib$1.RawDeflate.prototype.lz77 = function (dataArray) { + /** @type {number} input position */ + var position; + /** @type {number} input length */ + + var length; + /** @type {number} loop counter */ + + var i; + /** @type {number} loop limiter */ + + var il; + /** @type {number} chained-hash-table key */ + + var matchKey; + /** @type {Object.>} chained-hash-table */ + + var table = {}; + /** @const @type {number} */ + + var windowSize = Zlib$1.RawDeflate.WindowSize; + /** @type {Array.} match list */ + + var matchList; + /** @type {Zlib.RawDeflate.Lz77Match} longest match */ + + var longestMatch; + /** @type {Zlib.RawDeflate.Lz77Match} previous longest match */ + + var prevMatch; + /** @type {!(Array.|Uint16Array)} lz77 buffer */ + + var lz77buf = new Uint16Array(dataArray.length * 2) ; + /** @type {number} lz77 output buffer pointer */ + + var pos = 0; + /** @type {number} lz77 skip length */ + + var skipLength = 0; + /** @type {!(Array.|Uint32Array)} */ + + var freqsLitLen = new (Uint32Array )(286); + /** @type {!(Array.|Uint32Array)} */ + + var freqsDist = new (Uint32Array )(30); + /** @type {number} */ + + var lazy = this.lazy; + /** @type {*} temporary variable */ + + var tmp; // รฅห†ยรฆล“ลธรฅล’โ€“ + + freqsLitLen[256] = 1; // EOB รฃยยฎรฆล“โ‚ฌรคยฝลฝรฅโ€กยบรงยยพรฅโ€บลพรฆโ€ขยฐรฃยยฏ 1 + + /** + * รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟรฃยยฎรฆโ€บยธรฃยยรจยพยผรฃยยฟ + * @param {Zlib.RawDeflate.Lz77Match} match LZ77 Match data. + * @param {!number} offset รฃโ€šยนรฃโ€šยญรฃฦ’ฦ’รฃฦ’โ€”รฉโ€“โ€นรฅยงโ€นรคยฝยรงยฝยฎ(รงโ€บยธรฅยฏยพรฆล’โ€กรฅยฎลก). + * @private + */ + + function writeMatch(match, offset) { + /** @type {Array.} */ + var lz77Array = match.toLz77Array(); + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + + for (i = 0, il = lz77Array.length; i < il; ++i) { + lz77buf[pos++] = lz77Array[i]; + } + + freqsLitLen[lz77Array[0]]++; + freqsDist[lz77Array[3]]++; + skipLength = match.length + offset - 1; + prevMatch = null; + } // LZ77 รงยฌยฆรฅยยทรฅล’โ€“ + + + for (position = 0, length = dataArray.length; position < length; ++position) { + // รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฃโ€šยญรฃฦ’ยผรฃยยฎรคยฝล“รฆห†ย + for (matchKey = 0, i = 0, il = Zlib$1.RawDeflate.Lz77MinLength; i < il; ++i) { + if (position + i === length) { + break; + } + + matchKey = matchKey << 8 | dataArray[position + i]; + } // รฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃยล’รฆล“ยชรฅยฎลกรงยพยฉรฃย รฃยยฃรฃยลธรฃโ€šโ€ฐรคยฝล“รฆห†ยรฃยโ„ขรฃโ€šโ€น + + + if (table[matchKey] === void 0) { + table[matchKey] = []; + } + + matchList = table[matchKey]; // skip + + if (skipLength-- > 0) { + matchList.push(position); + continue; + } // รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃยยฎรฆโ€บยดรฆโ€“ยฐ (รฆล“โ‚ฌรฅยคยงรฆห†ยปรฃโ€šล รจยทยรฉโ€บยขรฃโ€šโ€™รจยถโ€ฆรฃยห†รฃยยฆรฃยโ€žรฃโ€šโ€นรฃโ€šโ€šรฃยยฎรฃโ€šโ€™รฅโ€ฐล รฉโ„ขยครฃยโ„ขรฃโ€šโ€น) + + + while (matchList.length > 0 && position - matchList[0] > windowSize) { + matchList.shift(); + } // รฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟรฆล“ยซรฅยฐยพรฃยยงรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยโ€”รฃโ€šห†รฃยโ€ รฃยล’รฃยยชรฃยโ€žรฅ ยดรฅยห†รฃยยฏรฃยยรฃยยฎรฃยยพรฃยยพรฆยตยรฃยโ€”รฃยโ€œรฃโ€šโ‚ฌ + + + if (position + Zlib$1.RawDeflate.Lz77MinLength >= length) { + if (prevMatch) { + writeMatch(prevMatch, -1); + } + + for (i = 0, il = length - position; i < il; ++i) { + tmp = dataArray[position + i]; + lz77buf[pos++] = tmp; + ++freqsLitLen[tmp]; + } + + break; + } // รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฅโ‚ฌโ„ขรจยฃล“รฃยโ€นรฃโ€šโ€ฐรฆล“โ‚ฌรฉโ€ขยทรฃยยฎรฃโ€šโ€šรฃยยฎรฃโ€šโ€™รฆลฝยขรฃยโ„ข + + + if (matchList.length > 0) { + longestMatch = this.searchLongestMatch_(dataArray, position, matchList); + + if (prevMatch) { + // รงยยพรฅล“ยจรฃยยฎรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยยฎรฆโ€“ยนรฃยล’รฅโ€ฐยรฅโ€บลพรฃยยฎรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃโ€šห†รฃโ€šล รฃโ€šโ€šรฉโ€ขยทรฃยโ€ž + if (prevMatch.length < longestMatch.length) { + // write previous literal + tmp = dataArray[position - 1]; + lz77buf[pos++] = tmp; + ++freqsLitLen[tmp]; // write current match + + writeMatch(longestMatch, 0); + } else { + // write previous match + writeMatch(prevMatch, -1); + } + } else if (longestMatch.length < lazy) { + prevMatch = longestMatch; + } else { + writeMatch(longestMatch, 0); + } // รฅโ€ฐยรฅโ€บลพรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยโ€”รฃยยฆรฃยโ€žรฃยยฆรคยปล รฅโ€บลพรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยล’รฃยยชรฃยโ€นรฃยยฃรฃยลธรฃโ€šโ€ฐรฅโ€ฐยรฅโ€บลพรฃยยฎรฃโ€šโ€™รฆลฝยกรงโ€ยจ + + } else if (prevMatch) { + writeMatch(prevMatch, -1); + } else { + tmp = dataArray[position]; + lz77buf[pos++] = tmp; + ++freqsLitLen[tmp]; + } + + matchList.push(position); // รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃยยซรงยยพรฅล“ยจรฃยยฎรคยฝยรงยฝยฎรฃโ€šโ€™รคยฟยรฅยญหœ + } // รงยตโ€šรงยซยฏรฅโ€กยฆรงยโ€  + + + lz77buf[pos++] = 256; + freqsLitLen[256]++; + this.freqsLitLen = freqsLitLen; + this.freqsDist = freqsDist; + return lz77buf.subarray(0, pos) ; + }; + /** + * รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยโ€”รฃยลธรฅโ‚ฌโ„ขรจยฃล“รฃยยฎรคยธยญรฃยโ€นรฃโ€šโ€ฐรฆล“โ‚ฌรฉโ€ขยทรคยธโ‚ฌรจโ€กยดรฃโ€šโ€™รฆลฝยขรฃยโ„ข + * @param {!Object} data plain data byte array. + * @param {!number} position plain data byte array position. + * @param {!Array.} matchList รฅโ‚ฌโ„ขรจยฃล“รฃยยจรฃยยชรฃโ€šโ€นรคยฝยรงยฝยฎรฃยยฎรฉโ€ฆยรฅห†โ€”. + * @return {!Zlib.RawDeflate.Lz77Match} รฆล“โ‚ฌรฉโ€ขยทรฃยโ€นรฃยยครฆล“โ‚ฌรงลธยญรจยทยรฉโ€บยขรฃยยฎรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†. + * @private + */ + + + Zlib$1.RawDeflate.prototype.searchLongestMatch_ = function (data, position, matchList) { + var match, + currentMatch, + matchMax = 0, + matchLength, + i, + j, + l, + dl = data.length; // รฅโ‚ฌโ„ขรจยฃล“รฃโ€šโ€™รฅยพล’รฃโ€šยรฃยโ€นรฃโ€šโ€ฐ 1 รฃยยครฃยลกรฃยยครงยตลพรฃโ€šล รจยพยผรฃโ€šโ€œรฃยยงรฃโ€šโ€ รฃยย + + permatch: for (i = 0, l = matchList.length; i < l; i++) { + match = matchList[l - i - 1]; + matchLength = Zlib$1.RawDeflate.Lz77MinLength; // รฅโ€ฐยรฅโ€บลพรฃยยพรฃยยงรฃยยฎรฆล“โ‚ฌรฉโ€ขยทรคยธโ‚ฌรจโ€กยดรฃโ€šโ€™รฆล“ยซรฅยฐยพรฃยโ€นรฃโ€šโ€ฐรคยธโ‚ฌรจโ€กยดรฆยคล“รงยดยขรฃยโ„ขรฃโ€šโ€น + + if (matchMax > Zlib$1.RawDeflate.Lz77MinLength) { + for (j = matchMax; j > Zlib$1.RawDeflate.Lz77MinLength; j--) { + if (data[match + j - 1] !== data[position + j - 1]) { + continue permatch; + } + } + + matchLength = matchMax; + } // รฆล“โ‚ฌรฉโ€ขยทรคยธโ‚ฌรจโ€กยดรฆลฝยขรงยดยข + + + while (matchLength < Zlib$1.RawDeflate.Lz77MaxLength && position + matchLength < dl && data[match + matchLength] === data[position + matchLength]) { + ++matchLength; + } // รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฉโ€ขยทรฃยล’รฅยล’รฃยหœรฅ ยดรฅยห†รฃยยฏรฅยพล’รฆโ€“ยนรฃโ€šโ€™รฅโ€žยชรฅโ€ฆห† + + + if (matchLength > matchMax) { + currentMatch = match; + matchMax = matchLength; + } // รฆล“โ‚ฌรฉโ€ขยทรฃยล’รงยขยบรฅยฎลกรฃยโ€”รฃยลธรฃโ€šโ€ฐรฅยพล’รฃยยฎรฅโ€กยฆรงยโ€ รฃยยฏรงล“ยรงโ€ขยฅ + + + if (matchLength === Zlib$1.RawDeflate.Lz77MaxLength) { + break; + } + } + + return new Zlib$1.RawDeflate.Lz77Match(matchMax, position - currentMatch); + }; + /** + * Tree-Transmit Symbols รฃยยฎรงยฎโ€”รฅโ€กยบ + * reference: PuTTY Deflate implementation + * @param {number} hlit HLIT. + * @param {!(Array.|Uint8Array)} litlenLengths รฃฦ’ยชรฃฦ’โ€ รฃฦ’ยฉรฃฦ’ยซรฃยยจรฉโ€ขยทรฃยโ€ขรงยฌยฆรฅยยทรฃยยฎรงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”. + * @param {number} hdist HDIST. + * @param {!(Array.|Uint8Array)} distLengths รจยทยรฉโ€บยขรงยฌยฆรฅยยทรฃยยฎรงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”. + * @return {{ + * codes: !(Array.|Uint32Array), + * freqs: !(Array.|Uint8Array) + * }} Tree-Transmit Symbols. + */ + + + Zlib$1.RawDeflate.prototype.getTreeSymbols_ = function (hlit, litlenLengths, hdist, distLengths) { + var src = new (Uint32Array )(hlit + hdist), + i, + j, + runLength, + l, + result = new (Uint32Array )(286 + 30), + nResult, + rpt, + freqs = new (Uint8Array )(19); + j = 0; + + for (i = 0; i < hlit; i++) { + src[j++] = litlenLengths[i]; + } + + for (i = 0; i < hdist; i++) { + src[j++] = distLengths[i]; + } // รฅห†ยรฆล“ลธรฅล’โ€“ + + + nResult = 0; + + for (i = 0, l = src.length; i < l; i += j) { + // Run Length Encoding + for (j = 1; i + j < l && src[i + j] === src[i]; ++j) {} + + runLength = j; + + if (src[i] === 0) { + // 0 รฃยยฎรงยนยฐรฃโ€šล รจยฟโ€รฃยโ€”รฃยล’ 3 รฅโ€บลพรฆล“ยชรฆยบโ‚ฌรฃยยชรฃโ€šโ€ฐรฃยยฐรฃยยรฃยยฎรฃยยพรฃยยพ + if (runLength < 3) { + while (runLength-- > 0) { + result[nResult++] = 0; + freqs[0]++; + } + } else { + while (runLength > 0) { + // รงยนยฐรฃโ€šล รจยฟโ€รฃยโ€”รฃยยฏรฆล“โ‚ฌรฅยคยง 138 รฃยยพรฃยยงรฃยยชรฃยยฎรฃยยงรฅห†โ€กรฃโ€šล รจยฉยฐรฃโ€šยรฃโ€šโ€น + rpt = runLength < 138 ? runLength : 138; + + if (rpt > runLength - 3 && rpt < runLength) { + rpt = runLength - 3; + } // 3-10 รฅโ€บลพ -> 17 + + + if (rpt <= 10) { + result[nResult++] = 17; + result[nResult++] = rpt - 3; + freqs[17]++; // 11-138 รฅโ€บลพ -> 18 + } else { + result[nResult++] = 18; + result[nResult++] = rpt - 11; + freqs[18]++; + } + + runLength -= rpt; + } + } + } else { + result[nResult++] = src[i]; + freqs[src[i]]++; + runLength--; // รงยนยฐรฃโ€šล รจยฟโ€รฃยโ€”รฅโ€บลพรฆโ€ขยฐรฃยล’3รฅโ€บลพรฆล“ยชรฆยบโ‚ฌรฃยยชรฃโ€šโ€ฐรฃยยฐรฃฦ’ยฉรฃฦ’ยณรฃฦ’ยฌรฃฦ’ยณรฃโ€šยฐรฃโ€šยนรงยฌยฆรฅยยทรฃยยฏรจยฆยรฃโ€šโ€ฐรฃยยชรฃยโ€ž + + if (runLength < 3) { + while (runLength-- > 0) { + result[nResult++] = src[i]; + freqs[src[i]]++; + } // 3 รฅโ€บลพรคยปยฅรคยธล รฃยยชรฃโ€šโ€ฐรฃยยฐรฃฦ’ยฉรฃฦ’ยณรฃฦ’ยฌรฃฦ’ยณรฃโ€šยฐรฃโ€šยนรงยฌยฆรฅยยทรฅล’โ€“ + + } else { + while (runLength > 0) { + // runLengthรฃโ€šโ€™ 3-6 รฃยยงรฅห†โ€ รฅโ€ฐยฒ + rpt = runLength < 6 ? runLength : 6; + + if (rpt > runLength - 3 && rpt < runLength) { + rpt = runLength - 3; + } + + result[nResult++] = 16; + result[nResult++] = rpt - 3; + freqs[16]++; + runLength -= rpt; + } + } + } + } + + return { + codes: result.subarray(0, nResult) , + freqs: freqs + }; + }; + /** + * รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยฎรฉโ€ขยทรฃยโ€ขรฃโ€šโ€™รฅยโ€“รฅยพโ€”รฃยโ„ขรฃโ€šโ€น + * @param {!(Array.|Uint8Array|Uint32Array)} freqs รฅโ€กยบรงยยพรฃโ€šยซรฃโ€šยฆรฃฦ’ยณรฃฦ’ห†. + * @param {number} limit รงยฌยฆรฅยยทรฉโ€ขยทรฃยยฎรฅห†ยถรฉโ„ขย. + * @return {!(Array.|Uint8Array)} รงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”. + * @private + */ + + + Zlib$1.RawDeflate.prototype.getLengths_ = function (freqs, limit) { + /** @type {number} */ + var nSymbols = freqs.length; + /** @type {Zlib.Heap} */ + + var heap = new Zlib$1.Heap(2 * Zlib$1.RawDeflate.HUFMAX); + /** @type {!(Array.|Uint8Array)} */ + + var length = new (Uint8Array )(nSymbols); + /** @type {Array} */ + + var nodes; + /** @type {!(Array.|Uint32Array)} */ + + var values; + /** @type {!(Array.|Uint8Array)} */ + + var codeLength; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; // รฉโ€ฆยรฅห†โ€”รฃยยฎรฅห†ยรฆล“ลธรฅล’โ€“ + + + for (i = 0; i < nSymbols; ++i) { + if (freqs[i] > 0) { + heap.push(i, freqs[i]); + } + } + + nodes = new Array(heap.length / 2); + values = new (Uint32Array )(heap.length / 2); // รฉยลพ 0 รฃยยฎรจยฆยรงยด รฃยล’รคยธโ‚ฌรฃยยครฃย รฃยโ€˜รฃย รฃยยฃรฃยลธรฅ ยดรฅยห†รฃยยฏรฃโ‚ฌยรฃยยรฃยยฎรฃโ€šยทรฃฦ’ยณรฃฦ’ล“รฃฦ’ยซรฃยยซรงยฌยฆรฅยยทรฉโ€ขยท 1 รฃโ€šโ€™รฅโ€ฐยฒรฃโ€šล รฅยฝโ€œรฃยยฆรฃยยฆรงยตโ€šรคยบโ€  + + if (nodes.length === 1) { + length[heap.pop().index] = 1; + return length; + } // Reverse Package Merge Algorithm รฃยยซรฃโ€šห†รฃโ€šโ€น Canonical Huffman Code รฃยยฎรงยฌยฆรฅยยทรฉโ€ขยทรฆยฑยบรฅยฎลก + + + for (i = 0, il = heap.length / 2; i < il; ++i) { + nodes[i] = heap.pop(); + values[i] = nodes[i].value; + } + + codeLength = this.reversePackageMerge_(values, values.length, limit); + + for (i = 0, il = nodes.length; i < il; ++i) { + length[nodes[i].index] = codeLength[i]; + } + + return length; + }; + /** + * Reverse Package Merge Algorithm. + * @param {!(Array.|Uint32Array)} freqs sorted probability. + * @param {number} symbols number of symbols. + * @param {number} limit code length limit. + * @return {!(Array.|Uint8Array)} code lengths. + */ + + + Zlib$1.RawDeflate.prototype.reversePackageMerge_ = function (freqs, symbols, limit) { + /** @type {!(Array.|Uint16Array)} */ + var minimumCost = new (Uint16Array )(limit); + /** @type {!(Array.|Uint8Array)} */ + + var flag = new (Uint8Array )(limit); + /** @type {!(Array.|Uint8Array)} */ + + var codeLength = new (Uint8Array )(symbols); + /** @type {Array} */ + + var value = new Array(limit); + /** @type {Array} */ + + var type = new Array(limit); + /** @type {Array.} */ + + var currentPosition = new Array(limit); + /** @type {number} */ + + var excess = (1 << limit) - symbols; + /** @type {number} */ + + var half = 1 << limit - 1; + /** @type {number} */ + + var i; + /** @type {number} */ + + var j; + /** @type {number} */ + + var t; + /** @type {number} */ + + var weight; + /** @type {number} */ + + var next; + /** + * @param {number} j + */ + + function takePackage(j) { + /** @type {number} */ + var x = type[j][currentPosition[j]]; + + if (x === symbols) { + takePackage(j + 1); + takePackage(j + 1); + } else { + --codeLength[x]; + } + + ++currentPosition[j]; + } + + minimumCost[limit - 1] = symbols; + + for (j = 0; j < limit; ++j) { + if (excess < half) { + flag[j] = 0; + } else { + flag[j] = 1; + excess -= half; + } + + excess <<= 1; + minimumCost[limit - 2 - j] = (minimumCost[limit - 1 - j] / 2 | 0) + symbols; + } + + minimumCost[0] = flag[0]; + value[0] = new Array(minimumCost[0]); + type[0] = new Array(minimumCost[0]); + + for (j = 1; j < limit; ++j) { + if (minimumCost[j] > 2 * minimumCost[j - 1] + flag[j]) { + minimumCost[j] = 2 * minimumCost[j - 1] + flag[j]; + } + + value[j] = new Array(minimumCost[j]); + type[j] = new Array(minimumCost[j]); + } + + for (i = 0; i < symbols; ++i) { + codeLength[i] = limit; + } + + for (t = 0; t < minimumCost[limit - 1]; ++t) { + value[limit - 1][t] = freqs[t]; + type[limit - 1][t] = t; + } + + for (i = 0; i < limit; ++i) { + currentPosition[i] = 0; + } + + if (flag[limit - 1] === 1) { + --codeLength[0]; + ++currentPosition[limit - 1]; + } + + for (j = limit - 2; j >= 0; --j) { + i = 0; + weight = 0; + next = currentPosition[j + 1]; + + for (t = 0; t < minimumCost[j]; t++) { + weight = value[j + 1][next] + value[j + 1][next + 1]; + + if (weight > freqs[i]) { + value[j][t] = weight; + type[j][t] = symbols; + next += 2; + } else { + value[j][t] = freqs[i]; + type[j][t] = i; + ++i; + } + } + + currentPosition[j] = 0; + + if (flag[j] === 1) { + takePackage(j); + } + } + + return codeLength; + }; + /** + * รงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”รฃยโ€นรฃโ€šโ€ฐรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃโ€šโ€™รฅยโ€“รฅยพโ€”รฃยโ„ขรฃโ€šโ€น + * reference: PuTTY Deflate implementation + * @param {!(Array.|Uint8Array)} lengths รงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”. + * @return {!(Array.|Uint16Array)} รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฉโ€ฆยรฅห†โ€”. + * @private + */ + + + Zlib$1.RawDeflate.prototype.getCodesFromLengths_ = function (lengths) { + var codes = new (Uint16Array )(lengths.length), + count = [], + startCode = [], + code = 0, + i, + il, + j, + m; // Count the codes of each length. + + for (i = 0, il = lengths.length; i < il; i++) { + count[lengths[i]] = (count[lengths[i]] | 0) + 1; + } // Determine the starting code for each length block. + + + for (i = 1, il = Zlib$1.RawDeflate.MaxCodeLength; i <= il; i++) { + startCode[i] = code; + code += count[i] | 0; + code <<= 1; + } // Determine the code for each symbol. Mirrored, of course. + + + for (i = 0, il = lengths.length; i < il; i++) { + code = startCode[lengths[i]]; + startCode[lengths[i]] += 1; + codes[i] = 0; + + for (j = 0, m = lengths[i]; j < m; j++) { + codes[i] = codes[i] << 1 | code & 1; + code >>>= 1; + } + } + + return codes; + }; + /** + * @param {!(Array.|Uint8Array)} input input buffer. + * @param {Object=} opt_params options. + * @constructor + */ + + + Zlib$1.Unzip = function (input, opt_params) { + opt_params = opt_params || {}; + /** @type {!(Array.|Uint8Array)} */ + + this.input = input instanceof Array ? new Uint8Array(input) : input; + /** @type {number} */ + + this.ip = 0; + /** @type {number} */ + + this.eocdrOffset; + /** @type {number} */ + + this.numberOfThisDisk; + /** @type {number} */ + + this.startDisk; + /** @type {number} */ + + this.totalEntriesThisDisk; + /** @type {number} */ + + this.totalEntries; + /** @type {number} */ + + this.centralDirectorySize; + /** @type {number} */ + + this.centralDirectoryOffset; + /** @type {number} */ + + this.commentLength; + /** @type {(Array.|Uint8Array)} */ + + this.comment; + /** @type {Array.} */ + + this.fileHeaderList; + /** @type {Object.} */ + + this.filenameToIndex; + /** @type {boolean} */ + + this.verify = opt_params['verify'] || false; + /** @type {(Array.|Uint8Array)} */ + + this.password = opt_params['password']; + }; + + Zlib$1.Unzip.CompressionMethod = Zlib$1.Zip.CompressionMethod; + /** + * @type {Array.} + * @const + */ + + Zlib$1.Unzip.FileHeaderSignature = Zlib$1.Zip.FileHeaderSignature; + /** + * @type {Array.} + * @const + */ + + Zlib$1.Unzip.LocalFileHeaderSignature = Zlib$1.Zip.LocalFileHeaderSignature; + /** + * @type {Array.} + * @const + */ + + Zlib$1.Unzip.CentralDirectorySignature = Zlib$1.Zip.CentralDirectorySignature; + /** + * @param {!(Array.|Uint8Array)} input input buffer. + * @param {number} ip input position. + * @constructor + */ + + Zlib$1.Unzip.FileHeader = function (input, ip) { + /** @type {!(Array.|Uint8Array)} */ + this.input = input; + /** @type {number} */ + + this.offset = ip; + /** @type {number} */ + + this.length; + /** @type {number} */ + + this.version; + /** @type {number} */ + + this.os; + /** @type {number} */ + + this.needVersion; + /** @type {number} */ + + this.flags; + /** @type {number} */ + + this.compression; + /** @type {number} */ + + this.time; + /** @type {number} */ + + this.date; + /** @type {number} */ + + this.crc32; + /** @type {number} */ + + this.compressedSize; + /** @type {number} */ + + this.plainSize; + /** @type {number} */ + + this.fileNameLength; + /** @type {number} */ + + this.extraFieldLength; + /** @type {number} */ + + this.fileCommentLength; + /** @type {number} */ + + this.diskNumberStart; + /** @type {number} */ + + this.internalFileAttributes; + /** @type {number} */ + + this.externalFileAttributes; + /** @type {number} */ + + this.relativeOffset; + /** @type {string} */ + + this.filename; + /** @type {!(Array.|Uint8Array)} */ + + this.extraField; + /** @type {!(Array.|Uint8Array)} */ + + this.comment; + }; + + Zlib$1.Unzip.FileHeader.prototype.parse = function () { + /** @type {!(Array.|Uint8Array)} */ + var input = this.input; + /** @type {number} */ + + var ip = this.offset; // central file header signature + + if (input[ip++] !== Zlib$1.Unzip.FileHeaderSignature[0] || input[ip++] !== Zlib$1.Unzip.FileHeaderSignature[1] || input[ip++] !== Zlib$1.Unzip.FileHeaderSignature[2] || input[ip++] !== Zlib$1.Unzip.FileHeaderSignature[3]) { + throw new Error('invalid file header signature'); + } // version made by + + + this.version = input[ip++]; + this.os = input[ip++]; // version needed to extract + + this.needVersion = input[ip++] | input[ip++] << 8; // general purpose bit flag + + this.flags = input[ip++] | input[ip++] << 8; // compression method + + this.compression = input[ip++] | input[ip++] << 8; // last mod file time + + this.time = input[ip++] | input[ip++] << 8; //last mod file date + + this.date = input[ip++] | input[ip++] << 8; // crc-32 + + this.crc32 = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // compressed size + + this.compressedSize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // uncompressed size + + this.plainSize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // file name length + + this.fileNameLength = input[ip++] | input[ip++] << 8; // extra field length + + this.extraFieldLength = input[ip++] | input[ip++] << 8; // file comment length + + this.fileCommentLength = input[ip++] | input[ip++] << 8; // disk number start + + this.diskNumberStart = input[ip++] | input[ip++] << 8; // internal file attributes + + this.internalFileAttributes = input[ip++] | input[ip++] << 8; // external file attributes + + this.externalFileAttributes = input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24; // relative offset of local header + + this.relativeOffset = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // file name + + this.filename = String.fromCharCode.apply(null, input.subarray(ip, ip += this.fileNameLength) ); // extra field + + this.extraField = input.subarray(ip, ip += this.extraFieldLength) ; // file comment + + this.comment = input.subarray(ip, ip + this.fileCommentLength) ; + this.length = ip - this.offset; + }; + /** + * @param {!(Array.|Uint8Array)} input input buffer. + * @param {number} ip input position. + * @constructor + */ + + + Zlib$1.Unzip.LocalFileHeader = function (input, ip) { + /** @type {!(Array.|Uint8Array)} */ + this.input = input; + /** @type {number} */ + + this.offset = ip; + /** @type {number} */ + + this.length; + /** @type {number} */ + + this.needVersion; + /** @type {number} */ + + this.flags; + /** @type {number} */ + + this.compression; + /** @type {number} */ + + this.time; + /** @type {number} */ + + this.date; + /** @type {number} */ + + this.crc32; + /** @type {number} */ + + this.compressedSize; + /** @type {number} */ + + this.plainSize; + /** @type {number} */ + + this.fileNameLength; + /** @type {number} */ + + this.extraFieldLength; + /** @type {string} */ + + this.filename; + /** @type {!(Array.|Uint8Array)} */ + + this.extraField; + }; + + Zlib$1.Unzip.LocalFileHeader.Flags = Zlib$1.Zip.Flags; + + Zlib$1.Unzip.LocalFileHeader.prototype.parse = function () { + /** @type {!(Array.|Uint8Array)} */ + var input = this.input; + /** @type {number} */ + + var ip = this.offset; // local file header signature + + if (input[ip++] !== Zlib$1.Unzip.LocalFileHeaderSignature[0] || input[ip++] !== Zlib$1.Unzip.LocalFileHeaderSignature[1] || input[ip++] !== Zlib$1.Unzip.LocalFileHeaderSignature[2] || input[ip++] !== Zlib$1.Unzip.LocalFileHeaderSignature[3]) { + throw new Error('invalid local file header signature'); + } // version needed to extract + + + this.needVersion = input[ip++] | input[ip++] << 8; // general purpose bit flag + + this.flags = input[ip++] | input[ip++] << 8; // compression method + + this.compression = input[ip++] | input[ip++] << 8; // last mod file time + + this.time = input[ip++] | input[ip++] << 8; //last mod file date + + this.date = input[ip++] | input[ip++] << 8; // crc-32 + + this.crc32 = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // compressed size + + this.compressedSize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // uncompressed size + + this.plainSize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // file name length + + this.fileNameLength = input[ip++] | input[ip++] << 8; // extra field length + + this.extraFieldLength = input[ip++] | input[ip++] << 8; // file name + + this.filename = String.fromCharCode.apply(null, input.subarray(ip, ip += this.fileNameLength) ); // extra field + + this.extraField = input.subarray(ip, ip += this.extraFieldLength) ; + this.length = ip - this.offset; + }; + + Zlib$1.Unzip.prototype.searchEndOfCentralDirectoryRecord = function () { + /** @type {!(Array.|Uint8Array)} */ + var input = this.input; + /** @type {number} */ + + var ip; + + for (ip = input.length - 12; ip > 0; --ip) { + if (input[ip] === Zlib$1.Unzip.CentralDirectorySignature[0] && input[ip + 1] === Zlib$1.Unzip.CentralDirectorySignature[1] && input[ip + 2] === Zlib$1.Unzip.CentralDirectorySignature[2] && input[ip + 3] === Zlib$1.Unzip.CentralDirectorySignature[3]) { + this.eocdrOffset = ip; + return; + } + } + + throw new Error('End of Central Directory Record not found'); + }; + + Zlib$1.Unzip.prototype.parseEndOfCentralDirectoryRecord = function () { + /** @type {!(Array.|Uint8Array)} */ + var input = this.input; + /** @type {number} */ + + var ip; + + if (!this.eocdrOffset) { + this.searchEndOfCentralDirectoryRecord(); + } + + ip = this.eocdrOffset; // signature + + if (input[ip++] !== Zlib$1.Unzip.CentralDirectorySignature[0] || input[ip++] !== Zlib$1.Unzip.CentralDirectorySignature[1] || input[ip++] !== Zlib$1.Unzip.CentralDirectorySignature[2] || input[ip++] !== Zlib$1.Unzip.CentralDirectorySignature[3]) { + throw new Error('invalid signature'); + } // number of this disk + + + this.numberOfThisDisk = input[ip++] | input[ip++] << 8; // number of the disk with the start of the central directory + + this.startDisk = input[ip++] | input[ip++] << 8; // total number of entries in the central directory on this disk + + this.totalEntriesThisDisk = input[ip++] | input[ip++] << 8; // total number of entries in the central directory + + this.totalEntries = input[ip++] | input[ip++] << 8; // size of the central directory + + this.centralDirectorySize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // offset of start of central directory with respect to the starting disk number + + this.centralDirectoryOffset = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // .ZIP file comment length + + this.commentLength = input[ip++] | input[ip++] << 8; // .ZIP file comment + + this.comment = input.subarray(ip, ip + this.commentLength) ; + }; + + Zlib$1.Unzip.prototype.parseFileHeader = function () { + /** @type {Array.} */ + var filelist = []; + /** @type {Object.} */ + + var filetable = {}; + /** @type {number} */ + + var ip; + /** @type {Zlib.Unzip.FileHeader} */ + + var fileHeader; + /*: @type {number} */ + + var i; + /*: @type {number} */ + + var il; + + if (this.fileHeaderList) { + return; + } + + if (this.centralDirectoryOffset === void 0) { + this.parseEndOfCentralDirectoryRecord(); + } + + ip = this.centralDirectoryOffset; + + for (i = 0, il = this.totalEntries; i < il; ++i) { + fileHeader = new Zlib$1.Unzip.FileHeader(this.input, ip); + fileHeader.parse(); + ip += fileHeader.length; + filelist[i] = fileHeader; + filetable[fileHeader.filename] = i; + } + + if (this.centralDirectorySize < ip - this.centralDirectoryOffset) { + throw new Error('invalid file header size'); + } + + this.fileHeaderList = filelist; + this.filenameToIndex = filetable; + }; + /** + * @param {number} index file header index. + * @param {Object=} opt_params + * @return {!(Array.|Uint8Array)} file data. + */ + + + Zlib$1.Unzip.prototype.getFileData = function (index, opt_params) { + opt_params = opt_params || {}; + /** @type {!(Array.|Uint8Array)} */ + + var input = this.input; + /** @type {Array.} */ + + var fileHeaderList = this.fileHeaderList; + /** @type {Zlib.Unzip.LocalFileHeader} */ + + var localFileHeader; + /** @type {number} */ + + var offset; + /** @type {number} */ + + var length; + /** @type {!(Array.|Uint8Array)} */ + + var buffer; + /** @type {number} */ + + var crc32; + /** @type {Array.|Uint32Array|Object} */ + + var key; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + + if (!fileHeaderList) { + this.parseFileHeader(); + } + + if (fileHeaderList[index] === void 0) { + throw new Error('wrong index'); + } + + offset = fileHeaderList[index].relativeOffset; + localFileHeader = new Zlib$1.Unzip.LocalFileHeader(this.input, offset); + localFileHeader.parse(); + offset += localFileHeader.length; + length = localFileHeader.compressedSize; // decryption + + if ((localFileHeader.flags & Zlib$1.Unzip.LocalFileHeader.Flags.ENCRYPT) !== 0) { + if (!(opt_params['password'] || this.password)) { + throw new Error('please set password'); + } + + key = this.createDecryptionKey(opt_params['password'] || this.password); // encryption header + + for (i = offset, il = offset + 12; i < il; ++i) { + this.decode(key, input[i]); + } + + offset += 12; + length -= 12; // decryption + + for (i = offset, il = offset + length; i < il; ++i) { + input[i] = this.decode(key, input[i]); + } + } + + switch (localFileHeader.compression) { + case Zlib$1.Unzip.CompressionMethod.STORE: + buffer = this.input.subarray(offset, offset + length) ; + break; + + case Zlib$1.Unzip.CompressionMethod.DEFLATE: + buffer = new Zlib$1.RawInflate(this.input, { + 'index': offset, + 'bufferSize': localFileHeader.plainSize + }).decompress(); + break; + + default: + throw new Error('unknown compression type'); + } + + if (this.verify) { + crc32 = Zlib$1.CRC32.calc(buffer); + + if (localFileHeader.crc32 !== crc32) { + throw new Error('wrong crc: file=0x' + localFileHeader.crc32.toString(16) + ', data=0x' + crc32.toString(16)); + } + } + + return buffer; + }; + /** + * @return {Array.} + */ + + + Zlib$1.Unzip.prototype.getFilenames = function () { + /** @type {Array.} */ + var filenameList = []; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + /** @type {Array.} */ + + var fileHeaderList; + + if (!this.fileHeaderList) { + this.parseFileHeader(); + } + + fileHeaderList = this.fileHeaderList; + + for (i = 0, il = fileHeaderList.length; i < il; ++i) { + filenameList[i] = fileHeaderList[i].filename; + } + + return filenameList; + }; + /** + * @param {string} filename extract filename. + * @param {Object=} opt_params + * @return {!(Array.|Uint8Array)} decompressed data. + */ + + + Zlib$1.Unzip.prototype.decompress = function (filename, opt_params) { + /** @type {number} */ + var index; + + if (!this.filenameToIndex) { + this.parseFileHeader(); + } + + index = this.filenameToIndex[filename]; + + if (index === void 0) { + throw new Error(filename + ' not found'); + } + + return this.getFileData(index, opt_params); + }; + /** + * @param {(Array.|Uint8Array)} password + */ + + + Zlib$1.Unzip.prototype.setPassword = function (password) { + this.password = password; + }; + /** + * @param {(Array.|Uint32Array|Object)} key + * @param {number} n + * @return {number} + */ + + + Zlib$1.Unzip.prototype.decode = function (key, n) { + n ^= this.getByte(key); + this.updateKeys(key, n); + return n; + }; // common method + + + Zlib$1.Unzip.prototype.updateKeys = Zlib$1.Zip.prototype.updateKeys; + Zlib$1.Unzip.prototype.createDecryptionKey = Zlib$1.Zip.prototype.createEncryptionKey; + Zlib$1.Unzip.prototype.getByte = Zlib$1.Zip.prototype.getByte; + /** + * @fileoverview รฉโ€บโ€˜รฅยคลกรฃยยชรฉโ€“ยขรฆโ€ขยฐรงยพยครฃโ€šโ€™รฃยยพรฃยยจรฃโ€šยรฃยลธรฃฦ’ยขรฃโ€šยธรฃฦ’ยฅรฃฦ’ยผรฃฦ’ยซรฅยฎลธรจยฃโ€ฆ. + */ + + /** + * Byte String รฃยโ€นรฃโ€šโ€ฐ Byte Array รฃยยซรฅยคโ€ฐรฆยโ€บ. + * @param {!string} str byte string. + * @return {!Array.} byte array. + */ + + Zlib$1.Util.stringToByteArray = function (str) { + /** @type {!Array.<(string|number)>} */ + var tmp = str.split(''); + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + + for (i = 0, il = tmp.length; i < il; i++) { + tmp[i] = (tmp[i].charCodeAt(0) & 0xff) >>> 0; + } + + return tmp; + }; + /** + * @fileoverview Adler32 checksum รฅยฎลธรจยฃโ€ฆ. + */ + + /** + * Adler32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยครฃยยฎรคยฝล“รฆห†ย + * @param {!(Array|Uint8Array|string)} array รงยฎโ€”รฅโ€กยบรฃยยซรคยฝยฟรงโ€ยจรฃยโ„ขรฃโ€šโ€น byte array. + * @return {number} Adler32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยค. + */ + + + Zlib$1.Adler32 = function (array) { + if (typeof array === 'string') { + array = Zlib$1.Util.stringToByteArray(array); + } + + return Zlib$1.Adler32.update(1, array); + }; + /** + * Adler32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยครฃยยฎรฆโ€บยดรฆโ€“ยฐ + * @param {number} adler รงยยพรฅล“ยจรฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยค. + * @param {!(Array|Uint8Array)} array รฆโ€บยดรฆโ€“ยฐรฃยยซรคยฝยฟรงโ€ยจรฃยโ„ขรฃโ€šโ€น byte array. + * @return {number} Adler32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยค. + */ + + + Zlib$1.Adler32.update = function (adler, array) { + /** @type {number} */ + var s1 = adler & 0xffff; + /** @type {number} */ + + var s2 = adler >>> 16 & 0xffff; + /** @type {number} array length */ + + var len = array.length; + /** @type {number} loop length (don't overflow) */ + + var tlen; + /** @type {number} array index */ + + var i = 0; + + while (len > 0) { + tlen = len > Zlib$1.Adler32.OptimizationParameter ? Zlib$1.Adler32.OptimizationParameter : len; + len -= tlen; + + do { + s1 += array[i++]; + s2 += s1; + } while (--tlen); + + s1 %= 65521; + s2 %= 65521; + } + + return (s2 << 16 | s1) >>> 0; + }; + /** + * Adler32 รฆล“โ‚ฌรฉยยฉรฅล’โ€“รฃฦ’โ€˜รฃฦ’ยฉรฃฦ’ยกรฃฦ’ยผรฃโ€šยฟ + * รงยยพรงล ยถรฃยยงรฃยยฏ 1024 รงยจโ€นรฅยบยฆรฃยล’รฆล“โ‚ฌรฉยยฉ. + * @see http://jsperf.com/adler-32-simple-vs-optimized/3 + * @define {number} + */ + + + Zlib$1.Adler32.OptimizationParameter = 1024; + /** + * รฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ + * @constructor + * @param {!(Array|Uint8Array)=} buffer output buffer. + * @param {number=} bufferPosition start buffer pointer. + */ + + Zlib$1.BitStream = function (buffer, bufferPosition) { + /** @type {number} buffer index. */ + this.index = typeof bufferPosition === 'number' ? bufferPosition : 0; + /** @type {number} bit index. */ + + this.bitindex = 0; + /** @type {!(Array|Uint8Array)} bit-stream output buffer. */ + + this.buffer = buffer instanceof (Uint8Array ) ? buffer : new (Uint8Array )(Zlib$1.BitStream.DefaultBlockSize); // รฅโ€ฆยฅรฅล โ€บรฃยโ€ขรฃโ€šล’รฃยลธ index รฃยล’รจยถยณรฃโ€šล รฃยยชรฃยโ€นรฃยยฃรฃยลธรฃโ€šโ€ฐรฆโ€นยกรฅยผยตรฃยโ„ขรฃโ€šโ€นรฃยล’รฃโ‚ฌยรฅโ‚ฌยรฃยยซรฃยโ€”รฃยยฆรฃโ€šโ€šรฃฦ’โ‚ฌรฃฦ’ยกรฃยยชรฃโ€šโ€ฐรคยธยรฆยญยฃรฃยยจรฃยโ„ขรฃโ€šโ€น + + if (this.buffer.length * 2 <= this.index) { + throw new Error("invalid index"); + } else if (this.buffer.length <= this.index) { + this.expandBuffer(); + } + }; + /** + * รฃฦ’โ€กรฃฦ’โ€ขรฃโ€šยฉรฃฦ’ยซรฃฦ’ห†รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃโ€šยตรฃโ€šยครฃโ€šยบ. + * @const + * @type {number} + */ + + + Zlib$1.BitStream.DefaultBlockSize = 0x8000; + /** + * expand buffer. + * @return {!(Array|Uint8Array)} new buffer. + */ + + Zlib$1.BitStream.prototype.expandBuffer = function () { + /** @type {!(Array|Uint8Array)} old buffer. */ + var oldbuf = this.buffer; + /** @type {number} loop limiter. */ + + var il = oldbuf.length; + /** @type {!(Array|Uint8Array)} new buffer. */ + + var buffer = new (Uint8Array )(il << 1); // copy buffer + + { + buffer.set(oldbuf); + } + + return this.buffer = buffer; + }; + /** + * รฆโ€ขยฐรฅโ‚ฌยครฃโ€šโ€™รฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃยยงรฆล’โ€กรฅยฎลกรฃยโ€”รฃยลธรฆโ€ขยฐรฃย รฃยโ€˜รฆโ€บยธรฃยยรจยพยผรฃโ€šโ‚ฌ. + * @param {number} number รฆโ€บยธรฃยยรจยพยผรฃโ€šโ‚ฌรฆโ€ขยฐรฅโ‚ฌยค. + * @param {number} n รฆโ€บยธรฃยยรจยพยผรฃโ€šโ‚ฌรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฆโ€ขยฐ. + * @param {boolean=} reverse รฉโ‚ฌโ€ รฉ โ€ รฃยยซรฆโ€บยธรฃยยรจยพยผรฃโ€šโ‚ฌรฃยยชรฃโ€šโ€ฐรฃยยฐ true. + */ + + + Zlib$1.BitStream.prototype.writeBits = function (number, n, reverse) { + var buffer = this.buffer; + var index = this.index; + var bitindex = this.bitindex; + /** @type {number} current octet. */ + + var current = buffer[index]; + /** @type {number} loop counter. */ + + var i; + /** + * 32-bit รฆโ€ขยดรฆโ€ขยฐรฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉ โ€ รฃโ€šโ€™รฉโ‚ฌโ€ รฃยยซรฃยโ„ขรฃโ€šโ€น + * @param {number} n 32-bit integer. + * @return {number} reversed 32-bit integer. + * @private + */ + + function rev32_(n) { + return Zlib$1.BitStream.ReverseTable[n & 0xFF] << 24 | Zlib$1.BitStream.ReverseTable[n >>> 8 & 0xFF] << 16 | Zlib$1.BitStream.ReverseTable[n >>> 16 & 0xFF] << 8 | Zlib$1.BitStream.ReverseTable[n >>> 24 & 0xFF]; + } + + if (reverse && n > 1) { + number = n > 8 ? rev32_(number) >> 32 - n : Zlib$1.BitStream.ReverseTable[number] >> 8 - n; + } // Byte รฅยขฦ’รงโ€ขล’รฃโ€šโ€™รจยถโ€ฆรฃยห†รฃยยชรฃยโ€žรฃยยจรฃยย + + + if (n + bitindex < 8) { + current = current << n | number; + bitindex += n; // Byte รฅยขฦ’รงโ€ขล’รฃโ€šโ€™รจยถโ€ฆรฃยห†รฃโ€šโ€นรฃยยจรฃยย + } else { + for (i = 0; i < n; ++i) { + current = current << 1 | number >> n - i - 1 & 1; // next byte + + if (++bitindex === 8) { + bitindex = 0; + buffer[index++] = Zlib$1.BitStream.ReverseTable[current]; + current = 0; // expand + + if (index === buffer.length) { + buffer = this.expandBuffer(); + } + } + } + } + + buffer[index] = current; + this.buffer = buffer; + this.bitindex = bitindex; + this.index = index; + }; + /** + * รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ รฃยยฎรงยตโ€šรงยซยฏรฅโ€กยฆรงยโ€ รฃโ€šโ€™รจยกล’รฃยโ€  + * @return {!(Array|Uint8Array)} รงยตโ€šรงยซยฏรฅโ€กยฆรงยโ€ รฅยพล’รฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šโ€™ byte array รฃยยงรจยฟโ€รฃยโ„ข. + */ + + + Zlib$1.BitStream.prototype.finish = function () { + var buffer = this.buffer; + var index = this.index; + /** @type {!(Array|Uint8Array)} output buffer. */ + + var output; // bitindex รฃยล’ 0 รฃยยฎรฆโ„ขโ€šรฃยยฏรคยฝโ„ขรฅห†โ€ รฃยยซ index รฃยล’รฉโ‚ฌยฒรฃโ€šโ€œรฃยยงรฃยโ€žรฃโ€šโ€นรงล ยถรฆโ€ฆโ€น + + if (this.bitindex > 0) { + buffer[index] <<= 8 - this.bitindex; + buffer[index] = Zlib$1.BitStream.ReverseTable[buffer[index]]; + index++; + } // array truncation + + + { + output = buffer.subarray(0, index); + } + + return output; + }; + /** + * 0-255 รฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉ โ€ รฃโ€šโ€™รฅยยรจยปยขรฃยโ€”รฃยลธรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ + * @const + * @type {!(Uint8Array|Array.)} + */ + + + Zlib$1.BitStream.ReverseTable = function (table) { + return table; + }(function () { + /** @type {!(Array|Uint8Array)} reverse table. */ + var table = new (Uint8Array )(256); + /** @type {number} loop counter. */ + + var i; // generate + + for (i = 0; i < 256; ++i) { + table[i] = function (n) { + var r = n; + var s = 7; + + for (n >>>= 1; n; n >>>= 1) { + r <<= 1; + r |= n & 1; + --s; + } + + return (r << s & 0xff) >>> 0; + }(i); + } + + return table; + }()); + /** + * CRC32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยครฃโ€šโ€™รฅยโ€“รฅยพโ€” + * @param {!(Array.|Uint8Array)} data data byte array. + * @param {number=} pos data position. + * @param {number=} length data length. + * @return {number} CRC32. + */ + + Zlib$1.CRC32.calc = function (data, pos, length) { + return Zlib$1.CRC32.update(data, 0, pos, length); + }; + /** + * CRC32รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยครฃโ€šโ€™รฆโ€บยดรฆโ€“ยฐ + * @param {!(Array.|Uint8Array)} data data byte array. + * @param {number} crc CRC32. + * @param {number=} pos data position. + * @param {number=} length data length. + * @return {number} CRC32. + */ + + + Zlib$1.CRC32.update = function (data, crc, pos, length) { + var table = Zlib$1.CRC32.Table; + var i = typeof pos === 'number' ? pos : pos = 0; + var il = typeof length === 'number' ? length : data.length; + crc ^= 0xffffffff; // loop unrolling for performance + + for (i = il & 7; i--; ++pos) { + crc = crc >>> 8 ^ table[(crc ^ data[pos]) & 0xff]; + } + + for (i = il >> 3; i--; pos += 8) { + crc = crc >>> 8 ^ table[(crc ^ data[pos]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 1]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 2]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 3]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 4]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 5]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 6]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 7]) & 0xff]; + } + + return (crc ^ 0xffffffff) >>> 0; + }; + /** + * @param {number} num + * @param {number} crc + * @returns {number} + */ + + + Zlib$1.CRC32.single = function (num, crc) { + return (Zlib$1.CRC32.Table[(num ^ crc) & 0xff] ^ num >>> 8) >>> 0; + }; + /** + * @type {Array.} + * @const + * @private + */ + + + Zlib$1.CRC32.Table_ = [0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d]; + /** + * @type {!(Array.|Uint32Array)} CRC-32 Table. + * @const + */ + + Zlib$1.CRC32.Table = new Uint32Array(Zlib$1.CRC32.Table_) ; + /** + * @fileoverview Deflate (RFC1951) รฅยฎลธรจยฃโ€ฆ. + * Deflateรฃโ€šยขรฃฦ’ยซรฃโ€šยดรฃฦ’ยชรฃโ€šยบรฃฦ’ รฆล“ยฌรคยฝโ€œรฃยยฏ Zlib.RawDeflate รฃยยงรฅยฎลธรจยฃโ€ฆรฃยโ€ขรฃโ€šล’รฃยยฆรฃยโ€žรฃโ€šโ€น. + */ + + /** + * Zlib Deflate + * @constructor + * @param {!(Array|Uint8Array)} input รงยฌยฆรฅยยทรฅล’โ€“รฃยโ„ขรฃโ€šโ€นรฅยฏยพรจยฑยกรฃยยฎ byte array. + * @param {Object=} opt_params option parameters. + */ + + Zlib$1.Deflate = function (input, opt_params) { + /** @type {!(Array|Uint8Array)} */ + this.input = input; + /** @type {!(Array|Uint8Array)} */ + + this.output = new (Uint8Array )(Zlib$1.Deflate.DefaultBufferSize); + /** @type {Zlib.Deflate.CompressionType} */ + + this.compressionType = Zlib$1.Deflate.CompressionType.DYNAMIC; + /** @type {Zlib.RawDeflate} */ + + this.rawDeflate; + /** @type {Object} */ + + var rawDeflateOption = {}; + /** @type {string} */ + + var prop; // option parameters + + if (opt_params || !(opt_params = {})) { + if (typeof opt_params['compressionType'] === 'number') { + this.compressionType = opt_params['compressionType']; + } + } // copy options + + + for (prop in opt_params) { + rawDeflateOption[prop] = opt_params[prop]; + } // set raw-deflate output buffer + + + rawDeflateOption['outputBuffer'] = this.output; + this.rawDeflate = new Zlib$1.RawDeflate(this.input, rawDeflateOption); + }; + /** + * @const + * @type {number} รฃฦ’โ€กรฃฦ’โ€ขรฃโ€šยฉรฃฦ’ยซรฃฦ’ห†รฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šยตรฃโ€šยครฃโ€šยบ. + */ + + + Zlib$1.Deflate.DefaultBufferSize = 0x8000; + /** + * @enum {number} + */ + + Zlib$1.Deflate.CompressionType = Zlib$1.RawDeflate.CompressionType; + /** + * รงโ€บยดรฆลฝยฅรฅล“ยงรงยธยฎรฃยยซรฆลฝโ€บรฃยโ€˜รฃโ€šโ€น. + * @param {!(Array|Uint8Array)} input target buffer. + * @param {Object=} opt_params option parameters. + * @return {!(Array|Uint8Array)} compressed data byte array. + */ + + Zlib$1.Deflate.compress = function (input, opt_params) { + return new Zlib$1.Deflate(input, opt_params).compress(); + }; + /** + * Deflate Compression. + * @return {!(Array|Uint8Array)} compressed data byte array. + */ + + + Zlib$1.Deflate.prototype.compress = function () { + /** @type {Zlib.CompressionMethod} */ + var cm; + /** @type {number} */ + + var cinfo; + /** @type {number} */ + + var cmf; + /** @type {number} */ + + var flg; + /** @type {number} */ + + var fcheck; + /** @type {number} */ + + var fdict; + /** @type {number} */ + + var flevel; + /** @type {number} */ + + var adler; + /** @type {!(Array|Uint8Array)} */ + + var output; + /** @type {number} */ + + var pos = 0; + output = this.output; // Compression Method and Flags + + cm = Zlib$1.CompressionMethod.DEFLATE; + + switch (cm) { + case Zlib$1.CompressionMethod.DEFLATE: + cinfo = Math.LOG2E * Math.log(Zlib$1.RawDeflate.WindowSize) - 8; + break; + + default: + throw new Error('invalid compression method'); + } + + cmf = cinfo << 4 | cm; + output[pos++] = cmf; // Flags + + fdict = 0; + + switch (cm) { + case Zlib$1.CompressionMethod.DEFLATE: + switch (this.compressionType) { + case Zlib$1.Deflate.CompressionType.NONE: + flevel = 0; + break; + + case Zlib$1.Deflate.CompressionType.FIXED: + flevel = 1; + break; + + case Zlib$1.Deflate.CompressionType.DYNAMIC: + flevel = 2; + break; + + default: + throw new Error('unsupported compression type'); + } + + break; + + default: + throw new Error('invalid compression method'); + } + + flg = flevel << 6 | fdict << 5; + fcheck = 31 - (cmf * 256 + flg) % 31; + flg |= fcheck; + output[pos++] = flg; // Adler-32 checksum + + adler = Zlib$1.Adler32(this.input); + this.rawDeflate.op = pos; + output = this.rawDeflate.compress(); + pos = output.length; + + { + // subarray รฅห†โ€ รฃโ€šโ€™รฅโ€ฆฦ’รฃยยซรฃโ€šโ€šรฃยยฉรฃยโ„ข + output = new Uint8Array(output.buffer); // expand buffer + + if (output.length <= pos + 4) { + this.output = new Uint8Array(output.length + 4); + this.output.set(output); + output = this.output; + } + + output = output.subarray(0, pos + 4); + } // adler32 + + + output[pos++] = adler >> 24 & 0xff; + output[pos++] = adler >> 16 & 0xff; + output[pos++] = adler >> 8 & 0xff; + output[pos++] = adler & 0xff; + return output; + }; + + /** + * Covers string literals and String objects + * @param x + * @returns {boolean} + */ + + function isString$6(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$3 = 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(str) { + if (str === undefined) { + return str; + } + + if (str.startsWith("'") || str.startsWith('"')) { + str = str.substring(1); + } + + if (str.endsWith("'") || str.endsWith('"')) { + str = str.substring(0, str.length - 1); + } + + return str; + } + + function hashCode$1(s) { + return s.split("").reduce(function (a, b) { + a = (a << 5) - a + b.charCodeAt(0); + return a & a; + }, 0); + } + /** + * Compress string and encode in a url safe form + * @param s + */ + + + function compressString(str) { + const bytes = []; + + for (var i = 0; i < str.length; i++) { + bytes.push(str.charCodeAt(i)); + } + + const compressedBytes = new Zlib$1.RawDeflate(bytes).compress(); // 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$1.RawInflate(compressedBytes).decompress(); + let str = ''; + + for (let b of bytes) { + str += String.fromCharCode(b); + } + + 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; + } + + 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. + */ + const knownFileExtensions = new Set(["narrowpeak", "broadpeak", "regionpeak", "peaks", "bedgraph", "wig", "gff3", "gff", "gtf", "fusionjuncspan", "refflat", "seg", "aed", "bed", "vcf", "bb", "bigbed", "bw", "bigwig", "bam", "tdf", "refgene", "genepred", "genepredext", "bedpe", "bp", "snp", "rmsk", "cram", "gwas"]); + /** + * Return a custom format object with the given name. + * @param name + * @returns {*} + */ + + function getFormat(name) { + if (FileFormats && FileFormats[name]) { + return expandFormat(FileFormats[name]); + } else { + return undefined; + } + + function expandFormat(format) { + const fields = format.fields; + const keys = ['chr', 'start', 'end']; + + for (let i = 0; i < fields.length; i++) { + for (let key of keys) { + if (key === fields[i]) { + format[key] = i; + } + } + } + + return format; + } + } + + function inferFileFormat(fn) { + var idx, ext; + fn = fn.toLowerCase(); // Special case -- UCSC refgene files + + if (fn.endsWith("refgene.txt.gz") || fn.endsWith("refgene.txt.bgz") || fn.endsWith("refgene.txt") || fn.endsWith("refgene.sorted.txt.gz") || fn.endsWith("refgene.sorted.txt.bgz")) { + return "refgene"; + } //Strip parameters -- handle local files later + + + idx = fn.indexOf("?"); + + if (idx > 0) { + fn = fn.substr(0, idx); + } //Strip aux extensions .gz, .tab, and .txt + + + if (fn.endsWith(".gz")) { + fn = fn.substr(0, fn.length - 3); + } + + if (fn.endsWith(".txt") || fn.endsWith(".tab") || fn.endsWith(".bgz")) { + fn = fn.substr(0, fn.length - 4); + } + + idx = fn.lastIndexOf("."); + ext = idx < 0 ? fn : fn.substr(idx + 1); + + switch (ext) { + case "bw": + return "bigwig"; + + case "bb": + return "bigbed"; + + default: + if (knownFileExtensions.has(ext)) { + return ext; + } else { + return undefined; + } + + } + } + + function inferIndexPath(url, extension) { + var idx; + + if (url instanceof File) { + throw new Error("Cannot infer an index path for a local File. Please select explicitly"); + } + + if (url.includes("?")) { + idx = url.indexOf("?"); + return url.substring(0, idx) + "." + extension + url.substring(idx); + } else { + return url + "." + extension; + } + } + + function isGoogleURL(url) { + return url.includes("googleapis") && !url.includes("urlshortener") || isGoogleStorageURL(url) || isGoogleDriveURL(url); + } + + function isGoogleStorageURL(url) { + return url.startsWith("gs://") || url.startsWith("https://www.googleapis.com/storage") || url.startsWith("https://storage.cloud.google.com") || url.startsWith("https://storage.googleapis.com"); + } + + function isGoogleDriveURL(url) { + return url.indexOf("drive.google.com") >= 0 || url.indexOf("www.googleapis.com/drive") > 0; + } + /** + * Translate gs:// urls to https + * See https://cloud.google.com/storage/docs/json_api/v1 + * @param gsUrl + * @returns {string|*} + */ + + + function translateGoogleCloudURL(gsUrl) { + let { + bucket, + object + } = parseBucketName(gsUrl); + object = encode(object); + const qIdx = gsUrl.indexOf('?'); + const paramString = qIdx > 0 ? gsUrl.substring(qIdx) + "&alt=media" : "?alt=media"; + return `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}${paramString}`; + } + /** + * Parse a google bucket and object name from a google storage URL. Known forms include + * + * gs://BUCKET_NAME/OBJECT_NAME + * https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME + * https://storage.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME + * https://www.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME" + * https://storage.googleapis.com/download/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME + * + * @param url + */ + + + function parseBucketName(url) { + let bucket; + let object; + + if (url.startsWith("gs://")) { + const i = url.indexOf('/', 5); + + if (i >= 0) { + bucket = url.substring(5, i); + const qIdx = url.indexOf('?'); + object = qIdx < 0 ? url.substring(i + 1) : url.substring(i + 1, qIdx); + } + } else if (url.startsWith("https://storage.googleapis.com") || url.startsWith("https://storage.cloud.google.com")) { + const bucketIdx = url.indexOf("/v1/b/", 8); + + if (bucketIdx > 0) { + const objIdx = url.indexOf("/o/", bucketIdx); + + if (objIdx > 0) { + const queryIdx = url.indexOf("?", objIdx); + bucket = url.substring(bucketIdx + 6, objIdx); + object = queryIdx > 0 ? url.substring(objIdx + 3, queryIdx) : url.substring(objIdx + 3); + } + } else { + const idx1 = url.indexOf("/", 8); + const idx2 = url.indexOf("/", idx1 + 1); + const idx3 = url.indexOf("?", idx2); + + if (idx2 > 0) { + bucket = url.substring(idx1 + 1, idx2); + object = idx3 < 0 ? url.substring(idx2 + 1) : url.substring(idx2 + 1, idx3); + } + } + } else if (url.startsWith("https://www.googleapis.com/storage/v1/b")) { + const bucketIdx = url.indexOf("/v1/b/", 8); + const objIdx = url.indexOf("/o/", bucketIdx); + + if (objIdx > 0) { + const queryIdx = url.indexOf("?", objIdx); + bucket = url.substring(bucketIdx + 6, objIdx); + object = queryIdx > 0 ? url.substring(objIdx + 3, queryIdx) : url.substring(objIdx + 3); + } + } + + if (bucket && object) { + return { + bucket, + object + }; + } else { + throw Error(`Unrecognized Google Storage URI: ${url}`); + } + } + + function driveDownloadURL(link) { + // Return a google drive download url for the sharable link + //https://drive.google.com/open?id=0B-lleX9c2pZFbDJ4VVRxakJzVGM + //https://drive.google.com/file/d/1_FC4kCeO8E3V4dJ1yIW7A0sn1yURKIX-/view?usp=sharing + var id = getGoogleDriveFileID(link); + return id ? "https://www.googleapis.com/drive/v3/files/" + id + "?alt=media&supportsTeamDrives=true" : link; + } + + function getGoogleDriveFileID(link) { + //https://drive.google.com/file/d/1_FC4kCeO8E3V4dJ1yIW7A0sn1yURKIX-/view?usp=sharing + //https://www.googleapis.com/drive/v3/files/1w-tvo6p1SH4p1OaQSVxpkV_EJgGIstWF?alt=media&supportsTeamDrives=true" + if (link.includes("/open?id=")) { + const i1 = link.indexOf("/open?id=") + 9; + const i2 = link.indexOf("&"); + + if (i1 > 0 && i2 > i1) { + return link.substring(i1, i2); + } else if (i1 > 0) { + return link.substring(i1); + } + } else if (link.includes("/file/d/")) { + const i1 = link.indexOf("/file/d/") + 8; + const i2 = link.lastIndexOf("/"); + return link.substring(i1, i2); + } else if (link.startsWith("https://www.googleapis.com/drive")) { + let i1 = link.indexOf("/files/"); + const i2 = link.indexOf("?"); + + if (i1 > 0) { + i1 += 7; + return i2 > 0 ? link.substring(i1, i2) : link.substring(i1); + } + } + + throw Error("Unknown Google Drive url format: " + link); + } + /** + * Percent a GCS object name. See https://cloud.google.com/storage/docs/request-endpoints + * Specific characters to encode: + * !, #, $, &, ', (, ), *, +, ,, /, :, ;, =, ?, @, [, ], and space characters. + * @param obj + */ + + + function encode(objectName) { + let result = ''; + objectName.split('').forEach(function (letter) { + if (encodings$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"); // For testing + + // Convenience functions for the gapi oAuth library. + const FIVE_MINUTES = 5 * 60 * 1000; + + async function load$1(library) { + return new Promise(function (resolve, reject) { + gapi.load(library, { + callback: resolve, + onerror: reject + }); + }); + } + + async function init$1(config) { + if (isInitialized()) { + console.warn("oAuth has already been initialized"); + return; + } + + gapi.apiKey = config.apiKey; // copy config, gapi will modify it + + const configCopy = Object.assign({}, config); + + if (!configCopy.scope) { + configCopy.scope = 'profile'; + } + + if (!config.client_id) { + config.client_id = config.clientId; + } + + await load$1("auth2"); + return new Promise(function (resolve, reject) { + gapi.auth2.init(configCopy).then(resolve, reject); + }); + } + + function isInitialized() { + return typeof gapi !== "undefined" && gapi.auth2 && gapi.auth2.getAuthInstance(); + } + + let inProgress = false; + + async function getAccessToken(scope) { + if (typeof gapi === "undefined") { + throw Error("Google authentication requires the 'gapi' library"); + } + + if (!gapi.auth2) { + throw Error("Google 'auth2' has not been initialized"); + } + + if (inProgress) { + return new Promise(function (resolve, reject) { + let intervalID; + + const checkForToken = () => { + // Wait for inProgress to equal "false" + try { + if (inProgress === false) { + //console.log("Delayed resolution for " + scope); + resolve(getAccessToken(scope)); + clearInterval(intervalID); + } + } catch (e) { + clearInterval(intervalID); + reject(e); + } + }; + + intervalID = setInterval(checkForToken, 100); + }); + } else { + inProgress = true; + + try { + let currentUser = gapi.auth2.getAuthInstance().currentUser.get(); + let token; + + if (currentUser.isSignedIn()) { + if (!currentUser.hasGrantedScopes(scope)) { + await currentUser.grant({ + scope + }); + } + + const { + access_token, + expires_at + } = currentUser.getAuthResponse(); + + if (Date.now() < expires_at - FIVE_MINUTES) { + token = { + access_token, + expires_at + }; + } else { + const { + access_token, + expires_at + } = currentUser.reloadAuthResponse(); + token = { + access_token, + expires_at + }; + } + } else { + currentUser = await signIn(scope); + const { + access_token, + expires_at + } = currentUser.getAuthResponse(); + token = { + access_token, + expires_at + }; + } + + return token; + } finally { + inProgress = false; + } + } + } + /** + * Return the current access token if the user is signed in, or undefined otherwise. This function does not + * attempt a signIn or request any specfic scopes. + * + * @returns access_token || undefined + */ + + + function getCurrentAccessToken() { + let currentUser = gapi.auth2.getAuthInstance().currentUser.get(); + + if (currentUser && currentUser.isSignedIn()) { + const { + access_token, + expires_at + } = currentUser.getAuthResponse(); + return { + access_token, + expires_at + }; + } else { + return undefined; + } + } + + async function signIn(scope) { + const options = new gapi.auth2.SigninOptionsBuilder(); + options.setPrompt('select_account'); + options.setScope(scope); + return gapi.auth2.getAuthInstance().signIn(options); + } + + function getScopeForURL(url) { + if (isGoogleDriveURL(url)) { + return "https://www.googleapis.com/auth/drive.file"; + } else if (isGoogleStorageURL(url)) { + return "https://www.googleapis.com/auth/devstorage.read_only"; + } else { + return 'https://www.googleapis.com/auth/userinfo.profile'; + } + } + + function getApiKey() { + return gapi.apiKey; + } + + async function getDriveFileInfo(googleDriveURL) { + const id = getGoogleDriveFileID(googleDriveURL); + let endPoint = "https://www.googleapis.com/drive/v3/files/" + id + "?supportsTeamDrives=true"; + const apiKey = getApiKey(); + + if (apiKey) { + endPoint += "&key=" + apiKey; + } + + const response = await fetch(endPoint); + let json = await response.json(); + + if (json.error && json.error.code === 404) { + const { + access_token + } = await getAccessToken("https://www.googleapis.com/auth/drive.readonly"); + + if (access_token) { + const response = await fetch(endPoint, { + headers: { + 'Authorization': `Bearer ${access_token}` + } + }); + json = await response.json(); + + if (json.error) { + throw Error(json.error); + } + } else { + throw Error(json.error); + } + } + + return json; + } + + if (typeof process === 'object' && typeof window === 'undefined') { + global.atob = function (str) { + return Buffer.from(str, 'base64').toString('binary'); + }; + } + /** + * @param dataURI + * @returns {Array|Uint8Array} + */ + + + function decodeDataURI$1(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); // URL encoded string -- not currently used of tested + } + + const bytes = new Uint8Array(dataString.length); + + for (let i = 0; i < dataString.length; i++) { + bytes[i] = dataString.charCodeAt(i); + } + + let plain; + + if (info.indexOf('gzip') > 0) { + const inflate = new Zlib$1.Gunzip(bytes); + plain = inflate.decompress(); + } else { + plain = bytes; + } + + return plain; + } + + function parseUri(str) { + var o = options, + m = o.parser["loose"].exec(str), + uri = {}, + i = 14; + + while (i--) uri[o.key[i]] = m[i] || ""; + + uri[o.q.name] = {}; + uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) { + if ($1) uri[o.q.name][$1] = $2; + }); + return uri; + } + + const options = { + strictMode: false, + key: ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"], + q: { + name: "queryKey", + parser: /(?:^|&)([^&=]*)=?([^&]*)/g + }, + parser: { + strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, + loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ + } + }; + /** + * Resolve a url, which might be a string, function (that returns a string or Promse), or Promise (that resolves to a string) + * + * @param url + * @returns {Promise<*>} + */ + + + async function resolveURL(url) { + return typeof url === 'function' ? url() : url; + } + + /** + * Return the filename from the path. Example + * https://foo.com/bar.bed?param=2 => bar.bed + * @param urlOrFile + */ + + + function getFilename$2(urlOrFile) { + if (urlOrFile instanceof File) { + return urlOrFile.name; + } else if (isString$6(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}`); + } + } + + function isFilePath(path) { + return path instanceof File; + } + + 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); + } + + // 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 = new Uint8Array(data, ptr, 18); + const xlen = ba[11] << 8 | ba[10]; + 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 inflate = new Zlib$1.RawInflate(a); + const unc = inflate.decompress(); + ptr += inflate.ip + 26; + totalSize += unc.byteLength; + oBlockList.push(unc); + } catch (e) { + console.error(e); + break; + } + } // Concatenate decompressed blocks + + + if (oBlockList.length === 1) { + return oBlockList[0]; + } else { + const out = new Uint8Array(totalSize); + let cursor = 0; + + for (let i = 0; i < oBlockList.length; ++i) { + var b = new Uint8Array(oBlockList[i]); + arrayCopy(b, 0, out, cursor, b.length); + cursor += b.length; + } + + return out; + } + } + + function bgzBlockSize(data) { + const ba = new Uint8Array(data); + const bsize = (ba[17] << 8 | ba[16]) + 1; + return bsize; + } // From Thomas Down's zlib implementation + + + const testArray = new Uint8Array(1); + const hasSubarray = typeof testArray.subarray === 'function'; + /* (typeof testArray.slice === 'function'); */ + // Chrome slice performance is so dire that we're currently not using it... + + function arrayCopy(src, srcOffset, dest, destOffset, count) { + if (count === 0) { + return; + } + + if (!src) { + throw "Undef src"; + } else if (!dest) { + throw "Undef dest"; + } + + if (srcOffset === 0 && count === src.length) { + arrayCopy_fast(src, dest, destOffset); + } else if (hasSubarray) { + arrayCopy_fast(src.subarray(srcOffset, srcOffset + count), dest, destOffset); + } else if (src.BYTES_PER_ELEMENT === 1 && count > 100) { + arrayCopy_fast(new Uint8Array(src.buffer, src.byteOffset + srcOffset, count), dest, destOffset); + } else { + arrayCopy_slow(src, srcOffset, dest, destOffset, count); + } + } + + function arrayCopy_slow(src, srcOffset, dest, destOffset, count) { + for (let i = 0; i < count; ++i) { + dest[destOffset + i] = src[srcOffset + i]; + } + } + + function arrayCopy_fast(src, dest, destOffset) { + dest.set(src, destOffset); + } + + /* + * 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 { + 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(str); + + if (str.includes(",")) { + return str.startsWith("rgb") ? str : "rgb(" + str + ")"; + } else { + return str; + } + }, + darkenLighten: function (color, amt) { + let src; + let hexColor = this.colorNameToHex(color); + + if (hexColor) { + src = IGVColor.hexToRgb(hexColor); + } else { + src = color.startsWith('rgb(') ? color : IGVColor.hexToRgb(color); + } + + const components = src.replace(")", "").substring(4).split(","); + const r = Math.max(0, Math.min(255, Number.parseInt(components[0].trim()) + amt)); + const g = Math.max(0, Math.min(255, Number.parseInt(components[1].trim()) + amt)); + const b = Math.max(0, Math.min(255, Number.parseInt(components[2].trim()) + amt)); + return 'rgb(' + r.toString() + ',' + g.toString() + ',' + b.toString() + ')'; + }, + + /** + * Convert html/css color name to hex value. Adapted from https://gist.github.com/mxfh/4719348 + * @param colorName + * @returns {*} + */ + colorNameToHex: function (colorName) { + // color list from http://stackoverflow.com/q/1573053/731179 with added gray/gray + const definedColorNames = { + "aliceblue": "#f0f8ff", + "antiquewhite": "#faebd7", + "aqua": "#00ffff", + "aquamarine": "#7fffd4", + "azure": "#f0ffff", + "beige": "#f5f5dc", + "bisque": "#ffe4c4", + "black": "#000000", + "blanchedalmond": "#ffebcd", + "blue": "#0000ff", + "blueviolet": "#8a2be2", + "brown": "#a52a2a", + "burlywood": "#deb887", + "cadetblue": "#5f9ea0", + "chartreuse": "#7fff00", + "chocolate": "#d2691e", + "coral": "#ff7f50", + "cornflowerblue": "#6495ed", + "cornsilk": "#fff8dc", + "crimson": "#dc143c", + "cyan": "#00ffff", + "darkblue": "#00008b", + "darkcyan": "#008b8b", + "darkgoldenrod": "#b8860b", + "darkgray": "#a9a9a9", + "darkgreen": "#006400", + "darkkhaki": "#bdb76b", + "darkmagenta": "#8b008b", + "darkolivegreen": "#556b2f", + "darkorange": "#ff8c00", + "darkorchid": "#9932cc", + "darkred": "#8b0000", + "darksalmon": "#e9967a", + "darkseagreen": "#8fbc8f", + "darkslateblue": "#483d8b", + "darkslategray": "#2f4f4f", + "darkturquoise": "#00ced1", + "darkviolet": "#9400d3", + "deeppink": "#ff1493", + "deepskyblue": "#00bfff", + "dimgray": "#696969", + "dodgerblue": "#1e90ff", + "firebrick": "#b22222", + "floralwhite": "#fffaf0", + "forestgreen": "#228b22", + "fuchsia": "#ff00ff", + "gainsboro": "#dcdcdc", + "ghostwhite": "#f8f8ff", + "gold": "#ffd700", + "goldenrod": "#daa520", + "gray": "#808080", + "green": "#008000", + "greenyellow": "#adff2f", + "honeydew": "#f0fff0", + "hotpink": "#ff69b4", + "indianred ": "#cd5c5c", + "indigo ": "#4b0082", + "ivory": "#fffff0", + "khaki": "#f0e68c", + "lavender": "#e6e6fa", + "lavenderblush": "#fff0f5", + "lawngreen": "#7cfc00", + "lemonchiffon": "#fffacd", + "lightblue": "#add8e6", + "lightcoral": "#f08080", + "lightcyan": "#e0ffff", + "lightgoldenrodyellow": "#fafad2", + "lightgrey": "#d3d3d3", + "lightgreen": "#90ee90", + "lightpink": "#ffb6c1", + "lightsalmon": "#ffa07a", + "lightseagreen": "#20b2aa", + "lightskyblue": "#87cefa", + "lightslategray": "#778899", + "lightsteelblue": "#b0c4de", + "lightyellow": "#ffffe0", + "lime": "#00ff00", + "limegreen": "#32cd32", + "linen": "#faf0e6", + "magenta": "#ff00ff", + "maroon": "#800000", + "mediumaquamarine": "#66cdaa", + "mediumblue": "#0000cd", + "mediumorchid": "#ba55d3", + "mediumpurple": "#9370d8", + "mediumseagreen": "#3cb371", + "mediumslateblue": "#7b68ee", + "mediumspringgreen": "#00fa9a", + "mediumturquoise": "#48d1cc", + "mediumvioletred": "#c71585", + "midnightblue": "#191970", + "mintcream": "#f5fffa", + "mistyrose": "#ffe4e1", + "moccasin": "#ffe4b5", + "navajowhite": "#ffdead", + "navy": "#000080", + "oldlace": "#fdf5e6", + "olive": "#808000", + "olivedrab": "#6b8e23", + "orange": "#ffa500", + "orangered": "#ff4500", + "orchid": "#da70d6", + "palegoldenrod": "#eee8aa", + "palegreen": "#98fb98", + "paleturquoise": "#afeeee", + "palevioletred": "#d87093", + "papayawhip": "#ffefd5", + "peachpuff": "#ffdab9", + "peru": "#cd853f", + "pink": "#ffc0cb", + "plum": "#dda0dd", + "powderblue": "#b0e0e6", + "purple": "#800080", + "red": "#ff0000", + "rosybrown": "#bc8f8f", + "royalblue": "#4169e1", + "saddlebrown": "#8b4513", + "salmon": "#fa8072", + "sandybrown": "#f4a460", + "seagreen": "#2e8b57", + "seashell": "#fff5ee", + "sienna": "#a0522d", + "silver": "#c0c0c0", + "skyblue": "#87ceeb", + "slateblue": "#6a5acd", + "slategray": "#708090", + "snow": "#fffafa", + "springgreen": "#00ff7f", + "steelblue": "#4682b4", + "tan": "#d2b48c", + "teal": "#008080", + "thistle": "#d8bfd8", + "tomato": "#ff6347", + "turquoise": "#40e0d0", + "violet": "#ee82ee", + "wheat": "#f5deb3", + "white": "#ffffff", + "whitesmoke": "#f5f5f5", + "yellow": "#ffff00", + "yellowgreen": "#9acd32", + "darkgrey": "#a9a9a9", + "darkslategrey": "#2f4f4f", + "dimgrey": "#696969", + "grey": "#808080", + "lightgray": "#d3d3d3", + "lightslategrey": "#778899", + "slategrey": "#708090" + }; + return definedColorNames[colorName]; + } + }; + + /** + * Make the target element movable by clicking and dragging on the handle. This is not a general purprose function, + * it makes several options specific to igv dialogs, the primary one being that the + * target is absolutely positioned in pixel coordinates + + */ + let dragData$1; // Its assumed we are only dragging one element at a time. + + function makeDraggable$1(target, handle) { + handle.addEventListener('mousedown', dragStart$1.bind(target)); + } + + function dragStart$1(event) { + event.stopPropagation(); + event.preventDefault(); + offset$1(this); + const dragFunction = drag$1.bind(this); + const dragEndFunction = dragEnd$1.bind(this); + const computedStyle = getComputedStyle(this); + const top = parseInt(computedStyle.top.replace("px", "")); + const left = parseInt(computedStyle.left.replace("px", "")); + dragData$1 = { + dragFunction: dragFunction, + dragEndFunction: dragEndFunction, + screenX: event.screenX, + screenY: event.screenY, + top: top, + left: left + }; + document.addEventListener('mousemove', dragFunction); + document.addEventListener('mouseup', dragEndFunction); + document.addEventListener('mouseleave', dragEndFunction); + document.addEventListener('mouseexit', dragEndFunction); + } + + function drag$1(event) { + if (!dragData$1) { + return; + } + + event.stopPropagation(); + event.preventDefault(); + const dx = event.screenX - dragData$1.screenX; + const dy = event.screenY - dragData$1.screenY; + this.style.left = `${dragData$1.left + dx}px`; + this.style.top = `${dragData$1.top + dy}px`; + } + + function dragEnd$1(event) { + if (!dragData$1) { + return; + } + + event.stopPropagation(); + event.preventDefault(); + const dragFunction = dragData$1.dragFunction; + const dragEndFunction = dragData$1.dragEndFunction; + document.removeEventListener('mousemove', dragFunction); + document.removeEventListener('mouseup', dragEndFunction); + document.removeEventListener('mouseleave', dragEndFunction); + document.removeEventListener('mouseexit', dragEndFunction); + dragData$1 = undefined; + } + + // Support for oauth token based authorization + // This class supports explicit setting of an oauth token either globally or for specific hosts. + // + // The variable oauth.google.access_token, which becomes igv.oauth.google.access_token on ES5 conversion is + // supported for backward compatibility + const DEFAULT_HOST = "googleapis"; + const oauth = { + oauthTokens: {}, + setToken: function (token, host) { + host = host || DEFAULT_HOST; + this.oauthTokens[host] = token; + + if (host === DEFAULT_HOST) { + this.google.access_token = token; // legacy support + } + }, + getToken: function (host) { + host = host || DEFAULT_HOST; + let token; + + for (let key of Object.keys(this.oauthTokens)) { + const regex = wildcardToRegExp(key); + + if (regex.test(host)) { + token = this.oauthTokens[key]; + break; + } + } + + return token; + }, + removeToken: function (host) { + host = host || DEFAULT_HOST; + + for (let key of Object.keys(this.oauthTokens)) { + const regex = wildcardToRegExp(key); + + if (regex.test(host)) { + this.oauthTokens[key] = undefined; + } + } + + if (host === DEFAULT_HOST) { + this.google.access_token = undefined; // legacy support + } + }, + // Special object for google -- legacy support + google: { + setToken: function (token) { + oauth.setToken(token); + } + } + }; + /** + * Creates a RegExp from the given string, converting asterisks to .* expressions, + * and escaping all other characters. + * + * credit https://gist.github.com/donmccurdy/6d073ce2c6f3951312dfa45da14a420f + */ + + function wildcardToRegExp(s) { + return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$'); + } + /** + * RegExp-escapes all characters in the given string. + * + * credit https://gist.github.com/donmccurdy/6d073ce2c6f3951312dfa45da14a420f + */ + + + function regExpEscape(s) { + return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); + } + + // The MIT License (MIT) + /** + * @constructor + * @param {Object} options A set op options to pass to the throttle function + * @param {number} requestsPerSecond The amount of requests per second + * the library will limit to + */ + + class Throttle$1 { + 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. + */ + var NONE = 0; + var GZIP = 1; + var BGZF = 2; + var UNKNOWN = 3; + let RANGE_WARNING_GIVEN = false; + const googleThrottle = new Throttle$1({ + requestsPerSecond: 8 + }); + const igvxhr = { + apiKey: undefined, + setApiKey: function (key) { + this.apiKey = key; + }, + load: load, + loadArrayBuffer: async function (url, options) { + options = options || {}; + + if (!options.responseType) { + options.responseType = "arraybuffer"; + } + + if (url instanceof File) { + return loadFileSlice(url, options); + } else { + return load(url, options); + } + }, + loadJson: async function (url, options) { + options = options || {}; + const method = options.method || (options.sendData ? "POST" : "GET"); + + if (method === "POST") { + options.contentType = "application/json"; + } + + const result = await this.loadString(url, options); + + if (result) { + return JSON.parse(result); + } else { + return result; + } + }, + loadString: async function (path, options) { + options = options || {}; + + if (path instanceof File) { + return loadStringFromFile(path, options); + } else { + return loadStringFromUrl(path, options); + } + } + }; + + async function load(url, options) { + options = options || {}; + const urlType = typeof url; // Resolve functions, promises, and functions that return promises + + url = await (typeof url === 'function' ? url() : url); + + if (url instanceof File) { + return loadFileSlice(url, options); + } else if (typeof url.startsWith === 'function') { + // Test for string + if (url.startsWith("data:")) { + return decodeDataURI$1(url); + } else { + if (url.startsWith("https://drive.google.com")) { + url = driveDownloadURL(url); + } + + if (isGoogleDriveURL(url) || url.startsWith("https://www.dropbox.com")) { + return googleThrottle.add(async function () { + return loadURL(url, options); + }); + } else { + return loadURL(url, options); + } + } + } else { + throw Error(`url must be either a 'File', 'string', 'function', or 'Promise'. Actual type: ${urlType}`); + } + } + + async function loadURL(url, options) { + //console.log(`${Date.now()} ${url}`) + url = mapUrl(url); + options = options || {}; + let oauthToken = options.oauthToken || getOauthToken(url); + + if (oauthToken) { + oauthToken = await (typeof oauthToken === 'function' ? oauthToken() : oauthToken); + } + + return new Promise(function (resolve, reject) { + // Various Google tansformations + if (isGoogleURL(url)) { + if (isGoogleStorageURL(url)) { + url = translateGoogleCloudURL(url); + } + + url = addApiKey(url); + + if (isGoogleDriveURL(url)) { + addTeamDrive(url); + } // If we have an access token try it, but don't force a signIn or request for scopes yet + + + if (!oauthToken) { + oauthToken = getCurrentGoogleAccessToken(); + } + } + + const headers = options.headers || {}; + + if (oauthToken) { + addOauthHeaders(headers, oauthToken); + } + + const range = options.range; + const isChrome = navigator.userAgent.indexOf('Chrome') > -1; + navigator.vendor.indexOf("Apple") === 0 && /\sSafari\//.test(navigator.userAgent); + + if (range && isChrome && !isAmazonV4Signed(url)) { + // Hack to prevent caching for byte-ranges. Attempt to fix net:err-cache errors in Chrome + url += url.includes("?") ? "&" : "?"; + url += "someRandomSeed=" + Math.random().toString(36); + } + + const xhr = new XMLHttpRequest(); + const sendData = options.sendData || options.body; + const method = options.method || (sendData ? "POST" : "GET"); + const responseType = options.responseType; + const contentType = options.contentType; + const mimeType = options.mimeType; + xhr.open(method, url); + + if (options.timeout) { + xhr.timeout = options.timeout; + } + + if (range) { + var rangeEnd = range.size ? range.start + range.size - 1 : ""; + xhr.setRequestHeader("Range", "bytes=" + range.start + "-" + rangeEnd); // xhr.setRequestHeader("Cache-Control", "no-cache"); <= This can cause CORS issues, disabled for now + } + + if (contentType) { + xhr.setRequestHeader("Content-Type", contentType); + } + + if (mimeType) { + xhr.overrideMimeType(mimeType); + } + + if (responseType) { + xhr.responseType = responseType; + } + + if (headers) { + for (let key of Object.keys(headers)) { + const value = headers[key]; + xhr.setRequestHeader(key, value); + } + } // NOTE: using withCredentials with servers that return "*" for access-allowed-origin will fail + + + if (options.withCredentials === true) { + xhr.withCredentials = true; + } + + xhr.onload = async function (event) { + // when the url points to a local file, the status is 0 but that is not an error + if (xhr.status === 0 || xhr.status >= 200 && xhr.status <= 300) { + if (range && xhr.status !== 206 && range.start !== 0) { + // For small files a range starting at 0 can return the whole file => 200 + // Provide just the slice we asked for, throw out the rest quietly + // If file is large warn user + if (xhr.response.length > 100000 && !RANGE_WARNING_GIVEN) { + alert(`Warning: Range header ignored for URL: ${url}. This can have performance impacts.`); + } + + resolve(xhr.response.slice(range.start, range.start + range.size)); + } else { + resolve(xhr.response); + } + } else if (typeof gapi !== "undefined" && (xhr.status === 404 || xhr.status === 401 || xhr.status === 403) && isGoogleURL(url) && !options.retries) { + tryGoogleAuth(); + } else { + if (xhr.status === 403) { + handleError("Access forbidden: " + url); + } else if (xhr.status === 416) { + // Tried to read off the end of the file. This shouldn't happen, but if it does return an + handleError("Unsatisfiable range"); + } else { + handleError(xhr.status); + } + } + }; + + xhr.onerror = function (event) { + if (isGoogleURL(url) && !options.retries) { + tryGoogleAuth(); + } + + handleError("Error accessing resource: " + url + " Status: " + xhr.status); + }; + + xhr.ontimeout = function (event) { + handleError("Timed out"); + }; + + xhr.onabort = function (event) { + reject(event); + }; + + try { + xhr.send(sendData); + } catch (e) { + reject(e); + } + + function handleError(error) { + if (reject) { + reject(error); + } else { + throw error; + } + } + + async function tryGoogleAuth() { + try { + const accessToken = await fetchGoogleAccessToken(url); + options.retries = 1; + options.oauthToken = accessToken; + const response = await load(url, options); + resolve(response); + } catch (e) { + if (e.error) { + const msg = e.error.startsWith("popup_blocked") ? "Google login popup blocked by browser." : e.error; + alert(msg); + } else { + handleError(e); + } + } + } + }); + } + + async function loadFileSlice(localfile, options) { + let blob = options && options.range ? localfile.slice(options.range.start, options.range.start + options.range.size) : localfile; + + if ("arraybuffer" === options.responseType) { + return blobToArrayBuffer(blob); + } else { + // binary string format, shouldn't be used anymore + return new Promise(function (resolve, reject) { + const fileReader = new FileReader(); + + fileReader.onload = function (e) { + resolve(fileReader.result); + }; + + fileReader.onerror = function (e) { + console.error("reject uploading local file " + localfile.name); + reject(null, fileReader); + }; + + fileReader.readAsBinaryString(blob); + console.warn("Deprecated method used: readAsBinaryString"); + }); + } + } + + async function loadStringFromFile(localfile, options) { + const blob = options.range ? localfile.slice(options.range.start, options.range.start + options.range.size) : localfile; + let compression = NONE; + + if (options && options.bgz || localfile.name.endsWith(".bgz")) { + compression = BGZF; + } else if (localfile.name.endsWith(".gz")) { + compression = GZIP; + } + + if (compression === NONE) { + return blobToText(blob); + } else { + const arrayBuffer = await blobToArrayBuffer(blob); + return arrayBufferToString(arrayBuffer, compression); + } + } + + async function blobToArrayBuffer(blob) { + if (typeof blob.arrayBuffer === 'function') { + return blob.arrayBuffer(); + } + + return new Promise(function (resolve, reject) { + const fileReader = new FileReader(); + + fileReader.onload = function (e) { + resolve(fileReader.result); + }; + + fileReader.onerror = function (e) { + console.error("reject uploading local file " + localfile.name); + reject(null, fileReader); + }; + + fileReader.readAsArrayBuffer(blob); + }); + } + + async function blobToText(blob) { + if (typeof blob.text === 'function') { + return blob.text(); + } + + return new Promise(function (resolve, reject) { + const fileReader = new FileReader(); + + fileReader.onload = function (e) { + resolve(fileReader.result); + }; + + fileReader.onerror = function (e) { + console.error("reject uploading local file " + localfile.name); + reject(null, fileReader); + }; + + fileReader.readAsText(blob); + }); + } + + async function loadStringFromUrl(url, options) { + options = options || {}; + const fn = options.filename || (await getFilename$1(url)); + let compression = UNKNOWN; + + if (options.bgz) { + compression = BGZF; + } else if (fn.endsWith(".gz")) { + compression = GZIP; + } + + options.responseType = "arraybuffer"; + const data = await igvxhr.load(url, options); + return arrayBufferToString(data, compression); + } + + function isAmazonV4Signed(url) { + return url.indexOf("X-Amz-Signature") > -1; + } + + function getOauthToken(url) { + // Google is the default provider, don't try to parse host for google URLs + const host = isGoogleURL(url) ? undefined : parseUri(url).host; + let token = oauth.getToken(host); + + if (token) { + return token; + } else if (host === undefined) { + const googleToken = getCurrentGoogleAccessToken(); + + if (googleToken && googleToken.expires_at > Date.now()) { + return googleToken.access_token; + } + } + } + /** + * Return a Google oAuth token, triggering a sign in if required. This method should not be called until we know + * a token is required, that is until we've tried the url and received a 401, 403, or 404. + * + * @param url + * @returns the oauth token + */ + + + async function fetchGoogleAccessToken(url) { + if (isInitialized()) { + const scope = getScopeForURL(url); + const googleToken = await getAccessToken(scope); + return googleToken ? googleToken.access_token : undefined; + } else { + throw Error(`Authorization is required, but Google oAuth has not been initalized. Contact your site administrator for assistance.`); + } + } + /** + * Return the current google access token, if one exists. Do not triger signOn or request additional scopes. + * @returns {undefined|access_token} + */ + + + function getCurrentGoogleAccessToken() { + if (isInitialized()) { + const googleToken = getCurrentAccessToken(); + return googleToken ? googleToken.access_token : undefined; + } else { + return undefined; + } + } + + function addOauthHeaders(headers, acToken) { + if (acToken) { + headers["Cache-Control"] = "no-cache"; + headers["Authorization"] = "Bearer " + acToken; + } + + return headers; + } + + function addApiKey(url) { + let apiKey = igvxhr.apiKey; + + if (!apiKey && typeof gapi !== "undefined") { + apiKey = gapi.apiKey; + } + + if (apiKey !== undefined && !url.includes("key=")) { + const paramSeparator = url.includes("?") ? "&" : "?"; + url = url + paramSeparator + "key=" + apiKey; + } + + return url; + } + + function addTeamDrive(url) { + if (url.includes("supportsTeamDrive")) { + return url; + } else { + const paramSeparator = url.includes("?") ? "&" : "?"; + url = url + paramSeparator + "supportsTeamDrive=true"; + } + } + /** + * Perform some well-known url mappings. + * @param url + */ + + + function mapUrl(url) { + if (url.includes("//www.dropbox.com")) { + return url.replace("//www.dropbox.com", "//dl.dropboxusercontent.com"); + } else if (url.includes("//drive.google.com")) { + return driveDownloadURL(url); + } else if (url.includes("//www.broadinstitute.org/igvdata")) { + return url.replace("//www.broadinstitute.org/igvdata", "//data.broadinstitute.org/igvdata"); + } else if (url.includes("//igvdata.broadinstitute.org")) { + return url.replace("//igvdata.broadinstitute.org", "https://dn7ywbm9isq8j.cloudfront.net"); + } else if (url.startsWith("ftp://ftp.ncbi.nlm.nih.gov/geo")) { + return url.replace("ftp://", "https://"); + } else { + return url; + } + } + + function arrayBufferToString(arraybuffer, compression) { + if (compression === UNKNOWN && arraybuffer.byteLength > 2) { + const m = new Uint8Array(arraybuffer, 0, 2); + + if (m[0] === 31 && m[1] === 139) { + compression = GZIP; + } + } + + let plain; + + if (compression === GZIP) { + const inflate = new Zlib$1.Gunzip(new Uint8Array(arraybuffer)); + plain = inflate.decompress(); + } else if (compression === BGZF) { + plain = unbgzf(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; + } + } + + async function getFilename$1(url) { + if (isString$6(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); + } + } + + /* + * 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); + + function logNode(node, indent) { + if (node.left !== NIL) logNode(node.left); + if (node.right !== NIL) logNode(node.right); + } + } + + 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 { + constructor(featureList, genome, range) { + featureList = featureList || []; + this.treeMap = this.buildTreeMap(featureList, genome); + this.range = range; + this.count = featureList.length; + } + + containsRange(genomicRange) { + // No range means cache contains all features + return this.range === undefined || this.range.contains(genomicRange.chr, genomicRange.start, genomicRange.end); + } + + queryFeatures(chr, start, end) { + const tree = this.treeMap[chr]; + if (!tree) return []; + const intervals = tree.findOverlapping(start, end); + + if (intervals.length === 0) { + return []; + } else { + // Trim the list of features in the intervals to those + // overlapping the requested range. + // Assumption: features are sorted by start position + const featureList = []; + const all = this.allFeatures[chr]; + + if (all) { + for (let interval of intervals) { + const indexRange = interval.value; + + for (let i = indexRange.start; i < indexRange.end; i++) { + let feature = all[i]; + if (feature.start > end) break;else if (feature.end >= start) { + featureList.push(feature); + } + } + } + + featureList.sort(function (a, b) { + return a.start - b.start; + }); + } + + return featureList; + } + } + + /** + * Returns all features, unsorted. + * + * @returns {Array} + */ + getAllFeatures() { + return this.allFeatures; + } + + buildTreeMap(featureList, genome) { + const treeMap = {}; + const chromosomes = []; + this.allFeatures = {}; + + if (featureList) { + for (let feature of featureList) { + let chr = feature.chr; // Translate to "official" name + + if (genome) { + chr = genome.getChromosomeName(chr); + } + + let geneList = this.allFeatures[chr]; + + if (!geneList) { + chromosomes.push(chr); + geneList = []; + this.allFeatures[chr] = geneList; + } + + geneList.push(feature); + } // Now build interval tree for each chromosome + + + for (let chr of chromosomes) { + const chrFeatures = this.allFeatures[chr]; + chrFeatures.sort(function (f1, f2) { + return f1.start === f2.start ? 0 : f1.start > f2.start ? 1 : -1; + }); + treeMap[chr] = buildIntervalTree$1(chrFeatures); + } + } + + return treeMap; + } + + } + /** + * Build an interval tree from the feature list for fast interval based queries. We lump features in groups + * of 10, or total size / 100, to reduce size of the tree. + * + * @param featureList + */ + + + function buildIntervalTree$1(featureList) { + const tree = new IntervalTree(); + const len = featureList.length; + const chunkSize = Math.max(10, Math.round(len / 10)); + + for (let i = 0; i < len; i += chunkSize) { + const e = Math.min(len, i + chunkSize); + const subArray = new IndexRange(i, e); //featureList.slice(i, e); + + const iStart = featureList[i].start; // + + let iEnd = iStart; + + for (let j = i; j < e; j++) { + iEnd = Math.max(iEnd, featureList[j].end); + } + + tree.insert(iStart, iEnd, subArray); + } + + return tree; + } + + class IndexRange { + constructor(start, end) { + this.start = start; + this.end = end; + } + + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 The Regents of the University of California + * Author: Jim Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + const FeatureUtils = { + packFeatures: function (features, maxRows, sorted) { + var start; + var end; + if (!features) return; + maxRows = maxRows || 10000; + + if (!sorted) { + features.sort(function (a, b) { + return a.start - b.start; + }); + } + + if (features.length === 0) { + return []; + } else { + var bucketList = [], + allocatedCount = 0, + lastAllocatedCount = 0, + nextStart, + row, + index, + bucket, + feature, + gap = 2, + bucketStart; + start = features[0].start; + end = features[features.length - 1].start; + bucketStart = Math.max(start, features[0].start); + nextStart = bucketStart; + features.forEach(function (alignment) { + var buckListIndex = Math.max(0, alignment.start - bucketStart); + + if (bucketList[buckListIndex] === undefined) { + bucketList[buckListIndex] = []; + } + + bucketList[buckListIndex].push(alignment); + }); + row = 0; + + while (allocatedCount < features.length && row <= maxRows) { + while (nextStart <= end) { + bucket = undefined; + + while (!bucket && nextStart <= end) { + index = nextStart - bucketStart; + + if (bucketList[index] === undefined) { + ++nextStart; // No buckets at this index + } else { + bucket = bucketList[index]; + } + } // while (bucket) + + + if (!bucket) { + break; + } + + feature = bucket.pop(); + + if (0 === bucket.length) { + bucketList[index] = undefined; + } + + feature.row = row; + nextStart = feature.end + gap; + ++allocatedCount; + } // while (nextStart) + + + row++; + nextStart = bucketStart; + if (allocatedCount === lastAllocatedCount) break; // Protect from infinite loops + + lastAllocatedCount = allocatedCount; + } // while (allocatedCount) + + } + }, + + /** + * Find features overlapping the given interval. It is assumed that all features share the same chromosome. + * + * TODO -- significant overlap with FeatureCache, refactor to combine + * + * @param featureList + * @param start + * @param end + */ + findOverlapping: function (featureList, start, end) { + if (!featureList || featureList.length === 0) { + return []; + } else { + const tree = buildIntervalTree(featureList); + const intervals = tree.findOverlapping(start, end); + + if (intervals.length === 0) { + return []; + } else { + // Trim the list of features in the intervals to those + // overlapping the requested range. + // Assumption: features are sorted by start position + featureList = []; + intervals.forEach(function (interval) { + const intervalFeatures = interval.value; + const len = intervalFeatures.length; + + for (let i = 0; i < len; i++) { + const feature = intervalFeatures[i]; + if (feature.start > end) break;else if (feature.end > start) { + featureList.push(feature); + } + } + }); + featureList.sort(function (a, b) { + return a.start - b.start; + }); + return featureList; + } + } + } + }; + /** + * Build an interval tree from the feature list for fast interval based queries. We lump features in groups + * of 10, or total size / 100, to reduce size of the tree. + * + * @param featureList + */ + + function buildIntervalTree(featureList) { + const tree = new IntervalTree(); + const len = featureList.length; + const chunkSize = Math.max(10, Math.round(len / 100)); + featureList.sort(function (f1, f2) { + return f1.start === f2.start ? 0 : f1.start > f2.start ? 1 : -1; + }); + + for (let i = 0; i < len; i += chunkSize) { + const e = Math.min(len, i + chunkSize); + const subArray = featureList.slice(i, e); + const iStart = subArray[0].start; + let iEnd = iStart; + subArray.forEach(function (feature) { + iEnd = Math.max(iEnd, feature.end); + }); + tree.insert(iStart, iEnd, subArray); + } + + return tree; + } + + function createCheckbox$1(name, initialState) { + const $container = $('
', { + class: 'igv-menu-popup-check-container' + }); + const $div = $('
'); + $container.append($div); + const svg = createIcon$2('check', true === initialState ? '#444' : 'transparent'); + $div.append($(svg)); + const $label = $('
' + /*, { class: 'igv-some-label-class' }*/ + ); + $label.text(name); + $container.append($label); + return $container; + } + + function createIcon$1(name, color) { + return $(createIcon$2(name, color)); + } + + /** + * 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" + })); + menuItems.push(colorPickerMenuItem({ + trackView, + label: "Set alt color", + option: "altColor" + })); + } + + if (trackView.track.menuItemList) { + menuItems = menuItems.concat(trackView.track.menuItemList()); + } + + if (hasVizWindow || vizWindowTypes.has(trackView.track.type)) { + menuItems.push('
'); + menuItems.push(visibilityWindowMenuItem(trackView)); + } + + if (trackView.track.removable !== false) { + menuItems.push('
'); + menuItems.push(trackRemovalMenuItem(trackView)); + } + + return menuItems; + }, + numericDataMenuItems: function (trackView) { + const menuItems = []; // Data range + + const $e = $('
'); + $e.text('Set data range'); + + const clickHandler = function () { + trackView.browser.dataRangeDialog.configure(trackView); + trackView.browser.dataRangeDialog.present($(trackView.trackDiv)); + }; + + menuItems.push({ + object: $e, + click: clickHandler + }); + + if (trackView.track.logScale !== undefined) { + menuItems.push({ + object: createCheckbox$1("Log scale", trackView.track.logScale), + click: () => { + trackView.track.logScale = !trackView.track.logScale; + trackView.repaintViews(); + } + }); + } + + menuItems.push({ + object: createCheckbox$1("Autoscale", trackView.track.autoscale), + click: () => { + trackView.track.autoscale = !trackView.track.autoscale; + trackView.updateViews(); + } + }); + return menuItems; + }, + trackMenuItemListHelper: function (itemList, $popover) { + 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 = $('
'); + $e.text(item.name); + } else if (item.object) { + $e = item.object; + } else if (typeof item.label === 'string') { + $e = $('
'); + $e.html(item.label); + } else if (typeof item === 'string') { + if (item.startsWith("<")) { + $e = $(item); + } else { + $e = $("
" + 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); + $popover.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; + } + + }; + + 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 = $('
'); + object.text('Set visibility window'); + return { + object, + click + }; + } + + function trackRemovalMenuItem(trackView) { + var $e, menuClickHandler; + $e = $('
'); + $e.text('Remove track'); + + menuClickHandler = function () { + trackView.browser.removeTrack(trackView.track); + }; + + return { + object: $e, + click: menuClickHandler + }; + } + + function colorPickerMenuItem({ + trackView, + label, + option + }) { + const $e = $('
'); + $e.text(label); + return { + object: $e, + click: () => trackView.presentColorPicker(option) + }; + } + + function unsetColorMenuItem({ + trackView, + label + }) { + const $e = $('
'); + $e.text(label); + return { + object: $e, + click: () => { + trackView.track.color = undefined; + trackView.repaintViews(); + } + }; + } + + function trackRenameMenuItem(trackView) { + const click = e => { + const callback = function () { + let value = trackView.browser.inputDialog.input.value; + value = '' === value || undefined === value ? 'untitled' : value.trim(); + trackView.browser.setTrackLabelName(trackView, value); + }; + + const config = { + label: 'Track Name', + value: getTrackLabelText(trackView.track) || 'unnamed', + callback + }; + trackView.browser.inputDialog.present(config, e); + }; + + const object = $('
'); + object.text('Set track name'); + return { + object, + click + }; + } + + function trackHeightMenuItem(trackView) { + const click = e => { + const callback = () => { + const number = parseFloat(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); // Explicitly setting track height turns off autoHeight + + trackView.track.autoHeight = false; + } + }; + + const config = { + label: 'Track Height', + value: trackView.trackDiv.clientHeight, + callback + }; + trackView.browser.inputDialog.present(config, e); + }; + + const object = $('
'); + object.text('Set track height'); + return { + object, + click + }; + } + + function getTrackLabelText(track) { + var vp, txt; + vp = track.trackView.viewports[0]; + txt = vp.$trackLabel.text(); + return txt; + } + + function div(options) { + return create("div", options); + } + + function create(tag, options) { + const elem = document.createElement(tag); + + if (options) { + if (options.class) { + elem.classList.add(options.class); + } + + if (options.id) { + elem.id = options.id; + } + + if (options.style) { + applyStyle(elem, options.style); + } + } + + return elem; + } + + function hide(elem) { + const cssStyle = getComputedStyle(elem); + + if (cssStyle.display !== "none") { + elem._initialDisplay = cssStyle.display; + } + + elem.style.display = "none"; + } + + function show(elem) { + const currentDisplay = getComputedStyle(elem).display; + + if (currentDisplay === "none") { + const d = elem._initialDisplay || "block"; + elem.style.display = d; + } + } + + function offset(elem) { + // Return zeros for disconnected and hidden (display: none) elements (gh-2310) + // Support: IE <=11 only + // Running getBoundingClientRect on a + // disconnected node in IE throws an error + if (!elem.getClientRects().length) { + return { + top: 0, + left: 0 + }; + } // Get document-relative position by adding viewport scroll to viewport-relative gBCR + + + const rect = elem.getBoundingClientRect(); + const win = elem.ownerDocument.defaultView; + return { + top: rect.top + win.pageYOffset, + left: rect.left + win.pageXOffset + }; + } + + function pageCoordinates(e) { + if (e.type.startsWith("touch")) { + const touch = e.touches[0]; + return { + x: touch.pageX, + y: touch.pageY + }; + } else { + return { + x: e.pageX, + y: e.pageY + }; + } + } + + function applyStyle(elem, style) { + for (let key of Object.keys(style)) { + elem.style[key] = style[key]; + } + } + + let getMouseXY = (domElement, { + clientX, + clientY + }) => { + // DOMRect object with eight properties: left, top, right, bottom, x, y, width, height + const { + left, + top, + width, + height + } = domElement.getBoundingClientRect(); + const x = clientX - left; + const y = clientY - top; + return { + x, + y, + xNormalized: x / width, + yNormalized: y / height, + width, + height + }; + }; + /** + * Translate the mouse coordinates for the event to the coordinates for the given target element + * @param event + * @param domElement + * @returns {{x: number, y: number}} + */ + + + function translateMouseCoordinates(event, domElement) { + const { + clientX, + clientY + } = event; + return getMouseXY(domElement, { + clientX, + clientY + }); + } + + function createCheckbox(name, initialState) { + const container = div({ + class: 'igv-ui-trackgear-popover-check-container' + }); + const svg = iconMarkup('check', true === initialState ? '#444' : 'transparent'); + svg.style.borderColor = 'gray'; + svg.style.borderWidth = '1px'; + svg.style.borderStyle = 'solid'; + container.appendChild(svg); + let label = div(); //{ class: 'igv-some-label-class' }); + + label.textContent = name; + container.appendChild(label); + return container; + } + + function createIcon(name, color) { + return iconMarkup(name, color); + } + + function iconMarkup(name, color) { + color = color || "currentColor"; + let icon = icons[name]; + + if (!icon) { + console.error(`No icon named: ${name}`); + icon = icons["question"]; + } + + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.setAttributeNS(null, 'viewBox', '0 0 ' + icon[0] + ' ' + icon[1]); + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttributeNS(null, 'fill', color); + path.setAttributeNS(null, 'd', icon[4]); + svg.appendChild(path); + return svg; + } + + const icons = { + "check": [512, 512, [], "f00c", "M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"], + "cog": [512, 512, [], "f013", "M444.788 291.1l42.616 24.599c4.867 2.809 7.126 8.618 5.459 13.985-11.07 35.642-29.97 67.842-54.689 94.586a12.016 12.016 0 0 1-14.832 2.254l-42.584-24.595a191.577 191.577 0 0 1-60.759 35.13v49.182a12.01 12.01 0 0 1-9.377 11.718c-34.956 7.85-72.499 8.256-109.219.007-5.49-1.233-9.403-6.096-9.403-11.723v-49.184a191.555 191.555 0 0 1-60.759-35.13l-42.584 24.595a12.016 12.016 0 0 1-14.832-2.254c-24.718-26.744-43.619-58.944-54.689-94.586-1.667-5.366.592-11.175 5.459-13.985L67.212 291.1a193.48 193.48 0 0 1 0-70.199l-42.616-24.599c-4.867-2.809-7.126-8.618-5.459-13.985 11.07-35.642 29.97-67.842 54.689-94.586a12.016 12.016 0 0 1 14.832-2.254l42.584 24.595a191.577 191.577 0 0 1 60.759-35.13V25.759a12.01 12.01 0 0 1 9.377-11.718c34.956-7.85 72.499-8.256 109.219-.007 5.49 1.233 9.403 6.096 9.403 11.723v49.184a191.555 191.555 0 0 1 60.759 35.13l42.584-24.595a12.016 12.016 0 0 1 14.832 2.254c24.718 26.744 43.619 58.944 54.689 94.586 1.667 5.366-.592 11.175-5.459 13.985L444.788 220.9a193.485 193.485 0 0 1 0 70.2zM336 256c0-44.112-35.888-80-80-80s-80 35.888-80 80 35.888 80 80 80 80-35.888 80-80z"], + "exclamation": [192, 512, [], "f12a", "M176 432c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zM25.26 25.199l13.6 272C39.499 309.972 50.041 320 62.83 320h66.34c12.789 0 23.331-10.028 23.97-22.801l13.6-272C167.425 11.49 156.496 0 142.77 0H49.23C35.504 0 24.575 11.49 25.26 25.199z"], + "exclamation-circle": [512, 512, [], "f06a", "M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"], + "exclamation-triangle": [576, 512, [], "f071", "M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"], + "minus": [448, 512, [], "f068", "M424 318.2c13.3 0 24-10.7 24-24v-76.4c0-13.3-10.7-24-24-24H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h400z"], + "minus-circle": [512, 512, [], "f056", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zM124 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H124z"], + "minus-square": [448, 512, [], "f146", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zM92 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H92z"], + "plus": [448, 512, [], "f067", "M448 294.2v-76.4c0-13.3-10.7-24-24-24H286.2V56c0-13.3-10.7-24-24-24h-76.4c-13.3 0-24 10.7-24 24v137.8H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h137.8V456c0 13.3 10.7 24 24 24h76.4c13.3 0 24-10.7 24-24V318.2H424c13.3 0 24-10.7 24-24z"], + "plus-circle": [512, 512, [], "f055", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"], + "plus-square": [448, 512, [], "f0fe", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"], + "question": [384, 512, [], "f128", "M202.021 0C122.202 0 70.503 32.703 29.914 91.026c-7.363 10.58-5.093 25.086 5.178 32.874l43.138 32.709c10.373 7.865 25.132 6.026 33.253-4.148 25.049-31.381 43.63-49.449 82.757-49.449 30.764 0 68.816 19.799 68.816 49.631 0 22.552-18.617 34.134-48.993 51.164-35.423 19.86-82.299 44.576-82.299 106.405V320c0 13.255 10.745 24 24 24h72.471c13.255 0 24-10.745 24-24v-5.773c0-42.86 125.268-44.645 125.268-160.627C377.504 66.256 286.902 0 202.021 0zM192 373.459c-38.196 0-69.271 31.075-69.271 69.271 0 38.195 31.075 69.27 69.271 69.27s69.271-31.075 69.271-69.271-31.075-69.27-69.271-69.27z"], + "save": [448, 512, [], "f0c7", "M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM224 416c-35.346 0-64-28.654-64-64 0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64zm96-304.52V212c0 6.627-5.373 12-12 12H76c-6.627 0-12-5.373-12-12V108c0-6.627 5.373-12 12-12h228.52c3.183 0 6.235 1.264 8.485 3.515l3.48 3.48A11.996 11.996 0 0 1 320 111.48z"], + "search": [512, 512, [], "f002", "M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"], + "share": [512, 512, [], "f064", "M503.691 189.836L327.687 37.851C312.281 24.546 288 35.347 288 56.015v80.053C127.371 137.907 0 170.1 0 322.326c0 61.441 39.581 122.309 83.333 154.132 13.653 9.931 33.111-2.533 28.077-18.631C66.066 312.814 132.917 274.316 288 272.085V360c0 20.7 24.3 31.453 39.687 18.164l176.004-152c11.071-9.562 11.086-26.753 0-36.328z"], + "spinner": [512, 512, [], "f110", "M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z"], + "square": [448, 512, [], "f0c8", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z"], + "square-full": [512, 512, [], "f45c", "M512 512H0V0h512v512z"], + "times": [384, 512, [], "f00d", "M323.1 441l53.9-53.9c9.4-9.4 9.4-24.5 0-33.9L279.8 256l97.2-97.2c9.4-9.4 9.4-24.5 0-33.9L323.1 71c-9.4-9.4-24.5-9.4-33.9 0L192 168.2 94.8 71c-9.4-9.4-24.5-9.4-33.9 0L7 124.9c-9.4 9.4-9.4 24.5 0 33.9l97.2 97.2L7 353.2c-9.4 9.4-9.4 24.5 0 33.9L60.9 441c9.4 9.4 24.5 9.4 33.9 0l97.2-97.2 97.2 97.2c9.3 9.3 24.5 9.3 33.9 0z"], + "times-circle": [512, 512, [], "f057", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"], + "wrench": [512, 512, [], "f0ad", "M481.156 200c9.3 0 15.12 10.155 10.325 18.124C466.295 259.992 420.419 288 368 288c-79.222 0-143.501-63.974-143.997-143.079C223.505 65.469 288.548-.001 368.002 0c52.362.001 98.196 27.949 123.4 69.743C496.24 77.766 490.523 88 481.154 88H376l-40 56 40 56h105.156zm-171.649 93.003L109.255 493.255c-24.994 24.993-65.515 24.994-90.51 0-24.993-24.994-24.993-65.516 0-90.51L218.991 202.5c16.16 41.197 49.303 74.335 90.516 90.503zM104 432c0-13.255-10.745-24-24-24s-24 10.745-24 24 10.745 24 24 24 24-10.745 24-24z"] + }; + + function attachDialogCloseHandlerWithParent(parent, closeHandler) { + var container = document.createElement("div"); + parent.appendChild(container); + container.appendChild(createIcon("times")); + container.addEventListener('click', function (e) { + e.preventDefault(); + e.stopPropagation(); + closeHandler(); + }); + } + /** + * @fileoverview Zlib namespace. Zlib รฃยยฎรคยปโ€ขรฆยงหœรฃยยซรฆยบโ€“รฆโ€น รฃยโ€”รฃยลธรฅล“ยงรงยธยฎรฃยยฏ Zlib.Deflate รฃยยงรฅยฎลธรจยฃโ€ฆ + * รฃยโ€ขรฃโ€šล’รฃยยฆรฃยโ€žรฃโ€šโ€น. รฃยโ€œรฃโ€šล’รฃยยฏ Inflate รฃยยจรฃยยฎรฅโ€ฆยฑรฅยญหœรฃโ€šโ€™รจโ‚ฌฦ’รฆโ€ฆยฎรฃยโ€”รฃยยฆรฃยโ€žรฃโ€šโ€นรงโ€šยบ. + */ + + + const ZLIB_STREAM_RAW_INFLATE_BUFFER_SIZE = 65000; + var Zlib = { + Huffman: {}, + Util: {}, + CRC32: {} + }; + /** + * Compression Method + * @enum {number} + */ + + Zlib.CompressionMethod = { + DEFLATE: 8, + RESERVED: 15 + }; + /** + * @param {Object=} opt_params options. + * @constructor + */ + + Zlib.Zip = function (opt_params) { + opt_params = opt_params || {}; + /** @type {Array.<{ + * buffer: !(Array.|Uint8Array), + * option: Object, + * compressed: boolean, + * encrypted: boolean, + * size: number, + * crc32: number + * }>} */ + + this.files = []; + /** @type {(Array.|Uint8Array)} */ + + this.comment = opt_params['comment']; + /** @type {(Array.|Uint8Array)} */ + + this.password; + }; + /** + * @enum {number} + */ + + + Zlib.Zip.CompressionMethod = { + STORE: 0, + DEFLATE: 8 + }; + /** + * @enum {number} + */ + + Zlib.Zip.OperatingSystem = { + MSDOS: 0, + UNIX: 3, + MACINTOSH: 7 + }; + /** + * @enum {number} + */ + + Zlib.Zip.Flags = { + ENCRYPT: 0x0001, + DESCRIPTOR: 0x0008, + UTF8: 0x0800 + }; + /** + * @type {Array.} + * @const + */ + + Zlib.Zip.FileHeaderSignature = [0x50, 0x4b, 0x01, 0x02]; + /** + * @type {Array.} + * @const + */ + + Zlib.Zip.LocalFileHeaderSignature = [0x50, 0x4b, 0x03, 0x04]; + /** + * @type {Array.} + * @const + */ + + Zlib.Zip.CentralDirectorySignature = [0x50, 0x4b, 0x05, 0x06]; + /** + * @param {Array.|Uint8Array} input + * @param {Object=} opt_params options. + */ + + Zlib.Zip.prototype.addFile = function (input, opt_params) { + opt_params = opt_params || {}; + /** @type {string} */ + + opt_params['filename']; + /** @type {boolean} */ + + var compressed; + /** @type {number} */ + + var size = input.length; + /** @type {number} */ + + var crc32 = 0; + + if (input instanceof Array) { + input = new Uint8Array(input); + } // default + + + if (typeof opt_params['compressionMethod'] !== 'number') { + opt_params['compressionMethod'] = Zlib.Zip.CompressionMethod.DEFLATE; + } // รฃยยรฃยยฎรฅ ยดรฃยยงรฅล“ยงรงยธยฎรฃยโ„ขรฃโ€šโ€นรฅ ยดรฅยห† + + + if (opt_params['compress']) { + switch (opt_params['compressionMethod']) { + case Zlib.Zip.CompressionMethod.STORE: + break; + + case Zlib.Zip.CompressionMethod.DEFLATE: + crc32 = Zlib.CRC32.calc(input); + input = this.deflateWithOption(input, opt_params); + compressed = true; + break; + + default: + throw new Error('unknown compression method:' + opt_params['compressionMethod']); + } + } + + this.files.push({ + buffer: input, + option: opt_params, + compressed: compressed, + encrypted: false, + size: size, + crc32: crc32 + }); + }; + /** + * @param {(Array.|Uint8Array)} password + */ + + + Zlib.Zip.prototype.setPassword = function (password) { + this.password = password; + }; + + Zlib.Zip.prototype.compress = function () { + /** @type {Array.<{ + * buffer: !(Array.|Uint8Array), + * option: Object, + * compressed: boolean, + * encrypted: boolean, + * size: number, + * crc32: number + * }>} */ + var files = this.files; + /** @type {{ + * buffer: !(Array.|Uint8Array), + * option: Object, + * compressed: boolean, + * encrypted: boolean, + * size: number, + * crc32: number + * }} */ + + var file; + /** @type {!(Array.|Uint8Array)} */ + + var output; + /** @type {number} */ + + var op1; + /** @type {number} */ + + var op2; + /** @type {number} */ + + var op3; + /** @type {number} */ + + var localFileSize = 0; + /** @type {number} */ + + var centralDirectorySize = 0; + /** @type {number} */ + + var endOfCentralDirectorySize; + /** @type {number} */ + + var offset; + /** @type {number} */ + + var needVersion; + /** @type {number} */ + + var flags; + /** @type {Zlib.Zip.CompressionMethod} */ + + var compressionMethod; + /** @type {Date} */ + + var date; + /** @type {number} */ + + var crc32; + /** @type {number} */ + + var size; + /** @type {number} */ + + var plainSize; + /** @type {number} */ + + var filenameLength; + /** @type {number} */ + + var extraFieldLength; + /** @type {number} */ + + var commentLength; + /** @type {(Array.|Uint8Array)} */ + + var filename; + /** @type {(Array.|Uint8Array)} */ + + var extraField; + /** @type {(Array.|Uint8Array)} */ + + var comment; + /** @type {(Array.|Uint8Array)} */ + + var buffer; + /** @type {*} */ + + var tmp; + /** @type {Array.|Uint32Array|Object} */ + + var key; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + /** @type {number} */ + + var j; + /** @type {number} */ + + var jl; // รฃฦ’โ€ขรฃโ€šยกรฃโ€šยครฃฦ’ยซรฃยยฎรฅล“ยงรงยธยฎ + + for (i = 0, il = files.length; i < il; ++i) { + file = files[i]; + filenameLength = file.option['filename'] ? file.option['filename'].length : 0; + extraFieldLength = file.option['extraField'] ? file.option['extraField'].length : 0; + commentLength = file.option['comment'] ? file.option['comment'].length : 0; // รฅล“ยงรงยธยฎรฃยโ€ขรฃโ€šล’รฃยยฆรฃยโ€žรฃยยชรฃยโ€นรฃยยฃรฃยลธรฃโ€šโ€ฐรฅล“ยงรงยธยฎ + + if (!file.compressed) { + // รฅล“ยงรงยธยฎรฅโ€ฐยรฃยยซ CRC32 รฃยยฎรจยจห†รงยฎโ€”รฃโ€šโ€™รฃยโ€”รฃยยฆรฃยล รฃยย + file.crc32 = Zlib.CRC32.calc(file.buffer); + + switch (file.option['compressionMethod']) { + case Zlib.Zip.CompressionMethod.STORE: + break; + + case Zlib.Zip.CompressionMethod.DEFLATE: + file.buffer = this.deflateWithOption(file.buffer, file.option); + file.compressed = true; + break; + + default: + throw new Error('unknown compression method:' + file.option['compressionMethod']); + } + } // encryption + + + if (file.option['password'] !== void 0 || this.password !== void 0) { + // init encryption + key = this.createEncryptionKey(file.option['password'] || this.password); // add header + + buffer = file.buffer; + { + tmp = new Uint8Array(buffer.length + 12); + tmp.set(buffer, 12); + buffer = tmp; + } + + for (j = 0; j < 12; ++j) { + buffer[j] = this.encode(key, i === 11 ? file.crc32 & 0xff : Math.random() * 256 | 0); + } // data encryption + + + for (jl = buffer.length; j < jl; ++j) { + buffer[j] = this.encode(key, buffer[j]); + } + + file.buffer = buffer; + } // รฅยฟโ€ฆรจยฆยรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šยตรฃโ€šยครฃโ€šยบรฃยยฎรจยจห†รงยฎโ€” + + + localFileSize += // local file header + 30 + filenameLength + // file data + file.buffer.length; + centralDirectorySize += // file header + 46 + filenameLength + commentLength; + } // end of central directory + + + endOfCentralDirectorySize = 22 + (this.comment ? this.comment.length : 0); + output = new Uint8Array(localFileSize + centralDirectorySize + endOfCentralDirectorySize); + op1 = 0; + op2 = localFileSize; + op3 = op2 + centralDirectorySize; // รฃฦ’โ€ขรฃโ€šยกรฃโ€šยครฃฦ’ยซรฃยยฎรฅล“ยงรงยธยฎ + + for (i = 0, il = files.length; i < il; ++i) { + file = files[i]; + filenameLength = file.option['filename'] ? file.option['filename'].length : 0; + extraFieldLength = 0; // TODO + + commentLength = file.option['comment'] ? file.option['comment'].length : 0; //------------------------------------------------------------------------- + // local file header & file header + //------------------------------------------------------------------------- + + offset = op1; // signature + // local file header + + output[op1++] = Zlib.Zip.LocalFileHeaderSignature[0]; + output[op1++] = Zlib.Zip.LocalFileHeaderSignature[1]; + output[op1++] = Zlib.Zip.LocalFileHeaderSignature[2]; + output[op1++] = Zlib.Zip.LocalFileHeaderSignature[3]; // file header + + output[op2++] = Zlib.Zip.FileHeaderSignature[0]; + output[op2++] = Zlib.Zip.FileHeaderSignature[1]; + output[op2++] = Zlib.Zip.FileHeaderSignature[2]; + output[op2++] = Zlib.Zip.FileHeaderSignature[3]; // compressor info + + needVersion = 20; + output[op2++] = needVersion & 0xff; + output[op2++] = + /** @type {Zlib.Zip.OperatingSystem} */ + file.option['os'] || Zlib.Zip.OperatingSystem.MSDOS; // need version + + output[op1++] = output[op2++] = needVersion & 0xff; + output[op1++] = output[op2++] = needVersion >> 8 & 0xff; // general purpose bit flag + + flags = 0; + + if (file.option['password'] || this.password) { + flags |= Zlib.Zip.Flags.ENCRYPT; + } + + output[op1++] = output[op2++] = flags & 0xff; + output[op1++] = output[op2++] = flags >> 8 & 0xff; // compression method + + compressionMethod = file.option['compressionMethod']; + output[op1++] = output[op2++] = compressionMethod & 0xff; + output[op1++] = output[op2++] = compressionMethod >> 8 & 0xff; // date + + date = + /** @type {(Date|undefined)} */ + file.option['date'] || new Date(); + output[op1++] = output[op2++] = (date.getMinutes() & 0x7) << 5 | (date.getSeconds() / 2 | 0); + output[op1++] = output[op2++] = date.getHours() << 3 | date.getMinutes() >> 3; // + + output[op1++] = output[op2++] = (date.getMonth() + 1 & 0x7) << 5 | date.getDate(); + output[op1++] = output[op2++] = (date.getFullYear() - 1980 & 0x7f) << 1 | date.getMonth() + 1 >> 3; // CRC-32 + + crc32 = file.crc32; + output[op1++] = output[op2++] = crc32 & 0xff; + output[op1++] = output[op2++] = crc32 >> 8 & 0xff; + output[op1++] = output[op2++] = crc32 >> 16 & 0xff; + output[op1++] = output[op2++] = crc32 >> 24 & 0xff; // compressed size + + size = file.buffer.length; + output[op1++] = output[op2++] = size & 0xff; + output[op1++] = output[op2++] = size >> 8 & 0xff; + output[op1++] = output[op2++] = size >> 16 & 0xff; + output[op1++] = output[op2++] = size >> 24 & 0xff; // uncompressed size + + plainSize = file.size; + output[op1++] = output[op2++] = plainSize & 0xff; + output[op1++] = output[op2++] = plainSize >> 8 & 0xff; + output[op1++] = output[op2++] = plainSize >> 16 & 0xff; + output[op1++] = output[op2++] = plainSize >> 24 & 0xff; // filename length + + output[op1++] = output[op2++] = filenameLength & 0xff; + output[op1++] = output[op2++] = filenameLength >> 8 & 0xff; // extra field length + + output[op1++] = output[op2++] = extraFieldLength & 0xff; + output[op1++] = output[op2++] = extraFieldLength >> 8 & 0xff; // file comment length + + output[op2++] = commentLength & 0xff; + output[op2++] = commentLength >> 8 & 0xff; // disk number start + + output[op2++] = 0; + output[op2++] = 0; // internal file attributes + + output[op2++] = 0; + output[op2++] = 0; // external file attributes + + output[op2++] = 0; + output[op2++] = 0; + output[op2++] = 0; + output[op2++] = 0; // relative offset of local header + + output[op2++] = offset & 0xff; + output[op2++] = offset >> 8 & 0xff; + output[op2++] = offset >> 16 & 0xff; + output[op2++] = offset >> 24 & 0xff; // filename + + filename = file.option['filename']; + + if (filename) { + { + output.set(filename, op1); + output.set(filename, op2); + op1 += filenameLength; + op2 += filenameLength; + } + } // extra field + + + extraField = file.option['extraField']; + + if (extraField) { + { + output.set(extraField, op1); + output.set(extraField, op2); + op1 += extraFieldLength; + op2 += extraFieldLength; + } + } // comment + + + comment = file.option['comment']; + + if (comment) { + { + output.set(comment, op2); + op2 += commentLength; + } + } //------------------------------------------------------------------------- + // file data + //------------------------------------------------------------------------- + + + { + output.set(file.buffer, op1); + op1 += file.buffer.length; + } + } //------------------------------------------------------------------------- + // end of central directory + //------------------------------------------------------------------------- + // signature + + + output[op3++] = Zlib.Zip.CentralDirectorySignature[0]; + output[op3++] = Zlib.Zip.CentralDirectorySignature[1]; + output[op3++] = Zlib.Zip.CentralDirectorySignature[2]; + output[op3++] = Zlib.Zip.CentralDirectorySignature[3]; // number of this disk + + output[op3++] = 0; + output[op3++] = 0; // number of the disk with the start of the central directory + + output[op3++] = 0; + output[op3++] = 0; // total number of entries in the central directory on this disk + + output[op3++] = il & 0xff; + output[op3++] = il >> 8 & 0xff; // total number of entries in the central directory + + output[op3++] = il & 0xff; + output[op3++] = il >> 8 & 0xff; // size of the central directory + + output[op3++] = centralDirectorySize & 0xff; + output[op3++] = centralDirectorySize >> 8 & 0xff; + output[op3++] = centralDirectorySize >> 16 & 0xff; + output[op3++] = centralDirectorySize >> 24 & 0xff; // offset of start of central directory with respect to the starting disk number + + output[op3++] = localFileSize & 0xff; + output[op3++] = localFileSize >> 8 & 0xff; + output[op3++] = localFileSize >> 16 & 0xff; + output[op3++] = localFileSize >> 24 & 0xff; // .ZIP file comment length + + commentLength = this.comment ? this.comment.length : 0; + output[op3++] = commentLength & 0xff; + output[op3++] = commentLength >> 8 & 0xff; // .ZIP file comment + + if (this.comment) { + { + output.set(this.comment, op3); + op3 += commentLength; + } + } + + return output; + }; + /** + * @param {!(Array.|Uint8Array)} input + * @param {Object=} opt_params options. + * @return {!(Array.|Uint8Array)} + */ + + + Zlib.Zip.prototype.deflateWithOption = function (input, opt_params) { + /** @type {Zlib.RawDeflate} */ + var deflator = new Zlib.RawDeflate(input, opt_params['deflateOption']); + return deflator.compress(); + }; + /** + * @param {(Array.|Uint32Array)} key + * @return {number} + */ + + + Zlib.Zip.prototype.getByte = function (key) { + /** @type {number} */ + var tmp = key[2] & 0xffff | 2; + return tmp * (tmp ^ 1) >> 8 & 0xff; + }; + /** + * @param {(Array.|Uint32Array|Object)} key + * @param {number} n + * @return {number} + */ + + + Zlib.Zip.prototype.encode = function (key, n) { + /** @type {number} */ + var tmp = this.getByte(key); + this.updateKeys(key, n); + return tmp ^ n; + }; + /** + * @param {(Array.|Uint32Array)} key + * @param {number} n + */ + + + Zlib.Zip.prototype.updateKeys = function (key, n) { + key[0] = Zlib.CRC32.single(key[0], n); + key[1] = (((key[1] + (key[0] & 0xff)) * 20173 >>> 0) * 6681 >>> 0) + 1 >>> 0; + key[2] = Zlib.CRC32.single(key[2], key[1] >>> 24); + }; + /** + * @param {(Array.|Uint8Array)} password + * @return {!(Array.|Uint32Array|Object)} + */ + + + Zlib.Zip.prototype.createEncryptionKey = function (password) { + /** @type {!(Array.|Uint32Array)} */ + var key = [305419896, 591751049, 878082192]; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + { + key = new Uint32Array(key); + } + + for (i = 0, il = password.length; i < il; ++i) { + this.updateKeys(key, password[i] & 0xff); + } + + return key; + }; + /** + * build huffman table from length list. + * @param {!(Array.|Uint8Array)} lengths length list. + * @return {!Array} huffman table. + */ + + + Zlib.Huffman.buildHuffmanTable = function (lengths) { + /** @type {number} length list size. */ + var listSize = lengths.length; + /** @type {number} max code length for table size. */ + + var maxCodeLength = 0; + /** @type {number} min code length for table size. */ + + var minCodeLength = Number.POSITIVE_INFINITY; + /** @type {number} table size. */ + + var size; + /** @type {!(Array|Uint8Array)} huffman code table. */ + + var table; + /** @type {number} bit length. */ + + var bitLength; + /** @type {number} huffman code. */ + + var code; + /** + * รฃโ€šยตรฃโ€šยครฃโ€šยบรฃยล’ 2^maxlength รฅโ‚ฌโ€นรฃยยฎรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃโ€šโ€™รฅลธโ€นรฃโ€šยรฃโ€šโ€นรฃยลธรฃโ€šยรฃยยฎรฃโ€šยนรฃโ€šยญรฃฦ’ฦ’รฃฦ’โ€”รฉโ€ขยท. + * @type {number} skip length for table filling. + */ + + var skip; + /** @type {number} reversed code. */ + + var reversed; + /** @type {number} reverse temp. */ + + var rtemp; + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limit. */ + + var il; + /** @type {number} loop counter. */ + + var j; + /** @type {number} table value. */ + + var value; // Math.max รฃยยฏรฉยโ€ฆรฃยโ€žรฃยยฎรฃยยงรฆล“โ‚ฌรฉโ€ขยทรฃยยฎรฅโ‚ฌยครฃยยฏ for-loop รฃยยงรฅยโ€“รฅยพโ€”รฃยโ„ขรฃโ€šโ€น + + for (i = 0, il = listSize; i < il; ++i) { + if (lengths[i] > maxCodeLength) { + maxCodeLength = lengths[i]; + } + + if (lengths[i] < minCodeLength) { + minCodeLength = lengths[i]; + } + } + + size = 1 << maxCodeLength; + table = new Uint32Array(size); // รฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฃยยฎรงลธยญรฃยโ€žรฉ โ€ รฃยโ€นรฃโ€šโ€ฐรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃโ€šโ€™รฅโ€ฐยฒรฃโ€šล รฅยฝโ€œรฃยยฆรฃโ€šโ€น + + for (bitLength = 1, code = 0, skip = 2; bitLength <= maxCodeLength;) { + for (i = 0; i < listSize; ++i) { + if (lengths[i] === bitLength) { + // รฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยชรฃฦ’ยผรฃฦ’โ‚ฌรฃฦ’ยผรฃยล’รฉโ‚ฌโ€ รฃยยซรฃยยชรฃโ€šโ€นรฃยลธรฃโ€šยรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฅห†โ€ รคยธยฆรฃยยณรฃโ€šโ€™รฅยยรจยปยขรฃยโ„ขรฃโ€šโ€น + for (reversed = 0, rtemp = code, j = 0; j < bitLength; ++j) { + reversed = reversed << 1 | rtemp & 1; + rtemp >>= 1; + } // รฆล“โ‚ฌรฅยคยงรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฃโ€šโ€™รฃโ€šโ€šรฃยยจรฃยยซรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃโ€šโ€™รคยฝล“รฃโ€šโ€นรฃยลธรฃโ€šยรฃโ‚ฌย + // รฆล“โ‚ฌรฅยคยงรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรคยปยฅรฅยคโ€“รฃยยงรฃยยฏ 0 / 1 รฃยยฉรฃยยกรฃโ€šโ€ฐรฃยยงรฃโ€šโ€šรจโ€ฐยฏรฃยโ€žรงยฎโ€กรฆโ€ฐโ‚ฌรฃยล’รฃยยงรฃยยรฃโ€šโ€น + // รฃยยรฃยยฎรฃยยฉรฃยยกรฃโ€šโ€ฐรฃยยงรฃโ€šโ€šรจโ€ฐยฏรฃยโ€žรฅ ยดรฆโ€ฐโ‚ฌรฃยยฏรฅยล’รฃยหœรฅโ‚ฌยครฃยยงรฅลธโ€นรฃโ€šยรฃโ€šโ€นรฃยโ€œรฃยยจรฃยยง + // รฆล“ยฌรฆยยฅรฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรคยปยฅรคยธล รฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฆโ€ขยฐรฅยโ€“รฅยพโ€”รฃยโ€”รฃยยฆรฃโ€šโ€šรฅโ€ขยรฉยกล’รฃยล’รจยตยทรฃยโ€œรฃโ€šโ€ฐรฃยยชรฃยโ€žรฃโ€šห†รฃยโ€ รฃยยซรฃยโ„ขรฃโ€šโ€น + + + value = bitLength << 16 | i; + + for (j = reversed; j < size; j += skip) { + table[j] = value; + } + + ++code; + } + } // รฆยฌยกรฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฃยยธ + + + ++bitLength; + code <<= 1; + skip <<= 1; + } + + return [table, maxCodeLength, minCodeLength]; + }; //----------------------------------------------------------------------------- + + /** @define {number} buffer block size. */ + + + var ZLIB_RAW_INFLATE_BUFFER_SIZE = 0x8000; // [ 0x8000 >= ZLIB_BUFFER_BLOCK_SIZE ] + //----------------------------------------------------------------------------- + + var buildHuffmanTable = Zlib.Huffman.buildHuffmanTable; + /** + * @constructor + * @param {!(Uint8Array|Array.)} input input buffer. + * @param {Object} opt_params option parameter. + * + * opt_params รฃยยฏรคยปยฅรคยธโ€นรฃยยฎรฃฦ’โ€”รฃฦ’ยญรฃฦ’โ€˜รฃฦ’โ€ รฃโ€šยฃรฃโ€šโ€™รฆล’โ€กรฅยฎลกรฃยโ„ขรฃโ€šโ€นรคยบโ€นรฃยล’รฃยยงรฃยยรฃยยพรฃยโ„ขรฃโ‚ฌโ€š + * - index: input buffer รฃยยฎ deflate รฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃฦ’ล รฃยยฎรฉโ€“โ€นรฅยงโ€นรคยฝยรงยฝยฎ. + * - blockSize: รฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃโ€šยตรฃโ€šยครฃโ€šยบ. + * - bufferType: Zlib.RawInflate.BufferType รฃยยฎรฅโ‚ฌยครฃยยซรฃโ€šห†รฃยยฃรฃยยฆรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรงยฎยกรงยโ€ รฆโ€“ยนรฆยณโ€ขรฃโ€šโ€™รฆล’โ€กรฅยฎลกรฃยโ„ขรฃโ€šโ€น. + * - resize: รงยขยบรคยฟยรฃยโ€”รฃยลธรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยล’รฅยฎลธรฉลกโ€บรฃยยฎรฅยคยงรฃยยรฃยโ€ขรฃโ€šห†รฃโ€šล รฅยคยงรฃยยรฃยโ€นรฃยยฃรฃยลธรฅ ยดรฅยห†รฃยยซรฅห†โ€กรฃโ€šล รจยฉยฐรฃโ€šยรฃโ€šโ€น. + */ + + Zlib.RawInflate = function (input, opt_params) { + /** @type {!(Array.|Uint8Array)} inflated buffer */ + this.buffer; + /** @type {!Array.<(Array.|Uint8Array)>} */ + + this.blocks = []; + /** @type {number} block size. */ + + this.bufferSize = ZLIB_RAW_INFLATE_BUFFER_SIZE; + /** @type {!number} total output buffer pointer. */ + + this.totalpos = 0; + /** @type {!number} input buffer pointer. */ + + this.ip = 0; + /** @type {!number} bit stream reader buffer. */ + + this.bitsbuf = 0; + /** @type {!number} bit stream reader buffer size. */ + + this.bitsbuflen = 0; + /** @type {!(Array.|Uint8Array)} input buffer. */ + + this.input = new Uint8Array(input); + /** @type {!(Uint8Array|Array.)} output buffer. */ + + this.output; + /** @type {!number} output buffer pointer. */ + + this.op; + /** @type {boolean} is final block flag. */ + + this.bfinal = false; + /** @type {Zlib.RawInflate.BufferType} buffer management. */ + + this.bufferType = Zlib.RawInflate.BufferType.ADAPTIVE; + /** @type {boolean} resize flag for memory size optimization. */ + + this.resize = false; // option parameters + + if (opt_params || !(opt_params = {})) { + if (opt_params['index']) { + this.ip = opt_params['index']; + } + + if (opt_params['bufferSize']) { + this.bufferSize = opt_params['bufferSize']; + } + + if (opt_params['bufferType']) { + this.bufferType = opt_params['bufferType']; + } + + if (opt_params['resize']) { + this.resize = opt_params['resize']; + } + } // initialize + + + switch (this.bufferType) { + case Zlib.RawInflate.BufferType.BLOCK: + this.op = Zlib.RawInflate.MaxBackwardLength; + this.output = new Uint8Array(Zlib.RawInflate.MaxBackwardLength + this.bufferSize + Zlib.RawInflate.MaxCopyLength); + break; + + case Zlib.RawInflate.BufferType.ADAPTIVE: + this.op = 0; + this.output = new Uint8Array(this.bufferSize); + break; + + default: + throw new Error('invalid inflate mode'); + } + }; + /** + * @enum {number} + */ + + + Zlib.RawInflate.BufferType = { + BLOCK: 0, + ADAPTIVE: 1 + }; + /** + * decompress. + * @return {!(Uint8Array|Array.)} inflated buffer. + */ + + Zlib.RawInflate.prototype.decompress = function () { + while (!this.bfinal) { + this.parseBlock(); + } + + switch (this.bufferType) { + case Zlib.RawInflate.BufferType.BLOCK: + return this.concatBufferBlock(); + + case Zlib.RawInflate.BufferType.ADAPTIVE: + return this.concatBufferDynamic(); + + default: + throw new Error('invalid inflate mode'); + } + }; + /** + * @const + * @type {number} max backward length for LZ77. + */ + + + Zlib.RawInflate.MaxBackwardLength = 32768; + /** + * @const + * @type {number} max copy length for LZ77. + */ + + Zlib.RawInflate.MaxCopyLength = 258; + /** + * huffman order + * @const + * @type {!(Array.|Uint8Array)} + */ + + Zlib.RawInflate.Order = function (table) { + return new Uint16Array(table); + }([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); + /** + * huffman length code table. + * @const + * @type {!(Array.|Uint16Array)} + */ + + + Zlib.RawInflate.LengthCodeTable = function (table) { + return new Uint16Array(table); + }([0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000d, 0x000f, 0x0011, 0x0013, 0x0017, 0x001b, 0x001f, 0x0023, 0x002b, 0x0033, 0x003b, 0x0043, 0x0053, 0x0063, 0x0073, 0x0083, 0x00a3, 0x00c3, 0x00e3, 0x0102, 0x0102, 0x0102]); + /** + * huffman length extra-bits table. + * @const + * @type {!(Array.|Uint8Array)} + */ + + + Zlib.RawInflate.LengthExtraTable = function (table) { + return new Uint8Array(table); + }([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, 0, 0]); + /** + * huffman dist code table. + * @const + * @type {!(Array.|Uint16Array)} + */ + + + Zlib.RawInflate.DistCodeTable = function (table) { + return new Uint16Array(table); + }([0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d, 0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1, 0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01, 0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001]); + /** + * huffman dist extra-bits table. + * @const + * @type {!(Array.|Uint8Array)} + */ + + + Zlib.RawInflate.DistExtraTable = function (table) { + return new Uint8Array(table); + }([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]); + /** + * fixed huffman length code table + * @const + * @type {!Array} + */ + + + Zlib.RawInflate.FixedLiteralLengthTable = function (table) { + return table; + }(function () { + var lengths = new Uint8Array(288); + var i, il; + + for (i = 0, il = lengths.length; i < il; ++i) { + lengths[i] = i <= 143 ? 8 : i <= 255 ? 9 : i <= 279 ? 7 : 8; + } + + return buildHuffmanTable(lengths); + }()); + /** + * fixed huffman distance code table + * @const + * @type {!Array} + */ + + + Zlib.RawInflate.FixedDistanceTable = function (table) { + return table; + }(function () { + var lengths = new Uint8Array(30); + var i, il; + + for (i = 0, il = lengths.length; i < il; ++i) { + lengths[i] = 5; + } + + return buildHuffmanTable(lengths); + }()); + /** + * parse deflated block. + */ + + + Zlib.RawInflate.prototype.parseBlock = function () { + /** @type {number} header */ + var hdr = this.readBits(3); // BFINAL + + if (hdr & 0x1) { + this.bfinal = true; + } // BTYPE + + + hdr >>>= 1; + + switch (hdr) { + // uncompressed + case 0: + this.parseUncompressedBlock(); + break; + // fixed huffman + + case 1: + this.parseFixedHuffmanBlock(); + break; + // dynamic huffman + + case 2: + this.parseDynamicHuffmanBlock(); + break; + // reserved or other + + default: + throw new Error('unknown BTYPE: ' + hdr); + } + }; + /** + * read inflate bits + * @param {number} length bits length. + * @return {number} read bits. + */ + + + Zlib.RawInflate.prototype.readBits = function (length) { + var bitsbuf = this.bitsbuf; + var bitsbuflen = this.bitsbuflen; + var input = this.input; + var ip = this.ip; + /** @type {number} */ + + var inputLength = input.length; + /** @type {number} input and output byte. */ + + var octet; // input byte + + if (ip + (length - bitsbuflen + 7 >> 3) >= inputLength) { + throw new Error('input buffer is broken'); + } // not enough buffer + + + while (bitsbuflen < length) { + bitsbuf |= input[ip++] << bitsbuflen; + bitsbuflen += 8; + } // output byte + + + octet = bitsbuf & (1 << length) - 1; + bitsbuf >>>= length; + bitsbuflen -= length; + this.bitsbuf = bitsbuf; + this.bitsbuflen = bitsbuflen; + this.ip = ip; + return octet; + }; + /** + * read huffman code using table + * @param {!(Array.|Uint8Array|Uint16Array)} table huffman code table. + * @return {number} huffman code. + */ + + + Zlib.RawInflate.prototype.readCodeByTable = function (table) { + var bitsbuf = this.bitsbuf; + var bitsbuflen = this.bitsbuflen; + var input = this.input; + var ip = this.ip; + /** @type {number} */ + + var inputLength = input.length; + /** @type {!(Array.|Uint8Array)} huffman code table */ + + var codeTable = table[0]; + /** @type {number} */ + + var maxCodeLength = table[1]; + /** @type {number} code length & code (16bit, 16bit) */ + + var codeWithLength; + /** @type {number} code bits length */ + + var codeLength; // not enough buffer + + while (bitsbuflen < maxCodeLength) { + if (ip >= inputLength) { + break; + } + + bitsbuf |= input[ip++] << bitsbuflen; + bitsbuflen += 8; + } // read max length + + + codeWithLength = codeTable[bitsbuf & (1 << maxCodeLength) - 1]; + codeLength = codeWithLength >>> 16; + + if (codeLength > bitsbuflen) { + throw new Error('invalid code length: ' + codeLength); + } + + this.bitsbuf = bitsbuf >> codeLength; + this.bitsbuflen = bitsbuflen - codeLength; + this.ip = ip; + return codeWithLength & 0xffff; + }; + /** + * parse uncompressed block. + */ + + + Zlib.RawInflate.prototype.parseUncompressedBlock = function () { + var input = this.input; + var ip = this.ip; + var output = this.output; + var op = this.op; + /** @type {number} */ + + var inputLength = input.length; + /** @type {number} block length */ + + var len; + /** @type {number} number for check block length */ + + var nlen; + /** @type {number} output buffer length */ + + var olength = output.length; + /** @type {number} copy counter */ + + var preCopy; // skip buffered header bits + + this.bitsbuf = 0; + this.bitsbuflen = 0; // len + + if (ip + 1 >= inputLength) { + throw new Error('invalid uncompressed block header: LEN'); + } + + len = input[ip++] | input[ip++] << 8; // nlen + + if (ip + 1 >= inputLength) { + throw new Error('invalid uncompressed block header: NLEN'); + } + + nlen = input[ip++] | input[ip++] << 8; // check len & nlen + + if (len === ~nlen) { + throw new Error('invalid uncompressed block header: length verify'); + } // check size + + + if (ip + len > input.length) { + throw new Error('input buffer is broken'); + } // expand buffer + + + switch (this.bufferType) { + case Zlib.RawInflate.BufferType.BLOCK: + // pre copy + while (op + len > output.length) { + preCopy = olength - op; + len -= preCopy; + { + output.set(input.subarray(ip, ip + preCopy), op); + op += preCopy; + ip += preCopy; + } + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + + break; + + case Zlib.RawInflate.BufferType.ADAPTIVE: + while (op + len > output.length) { + output = this.expandBufferAdaptive({ + fixRatio: 2 + }); + } + + break; + + default: + throw new Error('invalid inflate mode'); + } // copy + + + { + output.set(input.subarray(ip, ip + len), op); + op += len; + ip += len; + } + this.ip = ip; + this.op = op; + this.output = output; + }; + /** + * parse fixed huffman block. + */ + + + Zlib.RawInflate.prototype.parseFixedHuffmanBlock = function () { + switch (this.bufferType) { + case Zlib.RawInflate.BufferType.ADAPTIVE: + this.decodeHuffmanAdaptive(Zlib.RawInflate.FixedLiteralLengthTable, Zlib.RawInflate.FixedDistanceTable); + break; + + case Zlib.RawInflate.BufferType.BLOCK: + this.decodeHuffmanBlock(Zlib.RawInflate.FixedLiteralLengthTable, Zlib.RawInflate.FixedDistanceTable); + break; + + default: + throw new Error('invalid inflate mode'); + } + }; + /** + * parse dynamic huffman block. + */ + + + Zlib.RawInflate.prototype.parseDynamicHuffmanBlock = function () { + /** @type {number} number of literal and length codes. */ + var hlit = this.readBits(5) + 257; + /** @type {number} number of distance codes. */ + + var hdist = this.readBits(5) + 1; + /** @type {number} number of code lengths. */ + + var hclen = this.readBits(4) + 4; + /** @type {!(Uint8Array|Array.)} code lengths. */ + + var codeLengths = new Uint8Array(Zlib.RawInflate.Order.length); + /** @type {!Array} code lengths table. */ + + var codeLengthsTable; + /** @type {!(Uint8Array|Array.)} literal and length code table. */ + + var litlenTable; + /** @type {!(Uint8Array|Array.)} distance code table. */ + + var distTable; + /** @type {!(Uint8Array|Array.)} code length table. */ + + var lengthTable; + /** @type {number} */ + + var code; + /** @type {number} */ + + var prev; + /** @type {number} */ + + var repeat; + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limit. */ + + var il; // decode code lengths + + for (i = 0; i < hclen; ++i) { + codeLengths[Zlib.RawInflate.Order[i]] = this.readBits(3); + } // decode length table + + + codeLengthsTable = buildHuffmanTable(codeLengths); + lengthTable = new Uint8Array(hlit + hdist); + + for (i = 0, il = hlit + hdist; i < il;) { + code = this.readCodeByTable(codeLengthsTable); + + switch (code) { + case 16: + repeat = 3 + this.readBits(2); + + while (repeat--) { + lengthTable[i++] = prev; + } + + break; + + case 17: + repeat = 3 + this.readBits(3); + + while (repeat--) { + lengthTable[i++] = 0; + } + + prev = 0; + break; + + case 18: + repeat = 11 + this.readBits(7); + + while (repeat--) { + lengthTable[i++] = 0; + } + + prev = 0; + break; + + default: + lengthTable[i++] = code; + prev = code; + break; + } + } + + litlenTable = buildHuffmanTable(lengthTable.subarray(0, hlit)); + distTable = buildHuffmanTable(lengthTable.subarray(hlit)); + + switch (this.bufferType) { + case Zlib.RawInflate.BufferType.ADAPTIVE: + this.decodeHuffmanAdaptive(litlenTable, distTable); + break; + + case Zlib.RawInflate.BufferType.BLOCK: + this.decodeHuffmanBlock(litlenTable, distTable); + break; + + default: + throw new Error('invalid inflate mode'); + } + }; + /** + * decode huffman code + * @param {!(Array.|Uint16Array)} litlen literal and length code table. + * @param {!(Array.|Uint8Array)} dist distination code table. + */ + + + Zlib.RawInflate.prototype.decodeHuffmanBlock = function (litlen, dist) { + var output = this.output; + var op = this.op; + this.currentLitlenTable = litlen; + /** @type {number} output position limit. */ + + var olength = output.length - Zlib.RawInflate.MaxCopyLength; + /** @type {number} huffman code. */ + + var code; + /** @type {number} table index. */ + + var ti; + /** @type {number} huffman code distination. */ + + var codeDist; + /** @type {number} huffman code length. */ + + var codeLength; + var lengthCodeTable = Zlib.RawInflate.LengthCodeTable; + var lengthExtraTable = Zlib.RawInflate.LengthExtraTable; + var distCodeTable = Zlib.RawInflate.DistCodeTable; + var distExtraTable = Zlib.RawInflate.DistExtraTable; + + while ((code = this.readCodeByTable(litlen)) !== 256) { + // literal + if (code < 256) { + if (op >= olength) { + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + + output[op++] = code; + continue; + } // length code + + + ti = code - 257; + codeLength = lengthCodeTable[ti]; + + if (lengthExtraTable[ti] > 0) { + codeLength += this.readBits(lengthExtraTable[ti]); + } // dist code + + + code = this.readCodeByTable(dist); + codeDist = distCodeTable[code]; + + if (distExtraTable[code] > 0) { + codeDist += this.readBits(distExtraTable[code]); + } // lz77 decode + + + if (op >= olength) { + this.op = op; + output = this.expandBufferBlock(); + op = this.op; + } + + while (codeLength--) { + output[op] = output[op++ - codeDist]; + } + } + + while (this.bitsbuflen >= 8) { + this.bitsbuflen -= 8; + this.ip--; + } + + this.op = op; + }; + /** + * decode huffman code (adaptive) + * @param {!(Array.|Uint16Array)} litlen literal and length code table. + * @param {!(Array.|Uint8Array)} dist distination code table. + */ + + + Zlib.RawInflate.prototype.decodeHuffmanAdaptive = function (litlen, dist) { + var output = this.output; + var op = this.op; + this.currentLitlenTable = litlen; + /** @type {number} output position limit. */ + + var olength = output.length; + /** @type {number} huffman code. */ + + var code; + /** @type {number} table index. */ + + var ti; + /** @type {number} huffman code distination. */ + + var codeDist; + /** @type {number} huffman code length. */ + + var codeLength; + var lengthCodeTable = Zlib.RawInflate.LengthCodeTable; + var lengthExtraTable = Zlib.RawInflate.LengthExtraTable; + var distCodeTable = Zlib.RawInflate.DistCodeTable; + var distExtraTable = Zlib.RawInflate.DistExtraTable; + + while ((code = this.readCodeByTable(litlen)) !== 256) { + // literal + if (code < 256) { + if (op >= olength) { + output = this.expandBufferAdaptive(); + olength = output.length; + } + + output[op++] = code; + continue; + } // length code + + + ti = code - 257; + codeLength = lengthCodeTable[ti]; + + if (lengthExtraTable[ti] > 0) { + codeLength += this.readBits(lengthExtraTable[ti]); + } // dist code + + + code = this.readCodeByTable(dist); + codeDist = distCodeTable[code]; + + if (distExtraTable[code] > 0) { + codeDist += this.readBits(distExtraTable[code]); + } // lz77 decode + + + if (op + codeLength > olength) { + output = this.expandBufferAdaptive(); + olength = output.length; + } + + while (codeLength--) { + output[op] = output[op++ - codeDist]; + } + } + + while (this.bitsbuflen >= 8) { + this.bitsbuflen -= 8; + this.ip--; + } + + this.op = op; + }; + /** + * expand output buffer. + * @param {Object=} opt_param option parameters. + * @return {!(Array.|Uint8Array)} output buffer. + */ + + + Zlib.RawInflate.prototype.expandBufferBlock = function (opt_param) { + /** @type {!(Array.|Uint8Array)} store buffer. */ + var buffer = new Uint8Array(this.op - Zlib.RawInflate.MaxBackwardLength); + /** @type {number} backward base point */ + + var backward = this.op - Zlib.RawInflate.MaxBackwardLength; + var output = this.output; // copy to output buffer + + { + buffer.set(output.subarray(Zlib.RawInflate.MaxBackwardLength, buffer.length)); + } + this.blocks.push(buffer); + this.totalpos += buffer.length; // copy to backward buffer + + { + output.set(output.subarray(backward, backward + Zlib.RawInflate.MaxBackwardLength)); + } + this.op = Zlib.RawInflate.MaxBackwardLength; + return output; + }; + /** + * expand output buffer. (adaptive) + * @param {Object=} opt_param option parameters. + * @return {!(Array.|Uint8Array)} output buffer pointer. + */ + + + Zlib.RawInflate.prototype.expandBufferAdaptive = function (opt_param) { + /** @type {!(Array.|Uint8Array)} store buffer. */ + var buffer; + /** @type {number} expantion ratio. */ + + var ratio = this.input.length / this.ip + 1 | 0; + /** @type {number} maximum number of huffman code. */ + + var maxHuffCode; + /** @type {number} new output buffer size. */ + + var newSize; + /** @type {number} max inflate size. */ + + var maxInflateSize; + var input = this.input; + var output = this.output; + + if (opt_param) { + if (typeof opt_param.fixRatio === 'number') { + ratio = opt_param.fixRatio; + } + + if (typeof opt_param.addRatio === 'number') { + ratio += opt_param.addRatio; + } + } // calculate new buffer size + + + if (ratio < 2) { + maxHuffCode = (input.length - this.ip) / this.currentLitlenTable[2]; + maxInflateSize = maxHuffCode / 2 * 258 | 0; + newSize = maxInflateSize < output.length ? output.length + maxInflateSize : output.length << 1; + } else { + newSize = output.length * ratio; + } // buffer expantion + + + { + buffer = new Uint8Array(newSize); + buffer.set(output); + } + this.output = buffer; + return this.output; + }; + /** + * concat output buffer. + * @return {!(Array.|Uint8Array)} output buffer. + */ + + + Zlib.RawInflate.prototype.concatBufferBlock = function () { + /** @type {number} buffer pointer. */ + var pos = 0; + /** @type {number} buffer pointer. */ + + var limit = this.totalpos + (this.op - Zlib.RawInflate.MaxBackwardLength); + /** @type {!(Array.|Uint8Array)} output block array. */ + + var output = this.output; + /** @type {!Array} blocks array. */ + + var blocks = this.blocks; + /** @type {!(Array.|Uint8Array)} output block array. */ + + var block; + /** @type {!(Array.|Uint8Array)} output buffer. */ + + var buffer = new Uint8Array(limit); + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limiter. */ + + var il; + /** @type {number} loop counter. */ + + var j; + /** @type {number} loop limiter. */ + + var jl; // single buffer + + if (blocks.length === 0) { + return this.output.subarray(Zlib.RawInflate.MaxBackwardLength, this.op); + } // copy to buffer + + + for (i = 0, il = blocks.length; i < il; ++i) { + block = blocks[i]; + + for (j = 0, jl = block.length; j < jl; ++j) { + buffer[pos++] = block[j]; + } + } // current buffer + + + for (i = Zlib.RawInflate.MaxBackwardLength, il = this.op; i < il; ++i) { + buffer[pos++] = output[i]; + } + + this.blocks = []; + this.buffer = buffer; + return this.buffer; + }; + /** + * concat output buffer. (dynamic) + * @return {!(Array.|Uint8Array)} output buffer. + */ + + + Zlib.RawInflate.prototype.concatBufferDynamic = function () { + /** @type {Array.|Uint8Array} output buffer. */ + var buffer; + var op = this.op; + { + if (this.resize) { + buffer = new Uint8Array(op); + buffer.set(this.output.subarray(0, op)); + } else { + buffer = this.output.subarray(0, op); + } + } + this.buffer = buffer; + return this.buffer; + }; + + var buildHuffmanTable = Zlib.Huffman.buildHuffmanTable; + /** + * @param {!(Uint8Array|Array.)} input input buffer. + * @param {number} ip input buffer pointer. + * @param {number=} opt_buffersize buffer block size. + * @constructor + */ + + Zlib.RawInflateStream = function (input, ip, opt_buffersize) { + /** @type {!Array.<(Array|Uint8Array)>} */ + this.blocks = []; + /** @type {number} block size. */ + + this.bufferSize = opt_buffersize ? opt_buffersize : ZLIB_STREAM_RAW_INFLATE_BUFFER_SIZE; + /** @type {!number} total output buffer pointer. */ + + this.totalpos = 0; + /** @type {!number} input buffer pointer. */ + + this.ip = ip === void 0 ? 0 : ip; + /** @type {!number} bit stream reader buffer. */ + + this.bitsbuf = 0; + /** @type {!number} bit stream reader buffer size. */ + + this.bitsbuflen = 0; + /** @type {!(Array|Uint8Array)} input buffer. */ + + this.input = new Uint8Array(input); + /** @type {!(Uint8Array|Array)} output buffer. */ + + this.output = new Uint8Array(this.bufferSize); + /** @type {!number} output buffer pointer. */ + + this.op = 0; + /** @type {boolean} is final block flag. */ + + this.bfinal = false; + /** @type {number} uncompressed block length. */ + + this.blockLength; + /** @type {boolean} resize flag for memory size optimization. */ + + this.resize = false; + /** @type {Array} */ + + this.litlenTable; + /** @type {Array} */ + + this.distTable; + /** @type {number} */ + + this.sp = 0; // stream pointer + + /** @type {Zlib.RawInflateStream.Status} */ + + this.status = Zlib.RawInflateStream.Status.INITIALIZED; // + // backup + // + + /** @type {!number} */ + + this.ip_; + /** @type {!number} */ + + this.bitsbuflen_; + /** @type {!number} */ + + this.bitsbuf_; + }; + /** + * @enum {number} + */ + + + Zlib.RawInflateStream.BlockType = { + UNCOMPRESSED: 0, + FIXED: 1, + DYNAMIC: 2 + }; + /** + * @enum {number} + */ + + Zlib.RawInflateStream.Status = { + INITIALIZED: 0, + BLOCK_HEADER_START: 1, + BLOCK_HEADER_END: 2, + BLOCK_BODY_START: 3, + BLOCK_BODY_END: 4, + DECODE_BLOCK_START: 5, + DECODE_BLOCK_END: 6 + }; + /** + * decompress. + * @return {!(Uint8Array|Array)} inflated buffer. + */ + + Zlib.RawInflateStream.prototype.decompress = function (newInput, ip) { + /** @type {boolean} */ + var stop = false; + + if (newInput !== void 0) { + this.input = newInput; + } + + if (ip !== void 0) { + this.ip = ip; + } // decompress + + + while (!stop) { + switch (this.status) { + // block header + case Zlib.RawInflateStream.Status.INITIALIZED: + case Zlib.RawInflateStream.Status.BLOCK_HEADER_START: + if (this.readBlockHeader() < 0) { + stop = true; + } + + break; + // block body + + case Zlib.RawInflateStream.Status.BLOCK_HEADER_END: + /* FALLTHROUGH */ + + case Zlib.RawInflateStream.Status.BLOCK_BODY_START: + switch (this.currentBlockType) { + case Zlib.RawInflateStream.BlockType.UNCOMPRESSED: + if (this.readUncompressedBlockHeader() < 0) { + stop = true; + } + + break; + + case Zlib.RawInflateStream.BlockType.FIXED: + if (this.parseFixedHuffmanBlock() < 0) { + stop = true; + } + + break; + + case Zlib.RawInflateStream.BlockType.DYNAMIC: + if (this.parseDynamicHuffmanBlock() < 0) { + stop = true; + } + + break; + } + + break; + // decode data + + case Zlib.RawInflateStream.Status.BLOCK_BODY_END: + case Zlib.RawInflateStream.Status.DECODE_BLOCK_START: + switch (this.currentBlockType) { + case Zlib.RawInflateStream.BlockType.UNCOMPRESSED: + if (this.parseUncompressedBlock() < 0) { + stop = true; + } + + break; + + case Zlib.RawInflateStream.BlockType.FIXED: + /* FALLTHROUGH */ + + case Zlib.RawInflateStream.BlockType.DYNAMIC: + if (this.decodeHuffman() < 0) { + stop = true; + } + + break; + } + + break; + + case Zlib.RawInflateStream.Status.DECODE_BLOCK_END: + if (this.bfinal) { + stop = true; + } else { + this.status = Zlib.RawInflateStream.Status.INITIALIZED; + } + + break; + } + } + + return this.concatBuffer(); + }; + /** + * @const + * @type {number} max backward length for LZ77. + */ + + + Zlib.RawInflateStream.MaxBackwardLength = 32768; + /** + * @const + * @type {number} max copy length for LZ77. + */ + + Zlib.RawInflateStream.MaxCopyLength = 258; + /** + * huffman order + * @const + * @type {!(Array.|Uint8Array)} + */ + + Zlib.RawInflateStream.Order = function (table) { + return new Uint16Array(table); + }([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); + /** + * huffman length code table. + * @const + * @type {!(Array.|Uint16Array)} + */ + + + Zlib.RawInflateStream.LengthCodeTable = function (table) { + return new Uint16Array(table); + }([0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000d, 0x000f, 0x0011, 0x0013, 0x0017, 0x001b, 0x001f, 0x0023, 0x002b, 0x0033, 0x003b, 0x0043, 0x0053, 0x0063, 0x0073, 0x0083, 0x00a3, 0x00c3, 0x00e3, 0x0102, 0x0102, 0x0102]); + /** + * huffman length extra-bits table. + * @const + * @type {!(Array.|Uint8Array)} + */ + + + Zlib.RawInflateStream.LengthExtraTable = function (table) { + return new Uint8Array(table); + }([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, 0, 0]); + /** + * huffman dist code table. + * @const + * @type {!(Array.|Uint16Array)} + */ + + + Zlib.RawInflateStream.DistCodeTable = function (table) { + return new Uint16Array(table); + }([0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d, 0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1, 0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01, 0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001]); + /** + * huffman dist extra-bits table. + * @const + * @type {!(Array.|Uint8Array)} + */ + + + Zlib.RawInflateStream.DistExtraTable = function (table) { + return new Uint8Array(table); + }([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]); + /** + * fixed huffman length code table + * @const + * @type {!Array} + */ + + + Zlib.RawInflateStream.FixedLiteralLengthTable = function (table) { + return table; + }(function () { + var lengths = new Uint8Array(288); + var i, il; + + for (i = 0, il = lengths.length; i < il; ++i) { + lengths[i] = i <= 143 ? 8 : i <= 255 ? 9 : i <= 279 ? 7 : 8; + } + + return buildHuffmanTable(lengths); + }()); + /** + * fixed huffman distance code table + * @const + * @type {!Array} + */ + + + Zlib.RawInflateStream.FixedDistanceTable = function (table) { + return table; + }(function () { + var lengths = new Uint8Array(30); + var i, il; + + for (i = 0, il = lengths.length; i < il; ++i) { + lengths[i] = 5; + } + + return buildHuffmanTable(lengths); + }()); + /** + * parse deflated block. + */ + + + Zlib.RawInflateStream.prototype.readBlockHeader = function () { + /** @type {number} header */ + var hdr; + this.status = Zlib.RawInflateStream.Status.BLOCK_HEADER_START; + this.save_(); + + if ((hdr = this.readBits(3)) < 0) { + this.restore_(); + return -1; + } // BFINAL + + + if (hdr & 0x1) { + this.bfinal = true; + } // BTYPE + + + hdr >>>= 1; + + switch (hdr) { + case 0: + // uncompressed + this.currentBlockType = Zlib.RawInflateStream.BlockType.UNCOMPRESSED; + break; + + case 1: + // fixed huffman + this.currentBlockType = Zlib.RawInflateStream.BlockType.FIXED; + break; + + case 2: + // dynamic huffman + this.currentBlockType = Zlib.RawInflateStream.BlockType.DYNAMIC; + break; + + default: + // reserved or other + throw new Error('unknown BTYPE: ' + hdr); + } + + this.status = Zlib.RawInflateStream.Status.BLOCK_HEADER_END; + }; + /** + * read inflate bits + * @param {number} length bits length. + * @return {number} read bits. + */ + + + Zlib.RawInflateStream.prototype.readBits = function (length) { + var bitsbuf = this.bitsbuf; + var bitsbuflen = this.bitsbuflen; + var input = this.input; + var ip = this.ip; + /** @type {number} input and output byte. */ + + var octet; // not enough buffer + + while (bitsbuflen < length) { + // input byte + if (input.length <= ip) { + return -1; + } + + octet = input[ip++]; // concat octet + + bitsbuf |= octet << bitsbuflen; + bitsbuflen += 8; + } // output byte + + + octet = bitsbuf & (1 << length) - 1; + bitsbuf >>>= length; + bitsbuflen -= length; + this.bitsbuf = bitsbuf; + this.bitsbuflen = bitsbuflen; + this.ip = ip; + return octet; + }; + /** + * read huffman code using table + * @param {Array} table huffman code table. + * @return {number} huffman code. + */ + + + Zlib.RawInflateStream.prototype.readCodeByTable = function (table) { + var bitsbuf = this.bitsbuf; + var bitsbuflen = this.bitsbuflen; + var input = this.input; + var ip = this.ip; + /** @type {!(Array|Uint8Array)} huffman code table */ + + var codeTable = table[0]; + /** @type {number} */ + + var maxCodeLength = table[1]; + /** @type {number} input byte */ + + var octet; + /** @type {number} code length & code (16bit, 16bit) */ + + var codeWithLength; + /** @type {number} code bits length */ + + var codeLength; // not enough buffer + + while (bitsbuflen < maxCodeLength) { + if (input.length <= ip) { + return -1; + } + + octet = input[ip++]; + bitsbuf |= octet << bitsbuflen; + bitsbuflen += 8; + } // read max length + + + codeWithLength = codeTable[bitsbuf & (1 << maxCodeLength) - 1]; + codeLength = codeWithLength >>> 16; + + if (codeLength > bitsbuflen) { + throw new Error('invalid code length: ' + codeLength); + } + + this.bitsbuf = bitsbuf >> codeLength; + this.bitsbuflen = bitsbuflen - codeLength; + this.ip = ip; + return codeWithLength & 0xffff; + }; + /** + * read uncompressed block header + */ + + + Zlib.RawInflateStream.prototype.readUncompressedBlockHeader = function () { + /** @type {number} block length */ + var len; + /** @type {number} number for check block length */ + + var nlen; + var input = this.input; + var ip = this.ip; + this.status = Zlib.RawInflateStream.Status.BLOCK_BODY_START; + + if (ip + 4 >= input.length) { + return -1; + } + + len = input[ip++] | input[ip++] << 8; + nlen = input[ip++] | input[ip++] << 8; // check len & nlen + + if (len === ~nlen) { + throw new Error('invalid uncompressed block header: length verify'); + } // skip buffered header bits + + + this.bitsbuf = 0; + this.bitsbuflen = 0; + this.ip = ip; + this.blockLength = len; + this.status = Zlib.RawInflateStream.Status.BLOCK_BODY_END; + }; + /** + * parse uncompressed block. + */ + + + Zlib.RawInflateStream.prototype.parseUncompressedBlock = function () { + var input = this.input; + var ip = this.ip; + var output = this.output; + var op = this.op; + var len = this.blockLength; + this.status = Zlib.RawInflateStream.Status.DECODE_BLOCK_START; // copy + // XXX: รฃยยจรฃโ€šล รฃยโ€šรฃยห†รฃยลกรงยด รงโ€บยดรฃยยซรฃโ€šยณรฃฦ’โ€รฃฦ’ยผ + + while (len--) { + if (op === output.length) { + output = this.expandBuffer({ + fixRatio: 2 + }); + } // not enough input buffer + + + if (ip >= input.length) { + this.ip = ip; + this.op = op; + this.blockLength = len + 1; // รฃโ€šยณรฃฦ’โ€รฃฦ’ยผรฃยโ€”รฃยยฆรฃยยชรฃยโ€žรฃยยฎรฃยยงรฆห†ยปรฃยโ„ข + + return -1; + } + + output[op++] = input[ip++]; + } + + if (len < 0) { + this.status = Zlib.RawInflateStream.Status.DECODE_BLOCK_END; + } + + this.ip = ip; + this.op = op; + return 0; + }; + /** + * parse fixed huffman block. + */ + + + Zlib.RawInflateStream.prototype.parseFixedHuffmanBlock = function () { + this.status = Zlib.RawInflateStream.Status.BLOCK_BODY_START; + this.litlenTable = Zlib.RawInflateStream.FixedLiteralLengthTable; + this.distTable = Zlib.RawInflateStream.FixedDistanceTable; + this.status = Zlib.RawInflateStream.Status.BLOCK_BODY_END; + return 0; + }; + /** + * รฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†รฃยยฎรฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃโ€šยญรฃโ€šยนรฃฦ’ห†รฃโ€šโ€™รฅห†ยฅรฃยยฎรฃฦ’โ€”รฃฦ’ยญรฃฦ’โ€˜รฃฦ’โ€ รฃโ€šยฃรฃยยซรฉโ‚ฌโ‚ฌรฉยยฟรฃยโ„ขรฃโ€šโ€น. + * @private + */ + + + Zlib.RawInflateStream.prototype.save_ = function () { + this.ip_ = this.ip; + this.bitsbuflen_ = this.bitsbuflen; + this.bitsbuf_ = this.bitsbuf; + }; + /** + * รฅห†ยฅรฃยยฎรฃฦ’โ€”รฃฦ’ยญรฃฦ’โ€˜รฃฦ’โ€ รฃโ€šยฃรฃยยซรฉโ‚ฌโ‚ฌรฉยยฟรฃยโ€”รฃยลธรฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃโ€šยญรฃโ€šยนรฃฦ’ห†รฃโ€šโ€™รฅยพยฉรฅโ€ฆฦ’รฃยโ„ขรฃโ€šโ€น. + * @private + */ + + + Zlib.RawInflateStream.prototype.restore_ = function () { + this.ip = this.ip_; + this.bitsbuflen = this.bitsbuflen_; + this.bitsbuf = this.bitsbuf_; + }; + /** + * parse dynamic huffman block. + */ + + + Zlib.RawInflateStream.prototype.parseDynamicHuffmanBlock = function () { + /** @type {number} number of literal and length codes. */ + var hlit; + /** @type {number} number of distance codes. */ + + var hdist; + /** @type {number} number of code lengths. */ + + var hclen; + /** @type {!(Uint8Array|Array)} code lengths. */ + + var codeLengths = new Uint8Array(Zlib.RawInflateStream.Order.length); + /** @type {!Array} code lengths table. */ + + var codeLengthsTable; + this.status = Zlib.RawInflateStream.Status.BLOCK_BODY_START; + this.save_(); + hlit = this.readBits(5) + 257; + hdist = this.readBits(5) + 1; + hclen = this.readBits(4) + 4; + + if (hlit < 0 || hdist < 0 || hclen < 0) { + this.restore_(); + return -1; + } + + try { + parseDynamicHuffmanBlockImpl.call(this); + } catch (e) { + this.restore_(); + return -1; + } + + function parseDynamicHuffmanBlockImpl() { + /** @type {number} */ + var bits; + var code; + var prev = 0; + var repeat; + /** @type {!(Uint8Array|Array.)} code length table. */ + + var lengthTable; + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limit. */ + + var il; // decode code lengths + + for (i = 0; i < hclen; ++i) { + if ((bits = this.readBits(3)) < 0) { + throw new Error('not enough input'); + } + + codeLengths[Zlib.RawInflateStream.Order[i]] = bits; + } // decode length table + + + codeLengthsTable = buildHuffmanTable(codeLengths); + lengthTable = new Uint8Array(hlit + hdist); + + for (i = 0, il = hlit + hdist; i < il;) { + code = this.readCodeByTable(codeLengthsTable); + + if (code < 0) { + throw new Error('not enough input'); + } + + switch (code) { + case 16: + if ((bits = this.readBits(2)) < 0) { + throw new Error('not enough input'); + } + + repeat = 3 + bits; + + while (repeat--) { + lengthTable[i++] = prev; + } + + break; + + case 17: + if ((bits = this.readBits(3)) < 0) { + throw new Error('not enough input'); + } + + repeat = 3 + bits; + + while (repeat--) { + lengthTable[i++] = 0; + } + + prev = 0; + break; + + case 18: + if ((bits = this.readBits(7)) < 0) { + throw new Error('not enough input'); + } + + repeat = 11 + bits; + + while (repeat--) { + lengthTable[i++] = 0; + } + + prev = 0; + break; + + default: + lengthTable[i++] = code; + prev = code; + break; + } + } + + this.litlenTable = buildHuffmanTable(lengthTable.subarray(0, hlit)); + this.distTable = buildHuffmanTable(lengthTable.subarray(hlit)); + } + + this.status = Zlib.RawInflateStream.Status.BLOCK_BODY_END; + return 0; + }; + /** + * decode huffman code (dynamic) + * @return {(number|undefined)} -1 is error. + */ + + + Zlib.RawInflateStream.prototype.decodeHuffman = function () { + var output = this.output; + var op = this.op; + /** @type {number} huffman code. */ + + var code; + /** @type {number} table index. */ + + var ti; + /** @type {number} huffman code distination. */ + + var codeDist; + /** @type {number} huffman code length. */ + + var codeLength; + var litlen = this.litlenTable; + var dist = this.distTable; + var olength = output.length; + var bits; + this.status = Zlib.RawInflateStream.Status.DECODE_BLOCK_START; + + while (true) { + this.save_(); + code = this.readCodeByTable(litlen); + + if (code < 0) { + this.op = op; + this.restore_(); + return -1; + } + + if (code === 256) { + break; + } // literal + + + if (code < 256) { + if (op === olength) { + output = this.expandBuffer(); + olength = output.length; + } + + output[op++] = code; + continue; + } // length code + + + ti = code - 257; + codeLength = Zlib.RawInflateStream.LengthCodeTable[ti]; + + if (Zlib.RawInflateStream.LengthExtraTable[ti] > 0) { + bits = this.readBits(Zlib.RawInflateStream.LengthExtraTable[ti]); + + if (bits < 0) { + this.op = op; + this.restore_(); + return -1; + } + + codeLength += bits; + } // dist code + + + code = this.readCodeByTable(dist); + + if (code < 0) { + this.op = op; + this.restore_(); + return -1; + } + + codeDist = Zlib.RawInflateStream.DistCodeTable[code]; + + if (Zlib.RawInflateStream.DistExtraTable[code] > 0) { + bits = this.readBits(Zlib.RawInflateStream.DistExtraTable[code]); + + if (bits < 0) { + this.op = op; + this.restore_(); + return -1; + } + + codeDist += bits; + } // lz77 decode + + + if (op + codeLength >= olength) { + output = this.expandBuffer(); + olength = output.length; + } + + while (codeLength--) { + output[op] = output[op++ - codeDist]; + } // break + + + if (this.ip === this.input.length) { + this.op = op; + return -1; + } + } + + while (this.bitsbuflen >= 8) { + this.bitsbuflen -= 8; + this.ip--; + } + + this.op = op; + this.status = Zlib.RawInflateStream.Status.DECODE_BLOCK_END; + }; + /** + * expand output buffer. (dynamic) + * @param {Object=} opt_param option parameters. + * @return {!(Array|Uint8Array)} output buffer pointer. + */ + + + Zlib.RawInflateStream.prototype.expandBuffer = function (opt_param) { + /** @type {!(Array|Uint8Array)} store buffer. */ + var buffer; + /** @type {number} expantion ratio. */ + + var ratio = this.input.length / this.ip + 1 | 0; + /** @type {number} maximum number of huffman code. */ + + var maxHuffCode; + /** @type {number} new output buffer size. */ + + var newSize; + /** @type {number} max inflate size. */ + + var maxInflateSize; + var input = this.input; + var output = this.output; + + if (opt_param) { + if (typeof opt_param.fixRatio === 'number') { + ratio = opt_param.fixRatio; + } + + if (typeof opt_param.addRatio === 'number') { + ratio += opt_param.addRatio; + } + } // calculate new buffer size + + + if (ratio < 2) { + maxHuffCode = (input.length - this.ip) / this.litlenTable[2]; + maxInflateSize = maxHuffCode / 2 * 258 | 0; + newSize = maxInflateSize < output.length ? output.length + maxInflateSize : output.length << 1; + } else { + newSize = output.length * ratio; + } // buffer expantion + + + { + buffer = new Uint8Array(newSize); + buffer.set(output); + } + this.output = buffer; + return this.output; + }; + /** + * concat output buffer. (dynamic) + * @return {!(Array|Uint8Array)} output buffer. + */ + + + Zlib.RawInflateStream.prototype.concatBuffer = function () { + /** @type {!(Array|Uint8Array)} output buffer. */ + var buffer; + /** @type {number} */ + + var op = this.op; + /** @type {Uint8Array} */ + + var tmp; + + if (this.resize) { + { + buffer = new Uint8Array(this.output.subarray(this.sp, op)); + } + } else { + buffer = this.output.subarray(this.sp, op); + } + + this.sp = op; // compaction + + if (op > Zlib.RawInflateStream.MaxBackwardLength + this.bufferSize) { + this.op = this.sp = Zlib.RawInflateStream.MaxBackwardLength; + { + tmp = this.output; + this.output = new Uint8Array(this.bufferSize + Zlib.RawInflateStream.MaxBackwardLength); + this.output.set(tmp.subarray(op - Zlib.RawInflateStream.MaxBackwardLength, op)); + } + } + + return buffer; + }; + /** + * @constructor + * @param {!(Uint8Array|Array)} input deflated buffer. + * @param {Object=} opt_params option parameters. + * + * opt_params รฃยยฏรคยปยฅรคยธโ€นรฃยยฎรฃฦ’โ€”รฃฦ’ยญรฃฦ’โ€˜รฃฦ’โ€ รฃโ€šยฃรฃโ€šโ€™รฆล’โ€กรฅยฎลกรฃยโ„ขรฃโ€šโ€นรคยบโ€นรฃยล’รฃยยงรฃยยรฃยยพรฃยโ„ขรฃโ‚ฌโ€š + * - index: input buffer รฃยยฎ deflate รฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃฦ’ล รฃยยฎรฉโ€“โ€นรฅยงโ€นรคยฝยรงยฝยฎ. + * - blockSize: รฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃโ€šยตรฃโ€šยครฃโ€šยบ. + * - verify: รคยผยธรฅยผยตรฃยล’รงยตโ€šรฃโ€šยรฃยยฃรฃยลธรฅยพล’ adler-32 checksum รฃยยฎรฆยคล“รจยจยผรฃโ€šโ€™รจยกล’รฃยโ€ รฃยโ€น. + * - bufferType: Zlib.Inflate.BufferType รฃยยฎรฅโ‚ฌยครฃยยซรฃโ€šห†รฃยยฃรฃยยฆรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรงยฎยกรงยโ€ รฆโ€“ยนรฆยณโ€ขรฃโ€šโ€™รฆล’โ€กรฅยฎลกรฃยโ„ขรฃโ€šโ€น. + * Zlib.Inflate.BufferType รฃยยฏ Zlib.RawInflate.BufferType รฃยยฎรฃโ€šยจรฃโ€šยครฃฦ’ยชรฃโ€šยขรฃโ€šยน. + */ + + + Zlib.Inflate = function (input, opt_params) { + /** @type {number} */ + var cmf; + /** @type {number} */ + + var flg; + /** @type {!(Uint8Array|Array)} */ + + this.input = input; + /** @type {number} */ + + this.ip = 0; + /** @type {Zlib.RawInflate} */ + + this.rawinflate; + /** @type {(boolean|undefined)} verify flag. */ + + this.verify; // option parameters + + if (opt_params || !(opt_params = {})) { + if (opt_params['index']) { + this.ip = opt_params['index']; + } + + if (opt_params['verify']) { + this.verify = opt_params['verify']; + } + } // Compression Method and Flags + + + cmf = input[this.ip++]; + flg = input[this.ip++]; // compression method + + switch (cmf & 0x0f) { + case Zlib.CompressionMethod.DEFLATE: + this.method = Zlib.CompressionMethod.DEFLATE; + break; + + default: + throw new Error('unsupported compression method'); + } // fcheck + + + if (((cmf << 8) + flg) % 31 !== 0) { + throw new Error('invalid fcheck flag:' + ((cmf << 8) + flg) % 31); + } // fdict (not supported) + + + if (flg & 0x20) { + throw new Error('fdict flag is not supported'); + } // RawInflate + + + this.rawinflate = new Zlib.RawInflate(input, { + 'index': this.ip, + 'bufferSize': opt_params['bufferSize'], + 'bufferType': opt_params['bufferType'], + 'resize': opt_params['resize'] + }); + }; + /** + * @enum {number} + */ + + + Zlib.Inflate.BufferType = Zlib.RawInflate.BufferType; + /** + * decompress. + * @return {!(Uint8Array|Array)} inflated buffer. + */ + + Zlib.Inflate.prototype.decompress = function () { + /** @type {!(Array|Uint8Array)} input buffer. */ + var input = this.input; + /** @type {!(Uint8Array|Array)} inflated buffer. */ + + var buffer; + /** @type {number} adler-32 checksum */ + + var adler32; + buffer = this.rawinflate.decompress(); + this.ip = this.rawinflate.ip; // verify adler-32 + + if (this.verify) { + adler32 = (input[this.ip++] << 24 | input[this.ip++] << 16 | input[this.ip++] << 8 | input[this.ip++]) >>> 0; + + if (adler32 !== Zlib.Adler32(buffer)) { + throw new Error('invalid adler-32 checksum'); + } + } + + return buffer; + }; + /* vim:set expandtab ts=2 sw=2 tw=80: */ + + /** + * @param {!(Uint8Array|Array)} input deflated buffer. + * @constructor + */ + + + Zlib.InflateStream = function (input) { + /** @type {!(Uint8Array|Array)} */ + this.input = input === void 0 ? new Uint8Array() : input; + /** @type {number} */ + + this.ip = 0; + /** @type {Zlib.RawInflateStream} */ + + this.rawinflate = new Zlib.RawInflateStream(this.input, this.ip); + /** @type {Zlib.CompressionMethod} */ + + this.method; + /** @type {!(Array|Uint8Array)} */ + + this.output = this.rawinflate.output; + }; + /** + * decompress. + * @return {!(Uint8Array|Array)} inflated buffer. + */ + + + Zlib.InflateStream.prototype.decompress = function (input) { + /** @type {!(Uint8Array|Array)} inflated buffer. */ + var buffer; // รฆโ€“ยฐรฃยโ€”รฃยโ€žรฅโ€ฆยฅรฅล โ€บรฃโ€šโ€™รฅโ€ฆยฅรฅล โ€บรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยซรงยตยรฅยห†รฃยโ„ขรฃโ€šโ€น + // XXX Array, Uint8Array รฃยยฎรฃฦ’ยรฃโ€šยงรฃฦ’ฦ’รฃโ€šยฏรฃโ€šโ€™รจยกล’รฃยโ€ รฃยโ€นรงยขยบรจยชยรฃยโ„ขรฃโ€šโ€น + + if (input !== void 0) { + { + var tmp = new Uint8Array(this.input.length + input.length); + tmp.set(this.input, 0); + tmp.set(input, this.input.length); + this.input = tmp; + } + } + + if (this.method === void 0) { + if (this.readHeader() < 0) { + return new Uint8Array(); + } + } + + buffer = this.rawinflate.decompress(this.input, this.ip); + + if (this.rawinflate.ip !== 0) { + this.input = this.input.subarray(this.rawinflate.ip); + this.ip = 0; + } // verify adler-32 + + /* + if (this.verify) { + adler32 = + input[this.ip++] << 24 | input[this.ip++] << 16 | + input[this.ip++] << 8 | input[this.ip++]; + if (adler32 !== Zlib.Adler32(buffer)) { + throw new Error('invalid adler-32 checksum'); + } + } + */ + + + return buffer; + }; + + Zlib.InflateStream.prototype.readHeader = function () { + var ip = this.ip; + var input = this.input; // Compression Method and Flags + + var cmf = input[ip++]; + var flg = input[ip++]; + + if (cmf === void 0 || flg === void 0) { + return -1; + } // compression method + + + switch (cmf & 0x0f) { + case Zlib.CompressionMethod.DEFLATE: + this.method = Zlib.CompressionMethod.DEFLATE; + break; + + default: + throw new Error('unsupported compression method'); + } // fcheck + + + if (((cmf << 8) + flg) % 31 !== 0) { + throw new Error('invalid fcheck flag:' + ((cmf << 8) + flg) % 31); + } // fdict (not supported) + + + if (flg & 0x20) { + throw new Error('fdict flag is not supported'); + } + + this.ip = ip; + }; + /** + * @fileoverview GZIP (RFC1952) รฅยฑโ€ขรฉโ€“โ€นรฃโ€šยณรฃฦ’ยณรฃฦ’โ€ รฃฦ’ล รฅยฎลธรจยฃโ€ฆ. + */ + + /** + * @constructor + * @param {!(Array|Uint8Array)} input input buffer. + * @param {Object=} opt_params option parameters. + */ + + + Zlib.Gunzip = function (input, opt_params) { + /** @type {!(Array.|Uint8Array)} input buffer. */ + this.input = input; + /** @type {number} input buffer pointer. */ + + this.ip = 0; + /** @type {Array.} */ + + this.member = []; + /** @type {boolean} */ + + this.decompressed = false; + }; + /** + * @return {Array.} + */ + + + Zlib.Gunzip.prototype.getMembers = function () { + if (!this.decompressed) { + this.decompress(); + } + + return this.member.slice(); + }; + /** + * inflate gzip data. + * @return {!(Array.|Uint8Array)} inflated buffer. + */ + + + Zlib.Gunzip.prototype.decompress = function () { + /** @type {number} input length. */ + var il = this.input.length; + + while (this.ip < il) { + this.decodeMember(); + } + + this.decompressed = true; + return this.concatMember(); + }; + /** + * decode gzip member. + */ + + + Zlib.Gunzip.prototype.decodeMember = function () { + /** @type {Zlib.GunzipMember} */ + var member = new Zlib.GunzipMember(); + /** @type {number} */ + + var isize; + /** @type {Zlib.RawInflate} RawInflate implementation. */ + + var rawinflate; + /** @type {!(Array.|Uint8Array)} inflated data. */ + + var inflated; + /** @type {number} inflate size */ + + var inflen; + /** @type {number} character code */ + + var c; + /** @type {number} character index in string. */ + + var ci; + /** @type {Array.} character array. */ + + var str; + /** @type {number} modification time. */ + + var mtime; + /** @type {number} */ + + var crc32; + var input = this.input; + var ip = this.ip; + member.id1 = input[ip++]; + member.id2 = input[ip++]; // check signature + + if (member.id1 !== 0x1f || member.id2 !== 0x8b) { + throw new Error('invalid file signature:' + member.id1 + ',' + member.id2); + } // check compression method + + + member.cm = input[ip++]; + + switch (member.cm) { + case 8: + /* XXX: use Zlib const */ + break; + + default: + throw new Error('unknown compression method: ' + member.cm); + } // flags + + + member.flg = input[ip++]; // modification time + + mtime = input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24; + member.mtime = new Date(mtime * 1000); // extra flags + + member.xfl = input[ip++]; // operating system + + member.os = input[ip++]; // extra + + if ((member.flg & Zlib.Gzip.FlagsMask.FEXTRA) > 0) { + member.xlen = input[ip++] | input[ip++] << 8; + ip = this.decodeSubField(ip, member.xlen); + } // fname + + + if ((member.flg & Zlib.Gzip.FlagsMask.FNAME) > 0) { + for (str = [], ci = 0; (c = input[ip++]) > 0;) { + str[ci++] = String.fromCharCode(c); + } + + member.name = str.join(''); + } // fcomment + + + if ((member.flg & Zlib.Gzip.FlagsMask.FCOMMENT) > 0) { + for (str = [], ci = 0; (c = input[ip++]) > 0;) { + str[ci++] = String.fromCharCode(c); + } + + member.comment = str.join(''); + } // fhcrc + + + if ((member.flg & Zlib.Gzip.FlagsMask.FHCRC) > 0) { + member.crc16 = Zlib.CRC32.calc(input, 0, ip) & 0xffff; + + if (member.crc16 !== (input[ip++] | input[ip++] << 8)) { + throw new Error('invalid header crc16'); + } + } // isize รฃโ€šโ€™รคยบโ€นรฅโ€ฐยรฃยยซรฅยโ€“รฅยพโ€”รฃยโ„ขรฃโ€šโ€นรฃยยจรฅยฑโ€ขรฉโ€“โ€นรฅยพล’รฃยยฎรฃโ€šยตรฃโ€šยครฃโ€šยบรฃยล’รฅห†โ€ รฃยโ€นรฃโ€šโ€นรฃยลธรฃโ€šยรฃโ‚ฌย + // inflateรฅโ€กยฆรงยโ€ รฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šยตรฃโ€šยครฃโ€šยบรฃยล’รคยบโ€นรฅโ€ฐยรฃยยซรฅห†โ€ รฃยโ€นรฃโ€šล รฃโ‚ฌยรฉยซหœรฉโ‚ฌลธรฃยยซรฃยยชรฃโ€šโ€น + + + isize = input[input.length - 4] | input[input.length - 3] << 8 | input[input.length - 2] << 16 | input[input.length - 1] << 24; // isize รฃยยฎรฅยฆยฅรฅยฝโ€œรฆโ‚ฌยงรฃฦ’ยรฃโ€šยงรฃฦ’ฦ’รฃโ€šยฏ + // รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยงรฃยยฏรฆล“โ‚ฌรฅยฐย 2-bit รฃยยฎรฃยลธรฃโ€šยรฃโ‚ฌยรฆล“โ‚ฌรฅยคยงรฃยยง 1/4 รฃยยซรฃยยชรฃโ€šโ€น + // LZ77 รงยฌยฆรฅยยทรฃยยงรฃยยฏ รฉโ€ขยทรฃยโ€ขรฃยยจรจยทยรฉโ€บยข 2-Byte รฃยยงรฆล“โ‚ฌรฅยคยง 258-Byte รฃโ€šโ€™รจยกยจรงยยพรฃยยงรฃยยรฃโ€šโ€นรฃยลธรฃโ€šยรฃโ‚ฌย + // 1/128 รฃยยซรฃยยชรฃโ€šโ€นรฃยยจรฃยโ„ขรฃโ€šโ€น + // รฃยโ€œรฃยโ€œรฃยโ€นรฃโ€šโ€ฐรฅโ€ฆยฅรฅล โ€บรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃยยฎรฆยฎโ€นรฃโ€šล รฃยล’ isize รฃยยฎ 512 รฅโ‚ฌยรคยปยฅรคยธล รฃย รฃยยฃรฃยลธรฃโ€šโ€ฐ + // รฃโ€šยตรฃโ€šยครฃโ€šยบรฆล’โ€กรฅยฎลกรฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรงยขยบรคยฟยรฃยยฏรจยกล’รฃโ€šยรฃยยชรฃยโ€žรคยบโ€นรฃยยจรฃยโ„ขรฃโ€šโ€น + + if (input.length - ip - + /* CRC-32 */ + 4 - + /* ISIZE */ + 4 < isize * 512) { + inflen = isize; + } // compressed block + + + rawinflate = new Zlib.RawInflate(input, { + 'index': ip, + 'bufferSize': inflen + }); + member.data = inflated = rawinflate.decompress(); + ip = rawinflate.ip; // crc32 + + member.crc32 = crc32 = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; + + if (Zlib.CRC32.calc(inflated) !== crc32) { + throw new Error('invalid CRC-32 checksum: 0x' + Zlib.CRC32.calc(inflated).toString(16) + ' / 0x' + crc32.toString(16)); + } // input size + + + member.isize = isize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; + + if ((inflated.length & 0xffffffff) !== isize) { + throw new Error('invalid input size: ' + (inflated.length & 0xffffffff) + ' / ' + isize); + } + + this.member.push(member); + this.ip = ip; + }; + /** + * รฃโ€šยตรฃฦ’โ€“รฃฦ’โ€ขรฃโ€šยฃรฃฦ’ยผรฃฦ’ยซรฃฦ’โ€ฐรฃยยฎรฃฦ’โ€กรฃโ€šยณรฃฦ’ยผรฃฦ’โ€ฐ + * XXX: รงยยพรฅล“ยจรฃยยฏรคยฝโ€ขรฃโ€šโ€šรฃยโ€บรฃยลกรฃโ€šยนรฃโ€šยญรฃฦ’ฦ’รฃฦ’โ€”รฃยโ„ขรฃโ€šโ€น + */ + + + Zlib.Gunzip.prototype.decodeSubField = function (ip, length) { + return ip + length; + }; + /** + * @return {!(Array.|Uint8Array)} + */ + + + Zlib.Gunzip.prototype.concatMember = function () { + /** @type {Array.} */ + var member = this.member; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + /** @type {number} */ + + var p = 0; + /** @type {number} */ + + var size = 0; + /** @type {!(Array.|Uint8Array)} */ + + var buffer; + + for (i = 0, il = member.length; i < il; ++i) { + size += member[i].data.length; + } + + { + buffer = new Uint8Array(size); + + for (i = 0; i < il; ++i) { + buffer.set(member[i].data, p); + p += member[i].data.length; + } + } + return buffer; + }; + /** + * @constructor + */ + + + Zlib.GunzipMember = function () { + /** @type {number} signature first byte. */ + this.id1; + /** @type {number} signature second byte. */ + + this.id2; + /** @type {number} compression method. */ + + this.cm; + /** @type {number} flags. */ + + this.flg; + /** @type {Date} modification time. */ + + this.mtime; + /** @type {number} extra flags. */ + + this.xfl; + /** @type {number} operating system number. */ + + this.os; + /** @type {number} CRC-16 value for FHCRC flag. */ + + this.crc16; + /** @type {number} extra length. */ + + this.xlen; + /** @type {number} CRC-32 value for verification. */ + + this.crc32; + /** @type {number} input size modulo 32 value. */ + + this.isize; + /** @type {string} filename. */ + + this.name; + /** @type {string} comment. */ + + this.comment; + /** @type {!(Uint8Array|Array.)} */ + + this.data; + }; + + Zlib.GunzipMember.prototype.getName = function () { + return this.name; + }; + + Zlib.GunzipMember.prototype.getData = function () { + return this.data; + }; + + Zlib.GunzipMember.prototype.getMtime = function () { + return this.mtime; + }; + /** + * @fileoverview GZIP (RFC1952) รฅยฎลธรจยฃโ€ฆ. + */ + + /** + * @constructor + * @param {!(Array|Uint8Array)} input input buffer. + * @param {Object=} opt_params option parameters. + */ + + + Zlib.Gzip = function (input, opt_params) { + /** @type {!(Array.|Uint8Array)} input buffer. */ + this.input = input; + /** @type {number} input buffer pointer. */ + + this.ip = 0; + /** @type {!(Array.|Uint8Array)} output buffer. */ + + this.output; + /** @type {number} output buffer. */ + + this.op = 0; + /** @type {!Object} flags option flags. */ + + this.flags = {}; + /** @type {!string} filename. */ + + this.filename; + /** @type {!string} comment. */ + + this.comment; + /** @type {!Object} deflate options. */ + + this.deflateOptions; // option parameters + + if (opt_params) { + if (opt_params['flags']) { + this.flags = opt_params['flags']; + } + + if (typeof opt_params['filename'] === 'string') { + this.filename = opt_params['filename']; + } + + if (typeof opt_params['comment'] === 'string') { + this.comment = opt_params['comment']; + } + + if (opt_params['deflateOptions']) { + this.deflateOptions = opt_params['deflateOptions']; + } + } + + if (!this.deflateOptions) { + this.deflateOptions = {}; + } + }; + /** + * @type {number} + * @const + */ + + + Zlib.Gzip.DefaultBufferSize = 0x8000; + /** + * encode gzip members. + * @return {!(Array|Uint8Array)} gzip binary array. + */ + + Zlib.Gzip.prototype.compress = function () { + /** @type {number} flags. */ + var flg; + /** @type {number} modification time. */ + + var mtime; + /** @type {number} CRC-16 value for FHCRC flag. */ + + var crc16; + /** @type {number} CRC-32 value for verification. */ + + var crc32; + /** @type {!Zlib.RawDeflate} raw deflate object. */ + + var rawdeflate; + /** @type {number} character code */ + + var c; + /** @type {number} loop counter. */ + + var i; + /** @type {number} loop limiter. */ + + var il; + /** @type {!(Array|Uint8Array)} output buffer. */ + + var output = new Uint8Array(Zlib.Gzip.DefaultBufferSize); + /** @type {number} output buffer pointer. */ + + var op = 0; + var input = this.input; + var ip = this.ip; + var filename = this.filename; + var comment = this.comment; // check signature + + output[op++] = 0x1f; + output[op++] = 0x8b; // check compression method + + output[op++] = 8; + /* XXX: use Zlib const */ + // flags + + flg = 0; + if (this.flags['fname']) flg |= Zlib.Gzip.FlagsMask.FNAME; + if (this.flags['fcomment']) flg |= Zlib.Gzip.FlagsMask.FCOMMENT; + if (this.flags['fhcrc']) flg |= Zlib.Gzip.FlagsMask.FHCRC; // XXX: FTEXT + // XXX: FEXTRA + + output[op++] = flg; // modification time + + mtime = (Date.now ? Date.now() : +new Date()) / 1000 | 0; + output[op++] = mtime & 0xff; + output[op++] = mtime >>> 8 & 0xff; + output[op++] = mtime >>> 16 & 0xff; + output[op++] = mtime >>> 24 & 0xff; // extra flags + + output[op++] = 0; // operating system + + output[op++] = Zlib.Gzip.OperatingSystem.UNKNOWN; // extra + + /* NOP */ + // fname + + if (this.flags['fname'] !== void 0) { + for (i = 0, il = filename.length; i < il; ++i) { + c = filename.charCodeAt(i); + + if (c > 0xff) { + output[op++] = c >>> 8 & 0xff; + } + + output[op++] = c & 0xff; + } + + output[op++] = 0; // null termination + } // fcomment + + + if (this.flags['comment']) { + for (i = 0, il = comment.length; i < il; ++i) { + c = comment.charCodeAt(i); + + if (c > 0xff) { + output[op++] = c >>> 8 & 0xff; + } + + output[op++] = c & 0xff; + } + + output[op++] = 0; // null termination + } // fhcrc + + + if (this.flags['fhcrc']) { + crc16 = Zlib.CRC32.calc(output, 0, op) & 0xffff; + output[op++] = crc16 & 0xff; + output[op++] = crc16 >>> 8 & 0xff; + } // add compress option + + + this.deflateOptions['outputBuffer'] = output; + this.deflateOptions['outputIndex'] = op; // compress + + rawdeflate = new Zlib.RawDeflate(input, this.deflateOptions); + output = rawdeflate.compress(); + op = rawdeflate.op; // expand buffer + + { + if (op + 8 > output.buffer.byteLength) { + this.output = new Uint8Array(op + 8); + this.output.set(new Uint8Array(output.buffer)); + output = this.output; + } else { + output = new Uint8Array(output.buffer); + } + } // crc32 + + crc32 = Zlib.CRC32.calc(input); + output[op++] = crc32 & 0xff; + output[op++] = crc32 >>> 8 & 0xff; + output[op++] = crc32 >>> 16 & 0xff; + output[op++] = crc32 >>> 24 & 0xff; // input size + + il = input.length; + output[op++] = il & 0xff; + output[op++] = il >>> 8 & 0xff; + output[op++] = il >>> 16 & 0xff; + output[op++] = il >>> 24 & 0xff; + this.ip = ip; + + if (op < output.length) { + this.output = output = output.subarray(0, op); + } + + return output; + }; + /** @enum {number} */ + + + Zlib.Gzip.OperatingSystem = { + FAT: 0, + AMIGA: 1, + VMS: 2, + UNIX: 3, + VM_CMS: 4, + ATARI_TOS: 5, + HPFS: 6, + MACINTOSH: 7, + Z_SYSTEM: 8, + CP_M: 9, + TOPS_20: 10, + NTFS: 11, + QDOS: 12, + ACORN_RISCOS: 13, + UNKNOWN: 255 + }; + /** @enum {number} */ + + Zlib.Gzip.FlagsMask = { + FTEXT: 0x01, + FHCRC: 0x02, + FEXTRA: 0x04, + FNAME: 0x08, + FCOMMENT: 0x10 + }; + /** + * @fileoverview Heap Sort รฅยฎลธรจยฃโ€ฆ. รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“รฃยยงรคยฝยฟรงโ€ยจรฃยโ„ขรฃโ€šโ€น. + */ + + /** + * รฃโ€šยซรฃโ€šยนรฃโ€šยฟรฃฦ’ รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยงรคยฝยฟรงโ€ยจรฃยโ„ขรฃโ€šโ€นรฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฅยฎลธรจยฃโ€ฆ + * @param {number} length รฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฃโ€šยตรฃโ€šยครฃโ€šยบ. + * @constructor + */ + + Zlib.Heap = function (length) { + this.buffer = new Uint16Array(length * 2); + this.length = 0; + }; + /** + * รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index รฅยโ€“รฅยพโ€” + * @param {number} index รฅยญยรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index. + * @return {number} รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index. + * + */ + + + Zlib.Heap.prototype.getParent = function (index) { + return ((index - 2) / 4 | 0) * 2; + }; + /** + * รฅยญยรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index รฅยโ€“รฅยพโ€” + * @param {number} index รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index. + * @return {number} รฅยญยรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยฎ index. + */ + + + Zlib.Heap.prototype.getChild = function (index) { + return 2 * index + 2; + }; + /** + * Heap รฃยยซรฅโ‚ฌยครฃโ€šโ€™รจยฟยฝรฅล  รฃยโ„ขรฃโ€šโ€น + * @param {number} index รฃโ€šยญรฃฦ’ยผ index. + * @param {number} value รฅโ‚ฌยค. + * @return {number} รงยยพรฅล“ยจรฃยยฎรฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฉโ€ขยท. + */ + + + Zlib.Heap.prototype.push = function (index, value) { + var current, + parent, + heap = this.buffer, + swap; + current = this.length; + heap[this.length++] = value; + heap[this.length++] = index; // รฃฦ’ยซรฃฦ’ยผรฃฦ’ห†รฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยซรฃยลธรฃยยฉรฃโ€šล รงยโ‚ฌรฃยยรฃยยพรฃยยงรฅโ€ฆยฅรฃโ€šล’รฆโ€บยฟรฃยห†รฃโ€šโ€™รจยฉยฆรฃยยฟรฃโ€šโ€น + + while (current > 0) { + parent = this.getParent(current); // รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยจรฆยฏโ€รจยผฦ’รฃยโ€”รฃยยฆรจยฆยชรฃยยฎรฆโ€“ยนรฃยล’รฅยฐยรฃยโ€ขรฃยโ€˜รฃโ€šล’รฃยยฐรฅโ€ฆยฅรฃโ€šล’รฆโ€บยฟรฃยห†รฃโ€šโ€น + + if (heap[current] > heap[parent]) { + swap = heap[current]; + heap[current] = heap[parent]; + heap[parent] = swap; + swap = heap[current + 1]; + heap[current + 1] = heap[parent + 1]; + heap[parent + 1] = swap; + current = parent; // รฅโ€ฆยฅรฃโ€šล’รฆโ€บยฟรฃยห†รฃยล’รฅยฟโ€ฆรจยฆยรฃยยชรฃยยรฃยยชรฃยยฃรฃยลธรฃโ€šโ€ฐรฃยยรฃยโ€œรฃยยงรฆล ล“รฃยโ€˜รฃโ€šโ€น + } else { + break; + } + } + + return this.length; + }; + /** + * Heapรฃยโ€นรฃโ€šโ€ฐรคยธโ‚ฌรงโ€ขยชรฅยคยงรฃยยรฃยโ€žรฅโ‚ฌยครฃโ€šโ€™รจยฟโ€รฃยโ„ข + * @return {{index: number, value: number, length: number}} {index: รฃโ€šยญรฃฦ’ยผindex, + * value: รฅโ‚ฌยค, length: รฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฉโ€ขยท} รฃยยฎ Object. + */ + + + Zlib.Heap.prototype.pop = function () { + var index, + value, + heap = this.buffer, + swap, + current, + parent; + value = heap[0]; + index = heap[1]; // รฅยพล’รฃโ€šยรฃยโ€นรฃโ€šโ€ฐรฅโ‚ฌยครฃโ€šโ€™รฅยโ€“รฃโ€šโ€น + + this.length -= 2; + heap[0] = heap[this.length]; + heap[1] = heap[this.length + 1]; + parent = 0; // รฃฦ’ยซรฃฦ’ยผรฃฦ’ห†รฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยโ€นรฃโ€šโ€ฐรคยธโ€นรฃยล’รฃยยฃรฃยยฆรฃยโ€žรฃยย + + while (true) { + current = this.getChild(parent); // รงยฏโ€žรฅโ€บยฒรฃฦ’ยรฃโ€šยงรฃฦ’ฦ’รฃโ€šยฏ + + if (current >= this.length) { + break; + } // รฉลกยฃรฃยยฎรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยจรฆยฏโ€รจยผฦ’รฃยโ€”รฃยยฆรฃโ‚ฌยรฉลกยฃรฃยยฎรฆโ€“ยนรฃยล’รฅโ‚ฌยครฃยล’รฅยคยงรฃยยรฃยโ€˜รฃโ€šล’รฃยยฐรฉลกยฃรฃโ€šโ€™รงยยพรฅล“ยจรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยจรฃยโ€”รฃยยฆรฉยยธรฆล ลพ + + + if (current + 2 < this.length && heap[current + 2] > heap[current]) { + current += 2; + } // รจยฆยชรฃฦ’ลฝรฃฦ’ยผรฃฦ’โ€ฐรฃยยจรฆยฏโ€รจยผฦ’รฃยโ€”รฃยยฆรจยฆยชรฃยยฎรฆโ€“ยนรฃยล’รฅยฐยรฃยโ€ขรฃยโ€žรฅ ยดรฅยห†รฃยยฏรฅโ€ฆยฅรฃโ€šล’รฆโ€บยฟรฃยห†รฃโ€šโ€น + + + if (heap[current] > heap[parent]) { + swap = heap[parent]; + heap[parent] = heap[current]; + heap[current] = swap; + swap = heap[parent + 1]; + heap[parent + 1] = heap[current + 1]; + heap[current + 1] = swap; + } else { + break; + } + + parent = current; + } + + return { + index: index, + value: value, + length: this.length + }; + }; + /* vim:set expandtab ts=2 sw=2 tw=80: */ + + /** + * @fileoverview Deflate (RFC1951) รงยฌยฆรฅยยทรฅล’โ€“รฃโ€šยขรฃฦ’ยซรฃโ€šยดรฃฦ’ยชรฃโ€šยบรฃฦ’ รฅยฎลธรจยฃโ€ฆ. + */ + + /** + * Raw Deflate รฅยฎลธรจยฃโ€ฆ + * + * @constructor + * @param {!(Array.|Uint8Array)} input รงยฌยฆรฅยยทรฅล’โ€“รฃยโ„ขรฃโ€šโ€นรฅยฏยพรจยฑยกรฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยก. + * @param {Object=} opt_params option parameters. + * + * typed array รฃยล’รคยฝยฟรงโ€ยจรฅยยฏรจฦ’ยฝรฃยยชรฃยยจรฃยยรฃโ‚ฌยoutputBuffer รฃยล’ Array รฃยยฏรจโ€กยชรฅโ€นโ€ขรงลกโ€žรฃยยซ Uint8Array รฃยยซ + * รฅยคโ€ฐรฆยโ€บรฃยโ€ขรฃโ€šล’รฃยยพรฃยโ„ข. + * รฅห†ยฅรฃยยฎรฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†รฃยยซรฃยยชรฃโ€šโ€นรฃยลธรฃโ€šยรฅโ€กยบรฅล โ€บรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šโ€™รฅยโ€šรงโ€ฆยงรฃยโ€”รฃยยฆรฃยโ€žรฃโ€šโ€นรฅยคโ€ฐรฆโ€ขยฐรฃยยชรฃยยฉรฃยยฏ + * รฆโ€บยดรฆโ€“ยฐรฃยโ„ขรฃโ€šโ€นรฅยฟโ€ฆรจยฆยรฃยล’รฃยโ€šรฃโ€šล รฃยยพรฃยโ„ข. + */ + + + Zlib.RawDeflate = function (input, opt_params) { + /** @type {Zlib.RawDeflate.CompressionType} */ + this.compressionType = Zlib.RawDeflate.CompressionType.DYNAMIC; + /** @type {number} */ + + this.lazy = 0; + /** @type {!(Array.|Uint32Array)} */ + + this.freqsLitLen; + /** @type {!(Array.|Uint32Array)} */ + + this.freqsDist; + /** @type {!(Array.|Uint8Array)} */ + + this.input = input instanceof Array ? new Uint8Array(input) : input; + /** @type {!(Array.|Uint8Array)} output output buffer. */ + + this.output; + /** @type {number} pos output buffer position. */ + + this.op = 0; // option parameters + + if (opt_params) { + if (opt_params['lazy']) { + this.lazy = opt_params['lazy']; + } + + if (typeof opt_params['compressionType'] === 'number') { + this.compressionType = opt_params['compressionType']; + } + + if (opt_params['outputBuffer']) { + this.output = opt_params['outputBuffer'] instanceof Array ? new Uint8Array(opt_params['outputBuffer']) : opt_params['outputBuffer']; + } + + if (typeof opt_params['outputIndex'] === 'number') { + this.op = opt_params['outputIndex']; + } + } + + if (!this.output) { + this.output = new Uint8Array(0x8000); + } + }; + /** + * @enum {number} + */ + + + Zlib.RawDeflate.CompressionType = { + NONE: 0, + FIXED: 1, + DYNAMIC: 2, + RESERVED: 3 + }; + /** + * LZ77 รฃยยฎรฆล“โ‚ฌรฅยฐยรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฉโ€ขยท + * @const + * @type {number} + */ + + Zlib.RawDeflate.Lz77MinLength = 3; + /** + * LZ77 รฃยยฎรฆล“โ‚ฌรฅยคยงรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฉโ€ขยท + * @const + * @type {number} + */ + + Zlib.RawDeflate.Lz77MaxLength = 258; + /** + * LZ77 รฃยยฎรฃโ€šยฆรฃโ€šยฃรฃฦ’ยณรฃฦ’โ€ฐรฃโ€šยฆรฃโ€šยตรฃโ€šยครฃโ€šยบ + * @const + * @type {number} + */ + + Zlib.RawDeflate.WindowSize = 0x8000; + /** + * รฆล“โ‚ฌรฉโ€ขยทรฃยยฎรงยฌยฆรฅยยทรฉโ€ขยท + * @const + * @type {number} + */ + + Zlib.RawDeflate.MaxCodeLength = 16; + /** + * รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยฎรฆล“โ‚ฌรฅยคยงรฆโ€ขยฐรฅโ‚ฌยค + * @const + * @type {number} + */ + + Zlib.RawDeflate.HUFMAX = 286; + /** + * รฅโ€บยบรฅยฎลกรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยฎรงยฌยฆรฅยยทรฅล’โ€“รฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ + * @const + * @type {Array.>} + */ + + Zlib.RawDeflate.FixedHuffmanTable = function () { + var table = [], + i; + + for (i = 0; i < 288; i++) { + switch (true) { + case i <= 143: + table.push([i + 0x030, 8]); + break; + + case i <= 255: + table.push([i - 144 + 0x190, 9]); + break; + + case i <= 279: + table.push([i - 256 + 0x000, 7]); + break; + + case i <= 287: + table.push([i - 280 + 0x0C0, 8]); + break; + + default: + throw 'invalid literal: ' + i; + } + } + + return table; + }(); + /** + * DEFLATE รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยฎรคยฝล“รฆห†ย + * @return {!(Array.|Uint8Array)} รฅล“ยงรงยธยฎรฆยธห†รฃยยฟ byte array. + */ + + + Zlib.RawDeflate.prototype.compress = function () { + /** @type {!(Array.|Uint8Array)} */ + var blockArray; + /** @type {number} */ + + var position; + /** @type {number} */ + + var length; + var input = this.input; // compression + + switch (this.compressionType) { + case Zlib.RawDeflate.CompressionType.NONE: + // each 65535-Byte (length header: 16-bit) + for (position = 0, length = input.length; position < length;) { + blockArray = input.subarray(position, position + 0xffff); + position += blockArray.length; + this.makeNocompressBlock(blockArray, position === length); + } + + break; + + case Zlib.RawDeflate.CompressionType.FIXED: + this.output = this.makeFixedHuffmanBlock(input, true); + this.op = this.output.length; + break; + + case Zlib.RawDeflate.CompressionType.DYNAMIC: + this.output = this.makeDynamicHuffmanBlock(input, true); + this.op = this.output.length; + break; + + default: + throw 'invalid compression type'; + } + + return this.output; + }; + /** + * รฉยลพรฅล“ยงรงยธยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยฎรคยฝล“รฆห†ย + * @param {!(Array.|Uint8Array)} blockArray รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟ byte array. + * @param {!boolean} isFinalBlock รฆล“โ‚ฌรฅยพล’รฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยชรฃโ€šโ€ฐรฃยยฐtrue. + * @return {!(Array.|Uint8Array)} รฉยลพรฅล“ยงรงยธยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏ byte array. + */ + + + Zlib.RawDeflate.prototype.makeNocompressBlock = function (blockArray, isFinalBlock) { + /** @type {number} */ + var bfinal; + /** @type {Zlib.RawDeflate.CompressionType} */ + + var btype; + /** @type {number} */ + + var len; + /** @type {number} */ + + var nlen; + var output = this.output; + var op = this.op; // expand buffer + + { + output = new Uint8Array(this.output.buffer); + + while (output.length <= op + blockArray.length + 5) { + output = new Uint8Array(output.length << 1); + } + + output.set(this.output); + } // header + + bfinal = isFinalBlock ? 1 : 0; + btype = Zlib.RawDeflate.CompressionType.NONE; + output[op++] = bfinal | btype << 1; // length + + len = blockArray.length; + nlen = ~len + 0x10000 & 0xffff; + output[op++] = len & 0xff; + output[op++] = len >>> 8 & 0xff; + output[op++] = nlen & 0xff; + output[op++] = nlen >>> 8 & 0xff; // copy buffer + + { + output.set(blockArray, op); + op += blockArray.length; + output = output.subarray(0, op); + } + this.op = op; + this.output = output; + return output; + }; + /** + * รฅโ€บยบรฅยฎลกรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยฎรคยฝล“รฆห†ย + * @param {!(Array.|Uint8Array)} blockArray รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟ byte array. + * @param {!boolean} isFinalBlock รฆล“โ‚ฌรฅยพล’รฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยชรฃโ€šโ€ฐรฃยยฐtrue. + * @return {!(Array.|Uint8Array)} รฅโ€บยบรฅยฎลกรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏ byte array. + */ + + + Zlib.RawDeflate.prototype.makeFixedHuffmanBlock = function (blockArray, isFinalBlock) { + /** @type {Zlib.BitStream} */ + var stream = new Zlib.BitStream(new Uint8Array(this.output.buffer), this.op); + /** @type {number} */ + + var bfinal; + /** @type {Zlib.RawDeflate.CompressionType} */ + + var btype; + /** @type {!(Array.|Uint16Array)} */ + + var data; // header + + bfinal = isFinalBlock ? 1 : 0; + btype = Zlib.RawDeflate.CompressionType.FIXED; + stream.writeBits(bfinal, 1, true); + stream.writeBits(btype, 2, true); + data = this.lz77(blockArray); + this.fixedHuffman(data, stream); + return stream.finish(); + }; + /** + * รฅโ€นโ€ขรงลกโ€žรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยฎรคยฝล“รฆห†ย + * @param {!(Array.|Uint8Array)} blockArray รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟ byte array. + * @param {!boolean} isFinalBlock รฆล“โ‚ฌรฅยพล’รฃยยฎรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃยยชรฃโ€šโ€ฐรฃยยฐtrue. + * @return {!(Array.|Uint8Array)} รฅโ€นโ€ขรงลกโ€žรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏ byte array. + */ + + + Zlib.RawDeflate.prototype.makeDynamicHuffmanBlock = function (blockArray, isFinalBlock) { + /** @type {Zlib.BitStream} */ + var stream = new Zlib.BitStream(new Uint8Array(this.output.buffer), this.op); + /** @type {number} */ + + var bfinal; + /** @type {Zlib.RawDeflate.CompressionType} */ + + var btype; + /** @type {!(Array.|Uint16Array)} */ + + var data; + /** @type {number} */ + + var hlit; + /** @type {number} */ + + var hdist; + /** @type {number} */ + + var hclen; + /** @const @type {Array.} */ + + var hclenOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + /** @type {!(Array.|Uint8Array)} */ + + var litLenLengths; + /** @type {!(Array.|Uint16Array)} */ + + var litLenCodes; + /** @type {!(Array.|Uint8Array)} */ + + var distLengths; + /** @type {!(Array.|Uint16Array)} */ + + var distCodes; + /** @type {{ + * codes: !(Array.|Uint32Array), + * freqs: !(Array.|Uint8Array) + * }} */ + + var treeSymbols; + /** @type {!(Array.|Uint8Array)} */ + + var treeLengths; + /** @type {Array} */ + + var transLengths = new Array(19); + /** @type {!(Array.|Uint16Array)} */ + + var treeCodes; + /** @type {number} */ + + var code; + /** @type {number} */ + + var bitlen; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; // header + + bfinal = isFinalBlock ? 1 : 0; + btype = Zlib.RawDeflate.CompressionType.DYNAMIC; + stream.writeBits(bfinal, 1, true); + stream.writeBits(btype, 2, true); + data = this.lz77(blockArray); // รฃฦ’ยชรฃฦ’โ€ รฃฦ’ยฉรฃฦ’ยซรฃฦ’ยปรฉโ€ขยทรฃยโ€ข, รจยทยรฉโ€บยขรฃยยฎรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยจรงยฌยฆรฅยยทรฉโ€ขยทรฃยยฎรงยฎโ€”รฅโ€กยบ + + litLenLengths = this.getLengths_(this.freqsLitLen, 15); + litLenCodes = this.getCodesFromLengths_(litLenLengths); + distLengths = this.getLengths_(this.freqsDist, 7); + distCodes = this.getCodesFromLengths_(distLengths); // HLIT, HDIST รฃยยฎรฆยฑยบรฅยฎลก + + for (hlit = 286; hlit > 257 && litLenLengths[hlit - 1] === 0; hlit--) {} + + for (hdist = 30; hdist > 1 && distLengths[hdist - 1] === 0; hdist--) {} // HCLEN + + + treeSymbols = this.getTreeSymbols_(hlit, litLenLengths, hdist, distLengths); + treeLengths = this.getLengths_(treeSymbols.freqs, 7); + + for (i = 0; i < 19; i++) { + transLengths[i] = treeLengths[hclenOrder[i]]; + } + + for (hclen = 19; hclen > 4 && transLengths[hclen - 1] === 0; hclen--) {} + + treeCodes = this.getCodesFromLengths_(treeLengths); // รฅโ€กยบรฅล โ€บ + + stream.writeBits(hlit - 257, 5, true); + stream.writeBits(hdist - 1, 5, true); + stream.writeBits(hclen - 4, 4, true); + + for (i = 0; i < hclen; i++) { + stream.writeBits(transLengths[i], 3, true); + } // รฃฦ’โ€žรฃฦ’ยชรฃฦ’ยผรฃยยฎรฅโ€กยบรฅล โ€บ + + + for (i = 0, il = treeSymbols.codes.length; i < il; i++) { + code = treeSymbols.codes[i]; + stream.writeBits(treeCodes[code], treeLengths[code], true); // extra bits + + if (code >= 16) { + i++; + + switch (code) { + case 16: + bitlen = 2; + break; + + case 17: + bitlen = 3; + break; + + case 18: + bitlen = 7; + break; + + default: + throw 'invalid code: ' + code; + } + + stream.writeBits(treeSymbols.codes[i], bitlen, true); + } + } + + this.dynamicHuffman(data, [litLenCodes, litLenLengths], [distCodes, distLengths], stream); + return stream.finish(); + }; + /** + * รฅโ€นโ€ขรงลกโ€žรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“(รฃโ€šยซรฃโ€šยนรฃโ€šยฟรฃฦ’ รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ) + * @param {!(Array.|Uint16Array)} dataArray LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฆยธห†รฃยยฟ byte array. + * @param {!Zlib.BitStream} stream รฆโ€บยธรฃยยรจยพยผรฃยยฟรงโ€ยจรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ . + * @return {!Zlib.BitStream} รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“รฆยธห†รฃยยฟรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ รฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†. + */ + + + Zlib.RawDeflate.prototype.dynamicHuffman = function (dataArray, litLen, dist, stream) { + /** @type {number} */ + var index; + /** @type {number} */ + + var length; + /** @type {number} */ + + var literal; + /** @type {number} */ + + var code; + /** @type {number} */ + + var litLenCodes; + /** @type {number} */ + + var litLenLengths; + /** @type {number} */ + + var distCodes; + /** @type {number} */ + + var distLengths; + litLenCodes = litLen[0]; + litLenLengths = litLen[1]; + distCodes = dist[0]; + distLengths = dist[1]; // รงยฌยฆรฅยยทรฃโ€šโ€™ BitStream รฃยยซรฆโ€บยธรฃยยรจยพยผรฃโ€šโ€œรฃยยงรฃยโ€žรฃยย + + for (index = 0, length = dataArray.length; index < length; ++index) { + literal = dataArray[index]; // literal or length + + stream.writeBits(litLenCodes[literal], litLenLengths[literal], true); // รฉโ€ขยทรฃยโ€ขรฃฦ’ยปรจยทยรฉโ€บยขรงยฌยฆรฅยยท + + if (literal > 256) { + // length extra + stream.writeBits(dataArray[++index], dataArray[++index], true); // distance + + code = dataArray[++index]; + stream.writeBits(distCodes[code], distLengths[code], true); // distance extra + + stream.writeBits(dataArray[++index], dataArray[++index], true); // รงยตโ€šรงยซยฏ + } else if (literal === 256) { + break; + } + } + + return stream; + }; + /** + * รฅโ€บยบรฅยฎลกรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“ + * @param {!(Array.|Uint16Array)} dataArray LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฆยธห†รฃยยฟ byte array. + * @param {!Zlib.BitStream} stream รฆโ€บยธรฃยยรจยพยผรฃยยฟรงโ€ยจรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ . + * @return {!Zlib.BitStream} รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฅล’โ€“รฆยธห†รฃยยฟรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ รฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†. + */ + + + Zlib.RawDeflate.prototype.fixedHuffman = function (dataArray, stream) { + /** @type {number} */ + var index; + /** @type {number} */ + + var length; + /** @type {number} */ + + var literal; // รงยฌยฆรฅยยทรฃโ€šโ€™ BitStream รฃยยซรฆโ€บยธรฃยยรจยพยผรฃโ€šโ€œรฃยยงรฃยโ€žรฃยย + + for (index = 0, length = dataArray.length; index < length; index++) { + literal = dataArray[index]; // รงยฌยฆรฅยยทรฃยยฎรฆโ€บยธรฃยยรจยพยผรฃยยฟ + + Zlib.BitStream.prototype.writeBits.apply(stream, Zlib.RawDeflate.FixedHuffmanTable[literal]); // รฉโ€ขยทรฃยโ€ขรฃฦ’ยปรจยทยรฉโ€บยขรงยฌยฆรฅยยท + + if (literal > 0x100) { + // length extra + stream.writeBits(dataArray[++index], dataArray[++index], true); // distance + + stream.writeBits(dataArray[++index], 5); // distance extra + + stream.writeBits(dataArray[++index], dataArray[++index], true); // รงยตโ€šรงยซยฏ + } else if (literal === 0x100) { + break; + } + } + + return stream; + }; + /** + * รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฆฦ’โ€ฆรฅ ยฑ + * @param {!number} length รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยโ€”รฃยลธรฉโ€ขยทรฃยโ€ข. + * @param {!number} backwardDistance รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรคยฝยรงยฝยฎรฃยยจรฃยยฎรจยทยรฉโ€บยข. + * @constructor + */ + + + Zlib.RawDeflate.Lz77Match = function (length, backwardDistance) { + /** @type {number} match length. */ + this.length = length; + /** @type {number} backward distance. */ + + this.backwardDistance = backwardDistance; + }; + /** + * รฉโ€ขยทรฃยโ€ขรงยฌยฆรฅยยทรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ. + * [รฃโ€šยณรฃฦ’ยผรฃฦ’โ€ฐ, รฆโ€นยกรฅยผยตรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†, รฆโ€นยกรฅยผยตรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยท] รฃยยฎรฉโ€ฆยรฅห†โ€”รฃยยจรฃยยชรฃยยฃรฃยยฆรฃยโ€žรฃโ€šโ€น. + * @const + * @type {!(Array.|Uint32Array)} + */ + + + Zlib.RawDeflate.Lz77Match.LengthCodeTable = function (table) { + return new Uint32Array(table); + }(function () { + /** @type {!Array} */ + var table = []; + /** @type {number} */ + + var i; + /** @type {!Array.} */ + + var c; + + for (i = 3; i <= 258; i++) { + c = code(i); + table[i] = c[2] << 24 | c[1] << 16 | c[0]; + } + /** + * @param {number} length lz77 length. + * @return {!Array.} lz77 codes. + */ + + + function code(length) { + switch (true) { + case length === 3: + return [257, length - 3, 0]; + + case length === 4: + return [258, length - 4, 0]; + + case length === 5: + return [259, length - 5, 0]; + + case length === 6: + return [260, length - 6, 0]; + + case length === 7: + return [261, length - 7, 0]; + + case length === 8: + return [262, length - 8, 0]; + + case length === 9: + return [263, length - 9, 0]; + + case length === 10: + return [264, length - 10, 0]; + + case length <= 12: + return [265, length - 11, 1]; + + case length <= 14: + return [266, length - 13, 1]; + + case length <= 16: + return [267, length - 15, 1]; + + case length <= 18: + return [268, length - 17, 1]; + + case length <= 22: + return [269, length - 19, 2]; + + case length <= 26: + return [270, length - 23, 2]; + + case length <= 30: + return [271, length - 27, 2]; + + case length <= 34: + return [272, length - 31, 2]; + + case length <= 42: + return [273, length - 35, 3]; + + case length <= 50: + return [274, length - 43, 3]; + + case length <= 58: + return [275, length - 51, 3]; + + case length <= 66: + return [276, length - 59, 3]; + + case length <= 82: + return [277, length - 67, 4]; + + case length <= 98: + return [278, length - 83, 4]; + + case length <= 114: + return [279, length - 99, 4]; + + case length <= 130: + return [280, length - 115, 4]; + + case length <= 162: + return [281, length - 131, 5]; + + case length <= 194: + return [282, length - 163, 5]; + + case length <= 226: + return [283, length - 195, 5]; + + case length <= 257: + return [284, length - 227, 5]; + + case length === 258: + return [285, length - 258, 0]; + + default: + throw 'invalid length: ' + length; + } + } + + return table; + }()); + /** + * รจยทยรฉโ€บยขรงยฌยฆรฅยยทรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ + * @param {!number} dist รจยทยรฉโ€บยข. + * @return {!Array.} รฃโ€šยณรฃฦ’ยผรฃฦ’โ€ฐรฃโ‚ฌยรฆโ€นยกรฅยผยตรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ‚ฌยรฆโ€นยกรฅยผยตรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉโ€ขยทรฃยยฎรฉโ€ฆยรฅห†โ€”. + * @private + */ + + + Zlib.RawDeflate.Lz77Match.prototype.getDistanceCode_ = function (dist) { + /** @type {!Array.} distance code table. */ + var r; + + switch (true) { + case dist === 1: + r = [0, dist - 1, 0]; + break; + + case dist === 2: + r = [1, dist - 2, 0]; + break; + + case dist === 3: + r = [2, dist - 3, 0]; + break; + + case dist === 4: + r = [3, dist - 4, 0]; + break; + + case dist <= 6: + r = [4, dist - 5, 1]; + break; + + case dist <= 8: + r = [5, dist - 7, 1]; + break; + + case dist <= 12: + r = [6, dist - 9, 2]; + break; + + case dist <= 16: + r = [7, dist - 13, 2]; + break; + + case dist <= 24: + r = [8, dist - 17, 3]; + break; + + case dist <= 32: + r = [9, dist - 25, 3]; + break; + + case dist <= 48: + r = [10, dist - 33, 4]; + break; + + case dist <= 64: + r = [11, dist - 49, 4]; + break; + + case dist <= 96: + r = [12, dist - 65, 5]; + break; + + case dist <= 128: + r = [13, dist - 97, 5]; + break; + + case dist <= 192: + r = [14, dist - 129, 6]; + break; + + case dist <= 256: + r = [15, dist - 193, 6]; + break; + + case dist <= 384: + r = [16, dist - 257, 7]; + break; + + case dist <= 512: + r = [17, dist - 385, 7]; + break; + + case dist <= 768: + r = [18, dist - 513, 8]; + break; + + case dist <= 1024: + r = [19, dist - 769, 8]; + break; + + case dist <= 1536: + r = [20, dist - 1025, 9]; + break; + + case dist <= 2048: + r = [21, dist - 1537, 9]; + break; + + case dist <= 3072: + r = [22, dist - 2049, 10]; + break; + + case dist <= 4096: + r = [23, dist - 3073, 10]; + break; + + case dist <= 6144: + r = [24, dist - 4097, 11]; + break; + + case dist <= 8192: + r = [25, dist - 6145, 11]; + break; + + case dist <= 12288: + r = [26, dist - 8193, 12]; + break; + + case dist <= 16384: + r = [27, dist - 12289, 12]; + break; + + case dist <= 24576: + r = [28, dist - 16385, 13]; + break; + + case dist <= 32768: + r = [29, dist - 24577, 13]; + break; + + default: + throw 'invalid distance'; + } + + return r; + }; + /** + * รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฆฦ’โ€ฆรฅ ยฑรฃโ€šโ€™ LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฉโ€ฆยรฅห†โ€”รฃยยงรจยฟโ€รฃยโ„ข. + * รฃยยชรฃยล รฃโ‚ฌยรฃยโ€œรฃยโ€œรฃยยงรฃยยฏรคยปยฅรคยธโ€นรฃยยฎรฅโ€ โ€ฆรฉฦ’ยจรคยปโ€ขรฆยงหœรฃยยงรงยฌยฆรฅยยทรฅล’โ€“รฃยโ€”รฃยยฆรฃยโ€žรฃโ€šโ€น + * [ CODE, EXTRA-BIT-LEN, EXTRA, CODE, EXTRA-BIT-LEN, EXTRA ] + * @return {!Array.} LZ77 รงยฌยฆรฅยยทรฅล’โ€“ byte array. + */ + + + Zlib.RawDeflate.Lz77Match.prototype.toLz77Array = function () { + /** @type {number} */ + var length = this.length; + /** @type {number} */ + + var dist = this.backwardDistance; + /** @type {Array} */ + + var codeArray = []; + /** @type {number} */ + + var pos = 0; + /** @type {!Array.} */ + + var code; // length + + code = Zlib.RawDeflate.Lz77Match.LengthCodeTable[length]; + codeArray[pos++] = code & 0xffff; + codeArray[pos++] = code >> 16 & 0xff; + codeArray[pos++] = code >> 24; // distance + + code = this.getDistanceCode_(dist); + codeArray[pos++] = code[0]; + codeArray[pos++] = code[1]; + codeArray[pos++] = code[2]; + return codeArray; + }; + /** + * LZ77 รฅยฎลธรจยฃโ€ฆ + * @param {!(Array.|Uint8Array)} dataArray LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฃยโ„ขรฃโ€šโ€นรฃฦ’ยรฃโ€šยครฃฦ’ห†รฉโ€ฆยรฅห†โ€”. + * @return {!(Array.|Uint16Array)} LZ77 รงยฌยฆรฅยยทรฅล’โ€“รฃยโ€”รฃยลธรฉโ€ฆยรฅห†โ€”. + */ + + + Zlib.RawDeflate.prototype.lz77 = function (dataArray) { + /** @type {number} input position */ + var position; + /** @type {number} input length */ + + var length; + /** @type {number} loop counter */ + + var i; + /** @type {number} loop limiter */ + + var il; + /** @type {number} chained-hash-table key */ + + var matchKey; + /** @type {Object.>} chained-hash-table */ + + var table = {}; + /** @const @type {number} */ + + var windowSize = Zlib.RawDeflate.WindowSize; + /** @type {Array.} match list */ + + var matchList; + /** @type {Zlib.RawDeflate.Lz77Match} longest match */ + + var longestMatch; + /** @type {Zlib.RawDeflate.Lz77Match} previous longest match */ + + var prevMatch; + /** @type {!(Array.|Uint16Array)} lz77 buffer */ + + var lz77buf = new Uint16Array(dataArray.length * 2); + /** @type {number} lz77 output buffer pointer */ + + var pos = 0; + /** @type {number} lz77 skip length */ + + var skipLength = 0; + /** @type {!(Array.|Uint32Array)} */ + + var freqsLitLen = new Uint32Array(286); + /** @type {!(Array.|Uint32Array)} */ + + var freqsDist = new Uint32Array(30); + /** @type {number} */ + + var lazy = this.lazy; + /** @type {*} temporary variable */ + + var tmp; + freqsLitLen[256] = 1; // EOB รฃยยฎรฆล“โ‚ฌรคยฝลฝรฅโ€กยบรงยยพรฅโ€บลพรฆโ€ขยฐรฃยยฏ 1 + + /** + * รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟรฃยยฎรฆโ€บยธรฃยยรจยพยผรฃยยฟ + * @param {Zlib.RawDeflate.Lz77Match} match LZ77 Match data. + * @param {!number} offset รฃโ€šยนรฃโ€šยญรฃฦ’ฦ’รฃฦ’โ€”รฉโ€“โ€นรฅยงโ€นรคยฝยรงยฝยฎ(รงโ€บยธรฅยฏยพรฆล’โ€กรฅยฎลก). + * @private + */ + + function writeMatch(match, offset) { + /** @type {Array.} */ + var lz77Array = match.toLz77Array(); + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + + for (i = 0, il = lz77Array.length; i < il; ++i) { + lz77buf[pos++] = lz77Array[i]; + } + + freqsLitLen[lz77Array[0]]++; + freqsDist[lz77Array[3]]++; + skipLength = match.length + offset - 1; + prevMatch = null; + } // LZ77 รงยฌยฆรฅยยทรฅล’โ€“ + + + for (position = 0, length = dataArray.length; position < length; ++position) { + // รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฃโ€šยญรฃฦ’ยผรฃยยฎรคยฝล“รฆห†ย + for (matchKey = 0, i = 0, il = Zlib.RawDeflate.Lz77MinLength; i < il; ++i) { + if (position + i === length) { + break; + } + + matchKey = matchKey << 8 | dataArray[position + i]; + } // รฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃยล’รฆล“ยชรฅยฎลกรงยพยฉรฃย รฃยยฃรฃยลธรฃโ€šโ€ฐรคยฝล“รฆห†ยรฃยโ„ขรฃโ€šโ€น + + + if (table[matchKey] === void 0) { + table[matchKey] = []; + } + + matchList = table[matchKey]; // skip + + if (skipLength-- > 0) { + matchList.push(position); + continue; + } // รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃยยฎรฆโ€บยดรฆโ€“ยฐ (รฆล“โ‚ฌรฅยคยงรฆห†ยปรฃโ€šล รจยทยรฉโ€บยขรฃโ€šโ€™รจยถโ€ฆรฃยห†รฃยยฆรฃยโ€žรฃโ€šโ€นรฃโ€šโ€šรฃยยฎรฃโ€šโ€™รฅโ€ฐล รฉโ„ขยครฃยโ„ขรฃโ€šโ€น) + + + while (matchList.length > 0 && position - matchList[0] > windowSize) { + matchList.shift(); + } // รฃฦ’โ€กรฃฦ’ยผรฃโ€šยฟรฆล“ยซรฅยฐยพรฃยยงรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยโ€”รฃโ€šห†รฃยโ€ รฃยล’รฃยยชรฃยโ€žรฅ ยดรฅยห†รฃยยฏรฃยยรฃยยฎรฃยยพรฃยยพรฆยตยรฃยโ€”รฃยโ€œรฃโ€šโ‚ฌ + + + if (position + Zlib.RawDeflate.Lz77MinLength >= length) { + if (prevMatch) { + writeMatch(prevMatch, -1); + } + + for (i = 0, il = length - position; i < il; ++i) { + tmp = dataArray[position + i]; + lz77buf[pos++] = tmp; + ++freqsLitLen[tmp]; + } + + break; + } // รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฅโ‚ฌโ„ขรจยฃล“รฃยโ€นรฃโ€šโ€ฐรฆล“โ‚ฌรฉโ€ขยทรฃยยฎรฃโ€šโ€šรฃยยฎรฃโ€šโ€™รฆลฝยขรฃยโ„ข + + + if (matchList.length > 0) { + longestMatch = this.searchLongestMatch_(dataArray, position, matchList); + + if (prevMatch) { + // รงยยพรฅล“ยจรฃยยฎรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยยฎรฆโ€“ยนรฃยล’รฅโ€ฐยรฅโ€บลพรฃยยฎรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃโ€šห†รฃโ€šล รฃโ€šโ€šรฉโ€ขยทรฃยโ€ž + if (prevMatch.length < longestMatch.length) { + // write previous literal + tmp = dataArray[position - 1]; + lz77buf[pos++] = tmp; + ++freqsLitLen[tmp]; // write current match + + writeMatch(longestMatch, 0); + } else { + // write previous match + writeMatch(prevMatch, -1); + } + } else if (longestMatch.length < lazy) { + prevMatch = longestMatch; + } else { + writeMatch(longestMatch, 0); + } // รฅโ€ฐยรฅโ€บลพรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยโ€”รฃยยฆรฃยโ€žรฃยยฆรคยปล รฅโ€บลพรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยล’รฃยยชรฃยโ€นรฃยยฃรฃยลธรฃโ€šโ€ฐรฅโ€ฐยรฅโ€บลพรฃยยฎรฃโ€šโ€™รฆลฝยกรงโ€ยจ + + } else if (prevMatch) { + writeMatch(prevMatch, -1); + } else { + tmp = dataArray[position]; + lz77buf[pos++] = tmp; + ++freqsLitLen[tmp]; + } + + matchList.push(position); // รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซรฃยยซรงยยพรฅล“ยจรฃยยฎรคยฝยรงยฝยฎรฃโ€šโ€™รคยฟยรฅยญหœ + } // รงยตโ€šรงยซยฏรฅโ€กยฆรงยโ€  + + + lz77buf[pos++] = 256; + freqsLitLen[256]++; + this.freqsLitLen = freqsLitLen; + this.freqsDist = freqsDist; + return lz77buf.subarray(0, pos); + }; + /** + * รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃยโ€”รฃยลธรฅโ‚ฌโ„ขรจยฃล“รฃยยฎรคยธยญรฃยโ€นรฃโ€šโ€ฐรฆล“โ‚ฌรฉโ€ขยทรคยธโ‚ฌรจโ€กยดรฃโ€šโ€™รฆลฝยขรฃยโ„ข + * @param {!Object} data plain data byte array. + * @param {!number} position plain data byte array position. + * @param {!Array.} matchList รฅโ‚ฌโ„ขรจยฃล“รฃยยจรฃยยชรฃโ€šโ€นรคยฝยรงยฝยฎรฃยยฎรฉโ€ฆยรฅห†โ€”. + * @return {!Zlib.RawDeflate.Lz77Match} รฆล“โ‚ฌรฉโ€ขยทรฃยโ€นรฃยยครฆล“โ‚ฌรงลธยญรจยทยรฉโ€บยขรฃยยฎรฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฃโ€šยชรฃฦ’โ€“รฃโ€šยธรฃโ€šยงรฃโ€šยฏรฃฦ’ห†. + * @private + */ + + + Zlib.RawDeflate.prototype.searchLongestMatch_ = function (data, position, matchList) { + var match, + currentMatch, + matchMax = 0, + matchLength, + i, + j, + l, + dl = data.length; // รฅโ‚ฌโ„ขรจยฃล“รฃโ€šโ€™รฅยพล’รฃโ€šยรฃยโ€นรฃโ€šโ€ฐ 1 รฃยยครฃยลกรฃยยครงยตลพรฃโ€šล รจยพยผรฃโ€šโ€œรฃยยงรฃโ€šโ€ รฃยย + + permatch: for (i = 0, l = matchList.length; i < l; i++) { + match = matchList[l - i - 1]; + matchLength = Zlib.RawDeflate.Lz77MinLength; // รฅโ€ฐยรฅโ€บลพรฃยยพรฃยยงรฃยยฎรฆล“โ‚ฌรฉโ€ขยทรคยธโ‚ฌรจโ€กยดรฃโ€šโ€™รฆล“ยซรฅยฐยพรฃยโ€นรฃโ€šโ€ฐรคยธโ‚ฌรจโ€กยดรฆยคล“รงยดยขรฃยโ„ขรฃโ€šโ€น + + if (matchMax > Zlib.RawDeflate.Lz77MinLength) { + for (j = matchMax; j > Zlib.RawDeflate.Lz77MinLength; j--) { + if (data[match + j - 1] !== data[position + j - 1]) { + continue permatch; + } + } + + matchLength = matchMax; + } // รฆล“โ‚ฌรฉโ€ขยทรคยธโ‚ฌรจโ€กยดรฆลฝยขรงยดยข + + + while (matchLength < Zlib.RawDeflate.Lz77MaxLength && position + matchLength < dl && data[match + matchLength] === data[position + matchLength]) { + ++matchLength; + } // รฃฦ’ลพรฃฦ’ฦ’รฃฦ’ยรฉโ€ขยทรฃยล’รฅยล’รฃยหœรฅ ยดรฅยห†รฃยยฏรฅยพล’รฆโ€“ยนรฃโ€šโ€™รฅโ€žยชรฅโ€ฆห† + + + if (matchLength > matchMax) { + currentMatch = match; + matchMax = matchLength; + } // รฆล“โ‚ฌรฉโ€ขยทรฃยล’รงยขยบรฅยฎลกรฃยโ€”รฃยลธรฃโ€šโ€ฐรฅยพล’รฃยยฎรฅโ€กยฆรงยโ€ รฃยยฏรงล“ยรงโ€ขยฅ + + + if (matchLength === Zlib.RawDeflate.Lz77MaxLength) { + break; + } + } + + return new Zlib.RawDeflate.Lz77Match(matchMax, position - currentMatch); + }; + /** + * Tree-Transmit Symbols รฃยยฎรงยฎโ€”รฅโ€กยบ + * reference: PuTTY Deflate implementation + * @param {number} hlit HLIT. + * @param {!(Array.|Uint8Array)} litlenLengths รฃฦ’ยชรฃฦ’โ€ รฃฦ’ยฉรฃฦ’ยซรฃยยจรฉโ€ขยทรฃยโ€ขรงยฌยฆรฅยยทรฃยยฎรงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”. + * @param {number} hdist HDIST. + * @param {!(Array.|Uint8Array)} distLengths รจยทยรฉโ€บยขรงยฌยฆรฅยยทรฃยยฎรงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”. + * @return {{ + * codes: !(Array.|Uint32Array), + * freqs: !(Array.|Uint8Array) + * }} Tree-Transmit Symbols. + */ + + + Zlib.RawDeflate.prototype.getTreeSymbols_ = function (hlit, litlenLengths, hdist, distLengths) { + var src = new Uint32Array(hlit + hdist), + i, + j, + runLength, + l, + result = new Uint32Array(286 + 30), + nResult, + rpt, + freqs = new Uint8Array(19); + j = 0; + + for (i = 0; i < hlit; i++) { + src[j++] = litlenLengths[i]; + } + + for (i = 0; i < hdist; i++) { + src[j++] = distLengths[i]; + } // รงยฌยฆรฅยยทรฅล’โ€“ + + + nResult = 0; + + for (i = 0, l = src.length; i < l; i += j) { + // Run Length Encoding + for (j = 1; i + j < l && src[i + j] === src[i]; ++j) {} + + runLength = j; + + if (src[i] === 0) { + // 0 รฃยยฎรงยนยฐรฃโ€šล รจยฟโ€รฃยโ€”รฃยล’ 3 รฅโ€บลพรฆล“ยชรฆยบโ‚ฌรฃยยชรฃโ€šโ€ฐรฃยยฐรฃยยรฃยยฎรฃยยพรฃยยพ + if (runLength < 3) { + while (runLength-- > 0) { + result[nResult++] = 0; + freqs[0]++; + } + } else { + while (runLength > 0) { + // รงยนยฐรฃโ€šล รจยฟโ€รฃยโ€”รฃยยฏรฆล“โ‚ฌรฅยคยง 138 รฃยยพรฃยยงรฃยยชรฃยยฎรฃยยงรฅห†โ€กรฃโ€šล รจยฉยฐรฃโ€šยรฃโ€šโ€น + rpt = runLength < 138 ? runLength : 138; + + if (rpt > runLength - 3 && rpt < runLength) { + rpt = runLength - 3; + } // 3-10 รฅโ€บลพ -> 17 + + + if (rpt <= 10) { + result[nResult++] = 17; + result[nResult++] = rpt - 3; + freqs[17]++; // 11-138 รฅโ€บลพ -> 18 + } else { + result[nResult++] = 18; + result[nResult++] = rpt - 11; + freqs[18]++; + } + + runLength -= rpt; + } + } + } else { + result[nResult++] = src[i]; + freqs[src[i]]++; + runLength--; // รงยนยฐรฃโ€šล รจยฟโ€รฃยโ€”รฅโ€บลพรฆโ€ขยฐรฃยล’3รฅโ€บลพรฆล“ยชรฆยบโ‚ฌรฃยยชรฃโ€šโ€ฐรฃยยฐรฃฦ’ยฉรฃฦ’ยณรฃฦ’ยฌรฃฦ’ยณรฃโ€šยฐรฃโ€šยนรงยฌยฆรฅยยทรฃยยฏรจยฆยรฃโ€šโ€ฐรฃยยชรฃยโ€ž + + if (runLength < 3) { + while (runLength-- > 0) { + result[nResult++] = src[i]; + freqs[src[i]]++; + } // 3 รฅโ€บลพรคยปยฅรคยธล รฃยยชรฃโ€šโ€ฐรฃยยฐรฃฦ’ยฉรฃฦ’ยณรฃฦ’ยฌรฃฦ’ยณรฃโ€šยฐรฃโ€šยนรงยฌยฆรฅยยทรฅล’โ€“ + + } else { + while (runLength > 0) { + // runLengthรฃโ€šโ€™ 3-6 รฃยยงรฅห†โ€ รฅโ€ฐยฒ + rpt = runLength < 6 ? runLength : 6; + + if (rpt > runLength - 3 && rpt < runLength) { + rpt = runLength - 3; + } + + result[nResult++] = 16; + result[nResult++] = rpt - 3; + freqs[16]++; + runLength -= rpt; + } + } + } + } + + return { + codes: result.subarray(0, nResult), + freqs: freqs + }; + }; + /** + * รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃยยฎรฉโ€ขยทรฃยโ€ขรฃโ€šโ€™รฅยโ€“รฅยพโ€”รฃยโ„ขรฃโ€šโ€น + * @param {!(Array.|Uint8Array|Uint32Array)} freqs รฅโ€กยบรงยยพรฃโ€šยซรฃโ€šยฆรฃฦ’ยณรฃฦ’ห†. + * @param {number} limit รงยฌยฆรฅยยทรฉโ€ขยทรฃยยฎรฅห†ยถรฉโ„ขย. + * @return {!(Array.|Uint8Array)} รงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”. + * @private + */ + + + Zlib.RawDeflate.prototype.getLengths_ = function (freqs, limit) { + /** @type {number} */ + var nSymbols = freqs.length; + /** @type {Zlib.Heap} */ + + var heap = new Zlib.Heap(2 * Zlib.RawDeflate.HUFMAX); + /** @type {!(Array.|Uint8Array)} */ + + var length = new Uint8Array(nSymbols); + /** @type {Array} */ + + var nodes; + /** @type {!(Array.|Uint32Array)} */ + + var values; + /** @type {!(Array.|Uint8Array)} */ + + var codeLength; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; // รฃฦ’โ€™รฃฦ’ยผรฃฦ’โ€”รฃยยฎรฆยงโ€นรงยฏโ€ฐ + + for (i = 0; i < nSymbols; ++i) { + if (freqs[i] > 0) { + heap.push(i, freqs[i]); + } + } + + nodes = new Array(heap.length / 2); + values = new Uint32Array(heap.length / 2); // รฉยลพ 0 รฃยยฎรจยฆยรงยด รฃยล’รคยธโ‚ฌรฃยยครฃย รฃยโ€˜รฃย รฃยยฃรฃยลธรฅ ยดรฅยห†รฃยยฏรฃโ‚ฌยรฃยยรฃยยฎรฃโ€šยทรฃฦ’ยณรฃฦ’ล“รฃฦ’ยซรฃยยซรงยฌยฆรฅยยทรฉโ€ขยท 1 รฃโ€šโ€™รฅโ€ฐยฒรฃโ€šล รฅยฝโ€œรฃยยฆรฃยยฆรงยตโ€šรคยบโ€  + + if (nodes.length === 1) { + length[heap.pop().index] = 1; + return length; + } // Reverse Package Merge Algorithm รฃยยซรฃโ€šห†รฃโ€šโ€น Canonical Huffman Code รฃยยฎรงยฌยฆรฅยยทรฉโ€ขยทรฆยฑยบรฅยฎลก + + + for (i = 0, il = heap.length / 2; i < il; ++i) { + nodes[i] = heap.pop(); + values[i] = nodes[i].value; + } + + codeLength = this.reversePackageMerge_(values, values.length, limit); + + for (i = 0, il = nodes.length; i < il; ++i) { + length[nodes[i].index] = codeLength[i]; + } + + return length; + }; + /** + * Reverse Package Merge Algorithm. + * @param {!(Array.|Uint32Array)} freqs sorted probability. + * @param {number} symbols number of symbols. + * @param {number} limit code length limit. + * @return {!(Array.|Uint8Array)} code lengths. + */ + + + Zlib.RawDeflate.prototype.reversePackageMerge_ = function (freqs, symbols, limit) { + /** @type {!(Array.|Uint16Array)} */ + var minimumCost = new Uint16Array(limit); + /** @type {!(Array.|Uint8Array)} */ + + var flag = new Uint8Array(limit); + /** @type {!(Array.|Uint8Array)} */ + + var codeLength = new Uint8Array(symbols); + /** @type {Array} */ + + var value = new Array(limit); + /** @type {Array} */ + + var type = new Array(limit); + /** @type {Array.} */ + + var currentPosition = new Array(limit); + /** @type {number} */ + + var excess = (1 << limit) - symbols; + /** @type {number} */ + + var half = 1 << limit - 1; + /** @type {number} */ + + var i; + /** @type {number} */ + + var j; + /** @type {number} */ + + var t; + /** @type {number} */ + + var weight; + /** @type {number} */ + + var next; + /** + * @param {number} j + */ + + function takePackage(j) { + /** @type {number} */ + var x = type[j][currentPosition[j]]; + + if (x === symbols) { + takePackage(j + 1); + takePackage(j + 1); + } else { + --codeLength[x]; + } + + ++currentPosition[j]; + } + + minimumCost[limit - 1] = symbols; + + for (j = 0; j < limit; ++j) { + if (excess < half) { + flag[j] = 0; + } else { + flag[j] = 1; + excess -= half; + } + + excess <<= 1; + minimumCost[limit - 2 - j] = (minimumCost[limit - 1 - j] / 2 | 0) + symbols; + } + + minimumCost[0] = flag[0]; + value[0] = new Array(minimumCost[0]); + type[0] = new Array(minimumCost[0]); + + for (j = 1; j < limit; ++j) { + if (minimumCost[j] > 2 * minimumCost[j - 1] + flag[j]) { + minimumCost[j] = 2 * minimumCost[j - 1] + flag[j]; + } + + value[j] = new Array(minimumCost[j]); + type[j] = new Array(minimumCost[j]); + } + + for (i = 0; i < symbols; ++i) { + codeLength[i] = limit; + } + + for (t = 0; t < minimumCost[limit - 1]; ++t) { + value[limit - 1][t] = freqs[t]; + type[limit - 1][t] = t; + } + + for (i = 0; i < limit; ++i) { + currentPosition[i] = 0; + } + + if (flag[limit - 1] === 1) { + --codeLength[0]; + ++currentPosition[limit - 1]; + } + + for (j = limit - 2; j >= 0; --j) { + i = 0; + weight = 0; + next = currentPosition[j + 1]; + + for (t = 0; t < minimumCost[j]; t++) { + weight = value[j + 1][next] + value[j + 1][next + 1]; + + if (weight > freqs[i]) { + value[j][t] = weight; + type[j][t] = symbols; + next += 2; + } else { + value[j][t] = freqs[i]; + type[j][t] = i; + ++i; + } + } + + currentPosition[j] = 0; + + if (flag[j] === 1) { + takePackage(j); + } + } + + return codeLength; + }; + /** + * รงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”รฃยโ€นรฃโ€šโ€ฐรฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฃโ€šโ€™รฅยโ€“รฅยพโ€”รฃยโ„ขรฃโ€šโ€น + * reference: PuTTY Deflate implementation + * @param {!(Array.|Uint8Array)} lengths รงยฌยฆรฅยยทรฉโ€ขยทรฉโ€ฆยรฅห†โ€”. + * @return {!(Array.|Uint16Array)} รฃฦ’ยรฃฦ’โ€ขรฃฦ’ลพรฃฦ’ยณรงยฌยฆรฅยยทรฉโ€ฆยรฅห†โ€”. + * @private + */ + + + Zlib.RawDeflate.prototype.getCodesFromLengths_ = function (lengths) { + var codes = new Uint16Array(lengths.length), + count = [], + startCode = [], + code = 0, + i, + il, + j, + m; // Count the codes of each length. + + for (i = 0, il = lengths.length; i < il; i++) { + count[lengths[i]] = (count[lengths[i]] | 0) + 1; + } // Determine the starting code for each length block. + + + for (i = 1, il = Zlib.RawDeflate.MaxCodeLength; i <= il; i++) { + startCode[i] = code; + code += count[i] | 0; + code <<= 1; + } // Determine the code for each symbol. Mirrored, of course. + + + for (i = 0, il = lengths.length; i < il; i++) { + code = startCode[lengths[i]]; + startCode[lengths[i]] += 1; + codes[i] = 0; + + for (j = 0, m = lengths[i]; j < m; j++) { + codes[i] = codes[i] << 1 | code & 1; + code >>>= 1; + } + } + + return codes; + }; + /** + * @param {!(Array.|Uint8Array)} input input buffer. + * @param {Object=} opt_params options. + * @constructor + */ + + + Zlib.Unzip = function (input, opt_params) { + opt_params = opt_params || {}; + /** @type {!(Array.|Uint8Array)} */ + + this.input = input instanceof Array ? new Uint8Array(input) : input; + /** @type {number} */ + + this.ip = 0; + /** @type {number} */ + + this.eocdrOffset; + /** @type {number} */ + + this.numberOfThisDisk; + /** @type {number} */ + + this.startDisk; + /** @type {number} */ + + this.totalEntriesThisDisk; + /** @type {number} */ + + this.totalEntries; + /** @type {number} */ + + this.centralDirectorySize; + /** @type {number} */ + + this.centralDirectoryOffset; + /** @type {number} */ + + this.commentLength; + /** @type {(Array.|Uint8Array)} */ + + this.comment; + /** @type {Array.} */ + + this.fileHeaderList; + /** @type {Object.} */ + + this.filenameToIndex; + /** @type {boolean} */ + + this.verify = opt_params['verify'] || false; + /** @type {(Array.|Uint8Array)} */ + + this.password = opt_params['password']; + }; + + Zlib.Unzip.CompressionMethod = Zlib.Zip.CompressionMethod; + /** + * @type {Array.} + * @const + */ + + Zlib.Unzip.FileHeaderSignature = Zlib.Zip.FileHeaderSignature; + /** + * @type {Array.} + * @const + */ + + Zlib.Unzip.LocalFileHeaderSignature = Zlib.Zip.LocalFileHeaderSignature; + /** + * @type {Array.} + * @const + */ + + Zlib.Unzip.CentralDirectorySignature = Zlib.Zip.CentralDirectorySignature; + /** + * @param {!(Array.|Uint8Array)} input input buffer. + * @param {number} ip input position. + * @constructor + */ + + Zlib.Unzip.FileHeader = function (input, ip) { + /** @type {!(Array.|Uint8Array)} */ + this.input = input; + /** @type {number} */ + + this.offset = ip; + /** @type {number} */ + + this.length; + /** @type {number} */ + + this.version; + /** @type {number} */ + + this.os; + /** @type {number} */ + + this.needVersion; + /** @type {number} */ + + this.flags; + /** @type {number} */ + + this.compression; + /** @type {number} */ + + this.time; + /** @type {number} */ + + this.date; + /** @type {number} */ + + this.crc32; + /** @type {number} */ + + this.compressedSize; + /** @type {number} */ + + this.plainSize; + /** @type {number} */ + + this.fileNameLength; + /** @type {number} */ + + this.extraFieldLength; + /** @type {number} */ + + this.fileCommentLength; + /** @type {number} */ + + this.diskNumberStart; + /** @type {number} */ + + this.internalFileAttributes; + /** @type {number} */ + + this.externalFileAttributes; + /** @type {number} */ + + this.relativeOffset; + /** @type {string} */ + + this.filename; + /** @type {!(Array.|Uint8Array)} */ + + this.extraField; + /** @type {!(Array.|Uint8Array)} */ + + this.comment; + }; + + Zlib.Unzip.FileHeader.prototype.parse = function () { + /** @type {!(Array.|Uint8Array)} */ + var input = this.input; + /** @type {number} */ + + var ip = this.offset; // central file header signature + + if (input[ip++] !== Zlib.Unzip.FileHeaderSignature[0] || input[ip++] !== Zlib.Unzip.FileHeaderSignature[1] || input[ip++] !== Zlib.Unzip.FileHeaderSignature[2] || input[ip++] !== Zlib.Unzip.FileHeaderSignature[3]) { + throw new Error('invalid file header signature'); + } // version made by + + + this.version = input[ip++]; + this.os = input[ip++]; // version needed to extract + + this.needVersion = input[ip++] | input[ip++] << 8; // general purpose bit flag + + this.flags = input[ip++] | input[ip++] << 8; // compression method + + this.compression = input[ip++] | input[ip++] << 8; // last mod file time + + this.time = input[ip++] | input[ip++] << 8; //last mod file date + + this.date = input[ip++] | input[ip++] << 8; // crc-32 + + this.crc32 = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // compressed size + + this.compressedSize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // uncompressed size + + this.plainSize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // file name length + + this.fileNameLength = input[ip++] | input[ip++] << 8; // extra field length + + this.extraFieldLength = input[ip++] | input[ip++] << 8; // file comment length + + this.fileCommentLength = input[ip++] | input[ip++] << 8; // disk number start + + this.diskNumberStart = input[ip++] | input[ip++] << 8; // internal file attributes + + this.internalFileAttributes = input[ip++] | input[ip++] << 8; // external file attributes + + this.externalFileAttributes = input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24; // relative offset of local header + + this.relativeOffset = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // file name + + this.filename = String.fromCharCode.apply(null, input.subarray(ip, ip += this.fileNameLength)); // extra field + + this.extraField = input.subarray(ip, ip += this.extraFieldLength); // file comment + + this.comment = input.subarray(ip, ip + this.fileCommentLength); + this.length = ip - this.offset; + }; + /** + * @param {!(Array.|Uint8Array)} input input buffer. + * @param {number} ip input position. + * @constructor + */ + + + Zlib.Unzip.LocalFileHeader = function (input, ip) { + /** @type {!(Array.|Uint8Array)} */ + this.input = input; + /** @type {number} */ + + this.offset = ip; + /** @type {number} */ + + this.length; + /** @type {number} */ + + this.needVersion; + /** @type {number} */ + + this.flags; + /** @type {number} */ + + this.compression; + /** @type {number} */ + + this.time; + /** @type {number} */ + + this.date; + /** @type {number} */ + + this.crc32; + /** @type {number} */ + + this.compressedSize; + /** @type {number} */ + + this.plainSize; + /** @type {number} */ + + this.fileNameLength; + /** @type {number} */ + + this.extraFieldLength; + /** @type {string} */ + + this.filename; + /** @type {!(Array.|Uint8Array)} */ + + this.extraField; + }; + + Zlib.Unzip.LocalFileHeader.Flags = Zlib.Zip.Flags; + + Zlib.Unzip.LocalFileHeader.prototype.parse = function () { + /** @type {!(Array.|Uint8Array)} */ + var input = this.input; + /** @type {number} */ + + var ip = this.offset; // local file header signature + + if (input[ip++] !== Zlib.Unzip.LocalFileHeaderSignature[0] || input[ip++] !== Zlib.Unzip.LocalFileHeaderSignature[1] || input[ip++] !== Zlib.Unzip.LocalFileHeaderSignature[2] || input[ip++] !== Zlib.Unzip.LocalFileHeaderSignature[3]) { + throw new Error('invalid local file header signature'); + } // version needed to extract + + + this.needVersion = input[ip++] | input[ip++] << 8; // general purpose bit flag + + this.flags = input[ip++] | input[ip++] << 8; // compression method + + this.compression = input[ip++] | input[ip++] << 8; // last mod file time + + this.time = input[ip++] | input[ip++] << 8; //last mod file date + + this.date = input[ip++] | input[ip++] << 8; // crc-32 + + this.crc32 = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // compressed size + + this.compressedSize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // uncompressed size + + this.plainSize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // file name length + + this.fileNameLength = input[ip++] | input[ip++] << 8; // extra field length + + this.extraFieldLength = input[ip++] | input[ip++] << 8; // file name + + this.filename = String.fromCharCode.apply(null, input.subarray(ip, ip += this.fileNameLength)); // extra field + + this.extraField = input.subarray(ip, ip += this.extraFieldLength); + this.length = ip - this.offset; + }; + + Zlib.Unzip.prototype.searchEndOfCentralDirectoryRecord = function () { + /** @type {!(Array.|Uint8Array)} */ + var input = this.input; + /** @type {number} */ + + var ip; + + for (ip = input.length - 12; ip > 0; --ip) { + if (input[ip] === Zlib.Unzip.CentralDirectorySignature[0] && input[ip + 1] === Zlib.Unzip.CentralDirectorySignature[1] && input[ip + 2] === Zlib.Unzip.CentralDirectorySignature[2] && input[ip + 3] === Zlib.Unzip.CentralDirectorySignature[3]) { + this.eocdrOffset = ip; + return; + } + } + + throw new Error('End of Central Directory Record not found'); + }; + + Zlib.Unzip.prototype.parseEndOfCentralDirectoryRecord = function () { + /** @type {!(Array.|Uint8Array)} */ + var input = this.input; + /** @type {number} */ + + var ip; + + if (!this.eocdrOffset) { + this.searchEndOfCentralDirectoryRecord(); + } + + ip = this.eocdrOffset; // signature + + if (input[ip++] !== Zlib.Unzip.CentralDirectorySignature[0] || input[ip++] !== Zlib.Unzip.CentralDirectorySignature[1] || input[ip++] !== Zlib.Unzip.CentralDirectorySignature[2] || input[ip++] !== Zlib.Unzip.CentralDirectorySignature[3]) { + throw new Error('invalid signature'); + } // number of this disk + + + this.numberOfThisDisk = input[ip++] | input[ip++] << 8; // number of the disk with the start of the central directory + + this.startDisk = input[ip++] | input[ip++] << 8; // total number of entries in the central directory on this disk + + this.totalEntriesThisDisk = input[ip++] | input[ip++] << 8; // total number of entries in the central directory + + this.totalEntries = input[ip++] | input[ip++] << 8; // size of the central directory + + this.centralDirectorySize = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // offset of start of central directory with respect to the starting disk number + + this.centralDirectoryOffset = (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0; // .ZIP file comment length + + this.commentLength = input[ip++] | input[ip++] << 8; // .ZIP file comment + + this.comment = input.subarray(ip, ip + this.commentLength); + }; + + Zlib.Unzip.prototype.parseFileHeader = function () { + /** @type {Array.} */ + var filelist = []; + /** @type {Object.} */ + + var filetable = {}; + /** @type {number} */ + + var ip; + /** @type {Zlib.Unzip.FileHeader} */ + + var fileHeader; + /*: @type {number} */ + + var i; + /*: @type {number} */ + + var il; + + if (this.fileHeaderList) { + return; + } + + if (this.centralDirectoryOffset === void 0) { + this.parseEndOfCentralDirectoryRecord(); + } + + ip = this.centralDirectoryOffset; + + for (i = 0, il = this.totalEntries; i < il; ++i) { + fileHeader = new Zlib.Unzip.FileHeader(this.input, ip); + fileHeader.parse(); + ip += fileHeader.length; + filelist[i] = fileHeader; + filetable[fileHeader.filename] = i; + } + + if (this.centralDirectorySize < ip - this.centralDirectoryOffset) { + throw new Error('invalid file header size'); + } + + this.fileHeaderList = filelist; + this.filenameToIndex = filetable; + }; + /** + * @param {number} index file header index. + * @param {Object=} opt_params + * @return {!(Array.|Uint8Array)} file data. + */ + + + Zlib.Unzip.prototype.getFileData = function (index, opt_params) { + opt_params = opt_params || {}; + /** @type {!(Array.|Uint8Array)} */ + + var input = this.input; + /** @type {Array.} */ + + var fileHeaderList = this.fileHeaderList; + /** @type {Zlib.Unzip.LocalFileHeader} */ + + var localFileHeader; + /** @type {number} */ + + var offset; + /** @type {number} */ + + var length; + /** @type {!(Array.|Uint8Array)} */ + + var buffer; + /** @type {number} */ + + var crc32; + /** @type {Array.|Uint32Array|Object} */ + + var key; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + + if (!fileHeaderList) { + this.parseFileHeader(); + } + + if (fileHeaderList[index] === void 0) { + throw new Error('wrong index'); + } + + offset = fileHeaderList[index].relativeOffset; + localFileHeader = new Zlib.Unzip.LocalFileHeader(this.input, offset); + localFileHeader.parse(); + offset += localFileHeader.length; + length = localFileHeader.compressedSize; // decryption + + if ((localFileHeader.flags & Zlib.Unzip.LocalFileHeader.Flags.ENCRYPT) !== 0) { + if (!(opt_params['password'] || this.password)) { + throw new Error('please set password'); + } + + key = this.createDecryptionKey(opt_params['password'] || this.password); // encryption header + + for (i = offset, il = offset + 12; i < il; ++i) { + this.decode(key, input[i]); + } + + offset += 12; + length -= 12; // decryption + + for (i = offset, il = offset + length; i < il; ++i) { + input[i] = this.decode(key, input[i]); + } + } + + switch (localFileHeader.compression) { + case Zlib.Unzip.CompressionMethod.STORE: + buffer = this.input.subarray(offset, offset + length); + break; + + case Zlib.Unzip.CompressionMethod.DEFLATE: + buffer = new Zlib.RawInflate(this.input, { + 'index': offset, + 'bufferSize': localFileHeader.plainSize + }).decompress(); + break; + + default: + throw new Error('unknown compression type'); + } + + if (this.verify) { + crc32 = Zlib.CRC32.calc(buffer); + + if (localFileHeader.crc32 !== crc32) { + throw new Error('wrong crc: file=0x' + localFileHeader.crc32.toString(16) + ', data=0x' + crc32.toString(16)); + } + } + + return buffer; + }; + /** + * @return {Array.} + */ + + + Zlib.Unzip.prototype.getFilenames = function () { + /** @type {Array.} */ + var filenameList = []; + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + /** @type {Array.} */ + + var fileHeaderList; + + if (!this.fileHeaderList) { + this.parseFileHeader(); + } + + fileHeaderList = this.fileHeaderList; + + for (i = 0, il = fileHeaderList.length; i < il; ++i) { + filenameList[i] = fileHeaderList[i].filename; + } + + return filenameList; + }; + /** + * @param {string} filename extract filename. + * @param {Object=} opt_params + * @return {!(Array.|Uint8Array)} decompressed data. + */ + + + Zlib.Unzip.prototype.decompress = function (filename, opt_params) { + /** @type {number} */ + var index; + + if (!this.filenameToIndex) { + this.parseFileHeader(); + } + + index = this.filenameToIndex[filename]; + + if (index === void 0) { + throw new Error(filename + ' not found'); + } + + return this.getFileData(index, opt_params); + }; + /** + * @param {(Array.|Uint8Array)} password + */ + + + Zlib.Unzip.prototype.setPassword = function (password) { + this.password = password; + }; + /** + * @param {(Array.|Uint32Array|Object)} key + * @param {number} n + * @return {number} + */ + + + Zlib.Unzip.prototype.decode = function (key, n) { + n ^= this.getByte(key); + this.updateKeys(key, n); + return n; + }; // common method + + + Zlib.Unzip.prototype.updateKeys = Zlib.Zip.prototype.updateKeys; + Zlib.Unzip.prototype.createDecryptionKey = Zlib.Zip.prototype.createEncryptionKey; + Zlib.Unzip.prototype.getByte = Zlib.Zip.prototype.getByte; + /** + * @fileoverview รฉโ€บโ€˜รฅยคลกรฃยยชรฉโ€“ยขรฆโ€ขยฐรงยพยครฃโ€šโ€™รฃยยพรฃยยจรฃโ€šยรฃยลธรฃฦ’ยขรฃโ€šยธรฃฦ’ยฅรฃฦ’ยผรฃฦ’ยซรฅยฎลธรจยฃโ€ฆ. + */ + + /** + * Byte String รฃยโ€นรฃโ€šโ€ฐ Byte Array รฃยยซรฅยคโ€ฐรฆยโ€บ. + * @param {!string} str byte string. + * @return {!Array.} byte array. + */ + + Zlib.Util.stringToByteArray = function (str) { + /** @type {!Array.<(string|number)>} */ + var tmp = str.split(''); + /** @type {number} */ + + var i; + /** @type {number} */ + + var il; + + for (i = 0, il = tmp.length; i < il; i++) { + tmp[i] = (tmp[i].charCodeAt(0) & 0xff) >>> 0; + } + + return tmp; + }; + /** + * @fileoverview Adler32 checksum รฅยฎลธรจยฃโ€ฆ. + */ + + /** + * Adler32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยครฃยยฎรคยฝล“รฆห†ย + * @param {!(Array|Uint8Array|string)} array รงยฎโ€”รฅโ€กยบรฃยยซรคยฝยฟรงโ€ยจรฃยโ„ขรฃโ€šโ€น byte array. + * @return {number} Adler32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยค. + */ + + + Zlib.Adler32 = function (array) { + if (typeof array === 'string') { + array = Zlib.Util.stringToByteArray(array); + } + + return Zlib.Adler32.update(1, array); + }; + /** + * Adler32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยครฃยยฎรฆโ€บยดรฆโ€“ยฐ + * @param {number} adler รงยยพรฅล“ยจรฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยค. + * @param {!(Array|Uint8Array)} array รฆโ€บยดรฆโ€“ยฐรฃยยซรคยฝยฟรงโ€ยจรฃยโ„ขรฃโ€šโ€น byte array. + * @return {number} Adler32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยค. + */ + + + Zlib.Adler32.update = function (adler, array) { + /** @type {number} */ + var s1 = adler & 0xffff; + /** @type {number} */ + + var s2 = adler >>> 16 & 0xffff; + /** @type {number} array length */ + + var len = array.length; + /** @type {number} loop length (don't overflow) */ + + var tlen; + /** @type {number} array index */ + + var i = 0; + + while (len > 0) { + tlen = len > Zlib.Adler32.OptimizationParameter ? Zlib.Adler32.OptimizationParameter : len; + len -= tlen; + + do { + s1 += array[i++]; + s2 += s1; + } while (--tlen); + + s1 %= 65521; + s2 %= 65521; + } + + return (s2 << 16 | s1) >>> 0; + }; + /** + * Adler32 รฆล“โ‚ฌรฉยยฉรฅล’โ€“รฃฦ’โ€˜รฃฦ’ยฉรฃฦ’ยกรฃฦ’ยผรฃโ€šยฟ + * รงยยพรงล ยถรฃยยงรฃยยฏ 1024 รงยจโ€นรฅยบยฆรฃยล’รฆล“โ‚ฌรฉยยฉ. + * @see http://jsperf.com/adler-32-simple-vs-optimized/3 + * @define {number} + */ + + + Zlib.Adler32.OptimizationParameter = 1024; + /** + * รฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ + * @constructor + * @param {!(Array|Uint8Array)=} buffer output buffer. + * @param {number=} bufferPosition start buffer pointer. + */ + + Zlib.BitStream = function (buffer, bufferPosition) { + /** @type {number} buffer index. */ + this.index = typeof bufferPosition === 'number' ? bufferPosition : 0; + /** @type {number} bit index. */ + + this.bitindex = 0; + /** @type {!(Array|Uint8Array)} bit-stream output buffer. */ + + this.buffer = buffer instanceof Uint8Array ? buffer : new Uint8Array(Zlib.BitStream.DefaultBlockSize); // รฅโ€ฆยฅรฅล โ€บรฃยโ€ขรฃโ€šล’รฃยลธ index รฃยล’รจยถยณรฃโ€šล รฃยยชรฃยโ€นรฃยยฃรฃยลธรฃโ€šโ€ฐรฆโ€นยกรฅยผยตรฃยโ„ขรฃโ€šโ€นรฃยล’รฃโ‚ฌยรฅโ‚ฌยรฃยยซรฃยโ€”รฃยยฆรฃโ€šโ€šรฃฦ’โ‚ฌรฃฦ’ยกรฃยยชรฃโ€šโ€ฐรคยธยรฆยญยฃรฃยยจรฃยโ„ขรฃโ€šโ€น + + if (this.buffer.length * 2 <= this.index) { + throw new Error("invalid index"); + } else if (this.buffer.length <= this.index) { + this.expandBuffer(); + } + }; + /** + * รฃฦ’โ€กรฃฦ’โ€ขรฃโ€šยฉรฃฦ’ยซรฃฦ’ห†รฃฦ’โ€“รฃฦ’ยญรฃฦ’ฦ’รฃโ€šยฏรฃโ€šยตรฃโ€šยครฃโ€šยบ. + * @const + * @type {number} + */ + + + Zlib.BitStream.DefaultBlockSize = 0x8000; + /** + * expand buffer. + * @return {!(Array|Uint8Array)} new buffer. + */ + + Zlib.BitStream.prototype.expandBuffer = function () { + /** @type {!(Array|Uint8Array)} old buffer. */ + var oldbuf = this.buffer; + /** @type {number} loop limiter. */ + + var il = oldbuf.length; + /** @type {!(Array|Uint8Array)} new buffer. */ + + var buffer = new Uint8Array(il << 1); // copy buffer + + { + buffer.set(oldbuf); + } + return this.buffer = buffer; + }; + /** + * รฆโ€ขยฐรฅโ‚ฌยครฃโ€šโ€™รฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฃยยงรฆล’โ€กรฅยฎลกรฃยโ€”รฃยลธรฆโ€ขยฐรฃย รฃยโ€˜รฆโ€บยธรฃยยรจยพยผรฃโ€šโ‚ฌ. + * @param {number} number รฆโ€บยธรฃยยรจยพยผรฃโ€šโ‚ฌรฆโ€ขยฐรฅโ‚ฌยค. + * @param {number} n รฆโ€บยธรฃยยรจยพยผรฃโ€šโ‚ฌรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฆโ€ขยฐ. + * @param {boolean=} reverse รฉโ‚ฌโ€ รฉ โ€ รฃยยซรฆโ€บยธรฃยยรจยพยผรฃโ€šโ‚ฌรฃยยชรฃโ€šโ€ฐรฃยยฐ true. + */ + + + Zlib.BitStream.prototype.writeBits = function (number, n, reverse) { + var buffer = this.buffer; + var index = this.index; + var bitindex = this.bitindex; + /** @type {number} current octet. */ + + var current = buffer[index]; + /** @type {number} loop counter. */ + + var i; + /** + * 32-bit รฆโ€ขยดรฆโ€ขยฐรฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉ โ€ รฃโ€šโ€™รฉโ‚ฌโ€ รฃยยซรฃยโ„ขรฃโ€šโ€น + * @param {number} n 32-bit integer. + * @return {number} reversed 32-bit integer. + * @private + */ + + function rev32_(n) { + return Zlib.BitStream.ReverseTable[n & 0xFF] << 24 | Zlib.BitStream.ReverseTable[n >>> 8 & 0xFF] << 16 | Zlib.BitStream.ReverseTable[n >>> 16 & 0xFF] << 8 | Zlib.BitStream.ReverseTable[n >>> 24 & 0xFF]; + } + + if (reverse && n > 1) { + number = n > 8 ? rev32_(number) >> 32 - n : Zlib.BitStream.ReverseTable[number] >> 8 - n; + } // Byte รฅยขฦ’รงโ€ขล’รฃโ€šโ€™รจยถโ€ฆรฃยห†รฃยยชรฃยโ€žรฃยยจรฃยย + + + if (n + bitindex < 8) { + current = current << n | number; + bitindex += n; // Byte รฅยขฦ’รงโ€ขล’รฃโ€šโ€™รจยถโ€ฆรฃยห†รฃโ€šโ€นรฃยยจรฃยย + } else { + for (i = 0; i < n; ++i) { + current = current << 1 | number >> n - i - 1 & 1; // next byte + + if (++bitindex === 8) { + bitindex = 0; + buffer[index++] = Zlib.BitStream.ReverseTable[current]; + current = 0; // expand + + if (index === buffer.length) { + buffer = this.expandBuffer(); + } + } + } + } + + buffer[index] = current; + this.buffer = buffer; + this.bitindex = bitindex; + this.index = index; + }; + /** + * รฃโ€šยนรฃฦ’ห†รฃฦ’ยชรฃฦ’ยผรฃฦ’ รฃยยฎรงยตโ€šรงยซยฏรฅโ€กยฆรงยโ€ รฃโ€šโ€™รจยกล’รฃยโ€  + * @return {!(Array|Uint8Array)} รงยตโ€šรงยซยฏรฅโ€กยฆรงยโ€ รฅยพล’รฃยยฎรฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šโ€™ byte array รฃยยงรจยฟโ€รฃยโ„ข. + */ + + + Zlib.BitStream.prototype.finish = function () { + var buffer = this.buffer; + var index = this.index; + /** @type {!(Array|Uint8Array)} output buffer. */ + + var output; // bitindex รฃยล’ 0 รฃยยฎรฆโ„ขโ€šรฃยยฏรคยฝโ„ขรฅห†โ€ รฃยยซ index รฃยล’รฉโ‚ฌยฒรฃโ€šโ€œรฃยยงรฃยโ€žรฃโ€šโ€นรงล ยถรฆโ€ฆโ€น + + if (this.bitindex > 0) { + buffer[index] <<= 8 - this.bitindex; + buffer[index] = Zlib.BitStream.ReverseTable[buffer[index]]; + index++; + } // array truncation + + + { + output = buffer.subarray(0, index); + } + return output; + }; + /** + * 0-255 รฃยยฎรฃฦ’โ€œรฃฦ’ฦ’รฃฦ’ห†รฉ โ€ รฃโ€šโ€™รฅยยรจยปยขรฃยโ€”รฃยลธรฃฦ’โ€ รฃฦ’ยผรฃฦ’โ€“รฃฦ’ยซ + * @const + * @type {!(Uint8Array|Array.)} + */ + + + Zlib.BitStream.ReverseTable = function (table) { + return table; + }(function () { + /** @type {!(Array|Uint8Array)} reverse table. */ + var table = new Uint8Array(256); + /** @type {number} loop counter. */ + + var i; // generate + + for (i = 0; i < 256; ++i) { + table[i] = function (n) { + var r = n; + var s = 7; + + for (n >>>= 1; n; n >>>= 1) { + r <<= 1; + r |= n & 1; + --s; + } + + return (r << s & 0xff) >>> 0; + }(i); + } + + return table; + }()); + /** + * CRC32 รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยครฃโ€šโ€™รฅยโ€“รฅยพโ€” + * @param {!(Array.|Uint8Array)} data data byte array. + * @param {number=} pos data position. + * @param {number=} length data length. + * @return {number} CRC32. + */ + + + Zlib.CRC32.calc = function (data, pos, length) { + return Zlib.CRC32.update(data, 0, pos, length); + }; + /** + * CRC32รฃฦ’ยรฃฦ’ฦ’รฃโ€šยทรฃฦ’ยฅรฅโ‚ฌยครฃโ€šโ€™รฆโ€บยดรฆโ€“ยฐ + * @param {!(Array.|Uint8Array)} data data byte array. + * @param {number} crc CRC32. + * @param {number=} pos data position. + * @param {number=} length data length. + * @return {number} CRC32. + */ + + + Zlib.CRC32.update = function (data, crc, pos, length) { + var table = Zlib.CRC32.Table; + var i = typeof pos === 'number' ? pos : pos = 0; + var il = typeof length === 'number' ? length : data.length; + crc ^= 0xffffffff; // loop unrolling for performance + + for (i = il & 7; i--; ++pos) { + crc = crc >>> 8 ^ table[(crc ^ data[pos]) & 0xff]; + } + + for (i = il >> 3; i--; pos += 8) { + crc = crc >>> 8 ^ table[(crc ^ data[pos]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 1]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 2]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 3]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 4]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 5]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 6]) & 0xff]; + crc = crc >>> 8 ^ table[(crc ^ data[pos + 7]) & 0xff]; + } + + return (crc ^ 0xffffffff) >>> 0; + }; + /** + * @param {number} num + * @param {number} crc + * @returns {number} + */ + + + Zlib.CRC32.single = function (num, crc) { + return (Zlib.CRC32.Table[(num ^ crc) & 0xff] ^ num >>> 8) >>> 0; + }; + /** + * @type {Array.} + * @const + * @private + */ + + + Zlib.CRC32.Table_ = [0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d]; + /** + * @type {!(Array.|Uint32Array)} CRC-32 Table. + * @const + */ + + Zlib.CRC32.Table = new Uint32Array(Zlib.CRC32.Table_); + /** + * @fileoverview Deflate (RFC1951) รฅยฎลธรจยฃโ€ฆ. + * Deflateรฃโ€šยขรฃฦ’ยซรฃโ€šยดรฃฦ’ยชรฃโ€šยบรฃฦ’ รฆล“ยฌรคยฝโ€œรฃยยฏ Zlib.RawDeflate รฃยยงรฅยฎลธรจยฃโ€ฆรฃยโ€ขรฃโ€šล’รฃยยฆรฃยโ€žรฃโ€šโ€น. + */ + + /** + * Zlib Deflate + * @constructor + * @param {!(Array|Uint8Array)} input รงยฌยฆรฅยยทรฅล’โ€“รฃยโ„ขรฃโ€šโ€นรฅยฏยพรจยฑยกรฃยยฎ byte array. + * @param {Object=} opt_params option parameters. + */ + + Zlib.Deflate = function (input, opt_params) { + /** @type {!(Array|Uint8Array)} */ + this.input = input; + /** @type {!(Array|Uint8Array)} */ + + this.output = new Uint8Array(Zlib.Deflate.DefaultBufferSize); + /** @type {Zlib.Deflate.CompressionType} */ + + this.compressionType = Zlib.Deflate.CompressionType.DYNAMIC; + /** @type {Zlib.RawDeflate} */ + + this.rawDeflate; + /** @type {Object} */ + + var rawDeflateOption = {}; + /** @type {string} */ + + var prop; // option parameters + + if (opt_params || !(opt_params = {})) { + if (typeof opt_params['compressionType'] === 'number') { + this.compressionType = opt_params['compressionType']; + } + } // copy options + + + for (prop in opt_params) { + rawDeflateOption[prop] = opt_params[prop]; + } // set raw-deflate output buffer + + + rawDeflateOption['outputBuffer'] = this.output; + this.rawDeflate = new Zlib.RawDeflate(this.input, rawDeflateOption); + }; + /** + * @const + * @type {number} รฃฦ’โ€กรฃฦ’โ€ขรฃโ€šยฉรฃฦ’ยซรฃฦ’ห†รฃฦ’ยรฃฦ’ฦ’รฃฦ’โ€ขรฃโ€šยกรฃโ€šยตรฃโ€šยครฃโ€šยบ. + */ + + + Zlib.Deflate.DefaultBufferSize = 0x8000; + /** + * @enum {number} + */ + + Zlib.Deflate.CompressionType = Zlib.RawDeflate.CompressionType; + /** + * รงโ€บยดรฆลฝยฅรฅล“ยงรงยธยฎรฃยยซรฆลฝโ€บรฃยโ€˜รฃโ€šโ€น. + * @param {!(Array|Uint8Array)} input target buffer. + * @param {Object=} opt_params option parameters. + * @return {!(Array|Uint8Array)} compressed data byte array. + */ + + Zlib.Deflate.compress = function (input, opt_params) { + return new Zlib.Deflate(input, opt_params).compress(); + }; + /** + * Deflate Compression. + * @return {!(Array|Uint8Array)} compressed data byte array. + */ + + + Zlib.Deflate.prototype.compress = function () { + /** @type {Zlib.CompressionMethod} */ + var cm; + /** @type {number} */ + + var cinfo; + /** @type {number} */ + + var cmf; + /** @type {number} */ + + var flg; + /** @type {number} */ + + var fcheck; + /** @type {number} */ + + var fdict; + /** @type {number} */ + + var flevel; + /** @type {number} */ + + var adler; + /** @type {!(Array|Uint8Array)} */ + + var output; + /** @type {number} */ + + var pos = 0; + output = this.output; // Compression Method and Flags + + cm = Zlib.CompressionMethod.DEFLATE; + + switch (cm) { + case Zlib.CompressionMethod.DEFLATE: + cinfo = Math.LOG2E * Math.log(Zlib.RawDeflate.WindowSize) - 8; + break; + + default: + throw new Error('invalid compression method'); + } + + cmf = cinfo << 4 | cm; + output[pos++] = cmf; // Flags + + fdict = 0; + + switch (cm) { + case Zlib.CompressionMethod.DEFLATE: + switch (this.compressionType) { + case Zlib.Deflate.CompressionType.NONE: + flevel = 0; + break; + + case Zlib.Deflate.CompressionType.FIXED: + flevel = 1; + break; + + case Zlib.Deflate.CompressionType.DYNAMIC: + flevel = 2; + break; + + default: + throw new Error('unsupported compression type'); + } + + break; + + default: + throw new Error('invalid compression method'); + } + + flg = flevel << 6 | fdict << 5; + fcheck = 31 - (cmf * 256 + flg) % 31; + flg |= fcheck; + output[pos++] = flg; // Adler-32 checksum + + adler = Zlib.Adler32(this.input); + this.rawDeflate.op = pos; + output = this.rawDeflate.compress(); + pos = output.length; + { + // subarray รฅห†โ€ รฃโ€šโ€™รฅโ€ฆฦ’รฃยยซรฃโ€šโ€šรฃยยฉรฃยโ„ข + output = new Uint8Array(output.buffer); // expand buffer + + if (output.length <= pos + 4) { + this.output = new Uint8Array(output.length + 4); + this.output.set(output); + output = this.output; + } + + output = output.subarray(0, pos + 4); + } // adler32 + + output[pos++] = adler >> 24 & 0xff; + output[pos++] = adler >> 16 & 0xff; + output[pos++] = adler >> 8 & 0xff; + output[pos++] = adler & 0xff; + return output; + }; // %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D + + + const encodings = new Map(); + encodings.set("!", "%21"); + encodings.set("#", "%23"); + encodings.set("$", "%24"); + encodings.set("%", "%25"); + encodings.set("&", "%26"); + encodings.set("'", "%27"); + encodings.set("(", "%28"); + encodings.set(")", "%29"); + encodings.set("*", "%2A"); + encodings.set("+", "%2B"); + encodings.set(",", "%2C"); + encodings.set("/", "%2F"); + encodings.set(":", "%3A"); + encodings.set(";", "%3B"); + encodings.set("=", "%3D"); + encodings.set("?", "%3F"); + encodings.set("@", "%40"); + encodings.set("[", "%5B"); + encodings.set("]", "%5D"); + encodings.set(" ", "%20"); + + if (typeof process === 'object' && typeof window === 'undefined') { + global.atob = function (str) { + return Buffer.from(str, 'base64').toString('binary'); + }; + } + /** + * Make the target element movable by clicking and dragging on the handle. This is not a general purprose function, + * it makes several options specific to igv dialogs, the primary one being that the + * target is absolutely positioned in pixel coordinates + + */ + + + let dragData; // Its assumed we are only dragging one element at a time. + + function makeDraggable(target, handle) { + handle.addEventListener('mousedown', dragStart.bind(target)); + } + + function dragStart(event) { + event.stopPropagation(); + event.preventDefault(); + offset(this); + const dragFunction = drag.bind(this); + const dragEndFunction = dragEnd.bind(this); + const computedStyle = getComputedStyle(this); + const top = parseInt(computedStyle.top.replace("px", "")); + const left = parseInt(computedStyle.left.replace("px", "")); + dragData = { + dragFunction: dragFunction, + dragEndFunction: dragEndFunction, + screenX: event.screenX, + screenY: event.screenY, + top: top, + left: left + }; + document.addEventListener('mousemove', dragFunction); + document.addEventListener('mouseup', dragEndFunction); + document.addEventListener('mouseleave', dragEndFunction); + document.addEventListener('mouseexit', dragEndFunction); + } + + function drag(event) { + if (!dragData) { + return; + } + + event.stopPropagation(); + event.preventDefault(); + const dx = event.screenX - dragData.screenX; + const dy = event.screenY - dragData.screenY; + this.style.left = `${dragData.left + dx}px`; + this.style.top = `${dragData.top + dy}px`; + } + + function dragEnd(event) { + if (!dragData) { + 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 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" + }; // 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. + */ + + + new Throttle({ + requestsPerSecond: 8 + }); + + const httpMessages = { + "401": "Access unauthorized", + "403": "Access forbidden", + "404": "Not found" + }; + + class AlertDialog { + constructor(parent) { + // 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({ + id: 'igv-ui-alert-dialog-body' + }); + this.container.appendChild(bodyContainer); // body copy + + this.body = div({ + id: '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); + this.container.focus(); + } + + } + + let alertDialog; + const Alert = { + init(root) { + if (!alertDialog) { + alertDialog = new AlertDialog(root); + } + }, + + presentAlert(alert, callback) { + if (!alertDialog) { + this.init(document.body); + } + + alertDialog.present(alert, callback); + } + + }; + + class InputDialog { + constructor(parent) { + this.parent = parent; // dialog container + + this.container = div({ + class: 'igv-ui-generic-dialog-container' + }); + parent.appendChild(this.container); // const { x, y, width, height } = this.container.getBoundingClientRect(); + // console.log(`InputDialog - x ${ x } y ${ y } width ${ width } height ${ height }`) + // dialog header + + const header = div({ + class: 'igv-ui-generic-dialog-header' + }); + this.container.appendChild(header); // dialog label + + this.label = div({ + class: 'igv-ui-generic-dialog-one-liner' + }); + this.container.appendChild(this.label); + this.label.text = 'Unlabeled'; // input container + + this.input_container = div({ + class: 'igv-ui-generic-dialog-input' + }); + this.container.appendChild(this.input_container); // + + this.input = document.createElement("input"); + this.input_container.appendChild(this.input); // ok | cancel + + const buttons = div({ + class: 'igv-ui-generic-dialog-ok-cancel' + }); + this.container.appendChild(buttons); // ok + + this.ok = div(); + buttons.appendChild(this.ok); + this.ok.textContent = 'OK'; // cancel + + this.cancel = div(); + buttons.appendChild(this.cancel); + this.cancel.textContent = 'Cancel'; + hide(this.container); + this.input.addEventListener('keyup', e => { + if (13 === e.keyCode) { + if (typeof this.callback === 'function') { + this.callback(this.input.value); + this.callback = undefined; + } + + this.input.value = undefined; + hide(this.container); + } + }); + this.ok.addEventListener('click', () => { + if (typeof this.callback === 'function') { + this.callback(this.input.value); + this.callback = undefined; + } + + this.input.value = undefined; + hide(this.container); + }); + + const cancel = () => { + this.input.value = ''; + hide(this.container); + }; + + this.cancel.addEventListener('click', cancel); + attachDialogCloseHandlerWithParent(header, cancel); + makeDraggable(this.container, header); + } + + present(options, e) { + this.label.textContent = options.label; + this.input.value = options.value; + this.callback = options.callback || options.click; + show(this.container); + const { + x, + y + } = pageCoordinates(e); + this.clampLocation(x, y); + } + + clampLocation(pageX, pageY) { + const { + width: w, + height: h + } = this.container.getBoundingClientRect(); + const { + x: px, + y: py, + width: pw, + height: ph + } = this.parent.getBoundingClientRect(); + const y = Math.min(Math.max(pageY, py), py + ph - h); + const x = Math.min(Math.max(pageX, px), px + pw - w); + this.container.style.left = `${x}px`; + this.container.style.top = `${y}px`; + } + + } + + 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); + } + + 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); + } + + 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) { + const { + x, + y + } = translateMouseCoordinates(e, popover.parentNode); // parent bbox + + const { + width + } = popover.parentNode.getBoundingClientRect(); + const { + width: w + } = popover.getBoundingClientRect(); + const xmax = x + w; + popover.style.left = `${xmax > width ? x - (xmax - width) : x}px`; + popover.style.top = `${y}px`; + } + + function createMenuElements$1(itemList, popover) { + const list = itemList.map(function (item, i) { + let elem; + + if (typeof item === 'string') { + elem = div(); + elem.innerHTML = item; + } else if (typeof item === 'Node') { + elem = item; + } else { + if (typeof item.init === 'function') { + item.init(); + } + + if ("checkbox" === item.type) { + elem = createCheckbox("Show all bases", item.value); + } else if ("color" === item.type) { + const colorPicker = new ColorPicker({ + parent: popover.parentElement, + width: 364, + //defaultColor: 'aqua', + colorHandler: color => item.click(color) + }); + elem = div(); + + if (typeof item.label === 'string') { + elem.innerHTML = item.label; + } + + const clickHandler = e => { + colorPicker.show(); + hide(popover); + e.preventDefault(); + e.stopPropagation(); + }; + + elem.addEventListener('click', clickHandler); + elem.addEventListener('touchend', clickHandler); + elem.addEventListener('mouseup', function (e) { + e.preventDefault(); + e.stopPropagation(); + }); + } else { + elem = div(); + + if (typeof item.label === 'string') { + elem.innerHTML = item.label; + } + } + + if (item.click && "color" !== item.type) { + elem.addEventListener('click', handleClick); + elem.addEventListener('touchend', handleClick); + elem.addEventListener('mouseup', function (e) { + e.preventDefault(); + e.stopPropagation(); + }); // eslint-disable-next-line no-inner-declarations + + function handleClick(e) { + item.click(); + hide(popover); + e.preventDefault(); + e.stopPropagation(); + } + } + } + + return { + object: elem, + init: item.init + }; + }); + return list; + } + + function embedCSS$1() { + var css = '.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white; }\n .igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px; }\n .igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-popover > div:last-child {\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 1500px;\n background-color: white; }\n .igv-ui-popover > div:last-child > div {\n -webkit-user-select: all;\n /* Chrome/Safari */\n -moz-user-select: all;\n /* Firefox */\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n .igv-ui-popover > div:last-child > div > span {\n font-weight: bolder; }\n .igv-ui-popover > div:last-child hr {\n width: 100%; }\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px; }\n .igv-ui-alert-dialog-container #igv-ui-alert-dialog-body {\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll; }\n .igv-ui-alert-dialog-container #igv-ui-alert-dialog-body #igv-ui-alert-dialog-body-copy {\n cursor: pointer;\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset; }\n .igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center; }\n .igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f; }\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px; }\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray; }\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px; }\n .igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f; }\n .igv-ui-colorpicker-menu-close-button i.fa:hover,\n .igv-ui-colorpicker-menu-close-button i.fa:focus,\n .igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f; }\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center; }\n .igv-ui-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd; }\n .igv-ui-generic-container div:first-child div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px; }\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400; }\n .igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee; }\n .igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F; }\n .igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444; }\n .igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f; }\n .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f; }\n .igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center; }\n .igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF; }\n .igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f; }\n\n.igv-ui-panel, .igv-ui-panel-column, .igv-ui-panel-row {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column; }\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row; }\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start; }\n\n/*# sourceMappingURL=igv-ui.css.map */\n'; + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.innerHTML = css; + document.head.insertBefore(style, document.head.childNodes[document.head.childNodes.length - 1]); + } + + if (typeof document !== 'undefined') { + if (!stylesheetExists("igv-ui.css")) { + // console.log('igv-ui. will call embedCSS() ...'); + embedCSS$1(); // console.log('... done.'); + } + + function stylesheetExists(stylesheetName) { + for (let ss of document.styleSheets) { + ss = ss.href ? ss.href.replace(/^.*[\\\/]/, '') : ''; + + if (ss === stylesheetName) { + return true; + } + } + + return false; + } + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 The Regents of the University of California + * Author: Jim Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + class DataRangeDialog { + constructor($parent, alert) { + // dialog container + this.$container = $("
", { + class: 'igv-generic-dialog-container' + }); + $parent.append(this.$container); + this.$container.offset({ + left: 0, + top: 0 + }); // dialog header + + const $header = $("
", { + class: 'igv-generic-dialog-header' + }); + this.$container.append($header); + attachDialogCloseHandlerWithParent$1($header[0], () => { + this.$minimum_input.val(undefined); + this.$maximum_input.val(undefined); + this.$container.offset({ + left: 0, + top: 0 + }); + this.$container.hide(); + }); // minimun + + this.$minimum = $("
", { + class: 'igv-generic-dialog-label-input' + }); + this.$container.append(this.$minimum); + const $mindiv = $('
'); + $mindiv.text('Minimum'); + this.$minimum.append($mindiv); + this.$minimum_input = $(""); + this.$minimum.append(this.$minimum_input); // maximum + + this.$maximum = $("
", { + class: 'igv-generic-dialog-label-input' + }); + this.$container.append(this.$maximum); + const $maxdiv = $('
'); + $maxdiv.text('Maximum'); + this.$maximum.append($maxdiv); + this.$maximum_input = $(""); + this.$maximum.append(this.$maximum_input); // ok | cancel + + const $buttons = $("
", { + class: 'igv-generic-dialog-ok-cancel' + }); + this.$container.append($buttons); // ok + + this.$ok = $("
"); + $buttons.append(this.$ok); + this.$ok.text('OK'); // cancel + + this.$cancel = $("
"); + $buttons.append(this.$cancel); + this.$cancel.text('Cancel'); + this.$cancel.on('click', () => { + this.$minimum_input.val(undefined); + this.$maximum_input.val(undefined); + this.$container.offset({ + left: 0, + top: 0 + }); + this.$container.hide(); + }); //this.$container.draggable({ handle:$header.get(0) }); + + makeDraggable$1(this.$container.get(0), $header.get(0)); + this.$container.hide(); + } + + configure(trackView) { + const dataRange = trackView.dataRange(); + let min; + let max; + + if (dataRange) { + min = dataRange.min; + max = dataRange.max; + } else { + min = 0; + max = 100; + } + + this.$minimum_input.val(min); + this.$maximum_input.val(max); + this.$minimum_input.unbind(); + this.$minimum_input.on('keyup', e => { + if (13 === e.keyCode) { + this.processResults(trackView); + } + }); + this.$maximum_input.unbind(); + this.$maximum_input.on('keyup', e => { + if (13 === e.keyCode) { + this.processResults(trackView); + } + }); + this.$ok.unbind(); + this.$ok.on('click', e => { + this.processResults(trackView); + }); + } + + processResults(trackView) { + const min = parseFloat(this.$minimum_input.val()); + const max = parseFloat(this.$maximum_input.val()); + + if (isNaN(min) || isNaN(max)) { + Alert.presentAlert(new Error('Must input numeric values'), undefined); + } else { + trackView.setDataRange(min, max); + } + + this.$minimum_input.val(undefined); + this.$maximum_input.val(undefined); + this.$container.offset({ + left: 0, + top: 0 + }); + this.$container.hide(); + } + + present($parent) { + const offset_top = $parent.offset().top; + const scroll_top = $('body').scrollTop(); + this.$container.offset({ + left: $parent.width() - this.$container.width(), + top: offset_top + scroll_top + }); + this.$container.show(); + } + + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of ctx software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and ctx permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + const IGVGraphics = { + configureHighDPICanvas: function (ctx, w, h) { + const scaleFactor = window.devicePixelRatio; // const scaleFactor = 1 + + ctx.canvas.style.width = `${w}px`; + ctx.canvas.width = Math.floor(scaleFactor * w); + ctx.canvas.style.height = `${h}px`; + ctx.canvas.height = Math.floor(scaleFactor * h); + ctx.scale(scaleFactor, scaleFactor); + }, + setProperties: function (ctx, properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key)) { + var value = properties[key]; + ctx[key] = value; + } + } + }, + strokeLine: function (ctx, x1, y1, x2, y2, properties) { + x1 = Math.floor(x1) + 0.5; + y1 = Math.floor(y1) + 0.5; + x2 = Math.floor(x2) + 0.5; + y2 = Math.floor(y2) + 0.5; + + if (properties) { + ctx.save(); + IGVGraphics.setProperties(ctx, properties); + } + + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + if (properties) ctx.restore(); + }, + fillRect: function (ctx, x, y, w, h, properties) { + x = Math.round(x); + y = Math.round(y); + + if (properties) { + ctx.save(); + IGVGraphics.setProperties(ctx, properties); + } + + ctx.fillRect(x, y, w, h); + if (properties) ctx.restore(); + }, + fillPolygon: function (ctx, x, y, properties) { + if (properties) { + ctx.save(); + IGVGraphics.setProperties(ctx, properties); + } + + doPath(ctx, x, y); + ctx.fill(); + if (properties) ctx.restore(); + }, + strokePolygon: function (ctx, x, y, properties) { + if (properties) { + ctx.save(); + IGVGraphics.setProperties(ctx, properties); + } + + doPath(ctx, x, y); + ctx.stroke(); + if (properties) ctx.restore(); + }, + fillText: function (ctx, text, x, y, properties, transforms) { + if (properties || transforms) { + ctx.save(); + } + + if (properties) { + IGVGraphics.setProperties(ctx, properties); + } + + if (transforms) { + // Slow path with context saving and extra translate + ctx.translate(x, y); + + for (var transform in transforms) { + var value = transforms[transform]; // TODO: Add error checking for robustness + + if (transform === 'translate') { + ctx.translate(value['x'], value['y']); + } + + if (transform === 'rotate') { + ctx.rotate(value['angle'] * Math.PI / 180); + } + } + + ctx.fillText(text, 0, 0); + } else { + ctx.fillText(text, x, y); + } + + if (properties || transforms) ctx.restore(); + }, + strokeText: function (ctx, text, x, y, properties, transforms) { + if (properties || transforms) { + ctx.save(); + } + + if (properties) { + IGVGraphics.setProperties(ctx, properties); + } + + if (transforms) { + ctx.translate(x, y); + + for (var transform in transforms) { + var value = transforms[transform]; // TODO: Add error checking for robustness + + if (transform === 'translate') { + ctx.translate(value['x'], value['y']); + } + + if (transform === 'rotate') { + ctx.rotate(value['angle'] * Math.PI / 180); + } + } + + ctx.strokeText(text, 0, 0); + } else { + ctx.strokeText(text, x, y); + } + + if (properties || transforms) ctx.restore(); + }, + strokeCircle: function (ctx, x, y, radius, properties) { + if (properties) { + ctx.save(); + IGVGraphics.setProperties(ctx, properties); + } + + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI); + ctx.stroke(); + if (properties) ctx.restore(); + }, + fillCircle: function (ctx, x, y, radius, properties) { + if (properties) { + ctx.save(); + IGVGraphics.setProperties(ctx, properties); + } + + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI); + ctx.fill(); + if (properties) ctx.restore(); + }, + drawArrowhead: function (ctx, x, y, size, lineWidth) { + ctx.save(); + + if (!size) { + size = 5; + } + + if (lineWidth) { + ctx.lineWidth = lineWidth; + } + + ctx.beginPath(); + ctx.moveTo(x, y - size / 2); + ctx.lineTo(x, y + size / 2); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y - size / 2); + ctx.closePath(); + ctx.fill(); + ctx.restore(); + }, + dashedLine: function (ctx, x1, y1, x2, y2, dashLen, 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(); + } + } + }; + + 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(); + } + + // eslint-disable-next-line es/no-typed-arrays -- safe + var arrayBufferNative = typeof ArrayBuffer !== 'undefined' && typeof DataView !== 'undefined'; + + var fails = function (exec) { + try { + return !!exec(); + } catch (error) { + return true; + } + }; + + var descriptors = !fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty({}, 1, { + get: function () { + return 7; + } + })[1] != 7; + }); + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function createCommonjsModule(fn) { + var module = { exports: {} }; + return fn(module, module.exports), module.exports; + } + + var check = function (it) { + return it && it.Math == Math && it; + }; // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 + + + var global$1 = // eslint-disable-next-line es/no-global-this -- safe + check(typeof globalThis == 'object' && globalThis) || check(typeof window == 'object' && window) || // eslint-disable-next-line no-restricted-globals -- safe + check(typeof self == 'object' && self) || check(typeof commonjsGlobal == 'object' && commonjsGlobal) || // eslint-disable-next-line no-new-func -- fallback + function () { + return this; + }() || Function('return this')(); + + var isObject = function (it) { + return typeof it === 'object' ? it !== null : typeof it === 'function'; + }; + + // `RequireObjectCoercible` abstract operation + // https://tc39.es/ecma262/#sec-requireobjectcoercible + var requireObjectCoercible = function (it) { + if (it == undefined) throw TypeError("Can't call method on " + it); + return it; + }; + + // https://tc39.es/ecma262/#sec-toobject + + var toObject = function (argument) { + return Object(requireObjectCoercible(argument)); + }; + + var hasOwnProperty = {}.hasOwnProperty; + + var has$1 = Object.hasOwn || function hasOwn(it, key) { + return hasOwnProperty.call(toObject(it), key); + }; + + var document$1 = global$1.document; // typeof document.createElement is 'object' in old IE + + var EXISTS = isObject(document$1) && isObject(document$1.createElement); + + var documentCreateElement = function (it) { + return EXISTS ? document$1.createElement(it) : {}; + }; + + var ie8DomDefine = !descriptors && !fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- requied for testing + return Object.defineProperty(documentCreateElement('div'), 'a', { + get: function () { + return 7; + } + }).a != 7; + }); + + var anObject = function (it) { + if (!isObject(it)) { + throw TypeError(String(it) + ' is not an object'); + } + + return it; + }; + + // https://tc39.es/ecma262/#sec-toprimitive + // instead of the ES6 spec version, we didn't implement @@toPrimitive case + // and the second argument - flag - preferred type is a string + + var toPrimitive = function (input, PREFERRED_STRING) { + if (!isObject(input)) return input; + var fn, val; + if (PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject(val = fn.call(input))) return val; + if (typeof (fn = input.valueOf) == 'function' && !isObject(val = fn.call(input))) return val; + if (!PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject(val = fn.call(input))) return val; + throw TypeError("Can't convert object to primitive value"); + }; + + var $defineProperty = Object.defineProperty; // `Object.defineProperty` method + // https://tc39.es/ecma262/#sec-object.defineproperty + + var f$4 = descriptors ? $defineProperty : function defineProperty(O, P, Attributes) { + anObject(O); + P = toPrimitive(P, true); + anObject(Attributes); + if (ie8DomDefine) try { + return $defineProperty(O, P, Attributes); + } catch (error) { + /* empty */ + } + if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; + }; + var objectDefineProperty = { + f: f$4 + }; + + var createPropertyDescriptor = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; + }; + + var createNonEnumerableProperty = descriptors ? function (object, key, value) { + return objectDefineProperty.f(object, key, createPropertyDescriptor(1, value)); + } : function (object, key, value) { + object[key] = value; + return object; + }; + + var setGlobal = function (key, value) { + try { + createNonEnumerableProperty(global$1, key, value); + } catch (error) { + global$1[key] = value; + } + + return value; + }; + + var SHARED = '__core-js_shared__'; + var store$1 = global$1[SHARED] || setGlobal(SHARED, {}); + var sharedStore = store$1; + + var shared = createCommonjsModule(function (module) { + (module.exports = function (key, value) { + return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {}); + })('versions', []).push({ + version: '3.15.2', + mode: 'global', + copyright: 'ร‚ยฉ 2021 Denis Pushkarev (zloirock.ru)' + }); + }); + + var id = 0; + var postfix = Math.random(); + + var uid = function (key) { + return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id + postfix).toString(36); + }; + + var path = global$1; + + var aFunction$1 = function (variable) { + return typeof variable == 'function' ? variable : undefined; + }; + + var getBuiltIn = function (namespace, method) { + return arguments.length < 2 ? aFunction$1(path[namespace]) || aFunction$1(global$1[namespace]) : path[namespace] && path[namespace][method] || global$1[namespace] && global$1[namespace][method]; + }; + + var engineUserAgent = getBuiltIn('navigator', 'userAgent') || ''; + + var process$2 = global$1.process; + var versions = process$2 && process$2.versions; + var v8 = versions && versions.v8; + var match, version$1; + + if (v8) { + match = v8.split('.'); + version$1 = match[0] < 4 ? 1 : match[0] + match[1]; + } else if (engineUserAgent) { + match = engineUserAgent.match(/Edge\/(\d+)/); + + if (!match || match[1] >= 74) { + match = engineUserAgent.match(/Chrome\/(\d+)/); + if (match) version$1 = match[1]; + } + } + + var engineV8Version = version$1 && +version$1; + + /* eslint-disable es/no-symbol -- required for testing */ + + var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () { + var symbol = Symbol(); // Chrome 38 Symbol has incorrect toString conversion + // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances + + return !String(symbol) || !(Object(symbol) instanceof Symbol) || // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances + !Symbol.sham && engineV8Version && engineV8Version < 41; + }); + + /* eslint-disable es/no-symbol -- required for testing */ + var useSymbolAsUid = nativeSymbol && !Symbol.sham && typeof Symbol.iterator == 'symbol'; + + var WellKnownSymbolsStore = shared('wks'); + var Symbol$1 = global$1.Symbol; + var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid; + + var wellKnownSymbol = function (name) { + if (!has$1(WellKnownSymbolsStore, name) || !(nativeSymbol || typeof WellKnownSymbolsStore[name] == 'string')) { + if (nativeSymbol && has$1(Symbol$1, name)) { + WellKnownSymbolsStore[name] = Symbol$1[name]; + } else { + WellKnownSymbolsStore[name] = createWellKnownSymbol('Symbol.' + name); + } + } + + return WellKnownSymbolsStore[name]; + }; + + var TO_STRING_TAG$2 = wellKnownSymbol('toStringTag'); + var test = {}; + test[TO_STRING_TAG$2] = 'z'; + var toStringTagSupport = String(test) === '[object z]'; + + var toString = {}.toString; + + var classofRaw = function (it) { + return toString.call(it).slice(8, -1); + }; + + var TO_STRING_TAG$1 = wellKnownSymbol('toStringTag'); // ES3 wrong here + + var CORRECT_ARGUMENTS = classofRaw(function () { + return arguments; + }()) == 'Arguments'; // fallback for IE11 Script Access Denied error + + var tryGet = function (it, key) { + try { + return it[key]; + } catch (error) { + /* empty */ + } + }; // getting tag from ES6+ `Object.prototype.toString` + + + var classof = toStringTagSupport ? classofRaw : function (it) { + var O, tag, result; + return it === undefined ? 'Undefined' : it === null ? 'Null' // @@toStringTag case + : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$1)) == 'string' ? tag // builtinTag case + : CORRECT_ARGUMENTS ? classofRaw(O) // ES3 arguments fallback + : (result = classofRaw(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : result; + }; + + var functionToString = Function.toString; // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper + + if (typeof sharedStore.inspectSource != 'function') { + sharedStore.inspectSource = function (it) { + return functionToString.call(it); + }; + } + + var inspectSource = sharedStore.inspectSource; + + var WeakMap$1 = global$1.WeakMap; + var nativeWeakMap = typeof WeakMap$1 === 'function' && /native code/.test(inspectSource(WeakMap$1)); + + var keys = shared('keys'); + + var sharedKey = function (key) { + return keys[key] || (keys[key] = uid(key)); + }; + + var hiddenKeys$1 = {}; + + var OBJECT_ALREADY_INITIALIZED = 'Object already initialized'; + var WeakMap = global$1.WeakMap; + var set$1, get, has; + + var enforce = function (it) { + return has(it) ? get(it) : set$1(it, {}); + }; + + var getterFor = function (TYPE) { + return function (it) { + var state; + + if (!isObject(it) || (state = get(it)).type !== TYPE) { + throw TypeError('Incompatible receiver, ' + TYPE + ' required'); + } + + return state; + }; + }; + + if (nativeWeakMap || sharedStore.state) { + var store = sharedStore.state || (sharedStore.state = new WeakMap()); + var wmget = store.get; + var wmhas = store.has; + var wmset = store.set; + + set$1 = function (it, metadata) { + if (wmhas.call(store, it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + wmset.call(store, it, metadata); + return metadata; + }; + + get = function (it) { + return wmget.call(store, it) || {}; + }; + + has = function (it) { + return wmhas.call(store, it); + }; + } else { + var STATE = sharedKey('state'); + hiddenKeys$1[STATE] = true; + + set$1 = function (it, metadata) { + if (has$1(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED); + metadata.facade = it; + createNonEnumerableProperty(it, STATE, metadata); + return metadata; + }; + + get = function (it) { + return has$1(it, STATE) ? it[STATE] : {}; + }; + + has = function (it) { + return has$1(it, STATE); + }; + } + + var internalState = { + set: set$1, + get: get, + has: has, + enforce: enforce, + getterFor: getterFor + }; + + var redefine = createCommonjsModule(function (module) { + var getInternalState = internalState.get; + var enforceInternalState = internalState.enforce; + var TEMPLATE = String(String).split('String'); + (module.exports = function (O, key, value, options) { + var unsafe = options ? !!options.unsafe : false; + var simple = options ? !!options.enumerable : false; + var noTargetGet = options ? !!options.noTargetGet : false; + var state; + + if (typeof value == 'function') { + if (typeof key == 'string' && !has$1(value, 'name')) { + createNonEnumerableProperty(value, 'name', key); + } + + state = enforceInternalState(value); + + if (!state.source) { + state.source = TEMPLATE.join(typeof key == 'string' ? key : ''); + } + } + + if (O === global$1) { + if (simple) O[key] = value;else setGlobal(key, value); + return; + } else if (!unsafe) { + delete O[key]; + } else if (!noTargetGet && O[key]) { + simple = true; + } + + if (simple) O[key] = value;else createNonEnumerableProperty(O, key, value); // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative + })(Function.prototype, 'toString', function toString() { + return typeof this == 'function' && getInternalState(this).source || inspectSource(this); + }); + }); + + var correctPrototypeGetter = !fails(function () { + function F() { + /* empty */ + } + + F.prototype.constructor = null; // eslint-disable-next-line es/no-object-getprototypeof -- required for testing + + return Object.getPrototypeOf(new F()) !== F.prototype; + }); + + var IE_PROTO = sharedKey('IE_PROTO'); + var ObjectPrototype$1 = Object.prototype; // `Object.getPrototypeOf` method + // https://tc39.es/ecma262/#sec-object.getprototypeof + // eslint-disable-next-line es/no-object-getprototypeof -- safe + + var objectGetPrototypeOf = correctPrototypeGetter ? Object.getPrototypeOf : function (O) { + O = toObject(O); + if (has$1(O, IE_PROTO)) return O[IE_PROTO]; + + if (typeof O.constructor == 'function' && O instanceof O.constructor) { + return O.constructor.prototype; + } + + return O instanceof Object ? ObjectPrototype$1 : null; + }; + + var aPossiblePrototype = function (it) { + if (!isObject(it) && it !== null) { + throw TypeError("Can't set " + String(it) + ' as a prototype'); + } + + return it; + }; + + /* eslint-disable no-proto -- safe */ + // https://tc39.es/ecma262/#sec-object.setprototypeof + // Works with __proto__ only. Old v8 can't work with null proto objects. + // eslint-disable-next-line es/no-object-setprototypeof -- safe + + var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () { + var CORRECT_SETTER = false; + var test = {}; + var setter; + + try { + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + setter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set; + setter.call(test, []); + CORRECT_SETTER = test instanceof Array; + } catch (error) { + /* empty */ + } + + return function setPrototypeOf(O, proto) { + anObject(O); + aPossiblePrototype(proto); + if (CORRECT_SETTER) setter.call(O, proto);else O.__proto__ = proto; + return O; + }; + }() : undefined); + + var defineProperty = objectDefineProperty.f; + var Int8Array = global$1.Int8Array; + var Int8ArrayPrototype = Int8Array && Int8Array.prototype; + var Uint8ClampedArray = global$1.Uint8ClampedArray; + var Uint8ClampedArrayPrototype = Uint8ClampedArray && Uint8ClampedArray.prototype; + var TypedArray = Int8Array && objectGetPrototypeOf(Int8Array); + var TypedArrayPrototype = Int8ArrayPrototype && objectGetPrototypeOf(Int8ArrayPrototype); + var ObjectPrototype = Object.prototype; + var isPrototypeOf = ObjectPrototype.isPrototypeOf; + var TO_STRING_TAG = wellKnownSymbol('toStringTag'); + var TYPED_ARRAY_TAG = uid('TYPED_ARRAY_TAG'); // Fixing native typed arrays in Opera Presto crashes the browser, see #595 + + var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferNative && !!objectSetPrototypeOf && classof(global$1.opera) !== 'Opera'; + var TYPED_ARRAY_TAG_REQIRED = false; + var NAME; + var TypedArrayConstructorsList = { + Int8Array: 1, + Uint8Array: 1, + Uint8ClampedArray: 1, + Int16Array: 2, + Uint16Array: 2, + Int32Array: 4, + Uint32Array: 4, + Float32Array: 4, + Float64Array: 8 + }; + var BigIntArrayConstructorsList = { + BigInt64Array: 8, + BigUint64Array: 8 + }; + + var isView = function isView(it) { + if (!isObject(it)) return false; + var klass = classof(it); + return klass === 'DataView' || has$1(TypedArrayConstructorsList, klass) || has$1(BigIntArrayConstructorsList, klass); + }; + + var isTypedArray = function (it) { + if (!isObject(it)) return false; + var klass = classof(it); + return has$1(TypedArrayConstructorsList, klass) || has$1(BigIntArrayConstructorsList, klass); + }; + + var aTypedArray$1 = function (it) { + if (isTypedArray(it)) return it; + throw TypeError('Target is not a typed array'); + }; + + var aTypedArrayConstructor = function (C) { + if (objectSetPrototypeOf) { + if (isPrototypeOf.call(TypedArray, C)) return C; + } else for (var ARRAY in TypedArrayConstructorsList) if (has$1(TypedArrayConstructorsList, NAME)) { + var TypedArrayConstructor = global$1[ARRAY]; + + if (TypedArrayConstructor && (C === TypedArrayConstructor || isPrototypeOf.call(TypedArrayConstructor, C))) { + return C; + } + } + + throw TypeError('Target is not a typed array constructor'); + }; + + var exportTypedArrayMethod$1 = function (KEY, property, forced) { + if (!descriptors) return; + if (forced) for (var ARRAY in TypedArrayConstructorsList) { + var TypedArrayConstructor = global$1[ARRAY]; + if (TypedArrayConstructor && has$1(TypedArrayConstructor.prototype, KEY)) try { + delete TypedArrayConstructor.prototype[KEY]; + } catch (error) { + /* empty */ + } + } + + if (!TypedArrayPrototype[KEY] || forced) { + redefine(TypedArrayPrototype, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS && Int8ArrayPrototype[KEY] || property); + } + }; + + var exportTypedArrayStaticMethod = function (KEY, property, forced) { + var ARRAY, TypedArrayConstructor; + if (!descriptors) return; + + if (objectSetPrototypeOf) { + if (forced) for (ARRAY in TypedArrayConstructorsList) { + TypedArrayConstructor = global$1[ARRAY]; + if (TypedArrayConstructor && has$1(TypedArrayConstructor, KEY)) try { + delete TypedArrayConstructor[KEY]; + } catch (error) { + /* empty */ + } + } + + if (!TypedArray[KEY] || forced) { + // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable + try { + return redefine(TypedArray, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS && TypedArray[KEY] || property); + } catch (error) { + /* empty */ + } + } else return; + } + + for (ARRAY in TypedArrayConstructorsList) { + TypedArrayConstructor = global$1[ARRAY]; + + if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) { + redefine(TypedArrayConstructor, KEY, property); + } + } + }; + + for (NAME in TypedArrayConstructorsList) { + if (!global$1[NAME]) NATIVE_ARRAY_BUFFER_VIEWS = false; + } // WebKit bug - typed arrays constructors prototype is Object.prototype + + + if (!NATIVE_ARRAY_BUFFER_VIEWS || typeof TypedArray != 'function' || TypedArray === Function.prototype) { + // eslint-disable-next-line no-shadow -- safe + TypedArray = function TypedArray() { + throw TypeError('Incorrect invocation'); + }; + + if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) { + if (global$1[NAME]) objectSetPrototypeOf(global$1[NAME], TypedArray); + } + } + + if (!NATIVE_ARRAY_BUFFER_VIEWS || !TypedArrayPrototype || TypedArrayPrototype === ObjectPrototype) { + TypedArrayPrototype = TypedArray.prototype; + if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) { + if (global$1[NAME]) objectSetPrototypeOf(global$1[NAME].prototype, TypedArrayPrototype); + } + } // WebKit bug - one more object in Uint8ClampedArray prototype chain + + + if (NATIVE_ARRAY_BUFFER_VIEWS && objectGetPrototypeOf(Uint8ClampedArrayPrototype) !== TypedArrayPrototype) { + objectSetPrototypeOf(Uint8ClampedArrayPrototype, TypedArrayPrototype); + } + + if (descriptors && !has$1(TypedArrayPrototype, TO_STRING_TAG)) { + TYPED_ARRAY_TAG_REQIRED = true; + defineProperty(TypedArrayPrototype, TO_STRING_TAG, { + get: function () { + return isObject(this) ? this[TYPED_ARRAY_TAG] : undefined; + } + }); + + for (NAME in TypedArrayConstructorsList) if (global$1[NAME]) { + createNonEnumerableProperty(global$1[NAME], TYPED_ARRAY_TAG, NAME); + } + } + + var arrayBufferViewCore = { + NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS, + TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQIRED && TYPED_ARRAY_TAG, + aTypedArray: aTypedArray$1, + aTypedArrayConstructor: aTypedArrayConstructor, + exportTypedArrayMethod: exportTypedArrayMethod$1, + exportTypedArrayStaticMethod: exportTypedArrayStaticMethod, + isView: isView, + isTypedArray: isTypedArray, + TypedArray: TypedArray, + TypedArrayPrototype: TypedArrayPrototype + }; + + var aFunction = function (it) { + if (typeof it != 'function') { + throw TypeError(String(it) + ' is not a function'); + } + + return it; + }; + + var ceil = Math.ceil; + var floor$1 = Math.floor; // `ToInteger` abstract operation + // https://tc39.es/ecma262/#sec-tointeger + + var toInteger = function (argument) { + return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor$1 : ceil)(argument); + }; + + var min$1 = Math.min; // `ToLength` abstract operation + // https://tc39.es/ecma262/#sec-tolength + + var toLength = function (argument) { + return argument > 0 ? min$1(toInteger(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991 + }; + + // TODO: use something more complex like timsort? + var floor = Math.floor; + + var mergeSort = function (array, comparefn) { + var length = array.length; + var middle = floor(length / 2); + return length < 8 ? insertionSort(array, comparefn) : merge(mergeSort(array.slice(0, middle), comparefn), mergeSort(array.slice(middle), comparefn), comparefn); + }; + + var insertionSort = function (array, comparefn) { + var length = array.length; + var i = 1; + var element, j; + + while (i < length) { + j = i; + element = array[i]; + + while (j && comparefn(array[j - 1], element) > 0) { + array[j] = array[--j]; + } + + if (j !== i++) array[j] = element; + } + + return array; + }; + + var merge = function (left, right, comparefn) { + var llength = left.length; + var rlength = right.length; + var lindex = 0; + var rindex = 0; + var result = []; + + while (lindex < llength || rindex < rlength) { + if (lindex < llength && rindex < rlength) { + result.push(comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++]); + } else { + result.push(lindex < llength ? left[lindex++] : right[rindex++]); + } + } + + return result; + }; + + var arraySort = mergeSort; + + var firefox = engineUserAgent.match(/firefox\/(\d+)/i); + var engineFfVersion = !!firefox && +firefox[1]; + + var engineIsIeOrEdge = /MSIE|Trident/.test(engineUserAgent); + + var webkit = engineUserAgent.match(/AppleWebKit\/(\d+)\./); + var engineWebkitVersion = !!webkit && +webkit[1]; + + var aTypedArray = arrayBufferViewCore.aTypedArray; + var exportTypedArrayMethod = arrayBufferViewCore.exportTypedArrayMethod; + var Uint16Array$1 = global$1.Uint16Array; + var nativeSort = Uint16Array$1 && Uint16Array$1.prototype.sort; // WebKit + + var ACCEPT_INCORRECT_ARGUMENTS = !!nativeSort && !fails(function () { + var array = new Uint16Array$1(2); + array.sort(null); + array.sort({}); + }); + var STABLE_SORT = !!nativeSort && !fails(function () { + // feature detection can be too slow, so check engines versions + if (engineV8Version) return engineV8Version < 74; + if (engineFfVersion) return engineFfVersion < 67; + if (engineIsIeOrEdge) return true; + if (engineWebkitVersion) return engineWebkitVersion < 602; + var array = new Uint16Array$1(516); + var expected = Array(516); + var index, mod; + + for (index = 0; index < 516; index++) { + mod = index % 4; + array[index] = 515 - index; + expected[index] = index - 2 * mod + 3; + } + + array.sort(function (a, b) { + return (a / 4 | 0) - (b / 4 | 0); + }); + + for (index = 0; index < 516; index++) { + if (array[index] !== expected[index]) return true; + } + }); + + var getSortCompare = function (comparefn) { + return function (x, y) { + if (comparefn !== undefined) return +comparefn(x, y) || 0; // eslint-disable-next-line no-self-compare -- NaN check + + if (y !== y) return -1; // eslint-disable-next-line no-self-compare -- NaN check + + if (x !== x) return 1; + if (x === 0 && y === 0) return 1 / x > 0 && 1 / y < 0 ? 1 : -1; + return x > y; + }; + }; // `%TypedArray%.prototype.sort` method + // https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort + + + exportTypedArrayMethod('sort', function sort(comparefn) { + var array = this; + if (comparefn !== undefined) aFunction(comparefn); + if (STABLE_SORT) return nativeSort.call(array, comparefn); + aTypedArray(array); + var arrayLength = toLength(array.length); + var items = Array(arrayLength); + var index; + + for (index = 0; index < arrayLength; index++) { + items[index] = array[index]; + } + + items = arraySort(array, getSortCompare(comparefn)); + + for (index = 0; index < arrayLength; index++) { + array[index] = items[index]; + } + + return array; + }, !STABLE_SORT || ACCEPT_INCORRECT_ARGUMENTS); + + 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 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; + }; + + const Chromosome = function (name, order, bpStart, bpLength, rangeLocus) { + this.name = name; + this.order = order; + this.bpStart = bpStart; + this.bpLength = bpLength; + this.rangeLocus = rangeLocus; + }; + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + /** + * Test if the given value is a string or number. Not using typeof as it fails on boxed primitives. + * + * @param value + * @returns boolean + */ + + + function isSimpleType(value) { + const simpleTypes = new Set(["boolean", "number", "string", "symbol"]); + const valueType = typeof value; + return value !== undefined && (simpleTypes.has(valueType) || value.substring || value.toFixed); + } + + function buildOptions(config, options) { + var defaultOptions = { + oauthToken: config.oauthToken, + headers: config.headers, + withCredentials: config.withCredentials, + filename: config.filename + }; + return Object.assign(defaultOptions, options); + } + /** + * isMobile test from http://detectmobilebrowsers.com + * TODO -- improve UI design so this isn't neccessary + * @returns {boolean} + */ + // igv.isMobile = function () { + // + // const a = (navigator.userAgent || navigator.vendor || window.opera); + // return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || + // /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) + // + // } + + + const doAutoscale = function (features) { + var min, max; + + if (features.length > 0) { + min = Number.MAX_VALUE; + max = -Number.MAX_VALUE; + features.forEach(function (f) { + if (!Number.isNaN(f.value)) { + min = Math.min(min, f.value); + max = Math.max(max, f.value); + } + }); // Insure we have a zero baseline + + if (max > 0) min = Math.min(0, min); + if (max < 0) max = 0; + } else { + // No features -- default + min = 0; + max = 100; + } + + return { + min: min, + max: max + }; + }; + + const validateLocusExtent = function (chromosomeLengthBP, extent, minimumBP) { + let ss = extent.start; + let ee = extent.end; + + if (undefined === ee) { + ss -= minimumBP / 2; + ee = ss + minimumBP; + + if (ee > chromosomeLengthBP) { + ee = chromosomeLengthBP; + ss = ee - minimumBP; + } else if (ss < 0) { + ss = 0; + ee = minimumBP; + } + } else if (ee - ss < minimumBP) { + const center = (ee + ss) / 2; + + if (center - minimumBP / 2 < 0) { + ss = 0; + ee = ss + minimumBP; + } else if (center + minimumBP / 2 > chromosomeLengthBP) { + ee = chromosomeLengthBP; + ss = ee - minimumBP; + } else { + ss = center - minimumBP / 2; + ee = ss + minimumBP; + } + } + + extent.start = Math.ceil(ss); + extent.end = Math.floor(ee); + }; + /*! + * is-number + * + * 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; + }; + + function inferTrackType(config) { + translateDeprecatedTypes(config); + + if (config.type) { + return config.type; + } + + if (config.format) { + const format = config.format.toLowerCase(); + + switch (format) { + case "bw": + case "bigwig": + case "wig": + case "bedgraph": + case "tdf": + return "wig"; + + case "vcf": + return "variant"; + + case "seg": + return "seg"; + + case "mut": + case "maf": + return "mut"; + + case "bam": + case "cram": + return "alignment"; + + case "bedpe": + case "bedpe-loop": + return "interaction"; + + case "bp": + return "arc"; + + case "gwas": + return "gwas"; + + case "bed": + case "bigbed": + case "bb": + return "bedtype"; + + default: + return "annotation"; + } + } + } + + function translateDeprecatedTypes(config) { + if (config.featureType) { + // Translate deprecated "feature" type + config.type = config.type || config.featureType; + config.featureType = undefined; + } + + if ("junctions" === config.type) { + config.type = "spliceJunctions"; + } 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"; + } + } + + async function getFilename(url) { + if (isString$6(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); + } + } + + const splitLines$2 = splitLines$3; + const reservedProperties = new Set(['fastaURL', 'indexURL', 'cytobandURL', 'indexed']); + + class FastaSequence { + constructor(reference) { + if (typeof reference.fastaURL === 'string' && reference.fastaURL.startsWith('data:')) { + this.file = decodeDataUri(reference.fastaURL); + this.indexed = false; // dataURI is by definition not indexed + + this.isDataURI = true; + } else { + this.file = reference.fastaURL; + this.indexed = reference.indexed !== false; // Indexed unless it explicitly is not + + if (this.indexed) { + this.indexFile = reference.indexURL || reference.indexFile || this.file + ".fai"; + } + } + + 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() { + if (this.indexed) { + return this.getIndex(); + } else { + return this.loadAll(); + } + } + + async getSequence(chr, start, end) { + if (this.indexed) { + return this.getSequenceIndexed(chr, start, end); + } else { + return this.getSequenceNonIndexed(chr, start, end); + } + } + + async getSequenceIndexed(chr, start, end) { + if (!(this.interval && this.interval.contains(chr, start, end))) { + // 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 getSequenceNonIndexed(chr, start, end) { + if (this.offsets[chr]) { + start -= this.offsets[chr]; + end -= this.offsets[chr]; + } + + let prefix = ""; + + if (start < 0) { + for (let i = start; i < Math.min(end, 0); i++) { + prefix += "*"; + } + } + + if (end <= 0) { + return Promise.resolve(prefix); + } + + const seq = this.sequences[chr]; + const seqEnd = Math.min(end, seq.length); + return prefix + seq.substring(start, seqEnd); + } + + async getIndex() { + if (this.index) { + return this.index; + } else { + const data = await igvxhr.load(this.indexFile, buildOptions(this.config)); + const lines = splitLines$2(data); + const len = lines.length; + let lineNo = 0; + let order = 0; + this.index = {}; + + while (lineNo < len) { + const tokens = lines[lineNo++].split("\t"); + const nTokens = tokens.length; + + if (nTokens === 5) { + // Parse the index line. + const chr = tokens[0]; + const size = parseInt(tokens[1]); + const position = parseInt(tokens[2]); + const basesPerLine = parseInt(tokens[3]); + const bytesPerLine = parseInt(tokens[4]); + const indexEntry = { + size: size, + position: position, + basesPerLine: basesPerLine, + bytesPerLine: bytesPerLine + }; + this.chromosomeNames.push(chr); + this.index[chr] = indexEntry; + this.chromosomes[chr] = new Chromosome(chr, order++, 0, size); + } + } + + return this.index; + } + } + + async loadAll() { + let data; + + if (this.isDataURI) { + data = this.file; // <= Strange convention, revisit. + } else { + data = await igvxhr.load(this.file, buildOptions(this.config)); + } + + const lines = splitLines$2(data); + const len = lines.length; + let lineNo = 0; + let currentSeq; + let currentRangeLocus; + let currentOffset = 0; + let order = 0; + let nextLine; + let currentChr; + + while (lineNo < len) { + nextLine = lines[lineNo++].trim(); + + if (nextLine.startsWith("#") || nextLine.length === 0) ; else if (nextLine.startsWith(">")) { + // Start the next sequence + if (currentSeq) { + this.chromosomeNames.push(currentChr); + this.sequences[currentChr] = currentSeq; + this.chromosomes[currentChr] = new Chromosome(currentChr, order++, currentOffset, currentOffset + currentSeq.length, currentRangeLocus); + } + + const parts = nextLine.substr(1).split(/\s+/); // Check for samtools style locus string. This is not perfect, and could fail on weird sequence names + + const nameParts = parts[0].split(':'); + currentChr = nameParts[0]; + currentSeq = ""; + currentOffset = 0; + currentRangeLocus = undefined; + + if (nameParts.length > 1 && nameParts[1].indexOf('-') > 0) { + const locusParts = nameParts[1].split('-'); + + if (locusParts.length === 2 && /^[0-9]+$/.test(locusParts[0]) && /^[0-9]+$/.test(locusParts[1])) ; + + const from = Number.parseInt(locusParts[0]); + const to = Number.parseInt(locusParts[1]); + + if (to > from) { + currentOffset = from - 1; + this.offsets[currentChr] = currentOffset; + currentRangeLocus = nameParts[1]; + } + } + } else { + currentSeq += nextLine; + } + } // add last seq + + + if (currentSeq) { + this.chromosomeNames.push(currentChr); + this.sequences[currentChr] = currentSeq; + this.chromosomes[currentChr] = new Chromosome(currentChr, order++, currentOffset, currentOffset + currentSeq.length, currentRangeLocus); + } + } + + async readSequence(chr, qstart, qend) { + // let offset; + // let start; + // let end; + // let basesPerLine; + // let nEndBytes; + await this.getIndex(); + const idxEntry = this.index[chr]; + + if (!idxEntry) { + // Tag interval with null so we don't try again + this.interval = new GenomicInterval(chr, qstart, qend, null); + return null; + } else { + 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; + let allBytes; + + if (byteCount <= 0) { + console.error("No sequence for " + chr + ":" + qstart + "-" + qend); + } else { + allBytes = await igvxhr.load(this.file, buildOptions(this.config, { + range: { + start: startByte, + size: byteCount + } + })); + } + + if (!allBytes) { + return null; + } else { + 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; + } + } + } + + } + + 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 (let i = 0; i < dataString.length; i++) { + bytes[i] = dataString.charCodeAt(i); + } + + const inflate = new Zlib$1.Gunzip(bytes); + const plain = inflate.decompress(); + let s = ""; + const len = plain.length; + + for (let i = 0; i < len; i++) s += String.fromCharCode(plain[i]); + + return s; + } + + const _version = "2.9.3"; + + function version() { + return _version; + } + + 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$3; + const GenomeUtils = { + loadGenome: async function (options) { + const cytobandUrl = options.cytobandURL; + const aliasURL = options.aliasURL; + const sequence = new FastaSequence(options); + await sequence.init(); + let cytobands; + + if (cytobandUrl) { + cytobands = await loadCytobands(cytobandUrl, sequence.config); + } + + let aliases; + + if (aliasURL) { + aliases = await loadAliases(aliasURL, sequence.config); + } + + return new Genome(options, sequence, cytobands, aliases); + }, + initializeGenomes: async function (config) { + if (!GenomeUtils.KNOWN_GENOMES) { + const table = {}; // Get default genomes + + if (config.loadDefaultGenomes !== false) { + try { + const url = DEFAULT_GENOMES_URL + `?randomSeed=${Math.random().toString(36)}&version=${version()}`; // prevent caching + + const jsonArray = await igvxhr.loadJson(url, { + timeout: 3000 + }); + processJson(jsonArray); + } catch (e) { + console.error(e); + const url = BACKUP_GENOMES_URL + `?randomSeed=${Math.random().toString(36)}&version=${version()}`; // prevent caching + + const jsonArray = await igvxhr.loadJson(url, {}); + processJson(jsonArray); + } + } // Add user-defined genomes + + + const genomeList = config.genomeList || config.genomes; + + if (genomeList) { + if (typeof genomeList === 'string') { + const jsonArray = await igvxhr.loadJson(genomeList, {}); + processJson(jsonArray); + } else { + processJson(genomeList); + } + } + + GenomeUtils.KNOWN_GENOMES = table; + + function processJson(jsonArray) { + jsonArray.forEach(function (json) { + table[json.id] = json; + }); + return table; + } + } + }, + isWholeGenomeView: function (chr) { + return 'all' === chr.toLowerCase(); + }, + // Expand a genome id to a reference object, if needed + expandReference: function (idOrConfig) { + // idOrConfig might be json + if (isString$6(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$6(idOrConfig)) { + genomeID = idOrConfig; + } else if (idOrConfig.genome) { + genomeID = idOrConfig.genome; + } else if (idOrConfig.id !== undefined && idOrConfig.fastaURL === undefined) { + // Backward compatibility + genomeID = idOrConfig.id; + } + + if (genomeID) { + const knownGenomes = GenomeUtils.KNOWN_GENOMES; + const reference = knownGenomes[genomeID]; + + if (!reference) { + Alert.presentAlert(new Error(`Unknown genome id: ${genomeID}`), undefined); + } + + return reference; + } else { + return idOrConfig; + } + } + }; + + class Genome { + constructor(config, sequence, ideograms, aliases) { + this.config = config; + this.id = config.id; + this.sequence = sequence; + this.chromosomeNames = sequence.chromosomeNames; + this.chromosomes = sequence.chromosomes; // An object (functions as a dictionary) + + this.ideograms = ideograms; + this.featureDB = {}; // Hash of name -> feature, used for search function. + + this.wholeGenomeView = config.wholeGenomeView === undefined || config.wholeGenomeView; + + if (this.wholeGenomeView && Object.keys(sequence.chromosomes).length > 1) { + constructWG(this, config); + } else { + this.wgChromosomeNames = sequence.chromosomeNames; + } + /** + * Return the official chromosome name for the (possibly) alias. Deals with + * 1 <-> chr1, chrM <-> MT, IV <-> chr4, etc. + * @param str + */ + + + var chrAliasTable = {}, + self = this; // The standard mappings + + chrAliasTable["all"] = "all"; + this.chromosomeNames.forEach(function (name) { + var alias = name.startsWith("chr") ? name.substring(3) : "chr" + name; + chrAliasTable[alias.toLowerCase()] = name; + if (name === "chrM") chrAliasTable["mt"] = "chrM"; + if (name === "MT") chrAliasTable["chrm"] = "MT"; + chrAliasTable[name.toLowerCase()] = name; + }); // Custom mappings + + if (aliases) { + aliases.forEach(function (array) { + // Find the official chr name + var defName, i; + + for (i = 0; i < array.length; i++) { + if (self.chromosomes[array[i]]) { + defName = array[i]; + break; + } + } + + if (defName) { + array.forEach(function (alias) { + if (alias !== defName) { + chrAliasTable[alias.toLowerCase()] = defName; + chrAliasTable[alias] = defName; // Should not be needed + } + }); + } + }); + } + + this.chrAliasTable = chrAliasTable; + } + + showWholeGenomeView() { + return this.config.wholeGenomeView !== false; + } + + toJSON() { + return Object.assign({}, this.config, { + tracks: undefined + }); + } + + getInitialLocus() {} + + getHomeChromosomeName() { + if (this.showWholeGenomeView() && this.chromosomes.hasOwnProperty("all")) { + return "all"; + } else { + const chromosome = this.chromosomes[this.chromosomeNames[0]]; + + if (chromosome.rangeLocus) { + return chromosome.name + ":" + chromosome.rangeLocus; + } else { + return this.chromosomeNames[0]; + } + } + } + + getChromosomeName(str) { + var chr = this.chrAliasTable[str.toLowerCase()]; + return chr ? chr : str; + } + + getChromosome(chr) { + chr = this.getChromosomeName(chr); + return this.chromosomes[chr]; + } + + getCytobands(chr) { + return this.ideograms ? this.ideograms[chr] : null; + } + + getLongestChromosome() { + var longestChr, + chromosomes = this.chromosomes; + + for (let key in chromosomes) { + if (chromosomes.hasOwnProperty(key)) { + var chr = chromosomes[key]; + + if (longestChr === undefined || chr.bpLength > longestChr.bpLength) { + longestChr = chr; + } + } + + return longestChr; + } + } + + getChromosomes() { + return this.chromosomes; + } + /** + * Return the genome coordinate in kb for the give chromosome and position. + * NOTE: This might return undefined if the chr is filtered from whole genome view. + */ + + + getGenomeCoordinate(chr, bp) { + var offset = this.getCumulativeOffset(chr); + if (offset === undefined) return undefined; + return offset + bp; + } + /** + * Return the chromosome and coordinate in bp for the given genome coordinate + */ + + + getChromosomeCoordinate(genomeCoordinate) { + if (this.cumulativeOffsets === undefined) { + this.cumulativeOffsets = computeCumulativeOffsets.call(this); + } + + let lastChr = undefined; + let lastCoord = 0; + + for (let name of this.wgChromosomeNames) { + const cumulativeOffset = this.cumulativeOffsets[name]; + + if (cumulativeOffset > genomeCoordinate) { + const position = genomeCoordinate - lastCoord; + return { + chr: lastChr, + position: position + }; + } + + lastChr = name; + lastCoord = cumulativeOffset; + } // If we get here off the end + + + return { + chr: this.chromosomeNames[this.chromosomeNames.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; + } + + } + + function loadCytobands(cytobandUrl, config) { + if (cytobandUrl.startsWith("data:")) { + var data = decodeDataUri(cytobandUrl); + return Promise.resolve(getCytobands(data)); + } else { + return igvxhr.loadString(cytobandUrl, buildOptions(config)).then(function (data) { + return getCytobands(data); + }); + } + + function getCytobands(data) { + var bands = [], + lastChr, + n = 0, + lines = splitLines$1(data), + len = lines.length, + cytobands = {}; + + for (var i = 0; i < len; i++) { + var tokens = lines[i].split("\t"); + var chr = tokens[0]; + if (!lastChr) lastChr = chr; + + if (chr !== lastChr) { + cytobands[lastChr] = bands; + bands = []; + lastChr = chr; + n = 0; + } + + 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[n++] = new Cytoband(start, end, name, stain); + } + } + + return cytobands; + } + + function decodeDataUri(dataUri) { + let plain; + + if (dataUri.startsWith("data:application/gzip;base64")) { + plain = decodeDataURI$1(dataUri); + } else { + let bytes, + split = dataUri.split(','), + info = split[0].split(':')[1], + dataString = split[1]; + + if (info.indexOf('base64') >= 0) { + dataString = atob(dataString); + } else { + dataString = decodeURI(dataString); + } + + bytes = new Uint8Array(dataString.length); + + for (let i = 0; i < dataString.length; i++) { + bytes[i] = dataString.charCodeAt(i); + } + + var inflate = new Zlib$1.Gunzip(bytes); + plain = inflate.decompress(); + } + + let s = ""; + const len = plain.length; + + for (let i = 0; i < len; i++) { + s += String.fromCharCode(plain[i]); + } + + return s; + } + } + + 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) { + 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); + } + } + + /* + * 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 ViewportBase { + constructor(trackView, $viewportContainer, referenceFrame, width) { + this.guid = guid(); + this.trackView = trackView; + this.referenceFrame = referenceFrame; + this.browser = trackView.browser; + this.$viewport = $('
'); + $viewportContainer.append(this.$viewport); // store the viewport GUID for later use + + this.$viewport.data('viewportGUID', this.guid); + this.$content = $("
", { + class: 'igv-viewport-content' + }); + this.$viewport.append(this.$content); + this.$content.height(this.$viewport.height()); + this.contentDiv = this.$content.get(0); + this.$canvas = $(''); + this.$content.append(this.$canvas); + this.canvas = this.$canvas.get(0); + this.ctx = this.canvas.getContext("2d"); + this.setWidth(width); + this.initializationHelper(); + } + + initializationHelper() {} + + showMessage(message) { + if (!this.messageDiv) { + this.messageDiv = document.createElement('div'); + this.messageDiv.className = 'igv-viewport-message'; + this.contentDiv.append(this.messageDiv); + } + + this.messageDiv.textContent = message; + this.messageDiv.style.display = 'inline-block'; + } + + hideMessage(message) { + if (this.messageDiv) this.messageDiv.style.display = 'none'; + } + + setTrackLabel(label) {} + + startSpinner() {} + + stopSpinner() {} + + checkZoomIn() { + return true; + } + + shift() {} + + setTop(contentTop) { + const viewportHeight = this.$viewport.height(); + const viewTop = -contentTop; + const viewBottom = viewTop + viewportHeight; + this.$content.css('top', `${contentTop}px`); + + if (undefined === this.canvasVerticalRange || this.canvasVerticalRange.bottom < viewBottom || this.canvasVerticalRange.top > viewTop) { + this.repaint(); + } + } + + async loadFeatures() { + return undefined; + } + + async repaint() {} + + draw(drawConfiguration, features, roiFeatures) {} + + checkContentHeight() { + let track = this.trackView.track; + + if ("FILL" === track.displayMode) { + this.setContentHeight(this.$viewport.height()); + } else if (typeof track.computePixelHeight === 'function') { + let features = this.cachedFeatures; + + if (features && features.length > 0) { + let requiredContentHeight = track.computePixelHeight(features); + let currentContentHeight = this.$content.height(); + + if (requiredContentHeight !== currentContentHeight) { + this.setContentHeight(requiredContentHeight); + } + } + } + } + + getContentHeight() { + return this.$content.height(); + } + + setContentHeight(contentHeight) { + // Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms + contentHeight = Math.min(contentHeight, 32000); + this.$content.height(contentHeight); + if (this.tile) this.tile.invalidate = true; + } + + isLoading() { + return false; + } + + saveSVG() {} + + renderSVGContext(context, offset) {} + + drawSVGWithContext(context) {} + + isVisible() { + return this.$viewport.width(); + } + + setWidth(width) { + this.$viewport.width(width); + this.canvas.style.width = `${width}px`; + this.canvas.setAttribute('width', width); + } + + getWidth() { + return this.$viewport.width(); + } + + getContentTop() { + return this.contentDiv.offsetTop; + } + + containsPosition(chr, position) {} + /** + * Called when the associated track is removed. Do any needed cleanup here. + */ + + + dispose() { + if (this.popover) { + this.popover.dispose(); + } + + this.$canvas.off(); + this.$canvas.empty(); + this.$content.off(); + this.$content.empty(); + this.$viewport.off(); + this.$viewport.empty(); // 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; + } + } + + } + + /* + * 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 trackMenuItemListHelper = MenuUtils.trackMenuItemListHelper; + + const MenuPopup = function ($parent) { + // popover container + this.$popover = $('
', { + class: 'igv-menu-popup' + }); + $parent.append(this.$popover); // popover header + + let $popoverHeader = $('
', { + class: 'igv-menu-popup-header' + }); + this.$popover.append($popoverHeader); + attachDialogCloseHandlerWithParent$1($popoverHeader.get(0), () => this.$popover.hide()); + this.$popoverContent = $('
'); + this.$popover.append(this.$popoverContent); + makeDraggable$1(this.$popover.get(0), $popoverHeader.get(0)); + $popoverHeader.on('click.menu-popup-dismiss', function (e) { + e.stopPropagation(); + e.preventDefault(); // absorb click to prevent it leaking through to parent DOM element + }); + }; + + MenuPopup.prototype.presentMenuList = function (dx, dy, list) { + hideAllMenuPopups(); + + if (list.length > 0) { + this.$popoverContent.empty(); + list = trackMenuItemListHelper(list, this.$popover); + + for (let item of list) { + if (item.init) { + item.init(); + } + + let $e = item.object; + + if (0 === list.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.append($e); + } + + this.$popover.css({ + left: dx + 'px', + top: dy + 'px' + }); + this.$popover.show(); + } + }; + + MenuPopup.prototype.presentTrackContextMenu = function (e, menuItems) { + this.$popoverContent.empty(); + const menuElements = createMenuElements(menuItems, this.$popover.get(0)); + + for (let item of menuElements) { + this.$popoverContent.get(0).appendChild(item.object); + } + + present(e, this.$popover.get(0)); + this.$popover.show(); + }; + + MenuPopup.prototype.dispose = function () { + this.$popover.empty(); + this.$popoverContent.empty(); + Object.keys(this).forEach(function (key) { + this[key] = undefined; + }); + }; + + function createMenuElements(itemList, popover) { + return itemList.map(item => { + let elem; + + if (typeof item === 'string' && '
' === item) { + elem = document.createElement('hr'); + } else if (typeof item === 'string') { + elem = div$1({ + class: 'context-menu' + }); + elem.innerHTML = item; + } else if (typeof item === 'Node') { + elem = item; + } else { + if (typeof item.init === 'function') { + item.init(); + } + + if ("checkbox" === item.type) { + elem = Icon.createCheckbox("Show all bases", item.value); + } else if ("color" === item.type) { + const colorPicker = new ColorPicker({ + parent: popover.parentElement, + width: 364, + //defaultColor: 'aqua', + colorHandler: color => item.click(color) + }); + elem = div$1({ + class: 'context-menu' + }); + + if (typeof item.label === 'string') { + elem.innerHTML = item.label; + } + + const clickHandler = e => { + colorPicker.show(); + hide$1(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$1({ + class: 'context-menu' + }); + + 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$1(popover); + e.preventDefault(); + e.stopPropagation(); + } + } + } + + return { + object: elem, + init: item.init + }; + }); + } + + function present(e, popover) { + const { + x, + y + } = translateMouseCoordinates$1(e, popover.parentNode); // parent bbox + + const { + width + } = popover.parentNode.getBoundingClientRect(); + const { + width: w + } = popover.getBoundingClientRect(); + const xmax = x + w; + popover.style.left = `${xmax > width ? x - (xmax - width) : x}px`; + popover.style.top = `${y}px`; + } + + const hideAllMenuPopups = () => $('.igv-menu-popup').hide(); + + /*!! + * 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. + */ + + var STYLES, ctx, CanvasGradient, CanvasPattern, namedEntities; //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; + } // Unpack entities lookup where the numbers are in radix 32 to reduce the size + // entity mapping courtesy of tinymce + + + 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. + + 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 + */ + + CanvasGradient = function (gradientNode, ctx) { + this.__root = gradientNode; + this.__ctx = ctx; + }; + /** + * Adds a color stop to the gradient root + */ + + + CanvasGradient.prototype.addColorStop = function (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); + }; + + CanvasPattern = function (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) + */ + + + ctx = function (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; + }; + + ctx.prototype.setWidth = function (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); + }; + + ctx.prototype.setHeight = function (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 + */ + + + ctx.prototype.__createElement = function (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 + */ + + + ctx.prototype.__setDefaultStyles = function () { + //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 + */ + + + ctx.prototype.__applyStyleState = function (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 + */ + + + ctx.prototype.__getStyleState = function () { + 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 + */ + + + ctx.prototype.__applyStyleToCurrentElement = function (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 + */ + + + ctx.prototype.__closestGroupOrSvg = function (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 + */ + + + ctx.prototype.getSerializedSvg = function (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 + */ + + + ctx.prototype.getSvg = function () { + return this.__root; + }; + /** + * Will generate a group tag. + */ + + + ctx.prototype.saveWithTranslationAndClipRect = function (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()); + }; + + ctx.prototype.save = function () { + 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 + */ + + + ctx.prototype.restore = function () { + 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 + */ + + + ctx.prototype.__addTransform = function (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); + }; + + ctx.prototype.addTrackGroupWithTranslationAndClipRect = function (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 + */ + + + ctx.prototype.scale = function (x, y) { + if (y === undefined) { + y = x; + } + + this.__addTransform(format("scale({x},{y})", { + x: x, + y: y + })); + }; + /** + * rotates the current element + */ + + + ctx.prototype.rotate = function (angle) { + var degrees = angle * 180 / Math.PI; + + this.__addTransform(format("rotate({angle},{cx},{cy})", { + angle: degrees, + cx: 0, + cy: 0 + })); + }; + /** + * translates the current element + */ + + + ctx.prototype.translate = function (x, y) { + this.__addTransform(format("translate({x},{y})", { + x: x, + y: y + })); + }; + /** + * applies a transform to the current element + */ + + + ctx.prototype.transform = function (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 + */ + + + ctx.prototype.beginPath = function () { + 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 + */ + + + ctx.prototype.__applyCurrentDefaultPath = function () { + 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 + */ + + + ctx.prototype.__addPathCommand = function (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 + */ + + + ctx.prototype.moveTo = function (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 + */ + + + ctx.prototype.closePath = function () { + if (this.__currentDefaultPath) { + this.__addPathCommand("Z"); + } + }; + /** + * Adds a line to command + */ + + + ctx.prototype.lineTo = function (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 + */ + + + ctx.prototype.bezierCurveTo = function (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 + */ + + + ctx.prototype.quadraticCurveTo = function (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 + })); + }; + /** + * Return a new normalized vector of given vector + */ + + + var normalize$1 = function (vector) { + var len = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]); + return [vector[0] / len, vector[1] / len]; + }; + /** + * Adds the arcTo to the current path + * + * @see http://www.w3.org/TR/2015/WD-2dcontext-20150514/#dom-context-2d-arcto + */ + + + ctx.prototype.arcTo = function (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$1([x0 - x1, y0 - y1]); + var unit_vec_p1_p2 = normalize$1([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$1([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 + */ + + + ctx.prototype.stroke = function () { + 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 + */ + + + ctx.prototype.fill = function () { + if (this.__currentElement.nodeName === "path") { + this.__currentElement.setAttribute("paint-order", "stroke fill markers"); + } + + this.__applyCurrentDefaultPath(); + + this.__applyStyleToCurrentElement("fill"); + }; + /** + * Adds a rectangle to the path. + */ + + + ctx.prototype.rect = function (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 + */ + + + ctx.prototype.fillRect = function (x, y, width, height) { + if (height < 0) { + y += height; + height = -height; + } + + if (width < 0) { + x += width; + width = -width; + } // See if rect instersects 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"); + }; + + 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; + } + /** + * Draws a rectangle with no fill + * @param x + * @param y + * @param width + * @param height + */ + + + ctx.prototype.strokeRect = function (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"); + }; + /** + * Clear entire canvas: + * 1. save current transforms + * 2. remove all the childNodes of the root g element + */ + + + ctx.prototype.__clearCanvas = function () { + 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. + */ + + + ctx.prototype.clearRect = function (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 + */ + + + ctx.prototype.createLinearGradient = function (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 + */ + + + ctx.prototype.createRadialGradient = function (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 + */ + + + ctx.prototype.__parseFont = function () { + 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 + */ + + + ctx.prototype.__wrapTextLink = function (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 + */ + + + ctx.prototype.__applyText = function (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 + */ + + + ctx.prototype.fillText = function (text, x, y) { + this.__applyText(text, x, y, "fill"); + }; + /** + * Strokes text + * @param text + * @param x + * @param y + */ + + + ctx.prototype.strokeText = function (text, x, y) { + this.__applyText(text, x, y, "stroke"); + }; + /** + * No need to implement this for svg. + * @param text + * @return {TextMetrics} + */ + + + ctx.prototype.measureText = function (text) { + this.__ctx.font = this.font; + return this.__ctx.measureText(text); + }; + /** + * Arc command! + */ + + + ctx.prototype.arc = function (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 + }; + }; + /** + * Generates a ClipPath from the clip command. + */ + + + ctx.prototype.clip = function () { + 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 + */ + + + ctx.prototype.drawImage = function () { + //convert arguments to a real array + var args = Array.prototype.slice.call(arguments), + image = args[0], + dx, + dy, + dw, + dh, + sx = 0, + sy = 0, + sw, + sh, + parent, + svg, + defs, + group, + svgImage, + canvas, + context, + id; + + if (args.length === 3) { + dx = args[1]; + dy = args[2]; + sw = image.width; + sh = image.height; + dw = sw; + dh = sh; + } else if (args.length === 5) { + dx = args[1]; + dy = args[2]; + dw = args[3]; + dh = args[4]; + sw = image.width; + sh = image.height; + } else if (args.length === 9) { + sx = args[1]; + sy = args[2]; + sw = args[3]; + sh = args[4]; + dx = args[5]; + dy = args[6]; + dw = args[7]; + dh = args[8]; + } else { + throw new Error("Invalid number of arguments passed to drawImage: " + arguments.length); + } + + parent = this.__closestGroupOrSvg(); + var translateDirective = "translate(" + dx + ", " + dy + ")"; + + if (image instanceof ctx) { + //canvas2svg mock canvas context. In the future we may want to clone nodes instead. + //also I'm currently ignoring dw, dh, sw, sh, sx, sy for a mock context. + svg = image.getSvg().cloneNode(true); + + if (svg.childNodes && svg.childNodes.length > 1) { + defs = svg.childNodes[0]; + + while (defs.childNodes.length) { + id = defs.childNodes[0].getAttribute("id"); + this.__ids[id] = id; + + this.__defs.appendChild(defs.childNodes[0]); + } + + group = svg.childNodes[1]; + + if (group) { + //save original transform + var originTransform = group.getAttribute("transform"); + var transformDirective; + + if (originTransform) { + transformDirective = originTransform + " " + translateDirective; + } else { + transformDirective = translateDirective; + } + + group.setAttribute("transform", transformDirective); + parent.appendChild(group); + } + } + } else if (image.nodeName === "CANVAS" || image.nodeName === "IMG") { + //canvas or image + svgImage = this.__createElement("image"); + svgImage.setAttribute("width", dw); + svgImage.setAttribute("height", dh); + svgImage.setAttribute("preserveAspectRatio", "none"); + + if (sx || sy || sw !== image.width || sh !== image.height) { + //crop the image using a temporary canvas + canvas = this.__document.createElement("canvas"); + canvas.width = dw; + canvas.height = dh; + context = canvas.getContext("2d"); + context.drawImage(image, sx, sy, sw, sh, 0, 0, dw, dh); + image = canvas; + } + + svgImage.setAttribute("transform", translateDirective); + svgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", image.nodeName === "CANVAS" ? image.toDataURL() : image.getAttribute("src")); + parent.appendChild(svgImage); + } + }; + /** + * Generates a pattern tag + */ + + + ctx.prototype.createPattern = function (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); + }; + + ctx.prototype.setLineDash = function (dashArray) { + if (dashArray && dashArray.length > 0) { + this.lineDash = dashArray.join(","); + } else { + this.lineDash = null; + } + }; + /** + * Not yet implemented + */ + + + ctx.prototype.drawFocusRing = function () {}; + + ctx.prototype.createImageData = function () {}; + + ctx.prototype.getImageData = function () {}; + + ctx.prototype.putImageData = function () {}; + + ctx.prototype.globalCompositeOperation = function () {}; + + ctx.prototype.setTransform = function () {}; + + var C2S = ctx; + + /** + * Created by dat on 9/16/16. + */ + const NOT_LOADED_MESSAGE = 'Error loading track data'; + + class ViewPort extends ViewportBase { + constructor(trackView, $viewportContainer, referenceFrame, width) { + super(trackView, $viewportContainer, referenceFrame, width); + } + + initializationHelper() { + this.menuPopup = new MenuPopup(this.trackView.$viewportContainer); + this.menuPopup.$popover.hide(); + this.addMouseHandlers(); + this.$spinner = $('
'); + this.$spinner.append(createIcon$1("spinner")); + this.$viewport.append(this.$spinner); + this.stopSpinner(); + const { + track + } = this.trackView; + + if ('sequence' !== track.type) { + this.$zoomInNotice = this.createZoomInNotice(this.$content); + } + + if (track.name && "sequence" !== track.config.type) { + this.$trackLabel = $('
'); + this.$viewport.append(this.$trackLabel); + this.setTrackLabel(track.name); + + if (false === this.browser.trackLabelsVisible) { + this.$trackLabel.hide(); + } + + this.$trackLabel.click(e => { + let str; + e.stopPropagation(); + + if (typeof track.description === 'function') { + str = track.description(); + } else if (track.description) { + str = `
${track.description}
`; + } else { + if (track.url) { + if (track.url instanceof File) { + str = `
Filename: ${track.url.name}`; + } else { + str = `
URL: ${track.url}`; + } + } else { + str = track.name; + } + } + + if (this.popover) this.popover.dispose(); + this.popover = new Popover(this.trackView.$viewportContainer.get(0), track.name || 'unnamed'); + this.popover.presentContentWithEvent(e, str); + }); + this.$trackLabel.mousedown(function (e) { + // Prevent bubbling + e.stopPropagation(); + }); + this.$trackLabel.mouseup(function (e) { + // Prevent bubbling + e.stopPropagation(); + }); + this.$trackLabel.mousemove(function (e) { + // Prevent bubbling + e.stopPropagation(); + }); + } + } + + setTrackLabel(label) { + this.trackView.track.name = this.trackView.track.config.name = label; + this.$trackLabel.empty(); + this.$trackLabel.html(label); + const txt = this.$trackLabel.text(); + this.$trackLabel.attr('title', txt); + } + + startSpinner() { + const $spinner = this.$spinner; + + if ($spinner) { + $spinner.addClass("igv-fa5-spin"); + $spinner.show(); + } + } + + stopSpinner() { + const $spinner = this.$spinner; + + if ($spinner) { + $spinner.hide(); + $spinner.removeClass("igv-fa5-spin"); + } + } + + checkZoomIn() { + const showZoomInNotice = () => { + const referenceFrame = this.referenceFrame; + + if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome()) { + return true; + } else { + const visibilityWindow = typeof this.trackView.track.getVisibilityWindow === 'function' ? this.trackView.track.getVisibilityWindow() : this.trackView.track.visibilityWindow; + return visibilityWindow !== undefined && visibilityWindow > 0 && referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow; + } + }; + + if (!this.viewIsReady()) { + return false; + } + + if (this.$zoomInNotice) { + if (showZoomInNotice()) { + // Out of visibility window + if (this.canvas) { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.tile = undefined; + } + + this.$zoomInNotice.show(); + + if (this.trackView.track.autoHeight) { + const minHeight = this.trackView.minHeight || 0; + this.setContentHeight(minHeight); + } + + return false; + } else { + this.$zoomInNotice.hide(); + return true; + } + } + + return true; + } + + shift() { + const self = this; + const referenceFrame = self.referenceFrame; + + if (self.canvas && self.tile && self.tile.chr === self.referenceFrame.chr && self.tile.bpPerPixel === referenceFrame.bpPerPixel) { + const pixelOffset = Math.round((self.tile.startBP - referenceFrame.start) / referenceFrame.bpPerPixel); + self.canvas.style.left = pixelOffset + "px"; + } + } + + async loadFeatures() { + const referenceFrame = this.referenceFrame; + const chr = referenceFrame.chr; // Expand the requested range so we can pan a bit without reloading. But not beyond chromosome bounds + + const chrLength = this.browser.genome.getChromosome(chr).bpLength; + const pixelWidth = this.$content.width() * 3; + const bpWidth = pixelWidth * referenceFrame.bpPerPixel; + const bpStart = Math.floor(Math.max(0, referenceFrame.start - bpWidth / 3)); + const bpEnd = Math.ceil(Math.min(chrLength, bpStart + bpWidth)); + + if (this.loading && this.loading.start === bpStart && this.loading.end === bpEnd) { + return undefined; + } + + this.loading = { + start: bpStart, + end: bpEnd + }; + this.startSpinner(); // console.log('get features'); + + try { + const features = await this.getFeatures(this.trackView.track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel); + let roiFeatures = []; + const roi = mergeArrays(this.browser.roi, this.trackView.track.roi); + + if (roi) { + for (let r of roi) { + const f = await r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel); + roiFeatures.push({ + track: r, + features: f + }); + } + } + + this.tile = new Tile(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures); + this.loading = false; + this.hideMessage(); + this.stopSpinner(); + return this.tile; + } catch (error) { + // Track might have been removed during load + if (this.trackView && this.trackView.disposed !== true) { + this.showMessage(NOT_LOADED_MESSAGE); + Alert.presentAlert(error); + console.error(error); + } + } finally { + this.loading = false; + this.stopSpinner(); + } + } + + async repaint() { + if (undefined === this.tile) { + return; + } + + let { + features, + roiFeatures, + bpPerPixel, + startBP, + endBP + } = this.tile; + const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr); + let pixelWidth; + + if (isWGV) { + bpPerPixel = this.referenceFrame.initialEnd / this.$viewport.width(); + startBP = 0; + endBP = this.referenceFrame.initialEnd; + pixelWidth = this.$viewport.width(); + } else { + pixelWidth = Math.ceil((endBP - startBP) / bpPerPixel); + } // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position + + + const viewportHeight = this.$viewport.height(); + const contentHeight = this.getContentHeight(); + const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs. + + let pixelHeight = Math.min(minHeight, 3 * viewportHeight); + + if (0 === pixelWidth || 0 === pixelHeight) { + if (this.canvas) { + $(this.canvas).remove(); + } + + return; + } + + const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight); // Always use high DPI if in compressed display mode, otherwise use preference setting; + + let devicePixelRatio; + + if ("FILL" === this.trackView.track.displayMode) { + devicePixelRatio = window.devicePixelRatio; + } else { + devicePixelRatio = this.trackView.track.supportHiDPI === false ? 1 : window.devicePixelRatio; + } + + const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / this.referenceFrame.bpPerPixel); + const newCanvas = $('').get(0); + const ctx = newCanvas.getContext("2d"); + newCanvas.style.width = pixelWidth + "px"; + newCanvas.style.height = pixelHeight + "px"; + newCanvas.width = devicePixelRatio * pixelWidth; + newCanvas.height = devicePixelRatio * pixelHeight; + ctx.scale(devicePixelRatio, devicePixelRatio); + newCanvas.style.left = pixelXOffset + "px"; + newCanvas.style.top = canvasTop + "px"; + ctx.translate(0, -canvasTop); + const drawConfiguration = { + context: ctx, + pixelXOffset, + pixelWidth, + pixelHeight, + pixelTop: canvasTop, + bpStart: startBP, + bpEnd: endBP, + bpPerPixel, + referenceFrame: this.referenceFrame, + selection: this.selection, + viewport: this, + viewportWidth: this.$viewport.width() //viewportContainerX: this.referenceFrame.toPixels(this.referenceFrame.start - startBP), + //viewportContainerWidth: this.browser.getViewportContainerWidth() + + }; + this.draw(drawConfiguration, features, roiFeatures); + this.canvasVerticalRange = { + top: canvasTop, + bottom: canvasTop + pixelHeight + }; + + if (this.$canvas) { + this.$canvas.remove(); + } + + this.$canvas = $(newCanvas); + this.$content.append(this.$canvas); + this.canvas = newCanvas; + this.ctx = ctx; + } + + draw(drawConfiguration, features, roiFeatures) { + // console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`) + if (features) { + drawConfiguration.features = features; + this.trackView.track.draw(drawConfiguration); + } + + if (roiFeatures) { + for (let r of roiFeatures) { + drawConfiguration.features = r.features; + r.track.draw(drawConfiguration); + } + } + } // TODO: Nolonger used. Will discard + + + async toSVG(tile) { + // Nothing to do if zoomInNotice is active + if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) { + return; + } + + const referenceFrame = this.referenceFrame; + const bpPerPixel = tile.bpPerPixel; + const features = tile.features; + const roiFeatures = tile.roiFeatures; + const pixelWidth = this.$viewport.width(); + const pixelHeight = this.$viewport.height(); + const bpStart = referenceFrame.start; + const bpEnd = referenceFrame.start + pixelWidth * referenceFrame.bpPerPixel; + const ctx = new C2S({ + // svg + width: pixelWidth, + height: pixelHeight, + viewbox: { + x: 0, + y: -this.$content.position().top, + width: pixelWidth, + height: pixelHeight + } + }); + const drawConfiguration = { + viewport: this, + context: ctx, + top: -this.$content.position().top, + pixelTop: 0, + // for compatibility with canvas draw + pixelWidth, + pixelHeight, + bpStart, + bpEnd, + bpPerPixel, + referenceFrame: this.referenceFrame, + selection: this.selection, + viewportWidth: pixelWidth, + viewportContainerX: 0, + viewportContainerWidth: this.browser.getViewportContainerWidth() + }; + this.draw(drawConfiguration, features, roiFeatures); + return ctx.getSerializedSvg(true); + } + + 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; + } + + saveImage() { + if (!this.ctx) return; + const canvasTop = this.canvasVerticalRange ? this.canvasVerticalRange.top : 0; + const devicePixelRatio = window.devicePixelRatio; + const w = this.$viewport.width() * devicePixelRatio; + const h = this.$viewport.height() * devicePixelRatio; + const x = -$(this.canvas).position().left * devicePixelRatio; + const y = (-this.$content.position().top - canvasTop) * devicePixelRatio; + const imageData = this.ctx.getImageData(x, y, w, h); + const exportCanvas = document.createElement('canvas'); + const exportCtx = exportCanvas.getContext('2d'); + exportCanvas.width = imageData.width; + exportCanvas.height = imageData.height; + exportCtx.putImageData(imageData, 0, 0); // filename = this.trackView.track.name + ".png"; + + const filename = (this.$trackLabel.text() ? this.$trackLabel.text() : "image") + ".png"; + const data = exportCanvas.toDataURL("image/png"); + download(filename, data); + } + + saveSVG() { + const { + width, + height + } = this.$viewport.get(0).getBoundingClientRect(); + const config = { + width, + height, + viewbox: { + x: 0, + y: -this.$content.position().top, + width, + height + } + }; + const context = new C2S(config); + const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, ''); + const index = this.browser.referenceFrameList.indexOf(this.referenceFrame); + const id = `${str}_referenceFrame_${index}_guid_${guid()}`; + this.drawSVGWithContext(context, width, height, id, 0, 0, 0); + const svg = context.getSerializedSvg(true); + const data = URL.createObjectURL(new Blob([svg], { + type: "application/octet-stream" + })); + download(`${id}.svg`, data); + } // called by trackView.renderSVGContext() when rendering + // entire browser as SVG + + + renderSVGContext(context, { + deltaX, + deltaY + }) { + // Nothing to do if zoomInNotice is active + if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) { + return; + } + + const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, ''); + const index = this.browser.referenceFrameList.indexOf(this.referenceFrame); + const id = `${str}_referenceFrame_${index}_guid_${guid()}`; + const yScrollDelta = $(this.contentDiv).position().top; + const dx = deltaX + index * context.multiLocusGap; + const dy = deltaY + yScrollDelta; + const { + width, + height + } = this.$viewport.get(0).getBoundingClientRect(); + this.drawSVGWithContext(context, width, height, id, dx, dy, -yScrollDelta); + + if (this.$trackLabel && true === this.browser.trackLabelsVisible) { + const { + x: xTrackLabel, + y: yTrackLabel, + width: wTracklabel, + height: hTrackLabel + } = relativeDOMBBox(this.$viewport.get(0), this.$trackLabel.get(0)); + this.renderTrackLabelSVG(context, deltaX + xTrackLabel, deltaY + yTrackLabel, wTracklabel, hTrackLabel); + } + } // render track label element called from renderSVGContext() + + + renderTrackLabelSVG(context, tx, ty, width, height) { + const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, ''); + const id = `${str}_track_label_guid_${guid()}`; + 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, tx, ty, clipYOffset) { + let { + start, + bpPerPixel + } = this.referenceFrame; + context.saveWithTranslationAndClipRect(id, tx, ty, width, height, clipYOffset); + const top = -this.$content.position().top; + const config = { + context, + viewport: this, + referenceFrame: this.referenceFrame, + top, + pixelTop: top, + pixelWidth: width, + pixelHeight: height, + bpStart: start, + bpEnd: start + width * bpPerPixel, + bpPerPixel, + viewportWidth: width, + // viewportContainerX: 0, + // viewportContainerWidth: this.browser.getViewportContainerWidth(), + selection: this.selection + }; + const features = this.tile ? this.tile.features : []; + const roiFeatures = this.tile ? this.tile.roiFeatures : undefined; + this.draw(config, features, roiFeatures); + context.restore(); + } + + getCachedFeatures() { + return this.tile ? this.tile.features : []; + } + + async getFeatures(track, chr, start, end, bpPerPixel) { + if (this.tile && this.tile.containsRange(chr, start, end, bpPerPixel)) { + return this.tile.features; + } else if (typeof track.getFeatures === "function") { + const features = await track.getFeatures(chr, start, end, bpPerPixel, this); + this.cachedFeatures = features; + this.checkContentHeight(); + return features; + } else { + return undefined; + } + } + + createZoomInNotice($parent) { + const $notice = $('
'); + $parent.append($notice); + const $e = $('
'); + $notice.append($e); + $e.text('Zoom in to see features'); + $notice.hide(); + return $notice; + } + + viewIsReady() { + return this.browser && this.browser.referenceFrameList && this.referenceFrame; + } + + addMouseHandlers() { + const self = this; + const browser = this.browser; + let popupTimerID; + let lastClickTime = 0; + this.$viewport.on("contextmenu", function (e) { + // Ignore if we are doing a drag. This can happen with touch events. + if (self.browser.dragObject) { + return false; + } + + const clickState = createClickState(e, self); + + if (undefined === clickState) { + return false; + } + + e.preventDefault(); // Track specific items + + let menuItems = []; + + if (typeof self.trackView.track.contextMenuItemList === "function") { + const trackMenuItems = self.trackView.track.contextMenuItemList(clickState); + + if (trackMenuItems) { + menuItems = trackMenuItems; + } + } // Add items common to all tracks + + + if (menuItems.length > 0) { + menuItems.push({ + label: $('
') + }); + } + + menuItems.push({ + label: 'Save Image (PNG)', + click: () => self.saveImage() + }); + menuItems.push({ + label: 'Save Image (SVG)', + click: () => self.saveSVG() + }); + self.menuPopup.presentTrackContextMenu(e, menuItems); + }); + /** + * Mouse click down, notify browser for potential drag (pan), and record position for potential click. + */ + + this.$viewport.on('mousedown', function (e) { + self.enableClick = true; + browser.mouseDownOnViewport(e, self); + pageCoordinates$1(e); + }); + this.$viewport.on('touchstart', function (e) { + self.enableClick = true; + browser.mouseDownOnViewport(e, self); + pageCoordinates$1(e); + }); + /** + * Mouse is released. Ignore if this is a context menu click, or the end of a drag action. If neither of + * those, it is a click. + */ + + this.$viewport.on('mouseup', handleMouseUp); + this.$viewport.on('touchend', handleMouseUp); + this.$viewport.on('click', function (e) { + if (self.enableClick) { + handleClick(e); + } + }); + + function handleMouseUp(e) { + // Any mouse up cancels drag and scrolling + if (self.browser.dragObject || self.browser.isScrolling) { + self.browser.cancelTrackPan(); + e.preventDefault(); + e.stopPropagation(); + self.enableClick = false; // Until next mouse down + + return; + } else { + self.browser.cancelTrackPan(); + self.browser.endTrackDrag(); + } + } + + function handleClick(e) { + if (3 === e.which || e.ctrlKey) { + return; + } // Close any currently open popups + + + $('.igv-popover').hide(); + + if (browser.dragObject || browser.isScrolling) { + return; + } // // Interpret mouseDown + mouseUp < 5 pixels as a click. + // if(!mouseDownCoords) { + // return; + // } + // const coords = pageCoordinates(e); + // const dx = coords.x - mouseDownCoords.x; + // const dy = coords.y - mouseDownCoords.y; + // const dist2 = dx*dx + dy*dy; + // if(dist2 > 25) { + // mouseDownCoords = undefined; + // return; + // } + // Treat as a mouse click, its either a single or double click. + // Handle here and stop propogation / default + + + e.preventDefault(); + e.stopPropagation(); + const mouseX = translateMouseCoordinates$1(e, self.$viewport.get(0)).x; + const mouseXCanvas = translateMouseCoordinates$1(e, self.canvas).x; + const referenceFrame = self.referenceFrame; + const xBP = Math.floor(referenceFrame.start + referenceFrame.toBP(mouseXCanvas)); + const time = Date.now(); + + if (time - lastClickTime < 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' === self.referenceFrame.chr.toLowerCase()) { + const chr = browser.genome.getChromosomeCoordinate(centerBP).chr; + + if (1 === browser.referenceFrameList.length) { + string = chr; + } else { + let loci = browser.referenceFrameList.map(function (g) { + return g.locusSearchString; + }); + loci[browser.referenceFrameList.indexOf(self.referenceFrame)] = chr; + string = loci.join(' '); + } + + browser.search(string); + } else { + browser.zoomWithScaleFactor(0.5, centerBP, self); + } + } else { + // single-click + if (e.shiftKey && typeof self.trackView.track.shiftClick === "function") { + self.trackView.track.shiftClick(xBP, e); + } else if (typeof self.trackView.track.popupData === "function") { + popupTimerID = setTimeout(function () { + const content = getPopupContent(e, self); + + if (content) { + if (self.popover) self.popover.dispose(); + self.popover = new Popover(self.trackView.$viewportContainer.get(0)); + self.popover.presentContentWithEvent(e, content); + } + + clearTimeout(popupTimerID); + popupTimerID = undefined; + }, browser.constants.doubleClickDelay); + } + } + + lastClickTime = time; + } + + function createClickState(e, viewport) { + const referenceFrame = viewport.referenceFrame; + const viewportCoords = translateMouseCoordinates$1(e, viewport.contentDiv); + const canvasCoords = translateMouseCoordinates$1(e, viewport.canvas); + const genomicLocation = referenceFrame.start + referenceFrame.toBP(viewportCoords.x); + + if (undefined === genomicLocation || null === viewport.tile) { + return undefined; + } + + return { + event: e, + viewport: viewport, + referenceFrame: referenceFrame, + genomicLocation: genomicLocation, + x: viewportCoords.x, + y: viewportCoords.y, + canvasX: canvasCoords.x, + canvasY: canvasCoords.y + }; + } + /** + * Return markup for popup info window + * + * @param e + * @param viewport + * @returns {*} + */ + + + function getPopupContent(e, viewport) { + const clickState = createClickState(e, viewport); + + if (undefined === clickState) { + return; + } + + let track = viewport.trackView.track; + const dataList = track.popupData(clickState); + const popupClickHandlerResult = 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; + } + /** + * Format markup for popover text from an array of name value pairs [{name, value}] + */ + + + 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(''); + } + } + + } + + var Tile = function (chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures) { + this.chr = chr; + this.startBP = tileStart; + this.endBP = tileEnd; + this.bpPerPixel = bpPerPixel; + this.features = features; + this.roiFeatures = roiFeatures; + }; + + Tile.prototype.containsRange = function (chr, start, end, bpPerPixel) { + return this.bpPerPixel === bpPerPixel && start >= this.startBP && end <= this.endBP && chr === this.chr; + }; + + Tile.prototype.overlapsRange = function (chr, start, end) { + return this.chr === chr && end >= this.startBP && start <= this.endBP; + }; + /** + * Merge 2 arrays. a and/or b can be undefined. If both are undefined, return undefined + * @param a An array or undefined + * @param b An array or undefined + */ + + + function mergeArrays(a, b) { + if (a && b) return a.concat(b);else if (a) return a;else return b; + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 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 ViewPort { + constructor(trackView, $viewportContainer, referenceFrame, width) { + super(trackView, $viewportContainer, referenceFrame, width); + } + + initializationHelper() { + this.$ideogramCanvas = $('', { + class: 'igv-ideogram-canvas' + }); + this.$ideogramCanvas.insertBefore(this.$canvas); + const canvas = this.$ideogramCanvas.get(0); + this.ideogram_ctx = canvas.getContext('2d'); + this.$canvas.remove(); + this.canvas = undefined; + this.ctx = undefined; + this.$viewport.on('click.ideogram', e => { + clickHandler(e, canvas, this.browser, this.referenceFrame); + }); + } + + setWidth(width) { + this.$viewport.width(width); + } + + drawSVGWithContext(context, width, height, id, tx, ty, clipYOffset) { + context.saveWithTranslationAndClipRect(id, tx, ty, width, height, clipYOffset); + this.trackView.track.draw({ + context, + referenceFrame: this.referenceFrame, + pixelWidth: width, + pixelHeight: height + }); + context.restore(); + } + + async repaint() { + this.draw({ + referenceFrame: this.referenceFrame + }); + } + + draw({ + referenceFrame + }) { + this.$canvas.hide(); + 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() + }); + } + + } + + function clickHandler(e, canvas, browser, referenceFrame) { + const { + xNormalized, + width + } = translateMouseCoordinates$1(e, canvas); + const { + bpLength + } = browser.genome.getChromosome(referenceFrame.chr); + const locusLength = 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); + referenceFrame.start = ss; + referenceFrame.initialEnd = ee; + referenceFrame.bpPerPixel = (ee - ss) / width; + browser.updateLocusSearchWidget(browser.referenceFrameList > 1 ? browser.referenceFrameList : [referenceFrame]); + browser.updateViews(referenceFrame, browser.trackViews, 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. + */ + + class RulerSweeper { + constructor(viewport) { + this.viewport = viewport; + this.browser = viewport.browser; + this.$rulerSweeper = $('
'); + $(viewport.contentDiv).append(this.$rulerSweeper); + this.namespace = '.sweeper_' + guid(); + this.addMouseHandlers(); + } + + disableMouseHandlers() { + $(document).off(this.namespace); + this.viewport.$viewport.off(this.namespace); + } + + addMouseHandlers() { + const browser = this.browser; + const self = this; + var isMouseDown, isMouseIn, mouseDown, left, threshold, width, dx; + this.disableMouseHandlers(); + isMouseDown = isMouseIn = mouseDown = undefined; + threshold = 1; + $(this.browser.$root).on('mousedown' + this.namespace, function (e) { + isMouseIn = true; + mouseDown = translateMouseCoordinates$1(e, self.viewport.$viewport.get(0)).x; + + if (true === isMouseDown) { + self.$rulerSweeper.show(); + width = threshold; + left = mouseDown; + self.$rulerSweeper.css({ + left: left + 'px' + }); + self.$rulerSweeper.width(width); + } + }); + $(this.browser.$root).on('mousemove' + this.namespace, function (e) { + var mouseCurrent; + + if (isMouseDown && isMouseIn) { + mouseCurrent = translateMouseCoordinates$1(e, self.viewport.$viewport.get(0)).x; + mouseCurrent = Math.min(mouseCurrent, self.viewport.$viewport.width()); + mouseCurrent = Math.max(mouseCurrent, 0); + dx = mouseCurrent - mouseDown; + width = Math.abs(dx); + self.$rulerSweeper.width(width); + + if (dx < 0) { + left = mouseDown + dx; + self.$rulerSweeper.css({ + left: left + 'px' + }); + } + } + }); + $(this.browser.$root).on('mouseup' + this.namespace, function (e) { + let extent; + + if (true === isMouseDown && true === isMouseIn) { + isMouseDown = isMouseIn = undefined; + self.$rulerSweeper.hide(); + extent = {}; + extent.start = self.bp(left); + extent.end = self.bp(left + width); + + if (width > threshold) { + validateLocusExtent(browser.genome.getChromosome(self.viewport.referenceFrame.chr).bpLength, extent, browser.minimumBases()); + self.viewport.referenceFrame.bpPerPixel = (Math.round(extent.end) - Math.round(extent.start)) / self.viewport.$viewport.width(); + self.viewport.referenceFrame.start = Math.round(extent.start); + self.viewport.referenceFrame.initialEnd = Math.round(extent.end); + browser.updateViews(self.viewport.referenceFrame); + } + } + }); + this.viewport.$viewport.on('mousedown' + this.namespace, function (e) { + isMouseDown = true; + }); + } + + dispose() { + this.disableMouseHandlers(); + } + + bp(pixel) { + return this.viewport.referenceFrame.start + pixel * this.viewport.referenceFrame.bpPerPixel; + } + + } + + let timer; + let currentViewport = undefined; + const toolTipTimeout = 1e4; + + class RulerViewport extends ViewPort { + constructor(trackView, $viewportContainer, referenceFrame, width) { + super(trackView, $viewportContainer, referenceFrame, width); + } + + initializationHelper() { + this.rulerSweeper = new RulerSweeper(this); + appendMultiPanelCloseButton(this.browser, this.$viewport, this.referenceFrame); + this.$rulerLabel = $('
'); + this.$content.append(this.$rulerLabel); + this.$rulerLabel.click(() => this.browser.selectMultiLocusPanelWithReferenceFrame(this.referenceFrame)); + this.$tooltip = $('
', { + class: 'igv-ruler-tooltip' + }); + this.$tooltip.height(this.$viewport.height()); + this.$viewport.append(this.$tooltip); + this.$tooltipContent = $('
'); + this.$tooltip.append(this.$tooltipContent); + this.attachMouseHandlers(GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)); + this.$tooltip.hide(); + } + + updateLocusLabel() { + const str = this.referenceFrame.presentLocus(this.$viewport.width()); + this.$rulerLabel.text(str); + } + + 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 + } = translateMouseCoordinates$1(e, this.$viewport.get(0)); + const bp = Math.round(this.referenceFrame.start + this.referenceFrame.toBP(pixel)); + let searchString; + const { + chr + } = this.browser.genome.getChromosomeCoordinate(bp); + + if (1 === this.browser.referenceFrameList.length) { + searchString = chr; + } else { + let loci = this.browser.referenceFrameList.map(({ + locusSearchString + }) => locusSearchString); + loci[index] = chr; + searchString = loci.join(' '); + } + + this.browser.search(searchString); + }); + } + } + + mouseMove(event) { + if (true === this.browser.cursorGuideVisible) { + if (undefined === currentViewport) { + currentViewport = this; + this.$tooltip.show(); + } else if (currentViewport.guid !== this.guid) { + currentViewport.$tooltip.hide(); + this.$tooltip.show(); + currentViewport = this; + } else { + this.$tooltip.show(); + } + + const isWholeGenome = this.browser.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr); + + if (isWholeGenome) { + this.$tooltip.hide(); + return; + } + + const { + x + } = translateMouseCoordinates$1(event, this.$viewport.get(0)); + const { + start, + bpPerPixel + } = this.referenceFrame; + const bp = Math.round(0.5 + start + Math.max(0, x) * bpPerPixel); + this.$tooltipContent.text(numberFormatter$1(bp)); + const { + width: ww + } = this.$tooltipContent.get(0).getBoundingClientRect(); + const { + width: w + } = this.$viewport.get(0).getBoundingClientRect(); + this.$tooltip.css({ + left: `${IGVMath.clamp(x, 0, w - ww)}px` + }); // hide tooltip when movement stops + + clearTimeout(timer); + timer = setTimeout(() => this.$tooltip.hide(), toolTipTimeout); + } + } + + } + + function appendMultiPanelCloseButton(browser, $viewport, referenceFrame) { + $viewport.addClass('igv-viewport-ruler'); + const $close = $('
'); + $viewport.append($close); + $close.append(createIcon$1("times-circle")); + $close.click(() => browser.removeMultiLocusPanelWithReferenceFrame(referenceFrame, true)); + } + + const createViewport = (trackView, referenceFrameList, index, width) => { + if ('ruler' === trackView.track.type) { + return new RulerViewport(trackView, trackView.$viewportContainer, referenceFrameList[index], width); + } else if ('ideogram' === trackView.track.type) { + return new IdeogramViewport(trackView, trackView.$viewportContainer, referenceFrameList[index], width); + } else { + return new ViewPort(trackView, trackView.$viewportContainer, referenceFrameList[index], width); + } + }; + + /* + * 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'; + } + + updateLocusLabel() { + for (let viewport of this.trackView.viewports) { + viewport.updateLocusLabel(); + } + } + + async getFeatures(chr, start, end) { + return []; + } + + computePixelHeight(ignore) { + return this.height; + } + + draw(options) { + if (GenomeUtils.isWholeGenomeView(options.referenceFrame.chr)) { + options.viewport.rulerSweeper.disableMouseHandlers(); + this.drawWholeGenome(options); + } else { + options.viewport.rulerSweeper.addMouseHandlers(); + const tickHeight = 6; + const shim = 2; + const pixelWidthBP = 1 + Math.floor(options.referenceFrame.toBP(options.pixelWidth)); + const tick = new Tick(pixelWidthBP, options); + tick.drawTicks(options, tickHeight, shim, this.height); + IGVGraphics.strokeLine(options.context, 0, this.height - shim, options.pixelWidth, this.height - shim); + } + } + + drawWholeGenome(options) { + options.context.save(); + IGVGraphics.fillRect(options.context, 0, 0, options.pixelWidth, options.pixelHeight, { + 'fillStyle': 'white' + }); + let y = 0; + let h = options.pixelHeight; + + 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 / options.bpPerPixel); + let w = Math.round(wBP / options.bpPerPixel); + this.renderChromosomeRect(options.context, x, y, w, h, name); + } + + options.context.restore(); + } + + renderChromosomeRect(ctx, x, y, w, h, name) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.font = '12px sans-serif'; // IGVGraphics.fillRect(ctx, x, y, w, h, { 'fillStyle' : toggleColor(this.browser.genome.wgChromosomeNames.indexOf(name)) }); + + 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) + }); + } + } + + supportsWholeGenome() { + return true; + } + + dispose() {// do stuff + } + + } + + class Tick { + constructor(pixelWidthBP, options) { + initialize.call(this, pixelWidthBP, options); + + function initialize(pixelWidthBP, options) { + var numberOfZeroes, majorUnit, unitMultiplier, numberOfMajorTicks, str; + const isSVGContext = options.context.isSVG || false; + + if (pixelWidthBP < 10) { + set.call(this, 1, "bp", 1, isSVGContext); + } + + numberOfZeroes = Math.floor(Math.log10(pixelWidthBP)); + + if (numberOfZeroes > 9) { + majorUnit = "gb"; + unitMultiplier = 1e9; + } else if (numberOfZeroes > 6) { + majorUnit = "mb"; + unitMultiplier = 1e6; + } else if (numberOfZeroes > 3) { + majorUnit = "kb"; + unitMultiplier = 1e3; + } else { + majorUnit = "bp"; + unitMultiplier = 1; + } + + str = numberFormatter(Math.floor(pixelWidthBP / unitMultiplier)) + " " + majorUnit; + this.labelWidthBP = Math.round(options.referenceFrame.toBP(options.context.measureText(str).width)); + numberOfMajorTicks = pixelWidthBP / Math.pow(10, numberOfZeroes - 1); + + if (numberOfMajorTicks < 25) { + set.call(this, Math.pow(10, numberOfZeroes - 1), majorUnit, unitMultiplier, isSVGContext); + } else { + set.call(this, Math.pow(10, numberOfZeroes) / 2, majorUnit, unitMultiplier, isSVGContext); + } + } + + function set(majorTick, majorUnit, unitMultiplier, isSVGContext) { + // reduce label frequency by half for SVG rendering + this.majorTick = true === isSVGContext ? 2 * majorTick : majorTick; + this.majorUnit = majorUnit; + this.halfTick = majorTick / 2; + this.unitMultiplier = unitMultiplier; + } + } + + drawTicks(options, tickHeight, shim, height) { + var numberOfTicks, bp, pixel, label, labelWidth, labelX, numer, floored; + numberOfTicks = Math.floor(options.bpStart / this.majorTick) - 1; + labelWidth = 0; + labelX = 0; + pixel = 0; + + while (pixel < options.pixelWidth) { + bp = Math.floor(numberOfTicks * this.majorTick); + pixel = Math.round(options.referenceFrame.toPixels(bp - 1 - options.bpStart + 0.5)); + label = numberFormatter(Math.floor(bp / this.unitMultiplier)) + " " + this.majorUnit; + labelWidth = options.context.measureText(label).width; + labelX = Math.round(pixel - labelWidth / 2); + IGVGraphics.fillText(options.context, label, labelX, height - tickHeight / 0.75); + IGVGraphics.strokeLine(options.context, pixel, height - tickHeight, pixel, height - shim); + ++numberOfTicks; + } + + numberOfTicks = Math.floor(options.bpStart / this.halfTick) - 1; + pixel = 0; + + while (pixel < options.pixelWidth) { + bp = Math.floor(numberOfTicks * this.halfTick); + pixel = Math.round(options.referenceFrame.toPixels(bp - 1 - options.bpStart + 0.5)); + numer = bp / this.unitMultiplier; + floored = Math.floor(numer); + + if (numer === floored && this.majorTick / this.labelWidthBP > 8) { + label = numberFormatter(Math.floor(numer)) + " " + this.majorUnit; + labelWidth = options.context.measureText(label).width; + labelX = pixel - labelWidth / 2; + IGVGraphics.fillText(options.context, label, labelX, height - tickHeight / 0.75); + } + + IGVGraphics.strokeLine(options.context, pixel, height - tickHeight, pixel, height - shim); + ++numberOfTicks; + } + } + + description(blurb) {} + + } + + const appleCrayonRGBPalette = { + cantaloupe: { + r: 255, + g: 206, + b: 110 + }, + honeydew: { + r: 206, + g: 250, + b: 110 + }, + spindrift: { + r: 104, + g: 251, + b: 208 + }, + sky: { + r: 106, + g: 207, + b: 255 + }, + lavender: { + r: 210, + g: 120, + b: 255 + }, + carnation: { + r: 255, + g: 127, + b: 211 + }, + licorice: { + r: 0, + g: 0, + b: 0 + }, + snow: { + r: 255, + g: 255, + b: 255 + }, + salmon: { + r: 255, + g: 114, + b: 110 + }, + banana: { + r: 255, + g: 251, + b: 109 + }, + flora: { + r: 104, + g: 249, + b: 110 + }, + ice: { + r: 104, + g: 253, + b: 255 + }, + orchid: { + r: 110, + g: 118, + b: 255 + }, + bubblegum: { + r: 255, + g: 122, + b: 255 + }, + lead: { + r: 30, + g: 30, + b: 30 + }, + mercury: { + r: 232, + g: 232, + b: 232 + }, + tangerine: { + r: 255, + g: 136, + b: 2 + }, + lime: { + r: 131, + g: 249, + b: 2 + }, + sea_foam: { + r: 3, + g: 249, + b: 135 + }, + aqua: { + r: 0, + g: 140, + b: 255 + }, + grape: { + r: 137, + g: 49, + b: 255 + }, + strawberry: { + r: 255, + g: 41, + b: 135 + }, + tungsten: { + r: 58, + g: 58, + b: 58 + }, + silver: { + r: 208, + g: 208, + b: 208 + }, + maraschino: { + r: 255, + g: 33, + b: 1 + }, + lemon: { + r: 255, + g: 250, + b: 3 + }, + spring: { + r: 5, + g: 248, + b: 2 + }, + turquoise: { + r: 0, + g: 253, + b: 255 + }, + blueberry: { + r: 0, + g: 46, + b: 255 + }, + magenta: { + r: 255, + g: 57, + b: 255 + }, + iron: { + r: 84, + g: 84, + b: 83 + }, + magnesium: { + r: 184, + g: 184, + b: 184 + }, + mocha: { + r: 137, + g: 72, + b: 0 + }, + fern: { + r: 69, + g: 132, + b: 1 + }, + moss: { + r: 1, + g: 132, + b: 72 + }, + ocean: { + r: 0, + g: 74, + b: 136 + }, + eggplant: { + r: 73, + g: 26, + b: 136 + }, + maroon: { + r: 137, + g: 22, + b: 72 + }, + steel: { + r: 110, + g: 110, + b: 110 + }, + aluminum: { + r: 160, + g: 159, + b: 160 + }, + cayenne: { + r: 137, + g: 17, + b: 0 + }, + aspargus: { + r: 136, + g: 133, + b: 1 + }, + clover: { + r: 2, + g: 132, + b: 1 + }, + teal: { + r: 0, + g: 134, + b: 136 + }, + midnight: { + r: 0, + g: 24, + b: 136 + }, + plum: { + r: 137, + g: 30, + b: 136 + }, + tin: { + r: 135, + g: 134, + b: 135 + }, + nickel: { + r: 136, + g: 135, + b: 135 + } + }; + + function appleCrayonRGB(name) { + const { + r, + g, + b + } = appleCrayonRGBPalette[name]; + return `rgb(${r},${g},${b})`; + } + + const colorPalettes = { + Set1: ["rgb(228,26,28)", "rgb(55,126,184)", "rgb(77,175,74)", "rgb(166,86,40)", "rgb(152,78,163)", "rgb(255,127,0)", "rgb(247,129,191)", "rgb(153,153,153)", "rgb(255,255,51)"], + Dark2: ["rgb(27,158,119)", "rgb(217,95,2)", "rgb(117,112,179)", "rgb(231,41,138)", "rgb(102,166,30)", "rgb(230,171,2)", "rgb(166,118,29)", "rgb(102,102,102)"], + Set2: ["rgb(102, 194,165)", "rgb(252,141,98)", "rgb(141,160,203)", "rgb(231,138,195)", "rgb(166,216,84)", "rgb(255,217,47)", "rgb(229,196,148)", "rgb(179,179,179)"], + Set3: ["rgb(141,211,199)", "rgb(255,255,179)", "rgb(190,186,218)", "rgb(251,128,114)", "rgb(128,177,211)", "rgb(253,180,98)", "rgb(179,222,105)", "rgb(252,205,229)", "rgb(217,217,217)", "rgb(188,128,189)", "rgb(204,235,197)", "rgb(255,237,111)"], + Pastel1: ["rgb(251,180,174)", "rgb(179,205,227)", "rgb(204,235,197)", "rgb(222,203,228)", "rgb(254,217,166)", "rgb(255,255,204)", "rgb(229,216,189)", "rgb(253,218,236)"], + Pastel2: ["rgb(173,226,207)", "rgb(253,205,172)", "rgb(203,213,232)", "rgb(244,202,228)", "rgb(230,245,201)", "rgb(255,242,174)", "rgb(243,225,206)"], + Accent: ["rgb(127,201,127)", "rgb(190,174,212)", "rgb(253,192,134)", "rgb(255,255,153)", "rgb(56,108,176)", "rgb(240,2,127)", "rgb(191,91,23)"] + }; + + class PaletteColorTable { + constructor(palette) { + this.colors = colorPalettes[palette]; + if (!Array.isArray(this.colors)) this.colors = []; + this.colorTable = {}; + this.nextIdx = 0; + this.colorGenerator = new RandomColorGenerator(); + } + + getColor(key) { + if (!this.colorTable.hasOwnProperty(key)) { + if (this.nextIdx < this.colors.length) { + this.colorTable[key] = this.colors[this.nextIdx]; + } else { + this.colorTable[key] = this.colorGenerator.get(); + } + + this.nextIdx++; + } + + return this.colorTable[key]; + } + + } + + class ColorTable { + constructor(colors) { + this.colorTable = colors || {}; + this.nextIdx = 0; + this.colorGenerator = new RandomColorGenerator(); + } + + getColor(key) { + if (!this.colorTable.hasOwnProperty(key)) { + if (this.colorTable.hasOwnProperty("*")) { + return this.colorTable["*"]; + } + + this.colorTable[key] = this.colorGenerator.get(); + } + + return this.colorTable[key]; + } + + } // Random color generator from https://github.com/sterlingwes/RandomColor/blob/master/rcolor.js + // Free to use & distribute under the MIT license + // Wes Johnson (@SterlingWes) + // + // inspired by http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/ + + + function RandomColorGenerator() { + this.hue = Math.random(); + this.goldenRatio = 0.618033988749895; + this.hexwidth = 2; + } + + RandomColorGenerator.prototype.hsvToRgb = function (h, s, v) { + var h_i = Math.floor(h * 6), + f = h * 6 - h_i, + p = v * (1 - s), + q = v * (1 - f * s), + t = v * (1 - (1 - f) * s), + r = 255, + g = 255, + b = 255; + + switch (h_i) { + case 0: + r = v, g = t, b = p; + break; + + case 1: + r = q, g = v, b = p; + break; + + case 2: + r = p, g = v, b = t; + break; + + case 3: + r = p, g = q, b = v; + break; + + case 4: + r = t, g = p, b = v; + break; + + case 5: + r = v, g = p, b = q; + break; + } + + return [Math.floor(r * 256), Math.floor(g * 256), Math.floor(b * 256)]; + }; + + RandomColorGenerator.prototype.padHex = function (str) { + if (str.length > this.hexwidth) return str; + return new Array(this.hexwidth - str.length + 1).join('0') + str; + }; + + RandomColorGenerator.prototype.get = function (saturation, value) { + this.hue += this.goldenRatio; + this.hue %= 1; + if (typeof saturation !== "number") saturation = 0.5; + if (typeof value !== "number") value = 0.95; + var rgb = this.hsvToRgb(this.hue, saturation, value); + return "#" + this.padHex(rgb[0].toString(16)) + this.padHex(rgb[1].toString(16)) + this.padHex(rgb[2].toString(16)); + }; + + const randomColorGenerator = new RandomColorGenerator(); + + function randomColor() { + return randomColorGenerator.get(); + } // Returns a random number between min (inclusive) and max (exclusive) + + const maxFontSize = 10; + const fontConfigureTemplate = { + // font: '2pt sans-serif', + textAlign: 'start', + textBaseline: 'bottom', + strokeStyle: 'black', + fillStyle: 'black' + }; + + class SampleNameViewport { + constructor(trackView, $viewportContainer, referenceFrame, width) { + this.guid = guid(); + this.trackView = trackView; + this.referenceFrame = referenceFrame; + this.browser = trackView.browser; + this.$viewport = $('
'); + $viewportContainer.append(this.$viewport); + this.$canvas = $(''); + this.$viewport.append(this.$canvas); + this.canvas = this.$canvas.get(0); + this.ctx = this.canvas.getContext("2d"); + this.contentTop = 0; + this.setWidth(width); + this.$viewport.get(0).addEventListener('contextmenu', e => { + e.preventDefault(); + e.stopPropagation(); + const config = { + label: 'Name Panel Width', + value: this.browser.sampleNameViewportWidth, + callback: width => { + this.browser.sampleNameViewportWidth = width; + + for (let { + sampleNameViewport + } of this.browser.trackViews) { + sampleNameViewport.setWidth(this.browser.sampleNameViewportWidth); + } + + this.browser.resize(); + } + }; + this.browser.inputDialog.present(config, e); + }); + } + + checkCanvas() { + const dpi = window.devicePixelRatio; + const requiredHeight = this.$viewport.height(); + 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.width(width); + this.checkCanvas(); + } + + show() { + this.$viewport.show(); + } + + hide() { + this.$viewport.hide(); + } + + 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.get(0).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.toUpperCase(); + 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.get(0).getBoundingClientRect(); + const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, ''); + const id = `${str}_sample_names_guid_${guid()}`; + context.saveWithTranslationAndClipRect(id, deltaX, deltaY + yScrollDelta, width, height, -yScrollDelta); + this.draw({ + context, + samples + }); + context.restore(); + } + } + + addMouseHandler(context, pixelTop, samples) { + this.canvas.addEventListener('click', e => { + if ('block' === this.hover.style.display) { + this.hover.style.display = 'none'; + this.hover.textContent = ''; + } else { + const { + currentTarget, + clientY + } = e; + const { + y: target_bbox_min_y + } = currentTarget.getBoundingClientRect(); + const y = clientY - target_bbox_min_y + pixelTop; + let yMin = 0; + + for (let name of samples.names) { + const yMax = getYFont(context, name.toUpperCase(), yMin, samples.height); + + if (y < yMin || y > yMax) ; else { + this.hover.style.top = `${yMin + this.contentTop}px`; + this.hover.style.right = '0px'; + this.hover.textContent = name.toUpperCase(); + this.hover.style.display = 'block'; + } + + yMin += samples.height; + } + } + }); + this.canvas.addEventListener('mouseleave', () => { + this.hover.style.display = 'none'; + this.hover.textContent = ''; + }); + } + + } + + function getYFont(context, text, y, height) { + return y + height - getSampleNameYShim(context, text, height); + } + + function getSampleNameYShim(context, text, h) { + const { + fontBoundingBoxAscent, + fontBoundingBoxDescent + } = context.measureText(text); + return (h - (fontBoundingBoxAscent + fontBoundingBoxDescent)) / 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 namespace = '.trackscrollbar' + guid(); + + class TrackScrollbar { + constructor($viewportContainer, viewports, sampleNameViewport) { + let lastY; // Define mouse events first, use arrow function so "this" is in scope + + const mouseMove = event => { + event.preventDefault(); + event.stopPropagation(); + const page = pageCoordinates$1(event); + this.moveScrollerBy(page.y - lastY); + lastY = page.y; + }; + + const mouseUp = event => { + $(document).off(this.namespace); + }; + + const mouseDown = event => { + event.preventDefault(); + const page = pageCoordinates$1(event); + lastY = page.y; + $(document).on('mousemove' + namespace, mouseMove); + $(document).on('mouseup' + namespace, mouseUp); + $(document).on('mouseleave' + namespace, mouseUp); // prevents start of horizontal track panning) + + event.stopPropagation(); + }; + + this.namespace = namespace; + this.$outerScroll = $('
'); + this.$innerScroll = $('
'); + this.$outerScroll.append(this.$innerScroll); + this.$viewportContainer = $viewportContainer; + this.viewports = viewports; + this.sampleNameViewport = sampleNameViewport; + this.$innerScroll.on("mousedown", mouseDown); + this.$innerScroll.on("click", event => { + event.stopPropagation(); + }); + this.$outerScroll.on("click", event => { + this.moveScrollerBy(event.offsetY - this.$innerScroll.height() / 2); + event.stopPropagation(); + }); + } + + disableMouseHandlers() { + $(document).off(namespace); + this.$innerScroll.off(); + this.$outerScroll.off(); + } + + moveScrollerBy(delta) { + const y = this.$innerScroll.position().top + delta; + this.moveScrollerTo(y); + } + + moveScrollerTo(y) { + const outerScrollHeight = this.$outerScroll.height(); + const innerScrollHeight = this.$innerScroll.height(); + const newTop = Math.min(Math.max(0, y), outerScrollHeight - innerScrollHeight); + const contentDivHeight = maxViewportContentHeight(this.viewports); + const contentTop = -Math.round(newTop * (contentDivHeight / this.$viewportContainer.height())); + this.$innerScroll.css("top", newTop + "px"); + + for (let viewport of [...this.viewports, this.sampleNameViewport]) { + viewport.setTop(contentTop); + } + } + + dispose() { + $(window).off(this.namespace); + this.$innerScroll.off(); + } + + update() { + const viewportContainerHeight = this.$viewportContainer.height(); + const viewportContentHeight = maxViewportContentHeight(this.viewports); + const innerScrollHeight = Math.round(viewportContainerHeight / viewportContentHeight * viewportContainerHeight); + + if (viewportContentHeight > viewportContainerHeight) { + this.$innerScroll.show(); + this.$innerScroll.height(innerScrollHeight); + } else { + this.$innerScroll.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. + */ + 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(); + } + + class SequenceTrack { + constructor(config, browser) { + this.type = "sequence"; + this.browser = browser; + this.removable = false; + this.config = config; + this.name = "Sequence"; + this.id = "sequence"; + this.sequenceType = config.sequenceType || "dna"; // dna | rna | prot + + this.height = 25; + this.disableButtons = false; + this.order = config.order || defaultSequenceTrackOrder; + this.ignoreTrackMenu = false; + this.reversed = false; + this.frameTranslate = false; + } + + 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(115); + } + + this.trackView.setTrackHeight(115); + } else { + for (let vp of this.trackView.viewports) { + vp.setContentHeight(25); + } + + this.trackView.setTrackHeight(25); + } + + this.trackView.repaintViews(); + } + }]; + } + + contextMenuItemList(clickState) { + const viewport = clickState.viewport; + + if (viewport.referenceFrame.bpPerPixel <= 1) { + return [{ + label: 'Copy visible sequence...', + click: async () => { + const pixelWidth = viewport.getWidth(); + const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel; + const chr = viewport.referenceFrame.chr; + const start = viewport.referenceFrame.start; + const end = start + bpWindow; + const sequence = await this.browser.genome.sequence.getSequence(chr, start, end); + + if (viewport.trackView.alert) { + viewport.trackView.alert.present(sequence); + } else { + Alert.presentAlert(sequence); + } + } + }, '
']; + } else { + return undefined; + } + } + + translateSequence(seq) { + const threeFrame = [[], [], []]; + + for (let fNum of [0, 1, 2]) { + let idx = fNum; + + while (seq.length - idx >= 3) { + let st = seq.slice(idx, idx + 3); + + if (this.reversed) { + st = st.split('').reverse().join(''); + } + + const aa = translationDict[st.toUpperCase()] || ""; + threeFrame[fNum].push({ + codons: st, + aminoA: aa + }); + idx += 3; + } + } + + return threeFrame; + } + + async getFeatures(chr, start, end, bpPerPixel) { + if (bpPerPixel && bpPerPixel > 1) { + return null; + } else { + const sequence = await this.browser.genome.sequence.getSequence(chr, start, end); + return { + bpStart: start, + sequence: sequence + }; + } + } + + draw(options) { + const ctx = options.context; + + if (options.features) { + const sequence = options.features.sequence; + const sequenceBpStart = options.features.bpStart; + const bpEnd = 1 + options.bpStart + options.pixelWidth * options.bpPerPixel; + let height = 15; + + for (let bp = sequenceBpStart; bp <= bpEnd; bp++) { + let seqOffsetBp = Math.floor(bp - sequenceBpStart); + + if (seqOffsetBp < sequence.length) { + let letter = sequence[seqOffsetBp]; + + if (this.reversed) { + letter = complement[letter] || ""; + } + + let offsetBP = bp - options.bpStart; + let aPixel = offsetBP / options.bpPerPixel; + let bPixel = (offsetBP + 1) / options.bpPerPixel; + let color = this.fillColor(letter); + + if (options.bpPerPixel > 1 / 10) { + IGVGraphics.fillRect(ctx, aPixel, 5, bPixel - aPixel, height - 5, { + fillStyle: color + }); + } else { + let xPixel = 0.5 * (aPixel + bPixel - ctx.measureText(letter).width); + IGVGraphics.strokeText(ctx, letter, xPixel, height, { + strokeStyle: color + }); + } + } + } + + if (this.frameTranslate) { + let transSeq; + + if (this.reversed) { + transSeq = sequence.split('').map(function (cv) { + return complement[cv]; + }); + transSeq = transSeq.join(''); + } else { + transSeq = sequence; + } + + let y = height; + let translatedSequence = this.translateSequence(transSeq); + + for (let arr of translatedSequence) { + let i = translatedSequence.indexOf(arr); + let fNum = i; + let h = 25; + y = i === 0 ? y + 10 : y + 30; //Little less room at first. + + for (let cv of arr) { + let aaS; + let idx = arr.indexOf(cv); + let xSeed = idx + fNum + 2 * idx; + let color = 0 === idx % 2 ? 'rgb(160,160,160)' : 'rgb(224,224,224)'; + let p0 = Math.floor(xSeed / options.bpPerPixel); + let p1 = Math.floor((xSeed + 3) / options.bpPerPixel); + let pc = Math.round((p0 + p1) / 2); + + if (cv.aminoA.indexOf('STOP') > -1) { + color = 'rgb(255, 0, 0)'; + aaS = 'STOP'; //Color blind accessible + } else { + aaS = cv.aminoA; + } + + if (cv.aminoA === 'M') { + color = 'rgb(0, 153, 0)'; + aaS = 'START'; //Color blind accessible + } + + IGVGraphics.fillRect(ctx, p0, y, p1 - p0, h, { + fillStyle: color + }); + + if (options.bpPerPixel <= 1 / 10) { + IGVGraphics.strokeText(ctx, aaS, pc - ctx.measureText(aaS).width / 2, y + 15); + } + } + } + } + } + } + + supportsWholeGenome() { + return false; + } + + computePixelHeight(ignore) { + return this.height; + } + + fillColor(index) { + if (this.color) { + return this.color; + } else if ("dna" === this.sequenceType) { + return this.browser.nucleotideColors[index] || 'gray'; + } else { + return 'rgb(0, 0, 150)'; + } + } + + } + + /* + * 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 axisContainerWidth = 50; + + class TrackView { + constructor(browser, $container, track) { + this.browser = browser; + this.track = track; + track.trackView = this; + const $track = $('
'); + this.trackDiv = $track.get(0); + $container.append($track); + this.namespace = '.trackview_' + guid(); + + if (track instanceof RulerTrack) { + this.trackDiv.dataset.rulerTrack = "rulerTrack"; + } // Create an alert dialog for the sequence track to copy ref sequence to. Obviously a little hacky. + + + if (track instanceof SequenceTrack) { + this.alert = new AlertDialog(this.trackDiv); + } + + if (track.height) { + this.trackDiv.style.height = track.height + "px"; + } + + this.$viewportContainer = $('
'); + $track.append(this.$viewportContainer); // add axis to viewportContainer + + this.$axis = this.createAxis(this.$viewportContainer); // add viewport(s) to viewportContainer. One viewport per reference frame + + this.populateViewportContainer(browser, browser.referenceFrameList); // Track drag handle + + if ('ideogram' === track.type || 'ruler' === track.type) ; else { + this.attachDragWidget($track, this.$viewportContainer); + } // right hand gutter + + + if (true === track.ignoreTrackMenu) ; else { + this.appendRightHandGutter($track); + } // color picker + + + const trackColors = []; + const color = track.color || track.defaultColor; + + if (isString$6(color)) { + trackColors.push(color); + } + + if (track.altColor && isString$6(track.altColor)) { + trackColors.push(track.altColor); + } + + const defaultColors = trackColors.map(c => { + return c.startsWith("#") ? c : c.startsWith("rgb(") ? IGVColor.rgbToHex(c) : IGVColor.colorNameToHex(c); + }); + const options = { + parent: this.trackDiv, + top: undefined, + left: undefined, + width: 432, + height: undefined, + defaultColors, + colorHandler: color => { + this.track.color = color; + this.repaintViews(); + } + }; + this.colorPicker = new ColorPicker(options); // alt color picker -- TODO pass handler in at "show" time and use 1 color picker + + options.colorHandler = color => { + this.track.altColor = color; + this.repaintViews(); + }; + + this.altColorPicker = new ColorPicker(options); + } + + populateViewportContainer(browser, referenceFrameList) { + const width = browser.computeViewportWidth(referenceFrameList.length, browser.getViewportContainerWidth()); + this.viewports = []; + + for (let referenceFrame of referenceFrameList) { + const viewport = createViewport(this, referenceFrameList, referenceFrameList.indexOf(referenceFrame), width); + this.viewports.push(viewport); + } + + this.sampleNameViewport = new SampleNameViewport(this, this.$viewportContainer, undefined, browser.sampleNameViewportWidth); + + if (false === browser.showSampleNames) { + this.sampleNameViewport.$viewport.hide(); + } else { + this.sampleNameViewport.$viewport.show(); + } + + updateViewportShims(this.viewports, this.$viewportContainer); + this.updateViewportForMultiLocus(); + this.attachScrollbar($(this.trackDiv), this.$viewportContainer, this.viewports, this.sampleNameViewport); + } + + renderSVGContext(context, { + deltaX, + deltaY + }) { + renderSVGAxis(context, this.track, this.axisCanvas, deltaX, deltaY); + const { + width: axisWidth + } = this.$axis.get(0).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); + } + } + + attachScrollbar($track, $viewportContainer, viewports, sampleNameViewport) { + if ("hidden" === $viewportContainer.find('.igv-viewport').css("overflow-y")) { + this.scrollbar = new TrackScrollbar($viewportContainer, viewports, sampleNameViewport); + this.scrollbar.$outerScroll.insertAfter($viewportContainer); + + if ('ruler' === this.track.type || 'ideogram' === this.track.type) { + this.scrollbar.disableMouseHandlers(); + } + } + } + + removeViewportForReferenceFrame(referenceFrame) { + let index = -1; + + for (let i = 0; i < this.viewports.length; i++) { + if (this.viewports[i].referenceFrame === referenceFrame) { + index = i; + break; + } + } + + if (index >= 0) { + this.viewports[index].$viewport.remove(); + this.viewports.splice(index, 1); + this.updateViewportForMultiLocus(); + } + } + + updateViewportForMultiLocus() { + if ('ruler' === this.track.type) { + if (this.viewports.length > 1) { + this.$viewportContainer.find('.igv-multi-locus-panel-close-container').show(); + this.$viewportContainer.find('.igv-multi-locus-panel-label-div').show(); + this.track.updateLocusLabel(); + } else { + this.$viewportContainer.find('.igv-multi-locus-panel-close-container').hide(); + this.$viewportContainer.find('.igv-multi-locus-panel-label-div').hide(); + } + } + } + + createAxis($viewportContainer) { + const $axis = $('
'); + $viewportContainer.append($axis); + + if (typeof this.track.paintAxis === 'function') { + if (this.track.dataRange) { + $axis.click(() => { + this.browser.dataRangeDialog.configure(this); + this.browser.dataRangeDialog.present($(this.trackDiv)); + }); + $axis.addClass('igv-clickable'); + } + + this.resizeAxisCanvas($axis, $axis.outerWidth(), $axis.outerHeight()); + } + + return $axis; + } + + 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(option) { + if (option === "altColor") { + this.altColorPicker.show(); + } else { + this.colorPicker.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.track.config.height = newHeight; + $(this.trackDiv).height(newHeight); // If the track does not manage its own content height set it here + + if (typeof this.track.computePixelHeight !== "function") { + for (let vp of this.viewports) { + vp.setContentHeight(newHeight); + } + } + + this.repaintViews(); + this.resizeAxisCanvas(this.$axis, this.$axis.outerWidth(), newHeight); + + if (this.track.paintAxis) { + this.track.paintAxis(this.axisCanvasContext, this.axisCanvasContext.canvas.width, this.axisCanvasContext.canvas.height); + } + + if (this.scrollbar) { + this.scrollbar.update(); + } + } + + isLoading() { + for (let i = 0; i < this.viewports.length; i++) { + if (this.viewports[i].isLoading()) return true; + } + } + + resize(viewportWidth) { + for (let viewport of this.viewports) { + viewport.setWidth(viewportWidth); + } + + this.resizeAxisCanvas(this.$axis, this.$axis.outerWidth(), this.$axis.outerHeight()); + this.updateViews(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) { + viewport.repaint(); + } + + if (this.track.paintAxis) { + this.track.paintAxis(this.axisCanvasContext, this.axisCanvasContext.canvas.width, this.axisCanvasContext.canvas.height); + } // Repaint sample names last + + + this.repaintSamples(); + } + + repaintSamples() { + if (typeof this.track.getSamples === 'function') { + const samples = this.track.getSamples(); + this.sampleNameViewport.repaint(samples); + } + } + /** + * Update viewports to reflect current genomic state, possibly loading additional data. + */ + + + async updateViews(force) { + if (!(this.browser && this.browser.referenceFrameList)) return; + const visibleViewports = this.viewports.filter(vp => vp.isVisible()); // Shift viewports left/right to current genomic state (pans canvas) + + visibleViewports.forEach(function (viewport) { + viewport.shift(); + }); // rpv: viewports whose image (canvas) does not fully cover current genomic range + + const rpV = this.viewportsToReload(force); // Trigger viewport to load features needed to cover current genomic range + + for (let vp of rpV) { + await vp.loadFeatures(); + } // Very 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. + + + if (typeof this.track.variantRowCount === 'function') { + let maxRow = 0; + + for (let vp of this.viewports) { + if (vp.tile && vp.tile.features) { + maxRow = Math.max(maxRow, vp.tile.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 vp of this.viewports) { + vp.checkContentHeight(); + } + } + } + + if (this.disposed) return; // Track was removed during load + + const isDragging = this.browser.dragObject; + + if (!isDragging && this.track.autoscale) { + let allFeatures = []; + + for (let vp of visibleViewports) { + const referenceFrame = vp.referenceFrame; + const start = referenceFrame.start; + const end = start + referenceFrame.toBP($(vp.contentDiv).width()); + + if (vp.tile && vp.tile.features) { + if (typeof vp.tile.features.getMax === 'function') { + const max = vp.tile.features.getMax(start, end); + allFeatures.push({ + value: max + }); + } else { + allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.tile.features, start, end)); + } + } + } + + if (typeof this.track.doAutoscale === 'function') { + this.track.dataRange = this.track.doAutoscale(allFeatures); + } else { + this.track.dataRange = doAutoscale(allFeatures); + } + } // Must repaint all viewports if autoscaling + + + if (!isDragging && (this.track.autoscale || this.track.autoscaleGroup)) { + for (let vp of visibleViewports) { + vp.repaint(); + } + } else { + for (let vp of rpV) { + vp.repaint(); + } + } + + this.adjustTrackHeight(); // Repaint sample names last + + this.repaintSamples(); + } + /** + * Return a promise to get all in-view features. Used for group autoscaling. + */ + + + async getInViewFeatures(force) { + if (!(this.browser && this.browser.referenceFrameList)) { + return []; + } // List of viewports that need reloading + + + const rpV = this.viewportsToReload(force); + const promises = rpV.map(function (vp) { + return vp.loadFeatures(); + }); + await Promise.all(promises); + let allFeatures = []; + + for (let vp of this.viewports) { + if (vp.tile && vp.tile.features) { + const referenceFrame = vp.referenceFrame; + const start = referenceFrame.start; + const end = start + referenceFrame.toBP($(vp.contentDiv).width()); + + if (typeof vp.tile.features.getMax === 'function') { + const max = vp.tile.features.getMax(start, end); + allFeatures.push({ + value: max + }); + } else { + allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.tile.features, start, end)); + } + } + } + + return allFeatures; + } + + checkContentHeight() { + for (let viewport of this.viewports) { + viewport.checkContentHeight(); + } + + this.adjustTrackHeight(); + } + + adjustTrackHeight() { + var maxHeight = maxViewportContentHeight(this.viewports); + + if (this.track.autoHeight) { + this.setTrackHeight(maxHeight, false); + } else if (this.track.paintAxis) { + // Avoid duplication, paintAxis is already called in setTrackHeight + this.track.paintAxis(this.axisCanvasContext, this.axisCanvasContext.canvas.width, this.axisCanvasContext.canvas.height); + } + + if (this.scrollbar) { + const currentTop = this.viewports[0].getContentTop(); + const viewports = [...this.viewports, this.sampleNameViewport]; + const heights = this.viewports.map(viewport => viewport.getContentHeight()); + const minContentHeight = Math.min(...heights); + const newTop = Math.min(0, this.$viewportContainer.height() - minContentHeight); + + if (currentTop < newTop) { + for (let viewport of viewports) { + viewport.$content.css("top", `${newTop}px`); + } + } + + this.scrollbar.update(); + } + } + + resizeAxisCanvas($axis, width, height) { + if ($axis) { + if (this.axisCanvas) { + $(this.axisCanvas).remove(); + } + + const $canvas = $(''); + $axis.append($canvas); + this.axisCanvas = $canvas.get(0); + this.axisCanvasContext = this.axisCanvas.getContext('2d'); + this.axisCanvas.style.height = `${height}px`; + this.axisCanvas.style.width = `${width}px`; + this.axisCanvas.height = window.devicePixelRatio * height; + this.axisCanvas.width = window.devicePixelRatio * width; + this.axisCanvasContext.scale(window.devicePixelRatio, window.devicePixelRatio); + } + } + + attachDragWidget($track, $viewportContainer) { + const self = this; + const browser = this.browser; + this.$trackDragScrim = $('
'); + $viewportContainer.append(this.$trackDragScrim); + this.$trackDragScrim.hide(); + self.$trackManipulationHandle = $('
'); + $track.append(self.$trackManipulationHandle); + self.$trackManipulationHandle.on('mousedown', function (e) { + e.preventDefault(); + e.stopPropagation(); + self.$trackDragScrim.show(); + browser.startTrackDrag(self); + }); + self.$trackManipulationHandle.on('mouseup', function (e) { + e.preventDefault(); + e.stopPropagation(); + browser.endTrackDrag(); + self.$trackDragScrim.hide(); + }); + $track.on('mouseenter', function (e) { + if (browser.dragTrack) { + e.preventDefault(); + e.stopPropagation(); + browser.updateTrackDrag(self); + } + }); + self.$trackManipulationHandle.on('mouseleave', function (e) { + if (!browser.dragTrack) { + e.preventDefault(); + e.stopPropagation(); + self.$trackDragScrim.hide(); + } + }); + } + + viewportsToReload(force) { + // List of viewports that need reloading + const rpV = this.viewports.filter(function (viewport) { + if (!viewport.isVisible()) { + return false; + } + + if (!viewport.checkZoomIn()) { + return false; + } else { + const referenceFrame = viewport.referenceFrame; + const chr = viewport.referenceFrame.chr; + const start = referenceFrame.start; + const end = start + referenceFrame.toBP($(viewport.contentDiv).width()); + const bpPerPixel = referenceFrame.bpPerPixel; + return force || !viewport.tile || viewport.tile.invalidate || !viewport.tile.containsRange(chr, start, end, bpPerPixel); + } + }); + return rpV; + } + /** + * Do any cleanup here + */ + + + dispose() { + const self = this; + + if (this.$trackManipulationHandle) { + this.$trackManipulationHandle.off(); + } + + if (this.scrollbar) { + this.scrollbar.dispose(); + } + + $(document).off(this.namespace); + + if (typeof this.track.dispose === "function") { + this.track.dispose(); + } + + var track = this.track; + + if (typeof track.dispose === 'function') { + track.dispose(); + } + + Object.keys(track).forEach(function (key) { + track[key] = undefined; + }); + this.viewports.forEach(function (viewport) { + viewport.dispose(); + }); + + Object.keys(this).forEach(function (key) { + self[key] = undefined; + }); + + if (this.alert) { + this.alert.container.remove(); // This is quite obviously a hack, need a "dispose" method on AlertDialog + } + + this.disposed = true; + } + + scrollBy(delta) { + this.scrollbar.moveScrollerBy(delta); + } + + appendRightHandGutter($parent) { + let $div = $('
'); + $parent.append($div); + this.createTrackGearPopup($div); + } + + createTrackGearPopup($parent) { + let $container = $("
", { + class: 'igv-trackgear-container' + }); + $parent.append($container); + $container.append(createIcon$1('cog')); + this.trackGearPopup = new MenuPopup($parent); + this.trackGearPopup.$popover.hide(); + $container.click(e => { + e.preventDefault(); + e.stopPropagation(); + this.trackGearPopup.presentMenuList(-this.trackGearPopup.$popover.width(), 0, MenuUtils.trackMenuItemList(this)); + }); + } + + static computeViewportWidth(browser, viewportContainerWidth) { + return viewportContainerWidth - axisContainerWidth - browser.getSampleNameViewportWidth(); + } + + } + + function renderSVGAxis(context, track, axisCanvas, deltaX, deltaY) { + if (typeof track.paintAxis === 'function') { + const { + y, + width, + height + } = axisCanvas.getBoundingClientRect(); + const str = (track.name || track.id).replace(/\W/g, ''); + const id = `${str}_axis_guid_${guid()}`; + context.saveWithTranslationAndClipRect(id, deltaX, y + deltaY, width, height, 0); + track.paintAxis(context, width, height); + context.restore(); + } + } + + function emptyViewportContainers(trackViews) { + for (let trackView of trackViews) { + if (trackView.scrollbar) { + trackView.scrollbar.$outerScroll.remove(); + trackView.scrollbar = null; + trackView.scrollbar = undefined; + } + + for (let viewport of [...trackView.viewports, trackView.sampleNameViewport]) { + if (viewport.rulerSweeper) { + viewport.rulerSweeper.$rulerSweeper.remove(); + } + + if (viewport.popover) { + viewport.popover.dispose(); + } + + viewport.$viewport.remove(); + } + + delete trackView.sampleNameViewport; + delete trackView.viewports; + delete trackView.scrollbar; + } + } + + function updateViewportShims(viewports, $viewportContainer) { + const $trackContainer = $('.igv-track-container'); + $trackContainer.find('.igv-multi-locus-separator').remove(); + const { + x: tx + } = documentOffset($trackContainer.get(0)); + $viewportContainer.find('.igv-viewport-multi-locus-gap-shim').remove(); + + if (viewports.length > 1) { + for (let viewport of viewports) { + if (viewports.indexOf(viewport) <= viewports.length - 2) { + const { + $viewport + } = viewport; + const $shim = $('
'); + $shim.insertAfter($viewport); + const { + x: sx + } = documentOffset($shim.get(0)); // console.log(`trackContainer x ${ tx }. shim x ${ sx }`) + + const $multilLocusSeparator = $('
'); + $trackContainer.append($multilLocusSeparator); + $multilLocusSeparator.get(0).style.left = `${sx - tx}px`; + } + } + } + } + + function documentOffset(el) { + const { + x, + y + } = el.getBoundingClientRect(); + const scrollX = window.pageXOffset || document.documentElement.scrollLeft; + const scrollY = window.pageYOffset || document.documentElement.scrollTop; + return { + x: x + scrollX, + y: y + scrollY + }; + } + + function maxViewportContentHeight(viewports) { + const heights = viewports.filter(viewport => !(viewport.trackView.sampleNameViewport === viewport)).map(viewport => viewport.getContentHeight()); + return Math.max(...heights); + } + + /** + * Decoder for bedpe records. + * + * Bedpe format was created by Aaron Quinlan et al as part of the bedtools project. + * The spec is here: https://bedtools.readthedocs.io/en/latest/content/general-usage.html, however there + * are off spec variants, an important one being a 7 column format with score in place of the standard + * name column. + * + * Another common variant is a "hiccups" output file, which is standard bedpe with the exception of a header line + * of the form + * chr1 x1 x2 chr2 y1 y2 name score strand1 strand2 color observed expectedBL expectedDonut expectedH expectedV fdrBL fdrDonut fdrH fdrV + * + * @param tokens + * @param ignore + * @returns {{start1: number, end2: number, end1: number, chr1: *, chr2: *, start2: number}|undefined} + */ + + function decodeBedpe(tokens, header) { + if (tokens.length < 6) { + return undefined; + } + + var feature = { + chr1: tokens[0], + start1: Number.parseInt(tokens[1]), + end1: Number.parseInt(tokens[2]), + chr2: tokens[3], + start2: Number.parseInt(tokens[4]), + end2: Number.parseInt(tokens[5]) + }; + + if (isNaN(feature.start1) || isNaN(feature.end1) || isNaN(feature.start2) || isNaN(feature.end2)) { + //throw Error(`Error parsing line: ${tokens.join('\t')}`); + return undefined; + } + + if (tokens.length > 6 && tokens[6] !== ".") { + feature.name = tokens[6]; + } + + if (tokens.length > 7 && tokens[7] !== ".") { + feature.score = parseFloat(tokens[7]); + } + + if (tokens.length > 8 && tokens[8] !== ".") { + feature.strand1 = tokens[8]; + } + + if (tokens.length > 9 && tokens[9] !== ".") { + feature.strand2 = tokens[9]; + } // Optional extra columns + + + if (header) { + const colorColumn = header.colorColumn; + + if (colorColumn && colorColumn < tokens.length) { + feature.color = IGVColor.createColorString(tokens[colorColumn]); + } + + const thicknessColumn = header.thicknessColumn; + + if (thicknessColumn && thicknessColumn < tokens.length) { + feature.thickness = tokens[thicknessColumn]; + } + + if (tokens.length > 10 && header.columnNames && header.columnNames.length === tokens.length) { + feature.extras = tokens.slice(10); + } + } // Set total extent of feature + + + if (feature.chr1 === feature.chr2) { + feature.chr = feature.chr1; + feature.start = Math.min(feature.start1, feature.start2); + feature.end = Math.max(feature.end1, feature.end2); + } + + return feature; + } + /** + * Hack for non-standard bedPE formats, where numeric score can be in column 7 (name field from spec) + * @param features + */ + + + function fixBedPE(features) { + if (features.length == 0) return; // Assume all features have same properties + + const firstFeature = features[0]; + + if (firstFeature.score === undefined && firstFeature.name !== undefined) { + // Name field (col 7) is sometimes used for score. + for (let f of features) { + if (!(isNumber(f.name) || f.name === '.')) return; + } + + for (let f of features) { + f.score = parseFloat(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.parseFloat(tokens[7]) + }; + } + + /** + * Decode UCSC "interact" files. See https://genome.ucsc.edu/goldenpath/help/interact.html + * + 0 string chrom; "Chromosome (or contig, scaffold, etc.). For interchromosomal, use 2 records" + 1 uint chromStart; "Start position of lower region. For interchromosomal, set to chromStart of this region" + 2 uint chromEnd; "End position of upper region. For interchromosomal, set to chromEnd of this region" + 3 string name; "Name of item, for display. Usually 'sourceName/targetName/exp' or empty" + 4 uint score; "Score (0-1000)" + 5 double value; "Strength of interaction or other data value. Typically basis for score" + 6 string exp; "Experiment name (metadata for filtering). Use . if not applicable" + 7 string color; "Item color. Specified as r,g,b or hexadecimal #RRGGBB or html color name, as in //www.w3.org/TR/css3-color/#html4. Use 0 and spectrum setting to shade by score" + 8 string sourceChrom; "Chromosome of source region (directional) or lower region. For non-directional interchromosomal, chrom of this region." + 9 uint sourceStart; "Start position in chromosome of source/lower/this region" + 10 uint sourceEnd; "End position in chromosome of source/lower/this region" + 11 string sourceName; "Identifier of source/lower/this region" + 12 string sourceStrand; "Orientation of source/lower/this region: + or -. Use . if not applicable" + 13 string targetChrom; "Chromosome of target region (directional) or upper region. For non-directional interchromosomal, chrom of other region" + 14 uint targetStart; "Start position in chromosome of target/upper/this region" + 15 uint targetEnd; "End position in chromosome of target/upper/this region" + 16 string targetName; "Identifier of target/upper/this region" + 17 string targetStrand; "Orientation of target/upper/this region: + or -. Use . if not applicable" + * + * @param tokens + * @param ignore + * @returns {*} + */ + + function decodeInteract(tokens, header) { + if (tokens.length < 6) { + 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.parseFloat(tokens[4]), + value: Number.parseFloat(tokens[5]), + color: tokens[7] === '.' ? undefined : tokens[7] === "0" ? "rgb(0,0,0)" : tokens[7] + }; + return feature; + } + + const gffNameFields = ["Name", "gene_name", "gene", "gene_id", "alias", "locus", "name"]; + /** + * 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 decodeGFF(tokens, header) { + var tokenCount, + chr, + start, + end, + strand, + type, + score, + attributeString, + color, + name, + i, + format = header.format; + tokenCount = tokens.length; + + if (tokenCount < 9) { + return null; // Not a valid gff record + } + + chr = tokens[0]; + type = tokens[2]; + start = parseInt(tokens[3]) - 1; + end = parseInt(tokens[4]); + score = "." === tokens[5] ? 0 : parseFloat(tokens[5]); + strand = tokens[6]; + "." === tokens[7] ? 0 : parseInt(tokens[7]); + attributeString = tokens[8]; // Find ID and Parent, or transcript_id + + var delim = 'gff3' === format ? '=' : /\s+/; + var attributes = parseAttributeString(attributeString, delim); + + for (let [key, value] of Object.entries(attributes)) { + const keyLower = key.toLowerCase(); + + if ("color" === keyLower || "colour" === keyLower) { + color = IGVColor.createColorString(value); + } else if ('gff3' === format) try { + attributes[key] = unescape(value); + } catch (e) { + attributes[key] = value; // Invalid + + console.error(`Malformed gff3 attibute value: ${value}`); + } + } // Find name (label) property + + + if (header.nameField) { + name = attributes[header.nameField]; + } else { + for (i = 0; i < gffNameFields.length; i++) { + if (attributes.hasOwnProperty(gffNameFields[i])) { + header.nameField = gffNameFields[i]; + name = attributes[header.nameField]; + break; + } + } + } + + const id = attributes["ID"] || attributes["transcript_id"]; + const parent = attributes["Parent"]; + return { + id: id, + parent: parent, + name: name, + type: type, + chr: chr, + start: start, + end: end, + score: score, + strand: strand, + color: color, + attributeString: attributeString, + delim: delim, + popupData: popupData + }; + } + + function parseAttributeString(attributeString, keyValueDelim) { + // parse 'attributes' string (see column 9 docs in https://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md) + var attributes = {}; + + for (let kv of attributeString.split(';')) { + const t = kv.trim().split(keyValueDelim, 2); + + if (t.length === 2) { + const key = t[0].trim(); + let value = t[1].trim(); //Strip off quotes, if any + + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substr(1, value.length - 2); + } + + attributes[key] = value; + } + } + + return attributes; + } + + function popupData(genomicLocation) { + const kvs = this.attributeString.split(';'); + const pd = []; + + if (this.name) { + pd.push({ + name: 'name:', + value: this.name + }); + } + + pd.push({ + name: 'type:', + value: this.type + }); + + for (let kv of kvs) { + const t = kv.trim().split(this.delim, 2); + + if (t.length === 2 && t[1] !== undefined) { + const key = t[0].trim(); + if ('name' === key.toLowerCase()) continue; + let value = t[1].trim(); //Strip off quotes, if any + + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substr(1, value.length - 2); + } + + pd.push({ + name: key + ":", + value: value + }); + } + } + + pd.push({ + name: 'position:', + value: `${this.chr}:${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}` + }); + return pd; + } + + /** + * 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 = { + chr: chr, + start: start, + end: end, + score: 1000 + }; + + try { + if (tokens.length > 3) { + // Note: these are very special rules for the gencode gene files. + // tmp = tokens[3].replace(/"/g, ''); + // idName = tmp.split(';'); + // for (var i = 0; i < idName.length; i++) { + // var kv = idName[i].split('='); + // if (kv[0] == "gene_id") { + // id = kv[1]; + // } + // if (kv[0] == "gene_name") { + // name = kv[1]; + // } + // } + // feature.id = id ? id : tmp; + // feature.name = name ? name : tmp; + //parse gffTags in the name field + if (tokens[3].indexOf(';') > 0 && tokens[3].indexOf('=') > 0) { + const attributes = parseAttributeString(tokens[3], '='); + + for (let nmField of gffNameFields) { + if (attributes.hasOwnProperty(nmField)) { + feature.name = attributes[nmField]; + delete attributes[nmField]; + break; + } + } + + feature.attributes = attributes; + } + + if (!feature.name) { + feature.name = tokens[3] === '.' ? '' : tokens[3]; + } + } + + if (tokens.length > 4) { + feature.score = tokens[4] === '.' ? 0 : parseFloat(tokens[4]); + + if (isNaN(feature.score)) { + return feature; + } + } + + if (tokens.length > 5) { + feature.strand = tokens[5]; + + if (!(feature.strand === '.' || feature.strand === '+' || feature.strand === '-')) { + return feature; + } + } + + if (tokens.length > 6) { + feature.cdStart = parseInt(tokens[6]); + + if (isNaN(feature.cdStart)) { + return feature; + } + } + + if (tokens.length > 7) { + feature.cdEnd = parseInt(tokens[7]); + + if (isNaN(feature.cdEnd)) { + return feature; + } + } + + if (tokens.length > 8) { + if (tokens[8] !== "." && tokens[8] !== "0") feature.color = IGVColor.createColorString(tokens[8]); + } + + if (tokens.length > 11) { + const exonCount = parseInt(tokens[9]); // Some basic validation + + if (exonCount > 1000) { + // unlikely + return feature; + } + + const exonSizes = tokens[10].replace(/,$/, '').split(','); + const exonStarts = tokens[11].replace(/,$/, '').split(','); + + if (!(exonSizes.length === exonStarts.length && exonCount === exonSizes.length)) { + return feature; + } + + const exons = []; + + for (let i = 0; i < exonCount; i++) { + const eStart = start + parseInt(exonStarts[i]); + const eEnd = eStart + parseInt(exonSizes[i]); + exons.push({ + start: eStart, + end: eEnd + }); + } + + findUTRs(exons, feature.cdStart, feature.cdEnd); + feature.exons = exons; + } // Optional extra columns + + + if (header) { + let thicknessColumn = header.thicknessColumn; + let colorColumn = header.colorColumn; + + if (colorColumn && colorColumn < tokens.length) { + feature.color = IGVColor.createColorString(tokens[colorColumn]); + } + + if (thicknessColumn && thicknessColumn < tokens.length) { + feature.thickness = tokens[thicknessColumn]; + } + } + } catch (e) {} + + return feature; + } + /** + * Decode a UCSC repeat masker record. + * + * Columns, from UCSC documentation + * + * 0 bin 585 smallint(5) unsigned Indexing field to speed chromosome range queries. + * 1 swScore 1504 int(10) unsigned Smith Waterman alignment score + * 2 milliDiv 13 int(10) unsigned Base mismatches in parts per thousand + * 3 milliDel 4 int(10) unsigned Bases deleted in parts per thousand + * 4 milliIns 13 int(10) unsigned Bases inserted in parts per thousand + * 5 genoName chr1 varchar(255) Genomic sequence name + * 6 genoStart 10000 int(10) unsigned Start in genomic sequence + * 7 genoEnd 10468 int(10) unsigned End in genomic sequence + * 8 genoLeft -249240153 int(11) -#bases after match in genomic sequence + * 9 strand + char(1) Relative orientation + or - + * 10 repName (CCCTAA)n varchar(255) Name of repeat + * 11 repClass Simple_repeat varchar(255) Class of repeat + * 12 repFamily Simple_repeat varchar(255) Family of repeat + * 13 repStart 1 int(11) Start (if strand is +) or -#bases after match (if strand is -) in repeat sequence + * 14 repEnd 463 int(11) End in repeat sequence + * 15 repLeft 0 int(11) -#bases after match (if strand is +) or start (if strand is -) in repeat sequence + * 16 id 1 char(1) First digit of id field in RepeatMasker .out file. Best ignored. + */ + + + function decodeRepeatMasker(tokens, header) { + if (tokens.length <= 15) return undefined; + const feature = { + swScore: Number.parseInt(tokens[1]), + milliDiv: Number.parseInt(tokens[2]), + milliDel: Number.parseInt(tokens[3]), + milliIns: Number.parseInt(tokens[4]), + chr: tokens[5], + start: Number.parseInt(tokens[6]), + end: Number.parseInt(tokens[7]), + //genoLeft: tokens[8], + strand: tokens[9], + repName: tokens[10], + repClass: tokens[11], + repFamily: tokens[12], + repStart: Number.parseInt(tokens[13]), + repEnd: Number.parseInt(tokens[14]), + repLeft: Number.parseInt(tokens[15]) + }; + return feature; + } + /** + * Decode a UCSC "genePred" record. + * + * @param tokens + * @param ignore + * @returns {*} + */ + + + function decodeGenePred(tokens, header) { + var shift = header.shift === undefined ? 0 : 1; + if (tokens.length <= 9 + shift) return undefined; + const cdStart = parseInt(tokens[5 + shift]); + const cdEnd = parseInt(tokens[6 + shift]); + var feature = { + name: tokens[0 + shift], + chr: tokens[1 + shift], + strand: tokens[2 + shift], + start: parseInt(tokens[3 + shift]), + end: parseInt(tokens[4 + shift]), + cdStart: cdStart, + cdEnd: cdEnd, + id: tokens[0 + shift] + }, + exonCount = parseInt(tokens[7 + shift]), + exonStarts = tokens[8 + shift].split(','), + exonEnds = tokens[9 + shift].split(','), + exons = []; + + for (let i = 0; i < exonCount; i++) { + const start = parseInt(exonStarts[i]); + const end = parseInt(exonEnds[i]); + exons.push({ + start: start, + end: end + }); + } + + findUTRs(exons, cdStart, cdEnd); + feature.exons = exons; + return feature; + } + /** + * Decode a UCSC "genePredExt" record. refGene files are in this format. + * + * @param tokens + * @param ignore + * @returns {*} + */ + + + function decodeGenePredExt(tokens, header) { + var shift = header.shift === undefined ? 0 : 1; + if (tokens.length <= 11 + shift) return undefined; + const cdStart = parseInt(tokens[5 + shift]); + const cdEnd = parseInt(tokens[6 + shift]); + const feature = { + name: tokens[11 + shift], + chr: tokens[1 + shift], + strand: tokens[2 + shift], + start: parseInt(tokens[3 + shift]), + end: parseInt(tokens[4 + shift]), + cdStart: cdStart, + cdEnd: cdEnd, + id: tokens[0 + shift] + }, + exonCount = parseInt(tokens[7 + shift]), + exonStarts = tokens[8 + shift].split(','), + exonEnds = tokens[9 + shift].split(','), + exons = []; + + for (let i = 0; i < exonCount; i++) { + const start = parseInt(exonStarts[i]); + const end = parseInt(exonEnds[i]); + exons.push({ + start: start, + end: end + }); + } + + findUTRs(exons, cdStart, cdEnd); + feature.exons = exons; + return feature; + } + /** + * Decode a UCSC "refFlat" record + * @param tokens + * @param ignore + * @returns {*} + */ + + + function decodeReflat(tokens, header) { + var shift = header.shift === undefined ? 0 : 1; + if (tokens.length <= 10 + shift) return undefined; + const cdStart = parseInt(tokens[6 + shift]); + const cdEnd = parseInt(tokens[7 + shift]); + var feature = { + name: tokens[0 + shift], + id: tokens[1 + shift], + chr: tokens[2 + shift], + strand: tokens[3 + shift], + start: parseInt(tokens[4 + shift]), + end: parseInt(tokens[5 + shift]), + cdStart: cdStart, + cdEnd: cdEnd + }, + exonCount = parseInt(tokens[8 + shift]), + exonStarts = tokens[9 + shift].split(','), + exonEnds = tokens[10 + shift].split(','), + exons = []; + + for (let i = 0; i < exonCount; i++) { + const start = parseInt(exonStarts[i]); + const end = parseInt(exonEnds[i]); + exons.push({ + start: start, + end: end + }); + } + + findUTRs(exons, cdStart, cdEnd); + feature.exons = exons; + 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 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 = parseFloat(tokens[4]); + strand = tokens[5].trim(); + signal = parseFloat(tokens[6]); + pValue = parseFloat(tokens[7]); + qValue = parseFloat(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 = parseFloat(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 = parseFloat(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 = parseFloat(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; + } + + 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; + } // function expandFormat(format) { + + /** + * Decode a gcnv record, a bed style format encoding copy number variation + * + * @param tokens + * @param header + */ + function decodeGcnv(tokens, header) { + const columnNames = header.columnNames; + + if (!columnNames) { + throw Error("Sample names are not defined. Missing column headers?"); + } + + const sampleCount = columnNames.length - 3; + const chr = tokens[0]; + const start = parseInt(tokens[1]); + const end = parseInt(tokens[2]); + const values = tokens.slice(3).map(parseFloat); + + 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": + case "gtf": + this.decode = decodeGFF; + this.delimiter = "\t"; + break; + + case "fusionjuncspan": + // bhaas, needed for FusionInspector view + this.decode = decodeFusionJuncSpan; + this.delimiter = this.config.delimiter || /\s+/; + break; + + case "gtexgwas": + this.skipRows = 1; + this.decode = decodeGtexGWAS; + this.delimiter = "\t"; + break; + + case "refflat": + this.decode = decodeReflat; + this.delimiter = this.config.delimiter || /\s+/; + break; + + case "genepred": + this.decode = decodeGenePred; + this.delimiter = this.config.delimiter || /\s+/; + break; + + case "genepredext": + this.decode = decodeGenePredExt; + this.delimiter = this.config.delimiter || /\s+/; + break; + + case "ensgene": + this.decode = decodeGenePred; + this.header.shift = 1; + this.delimiter = this.config.delimiter || /\s+/; + break; + + case "refgene": + this.decode = decodeGenePredExt; + this.delimiter = this.config.delimiter || /\s+/; + this.header.shift = 1; + break; + + case "bed": + this.decode = decodeBed; + this.delimiter = this.config.delimiter || /\s+/; + break; + + case "bedpe": + this.decode = decodeBedpe; + this.delimiter = this.config.delimiter || "\t"; + break; + + case "bedpe-domain": + this.decode = decodeBedpeDomain; + this.headerLine = true; + this.delimiter = this.config.delimiter || "\t"; + break; + + case "bedpe-loop": + this.decode = decodeBedpe; + this.delimiter = this.config.delimiter || "\t"; + this.header = { + colorColumn: 7 + }; + break; + + case "interact": + this.decode = decodeInteract; + this.delimiter = this.config.delimiter || /\s+/; + break; + + case "snp": + this.decode = decodeSNP; + this.delimiter = "\t"; + break; + + case "rmsk": + this.decode = decodeRepeatMasker; + this.delimiter = "\t"; + break; + + case "gcnv": + this.decode = decodeGcnv; + this.delimiter = "\t"; + break; + + default: + const customFormat = getFormat(format); + + if (customFormat !== undefined) { + this.decode = decodeCustom; + this.header.customFormat = customFormat; + this.delimiter = customFormat.delimiter || "\t"; + } else { + this.decode = decodeBed; + this.delimiter = this.config.delimiter || /\s+/; + } + + } + } + + } + + function parseTrackLine$1(line) { + const properties = {}; + const tokens = line.split(/(?:")([^"]+)(?:")|([^\s"]+)(?=\s+|$)/g); // Clean up tokens array + + let curr; + const tmp = []; + + for (let tk of tokens) { + if (!tk || tk.trim().length === 0) continue; + + if (tk.endsWith("=")) { + curr = tk; + } else if (curr) { + tmp.push(curr + tk); + curr = undefined; + } else { + tmp.push(tk); + } + } + + for (let str of tmp) { + if (!str) return; + var kv = str.split('=', 2); + + if (kv.length === 2) { + const key = kv[0].trim(); + const value = kv[1].trim(); + + if (properties.hasOwnProperty(key)) { + let currentValue = properties[key]; + + if (Array.isArray(currentValue)) { + currentValue.push(value); + } else { + properties[key] = [currentValue, value]; + } + } else { + properties[key] = value; + } + } + } + + if ("interact" == properties["type"]) { + properties["format"] = "interact"; + } else if ("gcnv" === properties["type"]) { + properties["format"] = "gcnv"; + } + + return properties; + } + + function parseColumnsDirective$1(line) { + let properties = {}; + let t1 = line.split(/\s+/); + + if (t1.length === 2) { + let t2 = t1[1].split(";"); + t2.forEach(function (keyValue) { + let t = keyValue.split("="); + + if (t[0] === "color") { + properties.colorColumn = Number.parseInt(t[1]) - 1; + } else if (t[0] === "thickness") { + properties.thicknessColumn = Number.parseInt(t[1]) - 1; + } + }); + } + + return properties; + } + + function parseFixedStep(line) { + const tokens = line.split(/\s+/); + const chrom = tokens[1].split("=")[1]; + const start = parseInt(tokens[2].split("=")[1], 10) - 1; + const step = parseInt(tokens[3].split("=")[1], 10); + const span = tokens.length > 4 ? parseInt(tokens[4].split("=")[1], 10) : 1; + return { + format: "fixedStep", + chrom, + start, + step, + span, + index: 0 + }; + } + + function parseVariableStep(line) { + const tokens = line.split(/\s+/); + const chrom = tokens[1].split("=")[1]; + const span = tokens.length > 2 ? parseInt(tokens[2].split("=")[1], 10) : 1; + return { + format: "variableStep", + chrom, + span + }; + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + /** + * Define parser for seg files (.bed, .gff, .vcf, etc). A parser should implement 2 methods + * + * parseHeader(data) - return an object representing a header. Details are format specific + * + * parseFeatures(data) - return a list of features + * + */ + + class SegParser { + constructor(type) { + this.type = type || 'seg'; // One of seg, mut, or maf + + switch (this.type) { + case 'mut': + this.sampleColumn = 3; + this.chrColumn = 0; + this.startColumn = 1; + this.endColumn = 2; + this.dataColumn = 4; + break; + + case 'maf': + this.sampleColumn = 15; + this.chrColumn = 4; + this.startColumn = 5; + this.endColumn = 6; + this.dataColumn = 8; + break; + + default: + this.sampleColumn = 0; + this.chrColumn = 1; + this.startColumn = 2; + this.endColumn = 3; + // Data column determined after reading header + } + } + + async parseHeader(dataWrapper) { + let line; + + while ((line = await dataWrapper.nextLine()) !== undefined) { + if (line.startsWith("#")) ; else { + const tokens = line.split("\t"); + this.header = { + headings: tokens + }; + break; + } + } + + return this.header; + } + + async parseFeatures(dataWrapper) { + const allFeatures = []; + let extraHeaders; + + if (!this.header) { + this.header = await this.parseHeader(dataWrapper); // This will only work for non-indexed files + } + + if ('seg' === this.type) { + this.dataColumn = this.header.headings.length - 1; + } + + if (this.header.headings.length > 5) { + extraHeaders = this.extractExtraColumns(this.header.headings); + } + + const valueColumnName = this.header.headings[this.dataColumn]; + let line; + + while ((line = await dataWrapper.nextLine()) !== undefined) { + const tokens = line.split("\t"); + const value = 'seg' === this.type ? parseFloat(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) { + 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() { + 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 ? this.valueColumnName : "Value", + value: this.value + }]; + + if (this.attributeNames && this.attributeNames.length > 0) { + pd.push("
"); + + for (let i = 0; i < this.attributeNames.length; i++) { + pd.push({ + name: this.attributeNames[i], + value: this.attributeValues[i] + }); + } + + pd.push("
"); // In case there are multiple features selected + } + + return pd; + } + + } + + /* + * 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. + */ + /** + * A collection of properties and methods shared by all (or most) track types. + * + * @param config + * @param browser + * @constructor + */ + + class TrackBase { + constructor(config, browser) { + this.browser = browser; + this.init(config); + } + /** + * Initialize track properties from the config object. This method is typically overriden in subclasses, which + * will call this implementation as super.init(config). + * + * @param config + */ + + + init(config) { + if (config.displayMode) { + config.displayMode = config.displayMode.toUpperCase(); + } + + this.config = config; + this.url = config.url; + this.type = config.type; + this.description = config.description; + this.supportHiDPI = config.supportHiDPI === undefined ? true : config.supportHiDPI; + + if (config.name || config.label) { + this.name = config.name || config.label; + } else { + if (isFilePath(config.url)) this.name = config.url.name;else this.name = config.url; + } + + this.id = this.config.id === undefined ? this.name : this.config.id; + this.order = config.order; + this.color = config.color; + this.altColor = config.altColor; + + if ("civic-ws" === config.sourceType) { + // Ugly proxy for specialized track type + this.defaultColor = "rgb(155,20,20)"; + } else { + this.defaultColor = "rgb(0,0,150)"; + } + + this.autoscaleGroup = config.autoscaleGroup; + this.removable = config.removable === undefined ? true : config.removable; // Defaults to true + + this.height = config.height || 100; + this.autoHeight = config.autoHeight; + this.minHeight = config.minHeight || Math.min(25, this.height); + this.maxHeight = config.maxHeight || Math.max(1000, this.height); + this.visibilityWindow = config.visibilityWindow; + this.trackView = undefined; + } + /** + * Update track properties from the config object. + * + * @param config + */ + + + updateConfig(config) { + this.init(config); + } + /** + * Default implementation -- update config with current values. + * to create session object for bookmarking, sharing. Updates the track "config" object to reflect the + * current state. Only simple properties (string, number, boolean) are updated. + */ + + + getState() { + // Create copy of config, minus transient properties (convention is name starts with '_') + const state = {}; + + for (let key of Object.keys(this.config)) { + if (!key.startsWith("_")) { + 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")) { + state[key] = value; + } + } + + if (this.color) state.color = this.color; + if (this.altColor) state.altColor = this.altColor; // Flatten dataRange if present + + if (!this.autoscale && this.dataRange) { + state.min = this.dataRange.min; + state.max = this.dataRange.max; + } // Check for non-json-if-yable properties. Perhaps we should test what can be saved. + + + for (let key of Object.keys(state)) { + if (typeof state[key] === 'function') { + throw Error(`Property '${key}' of track '${this.name} is a function. Functions cannot be saved in sessions.`); + } + + if (state[key] instanceof File) { + const str = `Track ${this.name} is a local file. Sessions cannot be saved with local file references.`; + throw Error(str); + } + + if (state[key] instanceof Promise) { + throw Error(`Property '${key}' of track '${this.name} is a Promise. Promises cannot be saved in sessions.`); + } + } + + return state; + } + + supportsWholeGenome() { + return false; + } + /** + * Does the track support sample names. Current sample aware tracks include VCF (with genotypes), MUT, MAF, and SEG + * @returns {boolean} + */ + + + hasSamples() { + return false; + } + + getGenomeId() { + return this.browser.genome ? this.browser.genome.id : undefined; + } + /** + * Set certain track properties, usually from a "track" line. Not all UCSC properties are supported. + * + * Track configuration settings have precendence over track line properties, so if both are present ignore the + * track line. + * + * @param properties + */ + + + setTrackProperties(properties) { + const tracklineConfg = {}; + let tokens; + + for (let key of Object.keys(properties)) { + switch (key.toLowerCase()) { + case "usescore": + tracklineConfg.useScore = properties[key] === 1 || properties[key] === "1" || properties[key] === "on" || properties[key] === true; + break; + + case "visibility": + //0 - hide, 1 - dense, 2 - full, 3 - pack, and 4 - squish + switch (properties[key]) { + case "2": + case "3": + case "pack": + case "full": + tracklineConfg.displayMode = "EXPANDED"; + break; + + case "4": + case "squish": + tracklineConfg.displayMode = "SQUISHED"; + break; + + case "1": + case "dense": + tracklineConfg.displayMode = "COLLAPSED"; + } + + break; + + case "color": + case "altcolor": + tracklineConfg[key] = properties[key].startsWith("rgb(") ? properties[key] : "rgb(" + properties[key] + ")"; + break; + + case "featurevisiblitywindow": + case "visibilitywindow": + tracklineConfg.visibilityWindow = Number.parseInt(properties[key]); + break; + + case "maxheightpixels": + tokens = properties[key].split(":"); + + if (tokens.length === 3) { + tracklineConfg.minHeight = Number.parseInt(tokens[2]); + tracklineConfg.height = Number.parseInt(tokens[1]); + tracklineConfg.maxHeight = Number.parseInt(tokens[0]); + } + + break; + + case "viewlimits": + if (!this.config.autoscale) { + // autoscale in the config has precedence + tokens = properties[key].split(":"); + let min = 0; + let max; + + if (tokens.length == 1) { + max = Number.parseFloat(tokens[0]); + } else if (tokens.length == 2) { + min = Number.parseFloat(tokens[0]); + max = Number.parseFloat(tokens[1]); + } + + tracklineConfg.autoscale = false; + tracklineConfg.dataRange = { + min, + max + }; + } + + case "name": + tracklineConfg[key] = properties[key]; + break; + + case "url": + tracklineConfg["infoURL"] = properties[key]; + } + } // Track configuration objects have precendence over track line properties + + + for (let key of Object.keys(tracklineConfg)) { + if (!this.config.hasOwnProperty(key)) { + this[key] = tracklineConfg[key]; + } + } + } + + getVisibilityWindow() { + return this.visibilityWindow; + } + + clickedFeatures(clickState, features) { + // We use the cached features rather than method to avoid async load. If the + // feature is not already loaded this won't work, but the user wouldn't be mousing over it either. + if (!features) features = clickState.viewport.getCachedFeatures(); + + if (!features || features.length === 0) { + return []; + } + + const genomicLocation = clickState.genomicLocation; // When zoomed out we need some tolerance around genomicLocation + + const tolerance = clickState.referenceFrame.bpPerPixel > 0.2 ? 3 * clickState.referenceFrame.bpPerPixel : 0.2; + const ss = genomicLocation - tolerance; + const ee = genomicLocation + tolerance; + return FeatureUtils.findOverlapping(features, ss, ee); + } + /** + * Default popup text function -- just extracts string and number properties in random order. + * @param feature + * @returns {Array} + */ + + + extractPopupData(feature, genomeId) { + const filteredProperties = new Set(['row', 'color', 'chr', 'start', 'end', 'cdStart', 'cdEnd', 'strand', 'alpha']); + const data = []; + let alleles, alleleFreqs; + + for (var 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]; + } + } + } //const genomeId = this.getGenomeId() + + + 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.parseFloat(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) { + const cravatLink = TrackBase.getCravatLink(feature.chr, feature.start + 1, ref, alt, genomeId); + + 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 + }); + + if (this.infoURL) { + data.push("
"); + const href = this.infoURL.replace("$$", feature.name); + data.push({ + html: `${href}` + }); + } + + return data; + } + + static getCravatLink(chr, position, ref, alt, genomeID) { + if ("hg38" === genomeID || "GRCh38" === genomeID) { + const cravatChr = chr.startsWith("chr") ? chr : "chr" + chr; + return `Cravat ${ref}->${alt}`; // return "Cravat " + ref + "->" + alt + "" + } 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. + */ + const knownAltBases = new Set(["A", "C", "T", "G"].map(c => c.charCodeAt(0))); + + function createVCFVariant(tokens) { + return new Variant(tokens); + } + + class Variant { + constructor(tokens) { + this.chr = tokens[0]; // TODO -- use genome aliases + + this.pos = parseInt(tokens[1]); + this.names = tokens[2]; // id in VCF + + this.referenceBases = tokens[3]; + this.alternateBases = tokens[4]; + this.quality = tokens[5]; + this.filter = tokens[6]; + this.info = getInfoObject(tokens[7]); + this.init(); + } + + init() { + const ref = this.referenceBases; + const altBases = this.alternateBases; + + if (this.info) { + if (this.info["VT"]) { + this.type = this.info["VT"]; + } else if (this.info["SVTYPE"]) { + this.type = "SV"; + } else if (this.info["PERIOD"]) { + this.type = "STR"; + } + } + + if (this.type === undefined) { + this.type = determineType(ref, altBases); + } + + if (this.type === "NONVARIANT") { + this.heterozygosity = 0; + } // Determine start/end coordinates -- these are the coordinates representing the actual variant, + // not the leading or trailing reference + + + if (this.info["END"]) { + this.start = this.pos - 1; + + if (this.info["CHR2"] && this.info["CHR2"] !== this.chr) { + this.end = this.start + 1; + } else { + this.end = Number.parseInt(this.info["END"]); + } + } else { + if (this.type === "NONVARIANT") { + this.start = this.pos - 1; // convert to 0-based coordinate convention + + this.end = this.start + ref.length; + } else { + const altTokens = altBases.split(",").filter(token => token.length > 0); + this.alleles = []; + this.start = undefined; + this.end = undefined; + + for (let alt of altTokens) { + this.alleles.push(alt); // We don't yet handle SV and other special alt representations + + if ("SV" !== this.type && isKnownAlt(alt)) { + let altLength = alt.length; + let lengthOnRef = ref.length; + const lmin = Math.min(altLength, lengthOnRef); // Trim off matching bases. Try first match, then right -> left, then any remaining left -> right + + let s = 0; + + while (s < lmin && ref.charCodeAt(s) === alt.charCodeAt(s)) { + s++; + altLength--; + lengthOnRef--; + } // right -> left from end + + + while (altLength > 0 && lengthOnRef > 0) { + const altIdx = s + altLength - 1; + const refIdx = s + lengthOnRef - 1; + + if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) { + altLength--; + lengthOnRef--; + } else { + break; + } + } // if any remaining, left -> right + + + while (altLength > 0 && lengthOnRef > 0) { + const altIdx = s; + const refIdx = s; + + if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) { + s++; + altLength--; + lengthOnRef--; + } else { + break; + } + } + + const alleleStart = this.pos + s - 1; // -1 for zero based coordinates + + const alleleEnd = alleleStart + lengthOnRef; + this.start = this.start === undefined ? alleleStart : Math.min(this.start, alleleStart); + this.end = this.end === undefined ? alleleEnd : Math.max(this.end, alleleEnd); + } + } // Default to single base representation @ position for variant types not otherwise handled + + + if (this.start === undefined) { + this.start = this.pos - 1; + this.end = this.pos; + } + } + } + } + + popupData(genomicLocation, genomeId) { + const posString = `${numberFormatter$1(this.pos)}`; + const locString = this.start === this.end ? `${numberFormatter$1(this.start)} | ${numberFormatter$1(this.start + 1)}` : `${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}`; + const fields = [{ + name: "Chr", + value: this.chr + }, { + name: "Pos", + value: posString + }, { + name: "Loc", + value: locString + }, { + name: "Names", + value: this.names ? this.names : "" + }, { + name: "Ref", + value: this.referenceBases + }, { + name: "Alt", + value: this.alternateBases.replace("<", "<") + }, { + 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; + } + + isRefBlock() { + return "NONVARIANT" === this.type; + } + + } + + function getInfoObject(infoStr) { + var info = {}; + + if (infoStr) { + infoStr.split(';').forEach(function (elem) { + var element = elem.split('='); + info[element[0]] = element[1]; + }); + } + + return info; + } + + 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); + } + /** + * @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; + } + + /* + * 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)) { + 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 + + 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; + } + }); + } + } + } + } + } + + 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; + } + + /* + * 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; + } + + } + + /** + * Parser for IGV desktop GWAS files. See http://software.broadinstitute.org/software/igv/GWAS + */ + + class GWASParser { + constructor(config) { + // Defaults - can be overriden by header + this.config = config; + + if (config.columns) { + if (config.columns.chromosome === undefined || config.columns.position === undefined || config.columns.value === undefined) { + throw Error("columns property must define chrCol, posCol, and valueCol"); + } + + this.posCol = config.columns.position - 1; + this.chrCol = config.columns.chromosome - 1; + this.pvalueCol = config.columns.value - 1; + } else { + // Defaults -- can be overriden in header + this.posCol = 2; + this.chrCol = 1; + this.pvalueCol = 3; + } + } + + async parseHeader(dataWrapper) { + const headerLine = await dataWrapper.nextLine(); + return this.parseHeaderLine(headerLine); + } + + parseHeaderLine(headerLine) { + this.columns = headerLine.split(/\t/); + + if (!this.config.columns) { + for (let i = 0; i < this.columns.length; i++) { + const c = this.columns[i].toLowerCase(); + + switch (c) { + case 'chr': + case 'chromosome': + case 'chr_id': + this.chrCol = i; + break; + + case 'bp': + case 'pos': + case 'position': + case 'chr_pos': + this.posCol = i; + break; + + case 'p': + case 'pval': + case 'pvalue': + case 'p-value': + case 'p.value': + this.pvalueCol = 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 = parseFloat(tokens[this.pvalueCol]); + 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) { + 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) { + 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; + } + + print() { + return "" + this.block + ":" + this.offset; + } + + } + + 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.blockMax = 0; + this.sequenceIndexMap = {}; + this.minShift = parser.getInt(); + this.depth = parser.getInt(); + const lAux = parser.getInt(); + + 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; + 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 > this.blockMax) { + this.blockMax = ce.block; + } + + binIndex[binNumber].push([cs, ce]); + } + } + } + } + + if (nbin > 0) { + this.indices[ref] = { + binIndex: binIndex, + loffset: loffset + }; + } + } + } + /** + * Fetch blocks for a particular genomic range. This method is public so it can be unit-tested. + * + * @param refId the sequence dictionary index of the chromosome + * @param min genomic start position + * @param max genomic end position + * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}} + */ + + + blocksForRange(refId, min, max) { + const ba = this.indices[refId]; + + if (!ba) { + return []; + } else { + const overlappingBins = this.reg2bins(min, max); // List of bin #s that overlap min, max + + if (overlappingBins.length == 0) return []; + const chunks = []; // Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned + + for (let binRange of overlappingBins) { + for (let bin = binRange[0]; bin <= binRange[1]; bin++) { + if (ba.binIndex[bin]) { + const binChunks = ba.binIndex[bin]; + const nchnk = binChunks.length; + + for (let c = 0; c < nchnk; ++c) { + const cs = binChunks[c][0]; + const ce = binChunks[c][1]; + chunks.push({ + minv: cs, + maxv: ce, + bin: bin + }); + } + } + } + } + + const lowestOffset = ba.loffset[overlappingBins[0]]; + return optimizeChunks$1(chunks, lowestOffset); + } + } // reg2bins implementation adapted from GMOD/tabix-js https://github.com/GMOD/tabix-js/blob/master/src/csi.ts + + + reg2bins(beg, end) { + beg -= 1; // < convert to 1-based closed + + if (beg < 1) beg = 1; + if (end > 2 ** 50) end = 2 ** 34; // 17 GiB ought to be enough for anybody + + end -= 1; + let l = 0; + let t = 0; + let s = this.minShift + this.depth * 3; + const bins = []; + + for (; l <= this.depth; s -= 3, t += 1 << l * 3, l += 1) { + const b = t + (beg >> s); + const e = t + (end >> s); + if (e - b + bins.length > this.maxBinNumber) throw new Error(`query ${beg}-${end} is too large for current binning scheme (shift ${this.minShift}, depth ${this.depth}), try a smaller query or a coarser index binning scheme`); //for (let i = b; i <= e; i += 1) bins.push(i) + + bins.push([b, e]); + } + + return bins; + } // function reg2bins(beg, end, min_shift, depth) { + // let l, t, n, s = min_shift + depth * 3; + // const bins = []; + // for (--end, l = n = t = 0; l <= depth; s -= 3, t += 1 << l * 3, ++l) { + // let b = t + (beg >> s), e = t + (end >> s), i; + // for (i = b; i <= e; ++i) bins[n++] = i; + // } + // return bins; + // } + + + bin_limit() { + return ((1 << (this.depth + 1) * 3) - 1) / 7; + } + + } + + function optimizeChunks$1(chunks, lowest) { + const mergedChunks = []; + let lastChunk = null; + if (chunks.length === 0) return chunks; + chunks.sort(function (c0, c1) { + const dif = c0.minv.block - c1.minv.block; + + if (dif !== 0) { + return dif; + } else { + return c0.minv.offset - c1.minv.offset; + } + }); + chunks.forEach(function (chunk) { + if (!lowest || chunk.maxv.isGreaterThan(lowest)) { + if (lastChunk === null) { + mergedChunks.push(chunk); + lastChunk = chunk; + } else { + if (canMerge$1(lastChunk, chunk)) { + if (chunk.maxv.isGreaterThan(lastChunk.maxv)) { + lastChunk.maxv = chunk.maxv; + } + } else { + mergedChunks.push(chunk); + lastChunk = chunk; + } + } + } + }); + return mergedChunks; + } + + function canMerge$1(chunk1, chunk2) { + return chunk2.minv.block - chunk1.maxv.block < 65000 && chunk2.maxv.block - chunk1.minv.block < 5000000; // lastChunk.minv.block === lastChunk.maxv.block && + // lastChunk.maxv.block === chunk.minv.block && + // chunk.minv.block === chunk.maxv.block + } + + const BAI_MAGIC$1 = 21578050; + const TABIX_MAGIC$1 = 21578324; + + async function parseBamIndex(arrayBuffer, genome) { + return parseIndex(arrayBuffer, false, genome); + } + + async function parseTabixIndex(arrayBuffer, genome) { + return parseIndex(arrayBuffer, true, genome); + } + + async function parseIndex(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; + } + } + + 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 + }; + } + } + } else { + throw new Error(indexURL + " is not a " + (tabix ? "tabix" : "bai") + " file"); + } + + return new BamIndex(indices, blockMin, blockMax, sequenceIndexMap, tabix); + } + + class BamIndex { + constructor(indices, blockMin, blockMax, sequenceIndexMap, tabix) { + this.firstAlignmentBlock = blockMin; + this.lastAlignmentBlock = blockMax; + this.indices = indices; + this.sequenceIndexMap = sequenceIndexMap; + this.tabix = tabix; + } + /** + * Fetch blocks for a particular genomic range. This method is public so it can be unit-tested. + * + * @param refId the sequence dictionary index of the chromosome + * @param min genomic start position + * @param max genomic end position + * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}} + */ + + + blocksForRange(refId, min, max) { + const bam = this; + const ba = bam.indices[refId]; + + if (!ba) { + return []; + } else { + const overlappingBins = reg2bins(min, max); // List of bin #s that overlap min, max + + const chunks = []; // Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned + + for (let binRange of overlappingBins) { + for (let bin = binRange[0]; bin <= binRange[1]; bin++) { + if (ba.binIndex[bin]) { + const binChunks = ba.binIndex[bin], + nchnk = binChunks.length; + + for (let c = 0; c < nchnk; ++c) { + const cs = binChunks[c][0]; + const ce = binChunks[c][1]; + chunks.push({ + minv: cs, + maxv: ce, + bin: bin + }); + } + } + } + } // Use the linear index to find minimum file position of chunks that could contain alignments in the region + + + const nintv = ba.linearIndex.length; + let lowest = null; + const minLin = Math.min(min >> 14, nintv - 1); + const maxLin = Math.min(max >> 14, nintv - 1); + + for (let i = minLin; i <= maxLin; ++i) { + const vp = ba.linearIndex[i]; + + if (vp) { + // todo -- I think, but am not sure, that the values in the linear index have to be in increasing order. So the first non-null should be minimum + if (!lowest || vp.isLessThan(lowest)) { + lowest = vp; + } + } + } + + return optimizeChunks(chunks, lowest); + } + } + + } + + function optimizeChunks(chunks, lowest) { + const mergedChunks = []; + let lastChunk = null; + if (chunks.length === 0) return chunks; + chunks.sort(function (c0, c1) { + const dif = c0.minv.block - c1.minv.block; + + if (dif !== 0) { + return dif; + } else { + return c0.minv.offset - c1.minv.offset; + } + }); + chunks.forEach(function (chunk) { + if (!lowest || chunk.maxv.isGreaterThan(lowest)) { + if (lastChunk === null) { + mergedChunks.push(chunk); + lastChunk = chunk; + } else { + if (canMerge(lastChunk, chunk)) { + if (chunk.maxv.isGreaterThan(lastChunk.maxv)) { + lastChunk.maxv = chunk.maxv; + } + } else { + mergedChunks.push(chunk); + lastChunk = chunk; + } + } + } + }); + return mergedChunks; + } + + function canMerge(chunk1, chunk2) { + return chunk2.minv.block - chunk1.maxv.block < 65000 && chunk2.maxv.block - chunk1.minv.block < 5000000; // lastChunk.minv.block === lastChunk.maxv.block && + // lastChunk.maxv.block === chunk.minv.block && + // chunk.minv.block === chunk.maxv.block + } + /** + * 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 = {}; + 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 + const chrIdx = readLinear(parser); + index[chrIdx.chr] = chrIdx; + } + + return new TribbleIndex(index); + /** + * Read the header file. 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(); + } + } + } + + function readLinear(parser) { + let chr = parser.getString(); + + if (genome) chr = genome.getChromosomeName(chr); + 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 = new Array(); + + for (let binNumber = 0; binNumber < nBins; binNumber++) { + const nextPos = parser.getLong(); + blocks.push({ + min: pos, + max: nextPos + }); // {position: pos, size: size}); + + pos = nextPos; + } + + return { + chr: chr, + blocks: blocks, + longestFeature: longestFeature, + binWidth: binWidth + }; + } + } + + class TribbleIndex { + constructor(chrIndexTable) { + this.chrIndex = chrIndexTable; // Dictionary of chr -> tribble index + } + /** + * Fetch blocks for a particular genomic range. + * + * @param queryChr the sequence dictionary index of the chromosome + * @param min genomic start position + * @param max genomic end position + */ + + + blocksForRange(queryChr, min, max) { + const chrIdx = this.chrIndex[queryChr]; + + if (chrIdx) { + const blocks = chrIdx.blocks; + const longestFeature = chrIdx.longestFeature; + const binWidth = chrIdx.binWidth; + const adjustedPosition = Math.max(min - longestFeature, 0); + const startBinNumber = Math.floor(adjustedPosition / binWidth); + if (startBinNumber >= blocks.length) // are we off the end of the bin list, so return nothing + return [];else { + const endBinNumber = Math.min(Math.floor((max - 1) / binWidth), blocks.length - 1); // By definition blocks are adjacent in the file for the liner index. Combine them into one merged block + + const startPos = blocks[startBinNumber].min; + const endPos = blocks[endBinNumber].max; + const size = endPos - startPos; + + if (size === 0) { + return []; + } else { + const mergedBlock = { + minv: { + block: startPos, + offset: 0 + }, + maxv: { + block: endPos, + offset: 0 + } + }; + return [mergedBlock]; + } + } + } else { + return undefined; + } + } + + } + + const CSI1_MAGIC = 21582659; // CSI\1 + + const CSI2_MAGIC = 38359875; // CSI\2 + + const BAI_MAGIC = 21578050; + const TABIX_MAGIC = 21578324; + const TRIBBLE_MAGIC = 1480870228; // byte[]{'T', 'I', 'D', 'X'}; + + /** + * @param indexURL + * @param config + * @param tabix + * + */ + + async function loadIndex(indexURL, config, genome) { + let arrayBuffer = await igvxhr.loadArrayBuffer(indexURL, buildOptions(config)); + let dv = new DataView(arrayBuffer); // Some indexs are gzipped, 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 = new Zlib$1.Gunzip(new Uint8Array(arrayBuffer)); + arrayBuffer = inflate.decompress().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}`); + } + } + + class BGZipLineReader { + constructor(config) { + this.config = config; + this.filePtr = 0; + this.bufferPtr = 0; + this.buffer; + } + + async nextLine() { + let result = undefined; + + try { + while (true) { + const length = this.buffer ? this.buffer.length : 0; + + while (this.bufferPtr < length) { + const c = String.fromCharCode(this.buffer[this.bufferPtr++]); + if (c === '\r') continue; + + if (c === '\n') { + return result; + } + + result = result ? result + c : c; + } + + if (this.eof) { + return result; + } else { + await this.readNextBlock(); + } + } + } catch (e) { + console.warn(e); + this.eof = true; + return result; + } + } + + async readNextBlock() { + const bsizeOptions = buildOptions(this.config, { + range: { + start: this.filePtr, + size: 26 + } + }); + const abuffer = await igvxhr.loadArrayBuffer(this.config.url, bsizeOptions); + const bufferSize = bgzBlockSize(abuffer); //console.log(`next block ${this.filePtr} ${bufferSize}`); + + if (bufferSize === 0) { + this.eof = true; + this.buffer = undefined; + } else { + const options = buildOptions(this.config, { + range: { + start: this.filePtr, + size: bufferSize + } + }); + const data = await igvxhr.loadArrayBuffer(this.config.url, options); + + if (data.byteLength < bufferSize) { + this.eof = true; // Assumption + } + + this.buffer = unbgzf(data); + this.bufferPtr = 0; + this.filePtr += data.byteLength; //data.byteLength; + } + } + + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + const isString$5 = isString$6; + /** + * Reader for "bed like" files (tab delimited files with 1 feature per line: bed, gff, vcf, etc) + * + * @param config + * @constructor + */ + + class FeatureFileReader { + constructor(config, genome) { + var uriParts; + this.config = config || {}; + this.genome = genome; + this.indexURL = config.indexURL; + this.indexed = config.indexed; + + if (isFilePath(this.config.url)) { + this.filename = this.config.url.name; + } else if (isString$5(this.config.url) && this.config.url.startsWith('data:')) { + 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."); + } + } + /** + * Return a promise to load features for the genomic interval + * @param chr + * @param start + * @param end + */ + + + async readFeatures(chr, start, end) { + const index = await this.getIndex(); + + if (index) { + this.indexed = true; + return this.loadFeaturesWithIndex(chr, start, end); + } else if (this.dataURI) { + this.indexed = false; + return this.loadFeaturesFromDataURI(); + } else { + this.indexed = false; + return this.loadFeaturesNoIndex(); + } + } + + async readHeader() { + if (this.dataURI) { + await this.loadFeaturesFromDataURI(this.dataURI); + return this.header; + } else { + if (this.config.indexURL) { + const index = await this.getIndex(); + + if (!index) { + // Note - it should be impossible to get here + throw new Error("Unable to load index: " + this.config.indexURL); + } + + let dataWrapper; + + if (index.tabix) { + dataWrapper = new BGZipLineReader(this.config); + } else { + // Tribble + const maxSize = Object.values(index.chrIndex).flatMap(chr => chr.blocks).map(block => block.max).reduce((previous, current) => Math.min(previous, current), Number.MAX_SAFE_INTEGER); + const options = buildOptions(this.config, { + bgz: index.tabix, + range: { + start: 0, + size: maxSize + } + }); + const data = await igvxhr.loadString(this.config.url, options); + dataWrapper = getDataWrapper(data); + } + + this.header = await this.parser.parseHeader(dataWrapper); // Cache header, might be needed to parse features + + return this.header; + } else { + // If this is a non-indexed file we will load all features in advance + const options = buildOptions(this.config); + const data = await igvxhr.loadString(this.config.url, options); + let dataWrapper = getDataWrapper(data); + this.header = await this.parser.parseHeader(dataWrapper); // Reset data wrapper and parse features + + dataWrapper = getDataWrapper(data); + this.features = await this.parser.parseFeatures(dataWrapper); // cache features + + return this.header; + } + } + } + + getParser(config) { + switch (config.format) { + case "vcf": + return new VcfParser(config); + + case "seg": + return new SegParser("seg"); + + case "mut": + return new SegParser("mut"); + + case "maf": + return new SegParser("maf"); + + case "gwas": + return new GWASParser(config); + + case "aed": + return new AEDParser(config); + + default: + return new FeatureParser(config); + } + } + + async loadFeaturesNoIndex() { + if (this.features) { + // An optimization hack for non-indexed files, features are temporarily cached when header is read. + const tmp = this.features; + delete this.features; + return tmp; + } else { + const options = buildOptions(this.config); // Add oauth token, if any + + const data = await igvxhr.loadString(this.config.url, options); + + if (!this.header) { + const dataWrapper = getDataWrapper(data); + this.header = await this.parser.parseHeader(dataWrapper); + } + + const dataWrapper = getDataWrapper(data); + const features = await this.parser.parseFeatures(dataWrapper); // <= PARSING DONE HERE + + return features; + } + } + + async loadFeaturesWithIndex(chr, start, end) { + //console.log("Using index" + const config = this.config; + const parser = this.parser; + const tabix = this.index.tabix; + const refId = tabix ? this.index.sequenceIndexMap[chr] : chr; + + if (refId === undefined) { + return []; + } + + const genome = this.genome; + const blocks = this.index.blocksForRange(refId, start, end); + + if (!blocks || blocks.length === 0) { + return []; + } else { + const allFeatures = []; + + for (let block of blocks) { + const startPos = block.minv.block; + const startOffset = block.minv.offset; + const endOffset = block.maxv.offset; + let endPos; + + if (tabix) { + let lastBlockSize = 0; + + if (endOffset > 0) { + const bsizeOptions = buildOptions(config, { + range: { + start: block.maxv.block, + size: 26 + } + }); + const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions); + lastBlockSize = bgzBlockSize(abuffer); + } + + endPos = block.maxv.block + lastBlockSize; + } else { + endPos = block.maxv.block; + } + + const options = buildOptions(config, { + range: { + start: startPos, + size: endPos - startPos + 1 + } + }); + let inflated; + + if (tabix) { + const data = await igvxhr.loadArrayBuffer(config.url, options); + inflated = unbgzf(data); + } else { + inflated = await igvxhr.loadString(config.url, options); + } + + const slicedData = startOffset ? inflated.slice(startOffset) : inflated; + const dataWrapper = getDataWrapper(slicedData); + const slicedFeatures = await parser.parseFeatures(dataWrapper); // 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 || !this.config.indexURL) { + return this.index; + } else { + 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$4 = isString$6; + + 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$4(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) 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. + */ + /** + * Created by jrobinson on 4/7/16. + */ + + const transcriptTypes = new Set(['transcript', 'primary_transcript', 'processed_transcript', 'mRNA', 'mrna']); + 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 intronType = 'intron'; + + const transcriptModelTypes = new Set(); + + for (let cltn of [transcriptTypes, cdsTypes, codonTypes, utrTypes, exonTypes]) { + for (let t of cltn) { + transcriptModelTypes.add(t); + } + } + + class GFFHelper { + constructor(options) { + this.format = options.format; + this.filterTypes = options.filterTypes === undefined ? new Set(['chromosome']) : new Set(options.filterTypes); + } + + combineFeatures(features) { + let combinedFeatures; + + if ("gff3" === this.format) { + const tmp = this.combineFeaturesById(features); + combinedFeatures = this.combineFeaturesGFF(tmp); + } else { + combinedFeatures = this.combineFeaturesGTF(features); + } + + combinedFeatures.sort(function (a, b) { + return a.start - b.start; + }); + return combinedFeatures; + } + + combineFeaturesById(features) { + const combinedFeatures = []; + const chrIdHash = {}; + + for (let f of features) { + if (f.id === undefined) { + combinedFeatures.push(f); + } else { + let idHash = chrIdHash[f.chr]; + + if (!idHash) { + idHash = {}; + chrIdHash[f.chr] = idHash; + } + + if (idHash.hasOwnProperty(f.id)) { + const sf = idHash[f.id]; + + if (sf.hasOwnProperty("exons")) { + sf.start = Math.min(sf.start, f.start); + sf.end = Math.max(sf.end, f.end); + sf.exons.push(f); + } else { + const cf = { + id: f.id, + type: f.type, + chr: f.chr, + strand: f.strand, + start: Math.min(f.start, sf.start), + end: Math.max(f.end, sf.end), + exons: [sf, f] + }; + + if (f.parent && f.parent.trim() !== "") { + cf.parent = f.parent; + } + + idHash[f.id] = cf; + } + } else { + idHash[f.id] = f; + } + } + } + + for (let key of Object.keys(chrIdHash)) { + const idHash = chrIdHash[key]; + + for (let id of Object.keys(idHash)) { + combinedFeatures.push(idHash[id]); + } + } + + return combinedFeatures; + } + + combineFeaturesGTF(features) { + 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)); // 1. Build dictionary of transcripts + + for (let f of features) { + if (transcriptTypes.has(f.type)) { + const transcriptId = f.id; + + if (undefined !== transcriptId) { + const gffTranscript = new GFFTranscript(f); + transcripts[transcriptId] = gffTranscript; + combinedFeatures.push(gffTranscript); + consumedFeatures.add(f); + } + } + } // Add exons + + + for (let f of features) { + if (exonTypes.has(f.type)) { + const id = f.id; // transcript_id, GTF groups all features with the same ID, does not have a parent/child hierarchy + + if (id) { + let transcript = transcripts[id]; + + if (transcript === undefined) { + transcript = new GFFTranscript(f); // GTF does not require an explicit transcript record + + transcripts[id] = transcript; + combinedFeatures.push(transcript); + } + + transcript.addExon(f); + consumedFeatures.add(f); + } + } + } // Apply CDS and UTR + + + for (let f of features) { + if (cdsTypes.has(f.type) || utrTypes.has(f.type) || codonTypes.has(f.type)) { + const id = f.id; + + if (id) { + let transcript = transcripts[id]; + + if (transcript === undefined) { + transcript = new GFFTranscript(f); + transcripts[id] = transcript; + combinedFeatures.push(transcript); + } + + if (utrTypes.has(f.type)) { + transcript.addUTR(f); + } else if (cdsTypes.has(f.type)) { + transcript.addCDS(f); + } else if (codonTypes.has(f.type)) ; + + consumedFeatures.add(f); + } + } + } // Finish transcripts + + + for (let f of combinedFeatures) { + 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; + } + + combineFeaturesGFF(features) { + // Build dictionary of genes (optional) + const genes = features.filter(f => "gene" === f.type); + 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 (transcriptTypes.has(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.gene = geneMap[f.parent]; + consumedFeatures.add(g); + } + } + } + } // Remove assigned genes + // Add exons + + + for (let f of features) { + if (exonTypes.has(f.type)) { + const parents = getParents(f); + + if (parents) { + for (let id of parents) { + let transcript = transcripts[id]; + + if (transcript !== undefined) { + transcript.addExon(f); + consumedFeatures.add(f); + } + } + } + } + } // Apply CDS and UTR + + + for (let f of features) { + if (cdsTypes.has(f.type) || utrTypes.has(f.type) || codonTypes.has(f.type)) { + const parents = getParents(f); + + if (parents) { + for (let id of parents) { + let transcript = transcripts[id]; + + if (transcript !== undefined) { + if (utrTypes.has(f.type)) { + transcript.addUTR(f); + } else if (cdsTypes.has(f.type)) { + transcript.addCDS(f); + } else if (codonTypes.has(f.type)) ; + + consumedFeatures.add(f); + } + } + } + } + } // Introns are ignored, but are consumed + + + const introns = features.filter(f => intronType === f.type); + + for (let i of introns) { + const parents = getParents(i); + + for (let id of parents) { + if (transcripts[id]) { + consumedFeatures.add(i); + break; + } + } + } // 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; + } + } + } + + } + + var GFFTranscript = function (feature) { + Object.assign(this, feature); + this.exons = []; + }; + + GFFTranscript.prototype.addExon = function (feature) { + this.exons.push(feature); // Expand feature -- for transcripts not explicitly represented in the file + + this.start = Math.min(this.start, feature.start); + this.end = Math.max(this.end, feature.end); + }; + + GFFTranscript.prototype.addCDS = function (cds) { + let exon; + const exons = this.exons; // Find exon containing CDS + + for (let i = 0; i < exons.length; i++) { + if (exons[i].start <= cds.start && exons[i].end >= cds.end) { + exon = exons[i]; + 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; + + if (!exon.children) { + exon.children = []; + } + + exon.children.push(cds); + } else { + cds.cdStart = cds.start; + cds.cdEnd = cds.end; + exons.push(cds); + } // 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; + }; + + GFFTranscript.prototype.addUTR = function (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; + } + } + + if (!exon.children) { + exon.children = []; + } + + exon.children.push(utr); + } else { + utr.utr = true; + exons.push(utr); + } // 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); + }; + + GFFTranscript.prototype.finish = function () { + 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; + }); + } + }; + + GFFTranscript.prototype.popupData = function (genomicLocation) { + const kvs = this.attributeString.split(';'); + const pd = []; // If feature has an associated gene list its attributes first + + if (this.gene && typeof this.gene.popupData === 'function') { + const gd = this.gene.popupData(genomicLocation); + + for (let e of gd) { + pd.push(e); + } + + pd.push("
"); + } + + if (this.name) { + pd.push({ + name: 'name', + value: this.name + }); + } + + pd.push({ + name: 'type', + value: this.type + }); + + for (let kv of kvs) { + var t = kv.trim().split(this.delim, 2); + + if (t.length === 2 && t[1] !== undefined) { + const key = t[0].trim(); + if ('name' === key.toLowerCase()) continue; + let value = t[1].trim(); //Strip off quotes, if any + + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substr(1, value.length - 2); + } + + pd.push({ + name: key, + value: value + }); + } + } + + pd.push({ + name: 'position', + value: `${this.chr}:${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}` + }); // If clicked over an exon add its attributes + + for (let exon of this.exons) { + 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); + } + + if (exon.children) { + for (let c of exon.children) { + pd.push("
"); + const exonData = c.popupData(genomicLocation); + + for (let att of exonData) { + pd.push(att); + } + } + } + } + } + + return pd; + }; + + /* + * The MIT License (MIT) + * + * Copyright (c) 2015 UC San Diego + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + const GtexReader = function (config) { + this.config = config; + this.url = config.url; + this.tissueId = config.tissueSiteDetailId; + this.indexed = true; + this.datasetId = config.datasetId || "gtex_v8"; + }; + + GtexReader.prototype.readFeatures = async function (chr, bpStart, bpEnd) { + let self = this, + queryChr = chr.startsWith("chr") ? chr : "chr" + chr, + queryStart = Math.floor(bpStart), + queryEnd = Math.ceil(bpEnd), + datasetId = this.datasetId, + queryURL = this.url + "?chromosome=" + queryChr + "&start=" + queryStart + "&end=" + queryEnd + "&tissueSiteDetailId=" + this.tissueId + "&datasetId=" + datasetId; + const json = await igvxhr.loadJson(queryURL, { + withCredentials: self.config.withCredentials + }); + + if (json && json.singleTissueEqtl) { + //variants = json.variants; + //variants.sort(function (a, b) { + // return a.POS - b.POS; + //}); + //source.cache = new FeatureCache(chr, queryStart, queryEnd, variants); + json.singleTissueEqtl.forEach(function (eqtl) { + eqtl.chr = eqtl.chromosome; + eqtl.position = eqtl.pos; + eqtl.start = eqtl.pos - 1; + eqtl.end = eqtl.start + 1; + eqtl.snp = eqtl.snpId; + eqtl.geneName = eqtl.geneSymbol; + eqtl.geneId = eqtl.gencodeId; + }); + return json.singleTissueEqtl; + } else { + return undefined; + } + }; + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + // http://immvar.broadinstitute.org:3000/load_data?chromosome=&start=&end=&categories= + + const ImmVarReader = function (config) { + this.config = config; + this.url = config.url; + this.cellConditionId = config.cellConditionId; + this.valueThreshold = config.valueThreshold ? config.valueThreshold : 5E-2; + }; + + ImmVarReader.prototype.readFeatures = function (queryChr, queryStart, queryEnd) { + var self = this, + queryURL = this.url + "?chromosome=" + queryChr + "&start=" + queryStart + "&end=" + queryEnd + "&cell_condition_id=" + this.cellConditionId; + return new Promise(function (fulfill, reject) { + igvxhr.loadJson(queryURL, { + withCredentials: self.config.withCredentials + }).then(function (json) { + if (json) { + //variants = json.variants; + //variants.sort(function (a, b) { + // return a.POS - b.POS; + //}); + //source.cache = new FeatureCache(chr, queryStart, queryEnd, variants); + json.eqtls.forEach(function (eqtl) { + eqtl.chr = eqtl.chromosome; + eqtl.start = eqtl.position; + eqtl.end = eqtl.position + 1; + }); + fulfill(json.eqtls); + } else { + fulfill(null); + } + }).catch(function (error) { + reject(error); + }); + }); + }; + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + const 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 + ""; + } + } + + } + + // 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; + } + } + + class HtsgetReader { + constructor(config, genome) { + this.config = config; + this.genome = genome; + this.format = config.format ? config.format.toUpperCase() : "BAM"; // Backward compatibility + + if (!(this.format === "BAM" || this.format === "VCF")) { + throw Error(`htsget format ${config.format} is not supported`); + } + } + + async readHeaderData() { + const url = `${getUrl(this.config)}?class=header&format=${this.format}`; + const ticket = await igvxhr.loadJson(url, buildOptions(this.config)); + return await this.loadUrls(ticket.htsget.urls); + } + + async readData(chr, start, end) { + const url = `${getUrl(this.config)}?format=${this.format}&referenceName=${chr}&start=${Math.floor(start)}&end=${Math.ceil(end)}`; + const ticket = await igvxhr.loadJson(url, buildOptions(this.config)); + return this.loadUrls(ticket.htsget.urls); + } + + async loadUrls(urls) { + const promiseArray = []; + + for (let urlData of urls) { + if (urlData.url.startsWith('data:')) { + // this is a data-uri + promiseArray.push(Promise.resolve(dataUriToBytes(urlData.url))); + } else { + const options = buildOptions(this.config || {}); + + if (urlData.headers) { + options.headers = Object.assign(options.headers || {}, urlData.headers); + } + + promiseArray.push(igvxhr.loadArrayBuffer(urlData.url, options)); + } + } + + const arrayBuffers = await Promise.all(promiseArray); + return concatArrays(arrayBuffers); + } + + static async inferFormat(config) { + try { + const url = getUrl(config); + const headerURL = `${url}${url.includes("?") ? "&" : "?"}class=header`; + const ticket = await igvxhr.loadJson(headerURL, buildOptions(config)); + + if (ticket.htsget) { + const format = ticket.htsget.format; + + if (!(format === "BAM" || format === "VCF")) { + throw Error(`htsget format ${format} is not supported`); + } + + config.format = format.toLowerCase(); + config.sourceType = "htsget"; + + if (!config.name) { + config.name = await getFilename(config.url); + } + } + } catch (e) {// Errors => this is not an htsget source, not an application error. Ignore + } + } + + } + /** + * Extract the full url from the config. Striving for backward compatibility, "endpoint" and "id" are deprecated. + * + * @param config + */ + + + function getUrl(config) { + if (config.url && config.endpoint && config.id) { + return config.url + config.endpoint + config.id; // Deprecated + } else if (config.endpoint && config.id) { + return config.endpoint + config.id; // Deprecated + } else if (config.url) { + if (config.url.startsWith("htsget://")) { + return config.url.replace("htsget://", "https://"); // htsget -> http not supported + } else { + return config.url; + } + } else { + throw Error("Must specify either 'url', or 'endpoint' and 'id"); + } + } + /** + * Concatenate a list of array buffers, returning an UInt8Array + * @param arrayBuffers + */ + + + function concatArrays(arrayBuffers) { + let len = 0; + + for (let a of arrayBuffers) { + len += a.byteLength; + } + + let offset = 0; + const newArray = new Uint8Array(len); + + for (let buf of arrayBuffers) { + const a = new Uint8Array(buf); + newArray.set(a, offset); + offset += a.length; + } + + return newArray; + } + + function dataUriToBytes(dataUri) { + const split = dataUri.split(','); + const info = split[0].split(':')[1]; + let dataString = split[1]; + + if (info.indexOf('base64') >= 0) { + dataString = atob(dataString); + } else { + dataString = decodeURI(dataString); + } + + const bytes = new Uint8Array(dataString.length); + + for (var i = 0; i < dataString.length; i++) { + bytes[i] = dataString.charCodeAt(i); + } + + return bytes; + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 The Regents of the University of California + * Author: Jim Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + class HtsgetVariantReader extends HtsgetReader { + constructor(config, genome) { + super(config, genome); + this.parser = new VcfParser(); + } + + async readHeader() { + if (!this.header) { + const data = await this.readHeaderData(); + const dataWrapper = getDataWrapper(data); + this.header = await this.parser.parseHeader(dataWrapper, this.genome); + this.chrAliasTable = this.header.chrAliasTable; + } + + return this.header; + } + + async readFeatures(chr, start, end) { + if (this.config.format && this.config.format.toUpperCase() !== "VCF") { + throw Error(`htsget format ${this.config.format} is not supported`); + } + + if (!this.chrAliasTable) { + await this.readHeader(); + } + + let queryChr = this.chrAliasTable.has(chr) ? this.chrAliasTable.get(chr) : chr; + const data = await this.readData(queryChr, start, end); + const dataWrapper = getDataWrapper(data); + return this.parser.parseFeatures(dataWrapper); // return dataWrapper; + } + + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014-2015 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + /** + * 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.visibilityWindow = config.visibilityWindow; + const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "tdf"]); + + if (config.features && Array.isArray(config.features)) { + let features = fixFeatures(config.features, genome); + packFeatures(features); + + if (config.mappings) { + mapProperties(features, config.mappings); + } + + this.queryable = false; + this.featureCache = new FeatureCache(features, genome); + } else if (config.reader) { + this.reader = config.reader; + this.queryable = config.queryable !== undefined ? config.queryable : true; + this.expandQuery = config.expandQuery ? true : 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; + this.expandQuery = config.expandQuery ? true : false; + } else if ("htsget" === config.sourceType) { + this.reader = new HtsgetVariantReader(config, genome); + } else if (config.sourceType === 'ucscservice') { + this.reader = new UCSCServiceReader(config.source); + this.queryable = true; + } else if (config.sourceType === 'custom' || config.source !== undefined) { + // Second test for backward compatibility + this.reader = new CustomServiceReader(config.source); + this.queryable = config.source.queryable !== undefined ? config.source.queryable : true; + this.expandQuery = config.expandQuery ? true : false; + } else if ("civic-ws" === config.sourceType) { + this.reader = new CivicReader(config); + this.queryable = false; + this.expandQuery = config.expandQuery ? true : false; + } else { + this.reader = new FeatureFileReader(config, genome); + + if (config.queryable !== undefined) { + this.queryable = config.queryable; + } else if (queryableFormats.has(config.format)) { + this.queryable = queryableFormats.has(config.format) || this.reader.indexed; + } else ; + } + } + + supportsWholeGenome() { + return !this.queryable && (this.visibilityWindow === undefined || this.visibilityWindow <= 0); + } + + async trackType() { + const header = await this.getHeader(); + + if (header) { + return header.type; + } else { + return undefined; // Convention for unknown or unspecified + } + } + + async getHeader() { + if (!this.header) { + if (this.reader && typeof this.reader.readHeader === "function") { + const header = await this.reader.readHeader(); + + if (header) { + this.header = header; + + if (header.format) { + this.config.format = header.format; + } + } else { + this.header = {}; + } + } else { + this.header = {}; + } + } + + return this.header; + } + /** + * Required function for all data source objects. Fetches features for the + * range requested. + * + * This function is quite complex due to the variety of reader types backing it, some indexed, some queryable, + * some not. The whole scheme could use a refactoring. + * + * @param chr + * @param start + * @param end + * @param bpPerPixel + */ + + + async getFeatures({ + 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 create a feature load + // * view is "whole genome" but no features are loaded + // * cache is disabled + // * cache does not contain requested range + + if (isWholeGenome && !this.getWGFeatures || this.config.disableCache || !this.featureCache || !this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))) { + await this.loadFeatures(start, end, visibilityWindow, queryChr); + } + + if (isWholeGenome) { + if (!this.wgFeatures) { + if (this.queryable) { + // queryable sources don't support whole genome view + this.wgFeatures = []; + } else { + this.wgFeatures = this.getWGFeatures(this.featureCache.getAllFeatures()); + } + } + + return this.wgFeatures; + } else { + return this.featureCache.queryFeatures(queryChr, start, end); + } + } + + async loadFeatures(start, end, visibilityWindow, queryChr) { + 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.expandQuery !== false) { + // Whole chromosome + const chromosome = this.genome ? this.genome.getChromosome(queryChr) : undefined; + intervalStart = 0; + intervalEnd = chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER; + } else if (visibilityWindow > end - start && this.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 ("gtf" === this.config.format || "gff3" === this.config.format || "gff" === this.config.format) { + features = new GFFHelper(this.config).combineFeatures(features); + } // 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(features, this.genome, genomicInterval); // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive + + if (this.config.searchable) { + this.addFeaturesToDB(features); + } + } else { + this.featureCache = new FeatureCache([], genomicInterval); // Empty cache + } + } + + addFeaturesToDB(featureList) { + for (let feature of featureList) { + if (feature.name) { + this.genome.featureDB[feature.name.toUpperCase()] = feature; + } + + if (feature.gene && feature.gene.name) { + this.genome.featureDB[feature.gene.name.toUpperCase()] = feature; + } + } + } // TODO -- filter by pixel size + + + getWGFeatures(allFeatures) { + const genome = this.genome; + const wgChromosomeNames = new Set(genome.wgChromosomeNames); + const wgFeatures = []; + + for (let c of genome.wgChromosomeNames) { + const features = allFeatures[c]; + + if (features) { + for (let f of features) { + let queryChr = genome.getChromosomeName(f.chr); + + if (wgChromosomeNames.has(queryChr)) { + 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"]; + wgFeatures.push(wg); + } + } + } + } + + wgFeatures.sort(function (a, b) { + return a.start - b.start; + }); + return wgFeatures; + } + + } + + function packFeatures(features, maxRows) { + maxRows = maxRows || 1000; + + if (features == null || features.length === 0) { + return; + } // Segregate by chromosome + + + const chrFeatureMap = {}; + const chrs = []; + + for (let feature of features) { + const chr = feature.chr; + let flist = chrFeatureMap[chr]; + + if (!flist) { + flist = []; + chrFeatureMap[chr] = flist; + chrs.push(chr); + } + + flist.push(feature); + } // Loop through chrosomosomes and pack features; + + + for (let chr of chrs) { + pack(chrFeatureMap[chr], maxRows); + } + } + /** + * This function is used to apply properties normally added during parsing to features supplied directly in the + * config as an array of objects. At the moment the only application is bedpe type features. + * @param features + */ + + + function fixFeatures(features, genome) { + if (!features || features.length === 0) return []; + const isBedPE = features[0].chr === undefined && features[0].chr1 !== undefined; + + if (isBedPE) { + const interChrFeatures = []; + + for (let feature of features) { + if (genome) { + feature.chr1 = genome.getChromosomeName(feature.chr1); + feature.chr2 = genome.getChromosomeName(feature.chr2); + } // Set total extent of feature + + + if (feature.chr1 === feature.chr2) { + feature.chr = feature.chr1; + feature.start = Math.min(feature.start1, feature.start2); + feature.end = Math.max(feature.end1, feature.end2); + } else { + interChrFeatures.push(feature); + } + } // Make copies of inter-chr features, one for each chromosome + + + for (let f1 of interChrFeatures) { + const f2 = Object.assign({ + dup: true + }, f1); + features.push(f2); + f1.chr = f1.chr1; + f1.start = f1.start1; + f1.end = f1.end1; + f2.chr = f2.chr2; + f2.start = f2.start2; + f2.end = f2.end2; + } + } else if (genome) { + for (let feature of features) { + feature.chr = genome.getChromosomeName(feature.chr); + } + } + + return features; + } + + function mapProperties(features, mappings) { + let mappingKeys = Object.keys(mappings); + features.forEach(function (f) { + mappingKeys.forEach(function (key) { + f[key] = f[mappings[key]]; + }); + }); + } + + class BufferedReader { + constructor(config, contentLength, bufferSize) { + this.path = config.url; + this.bufferSize = bufferSize ? bufferSize : 512000; + this.range = { + start: -1, + size: -1 + }; + this.config = config; + } + /** + * + * @param requestedRange - byte rangeas {start, size} + * @param fulfill - function to receive result + * @param asUint8 - optional flag to return result as an UInt8Array + */ + + + async dataViewForRange(requestedRange, asUint8) { + const hasData = this.data && this.range.start <= requestedRange.start && this.range.start + this.range.size >= requestedRange.start + requestedRange.size; + + if (!hasData) { + let bufferSize; // If requested range size is specified, potentially expand buffer size + + if (requestedRange.size) { + bufferSize = Math.max(this.bufferSize, requestedRange.size); + } else { + bufferSize = this.bufferSize; + } + + const loadRange = { + start: requestedRange.start, + size: bufferSize + }; + const arrayBuffer = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, { + range: loadRange + })); + this.data = arrayBuffer; + this.range = loadRange; + } + + const len = this.data.byteLength; + const bufferStart = requestedRange.start - this.range.start; + return asUint8 ? new Uint8Array(this.data, bufferStart, len - bufferStart) : new DataView(this.data, bufferStart, len - bufferStart); + } + + } + + function 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 + }; + } + + //table chromatinInteract + + function getDecoder(definedFieldCount, fieldCount, autoSql) { + if (autoSql && 'chromatinInteract' === 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 = parseFloat(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 + }); + } + + 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; + } + } + } + }; + } + + 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.parseFloat(tokens[1]); + feature.value = Number.parseFloat(tokens[2]); + feature.color = tokens[4] === '.' ? undefined : tokens[4] === "0" ? "rgb(0,0,0)" : tokens[4]; + return feature; + } + } + + 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.genome = genome; + this.rpTreeCache = {}; + this.config = config; + } + + 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 igvxhr.loadArrayBuffer(this.config.url, buildOptions(this.config, { + range: { + start: start, + size: size + } + })); // Parse data and return features + + const allFeatures = []; + const buffer = new Uint8Array(arrayBuffer); + + for (let item of leafItems) { + const uint8Array = buffer.subarray(item.dataOffset - start, item.dataOffset + item.dataSize); + let plain; + const isCompressed = this.header.uncompressBuffSize > 0; + + if (isCompressed) { + const inflate = new Zlib$1.Inflate(uint8Array); + plain = inflate.decompress(); + } 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 igvxhr.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 igvxhr.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); + 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) { + this.config = config; + 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 = 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 = 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) { + return false; + } + + return (chrIdx2 > item.startChrom || chrIdx2 === item.startChrom && endBase >= item.startBase) && (chrIdx1 < item.endChrom || chrIdx1 === item.endChrom && startBase <= item.endBase); + } + + class BWTotalSummary { + constructor(byteBuffer) { + if (byteBuffer) { + this.basesCovered = byteBuffer.getLong(); + this.minVal = byteBuffer.getDouble(); + this.maxVal = byteBuffer.getDouble(); + this.sumData = byteBuffer.getDouble(); + this.sumSquares = byteBuffer.getDouble(); + computeStats.call(this); + } else { + this.basesCovered = 0; + this.minVal = 0; + this.maxVal = 0; + this.sumData = 0; + this.sumSquares = 0; + this.mean = 0; + this.stddev = 0; + } + } + + } + + function computeStats() { + let n = this.basesCovered; + + if (n > 0) { + this.mean = this.sumData / n; + this.stddev = Math.sqrt(this.sumSquares / (n - 1)); + let min = this.minVal < 0 ? this.mean - 2 * this.stddev : 0, + max = this.maxVal > 0 ? this.mean + 2 * this.stddev : 0; + this.defaultRange = { + min: min, + max: max + }; + } + } + + function zoomLevelForScale$1(bpPerPixel, zoomLevelHeaders) { + let level; + + for (let i = 0; i < zoomLevelHeaders.length; i++) { + const zl = zoomLevelHeaders[i]; + + if (zl.reductionLevel < bpPerPixel) { + level = zl; + break; + } + } + + return level; + } + + function decodeWigData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict) { + const binaryParser = new BinaryParser(data); + const chromId = binaryParser.getInt(); + let chromStart = binaryParser.getInt(); + let chromEnd = binaryParser.getInt(); + const itemStep = binaryParser.getInt(); + const itemSpan = binaryParser.getInt(); + const type = binaryParser.getByte(); + binaryParser.getByte(); + let itemCount = binaryParser.getUShort(); + + if (chromId >= chrIdx1 && chromId <= chrIdx2) { + while (itemCount-- > 0) { + let value; + + switch (type) { + case 1: + chromStart = binaryParser.getInt(); + chromEnd = binaryParser.getInt(); + value = binaryParser.getFloat(); + break; + + case 2: + chromStart = binaryParser.getInt(); + value = binaryParser.getFloat(); + chromEnd = chromStart + itemSpan; + break; + + case 3: + // Fixed step + value = binaryParser.getFloat(); + chromEnd = chromStart + itemSpan; + chromStart += itemStep; + break; + } + + if (chromId < chrIdx1 || chromId === chrIdx1 && chromEnd < bpStart) continue;else if (chromId > chrIdx2 || chromId === chrIdx2 && chromStart >= bpEnd) break; + + if (Number.isFinite(value)) { + const chr = chrDict[chromId]; + featureArray.push({ + chr: chr, + start: chromStart, + end: chromEnd, + value: value + }); + } + } + } + } + + function getBedDataDecoder() { + const minSize = 3 * 4 + 1; // Minimum # of bytes required for a bed record + + const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql); + 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 + }); + } + } + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + class BWSource { + constructor(config, genome) { + this.reader = new BWReader(config, genome); + this.genome = genome; + this.format = config.format || "bigwig"; + this.wgValues = {}; + } + + async getFeatures({ + chr, + start, + end, + bpPerPixel, + windowFunction + }) { + const features = chr.toLowerCase() === "all" ? await this.getWGValues(windowFunction) : await this.reader.readFeatures(chr, start, chr, end, bpPerPixel, windowFunction); + const isBigWig = this.reader.type === "bigwig"; + + if (!isBigWig) { + pack(features); + } + + return features; + } + + async getHeader() { + return this.reader.loadHeader(); + } + + getDefaultRange() { + if (this.reader.totalSummary !== undefined) { + return this.reader.totalSummary.defaultRange; + } else { + return undefined; + } + } + + async defaultVisibilityWindow() { + return this.reader.defaultVisibilityWindow; + } + + async getWGValues(windowFunction) { + const nominalScreenWidth = 1000; // This doesn't need to be precise + + const genome = this.genome; + + if (this.wgValues[windowFunction]) { + return this.wgValues[windowFunction]; + } else { + const bpPerPixel = genome.getGenomeLength() / nominalScreenWidth; + const features = await this.reader.readWGFeatures(bpPerPixel, windowFunction); + let wgValues = []; + + for (let f of features) { + const chr = f.chr; + const offset = genome.getCumulativeOffset(chr); + const wgFeature = Object.assign({}, f); + wgFeature.chr = "all"; + wgFeature.start = offset + f.start; + wgFeature.end = offset + f.end; + wgValues.push(wgFeature); + } + + this.wgValues[windowFunction] = wgValues; + return wgValues; + } + } + + supportsWholeGenome() { + return this.reader.type === "bigwig" || this.defaultVisibilityWindow() <= 0; + } + + async trackType() { + return this.reader.getTrackType(); + } + + } + + 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.parseFloat(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 inflate = new Zlib$1.Inflate(new Uint8Array(data, start, size)); + const plain = inflate.decompress(); + 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 inflate = new Zlib$1.Inflate(new Uint8Array(data)); + const plain = inflate.decompress(); + data = plain.buffer; + } + + const binaryParser = new BinaryParser(new DataView(data)); + const type = binaryParser.getString(); + + switch (type) { + case "fixedStep": + return createFixedStep(binaryParser, nTracks); + + case "variableStep": + return createVariableStep(binaryParser, nTracks); + + case "bed": + case "bedWithName": + return createBed(binaryParser, nTracks, type); + + default: + throw "Unknown tile type: " + type; + } + } + + } + + function createFixedStep(binaryParser, nTracks) { + const nPositions = binaryParser.getInt(); + const start = binaryParser.getInt(); + const span = binaryParser.getFloat(); + const data = []; + let nt = nTracks; + + while (nt-- > 0) { + let np = nPositions; + const dtrack = []; + + while (np-- > 0) { + dtrack.push(binaryParser.getFloat()); + } + + data.push(dtrack); + } + + return { + type: "fixedStep", + start: start, + span: span, + data: data, + nTracks: nTracks, + nPositions: nPositions + }; + } + + function createVariableStep(binaryParser, nTracks) { + const tileStart = binaryParser.getInt(); + const span = binaryParser.getFloat(); + const nPositions = binaryParser.getInt(); + const start = []; + let np = nPositions; + + while (np-- > 0) { + start.push(binaryParser.getInt()); + } + + binaryParser.getInt(); // # of samples, ignored but should === nTracks + + const data = []; + let nt = nTracks; + + while (nt-- > 0) { + np = nPositions; + const dtrack = []; + + while (np-- > 0) { + dtrack.push(binaryParser.getFloat()); + } + + data.push(dtrack); + } + + return { + type: "variableStep", + tileStart: tileStart, + span: span, + start: start, + data: data, + nTracks: nTracks, + nPositions: nPositions + }; + } + + function createBed(binaryParser, nTracks, type) { + const nPositions = binaryParser.getInt(); + let n = nPositions; + const start = []; + + while (n-- > 0) { + start.push(binaryParser.getInt()); + } + + n = nPositions; + const end = []; + + while (n-- > 0) { + end.push(binaryParser.getInt()); + } + + binaryParser.getInt(); // # of samples, ignored but should === nTracks + + const data = []; + let nt = nTracks; + + while (nt-- > 0) { + let np = nPositions; + const dtrack = []; + + while (np-- > 0) { + dtrack.push(binaryParser.getFloat()); + } + + data.push(dtrack); + } + + if (type === "bedWithName") { + n = nPositions; + const name = []; + + while (n-- > 0) { + name.push(binaryParser.getString()); + } + } + + return { + type: type, + start: start, + end: end, + data: data, + nTracks: nTracks, + nPositions: nPositions + }; + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2016 University of California San Diego + * Author: Jim Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + class TDFSource { + constructor(config, genome) { + this.genome = genome; + this.windowFunction = config.windowFunction || "mean"; + this.reader = new TDFReader(config, genome); + } + + async getFeatures({ + 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; + } + } + } + + if (chr.toLowerCase() === "all") { + return []; // Whole genome view not yet supported + } + + 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; + } + + supportsWholeGenome() { + return false; + } + + } + + 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. + */ + + function FeatureSource(config, genome) { + const format = config.format ? config.format.toLowerCase() : undefined; + + if ('bigwig' === format || 'bigbed' === format || 'bb' === format) { + return new BWSource(config, genome); + } else if ("tdf" === format) { + return new TDFSource(config, genome); + } else { + return new TextFeatureSource(config, genome); + } + } + + const GtexUtils = { + getTissueInfo: function (datasetId, baseURL) { + datasetId = datasetId || 'gtex_v8'; + baseURL = baseURL || 'https://gtexportal.org/rest/v1'; + let url = baseURL + '/dataset/tissueInfo?datasetId=' + datasetId; + return igvxhr.loadJson(url, {}); + }, + //https://gtexportal.org/rest/v1/association/singleTissueEqtlByLocation?chromosome=7&start=98358766&end=101523798&tissueName=Liver&datasetId=gtex_v7 + //https://gtexportal.org/rest/v1/association/singleTissueEqtlByLocation?chromosome=7&start=98358766&end=101523798&tissueSiteDetailId=Liver&datasetId=gtex_v8 + trackConfiguration: function (tissueSummary, baseURL) { + baseURL = baseURL || 'https://gtexportal.org/rest/v1'; + return { + type: "eqtl", + sourceType: "gtex-ws", + url: baseURL + '/association/singleTissueEqtlByLocation', + tissueSiteDetailId: tissueSummary.tissueSiteDetailId, + name: tissueSummary.tissueSiteDetailId.split('_').join(' '), + visibilityWindow: 250000 + }; + } + }; + + /* + * 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 FeatureTrack extends TrackBase { + constructor(config, browser) { + super(config, browser); + } + + init(config) { + super.init(config); + this.type = config.type || "annotation"; // Set maxRows -- protects against pathological feature packing cases (# of rows of overlapping feaures) + + this.maxRows = config.maxRows === undefined ? 1000 : config.maxRows; + this.displayMode = config.displayMode || "EXPANDED"; // COLLAPSED | EXPANDED | SQUISHED + + this.labelDisplayMode = config.labelDisplayMode; + + if (config._featureSource) { + this.featureSource = config._featureSource; + delete config._featureSource; + } else { + this.featureSource = config.featureSource ? config.featureSource : FeatureSource(config, this.browser.genome); + } // Set default heights + + + this.autoHeight = config.autoHeight; + this.margin = config.margin === undefined ? 10 : config.margin; + this.featureHeight = config.featureHeight || 14; + + if ("FusionJuncSpan" === config.type) { + this.squishedRowHeight = config.squishedRowHeight || 50; + this.expandedRowHeight = config.expandedRowHeight || 50; + this.height = config.height || this.margin + 2 * this.expandedRowHeight; + } else if ('snp' === config.type) { + this.expandedRowHeight = config.expandedRowHeight || 10; + this.squishedRowHeight = config.squishedRowHeight || 5; + this.height = config.height || 30; + } else { + this.squishedRowHeight = config.squishedRowHeight || 15; + this.expandedRowHeight = config.expandedRowHeight || 30; + this.height = config.height || this.margin + 2 * this.expandedRowHeight; + } + + if (this.height === undefined || !this.height) { + this.height = 100; + } //set defaults + + + if ('spliceJunctions' === config.type && config.colorByNumReadsThreshold === undefined) { + config.colorByNumReadsThreshold = 5; + } // Set the render function. This can optionally be passed in the config + + + if (config.render) { + this.render = config.render; + } else if ("FusionJuncSpan" === config.type) { + this.render = renderFusionJuncSpan; + } else if ('spliceJunctions' === config.type) { + this.render = renderJunctions; + this.displayMode = "COLLAPSED"; // needed for this.clickedFeatures(..) to work + } else if ('snp' === config.type) { + this.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'; + } else { + this.render = renderFeature; + this.arrowSpacing = 30; // adjust label positions to make sure they're always visible + + monitorTrackDrag(this); + } //UCSC useScore option + + + this.useScore = config.useScore; + } + + async postInit() { + if (typeof this.featureSource.getHeader === "function") { + this.header = await this.featureSource.getHeader(); + } // Set properties from track line + + + if (this.header) { + this.setTrackProperties(this.header); + } + + if (this.visibilityWindow === undefined && typeof this.featureSource.defaultVisibilityWindow === 'function') { + this.visibilityWindow = await this.featureSource.defaultVisibilityWindow(); + this.featureSource.visibilityWindow = this.visibilityWindow; // <- this looks odd + } + + return this; + } + + supportsWholeGenome() { + return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== 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) { + if (this.type === 'spliceJunctions') { + return this.height; + } else 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 = []; + + for (let feature of featureList) { + const row = feature.row || 0; + + if (rowFeatureCount[row] === undefined) { + rowFeatureCount[row] = 1; + } else { + rowFeatureCount[row]++; + } + + options.rowLastX[row] = -Number.MAX_SAFE_INTEGER; + } + + if (this.config.type == 'spliceJunctions') { + junctionRenderingContext.referenceFrame = options.viewport.referenceFrame; + junctionRenderingContext.referenceFrameStart = junctionRenderingContext.referenceFrame.start; + junctionRenderingContext.referenceFrameEnd = junctionRenderingContext.referenceFrameStart + junctionRenderingContext.referenceFrame.toBP($(options.viewport.contentDiv).width()); // For a given viewport, records where features that are < 2px in width have been rendered already. + // This prevents wasteful rendering of multiple such features onto the same pixels. + + junctionRenderingContext.featureZoomOutTracker = {}; + } + + 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; + const featureDensity = pixelWidth / rowFeatureCount[row]; + options.drawLabel = options.labelAllFeatures || featureDensity > 10; + const pxEnd = Math.ceil((feature.end - bpStart) / bpPerPixel); + const last = lastPxEnd[row]; + + if (!last || pxEnd > last || this.config.type === 'spliceJunctions') { + this.render.call(this, feature, bpStart, bpPerPixel, pixelHeight, ctx, options); + + if (this.config.type !== 'spliceJunctions') { + // 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; + } + } + } + } + } + + clickedFeatures(clickState, features) { + const y = clickState.y - this.margin; + const allFeatures = super.clickedFeatures(clickState, features); + let row; + + switch (this.displayMode) { + case 'SQUISHED': + row = Math.floor(y / this.squishedRowHeight); + break; + + case 'EXPANDED': + row = Math.floor(y / this.expandedRowHeight); + break; + + default: + row = undefined; + } + + return allFeatures.filter(function (feature) { + return row === undefined || feature.row === undefined || row === feature.row; + }); + } + /** + * Return "popup data" for feature @ genomic location. Data is an array of key-value pairs + */ + + + popupData(clickState, features) { + features = this.clickedFeatures(clickState, features); + const genomicLocation = clickState.genomicLocation; + const data = []; + + for (let feature of features) { + if (this.config.type === 'spliceJunctions') { + if (!feature.isVisible || !feature.attributes) { + continue; + } + } + + 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; + } + + menuItemList() { + const self = this; + const menuItems = []; + + if (this.render === renderSnp) { + ["function", "class"].forEach(function (colorScheme) { + menuItems.push({ + object: createCheckbox$1('Color by ' + colorScheme, colorScheme === self.colorBy), + click: function () { + self.colorBy = colorScheme; + self.trackView.repaintViews(); + } + }); + }); + menuItems.push({ + object: $('
') + }); + } + + menuItems.push({ + object: $('
') + }); + ["COLLAPSED", "SQUISHED", "EXPANDED"].forEach(function (displayMode) { + const lut = { + "COLLAPSED": "Collapse", + "SQUISHED": "Squish", + "EXPANDED": "Expand" + }; + menuItems.push({ + object: createCheckbox$1(lut[displayMode], displayMode === self.displayMode), + click: function () { + self.displayMode = displayMode; + self.config.displayMode = displayMode; + self.trackView.checkContentHeight(); + self.trackView.repaintViews(); + } + }); + }); + return menuItems; + } + + 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 this.name; + } + } + + /** + * Called when the track is removed. Do any needed cleanup here + */ + dispose() { + this.trackView = undefined; + } + + } + /** + * Monitors track drag events, updates label position to ensure that they're always visible. + * @param track + */ + + + function monitorTrackDrag(track) { + if (track.browser.on) { + track.browser.on('trackdragend', onDragEnd); + track.browser.on('trackremoved', unSubscribe); + } + + function onDragEnd() { + if (track.trackView && track.displayMode !== "SQUISHED") { + track.trackView.repaintViews(); // TODO -- refine this to the viewport that was dragged after DOM refactor + } + } + + function unSubscribe(removedTrack) { + if (track.browser.un && track === removedTrack) { + track.browser.un('trackdrag', onDragEnd); + track.browser.un('trackremoved', unSubscribe); + } + } + } + /** + * @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 + }; + } + /** + * Return color for feature. Called in the context of a FeatureTrack instance. + * @param feature + * @returns {string} + */ + + + function getColorForFeature(feature) { + let color; + + if (this.altColor && "-" === feature.strand) { + color = this.altColor; + } else if (this.color) { + color = this.color; // Explicit setting via menu, or possibly track line if !config.color + } else if (this.config.colorBy) { + const colorByValue = feature[this.config.colorBy.field]; + + if (colorByValue) { + color = this.config.colorBy.pallete[colorByValue]; // This is an undocumented option, and its not clear if its used + } + } else if (feature.color) { + color = feature.color; // Explicit color for feature + } else { + color = this.defaultColor; // Track default + } + + if (feature.alpha && feature.alpha !== 1) { + color = IGVColor.addAlpha(color, feature.alpha); + } else if (this.useScore && feature.score && !Number.isNaN(feature.score)) { + // UCSC useScore option, for scores between 0-1000. See https://genome.ucsc.edu/goldenPath/help/customTrack.html#TRACK + const min = this.config.min ? this.config.min : 0; //getViewLimitMin(track); + + const max = this.config.max ? this.config.max : 1000; //getViewLimitMax(track); + + const alpha = getAlpha(min, max, feature.score); + feature.alpha = alpha; // Avoid computing again + + color = IGVColor.addAlpha(color, alpha); + } + + function getAlpha(min, max, score) { + const binWidth = (max - min) / 9; + const binNumber = Math.floor((score - min) / binWidth); + return Math.min(1.0, 0.2 + binNumber * 0.8 / 9); + } + + return color; + } + /** + * + * @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) { + let color = getColorForFeature.call(this, feature); + ctx.fillStyle = color; + ctx.strokeStyle = color; + let h; + let py; + + if (this.displayMode === "SQUISHED" && feature.row !== undefined) { + h = this.featureHeight / 2; + py = this.margin + this.squishedRowHeight * feature.row; + } else if (this.displayMode === "EXPANDED" && feature.row !== undefined) { + h = this.featureHeight; + py = this.margin + this.expandedRowHeight * feature.row; + } else { + // collapsed + h = this.featureHeight; + py = this.margin; + } + + const 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 + ctx.fillRect(coord.px, py, coord.pw, h); // Arrows + // Do not draw if strand is not +/- + + if (direction !== 0) { + ctx.fillStyle = "white"; + ctx.strokeStyle = "white"; + + for (let x = coord.px + step / 2; x < coord.px1; 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 pixelWidth = options.pixelWidth; + 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; + } + + 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); + } + } + /** + * @param ctx the canvas 2d context + * @param feature + * @param featureX feature start x-coordinate + * @param featureX1 feature end x-coordinate + * @param featureY feature y-coordinate + * @param windowX visible window start x-coordinate + * @param windowX1 visible window end x-coordinate + * @param referenceFrame genomic state + * @param options options + */ + + + function renderFeatureLabel(ctx, feature, featureX, featureX1, featureY, referenceFrame, options) { + let name = feature.name; + if (name === undefined && feature.gene) name = feature.gene.name; + if (name === undefined) name = feature.id || feature.ID; + if (!name || name === '.') return; + let pixelXOffset = options.pixelXOffset || 0; + const t1 = Math.max(featureX, -pixelXOffset); + const t2 = Math.min(featureX1, -pixelXOffset + options.viewportWidth); + const centerX = (t1 + t2) / 2; + let transform; + + if (this.displayMode === "COLLAPSED" && this.labelDisplayMode === "SLANT") { + transform = { + rotate: { + angle: 45 + } + }; + } + + const labelY = getFeatureLabelY(featureY, transform); + let color = getColorForFeature.call(this, feature); + let geneColor; + let gtexSelection = false; + + if (referenceFrame.selection && GtexUtils.gtexLoaded) { + // TODO -- for gtex, figure out a better way to do this + gtexSelection = true; + geneColor = referenceFrame.selection.colorForGene(name); + } + + const geneFontStyle = { + textAlign: "SLANT" === this.labelDisplayMode ? undefined : 'center', + fillStyle: geneColor || color, + strokeStyle: geneColor || color + }; + const textBox = ctx.measureText(name); + const xleft = centerX - textBox.width / 2; + const xright = centerX + textBox.width / 2; + + if (options.labelAllFeatures || xleft > options.rowLastX[feature.row] || gtexSelection) { + options.rowLastX[feature.row] = xright; + IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform); + } + } + + function getFeatureLabelY(featureY, transform) { + return transform ? featureY + 20 : featureY + 25; + } + /** + * + * @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) { + var py; + var rowHeight = this.displayMode === "EXPANDED" ? this.expandedRowHeight : this.squishedRowHeight; + + if (this.display === "COLLAPSED") { + py = this.margin; + } + + if (this.displayMode === "SQUISHED" && feature.row !== undefined) { + py = this.margin + rowHeight * feature.row; + } else if (this.displayMode === "EXPANDED" && feature.row !== undefined) { + py = this.margin + rowHeight * feature.row; + } + + var cy = py + 0.5 * rowHeight; + var topY = cy - 0.5 * rowHeight; + var bottomY = cy + 0.5 * rowHeight; // draw the junction arc + + var junctionLeftPx = Math.round((feature.junction_left - bpStart) / xScale); + var 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 + + var spanningCoords = feature.spanning_frag_coords; + + for (var i = 0; i < spanningCoords.length; i++) { + var spanningInfo = spanningCoords[i]; + var spanLeftPx = Math.round((spanningInfo.left - bpStart) / xScale); + var 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(); + } + } + /** + * + * @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 renderJunctions(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 / parseFloat(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 = parseFloat(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); + } // 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; + } + } + } + + 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; + } + + 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(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(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); + } + } + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + const DEFAULT_COLOR = "rgb(150,150,150)"; + + class WigTrack extends TrackBase { + constructor(config, browser) { + super(config, browser); + } + + init(config) { + super.init(config); + this.type = "wig"; + this.height = config.height || 50; + this.featureType = 'numeric'; + this.paintAxis = paintAxis; + const format = config.format ? config.format.toLowerCase() : config.format; + + if ("bigwig" === format) { + this.featureSource = new BWSource(config, this.browser.genome); + } else if ("tdf" === format) { + this.featureSource = new TDFSource(config, this.browser.genome); + } else { + this.featureSource = FeatureSource(config, this.browser.genome); + } + + this.autoscale = config.autoscale || config.max === undefined; + + if (!this.autoscale) { + this.dataRange = { + min: config.min || 0, + max: config.max + }; + } + + this.windowFunction = config.windowFunction || "mean"; + this.graphType = config.graphType || "bar"; + this.normalize = config.normalize; // boolean, for use with "TDF" files + + this.scaleFactor = config.scaleFactor; // optional scale factor, ignored if normalize === true; + } + + async postInit() { + const header = await this.getHeader(); + if (header) this.setTrackProperties(header); + } + + async getFeatures(chr, start, end, bpPerPixel) { + const features = await this.featureSource.getFeatures({ + chr, + start, + end, + bpPerPixel, + windowFunction: this.windowFunction + }); + + if (this.normalize && this.featureSource.normalizationFactor) { + const scaleFactor = this.featureSource.normalizationFactor; + + for (let f of features) { + f.value *= scaleFactor; + } + } + + if (this.scaleFactor) { + const scaleFactor = this.scaleFactor; + + for (let f of features) { + f.value *= scaleFactor; + } + } + + return features; + } + + menuItemList() { + return MenuUtils.numericDataMenuItems(this.trackView); + } + + async getHeader() { + if (typeof this.featureSource.getHeader === "function") { + this.header = await this.featureSource.getHeader(); + } + + return this.header; + } + + draw(options) { + const features = 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; + let lastPixelEnd = -1; + let lastValue = -1; + let lastNegValue = 1; + const posColor = this.color || DEFAULT_COLOR; + let baselineColor; + + if (typeof posColor === "string" && posColor.startsWith("rgb(")) { + baselineColor = IGVColor.addAlpha(posColor, 0.1); + } + + const yScale = yValue => { + return (this.dataRange.max - yValue) / (this.dataRange.max - this.dataRange.min) * pixelHeight; + }; + + if (features && features.length > 0) { + if (this.dataRange.min === undefined) this.dataRange.min = 0; // Max can be less than min if config.min is set but max left to autoscale. If that's the case there is + // nothing to paint. + + if (this.dataRange.max > this.dataRange.min) { + const y0 = this.dataRange.min == 0 ? pixelHeight : yScale(0); + + for (let f of features) { + if (f.end < bpStart) continue; + if (f.start > bpEnd) break; + const x = Math.floor((f.start - bpStart) / bpPerPixel); + if (isNaN(x)) continue; + let y = yScale(f.value); + const rectEnd = Math.ceil((f.end - bpStart) / bpPerPixel); + const width = Math.max(1, rectEnd - x); + let c = f.value < 0 && this.altColor ? this.altColor : posColor; + const color = typeof c === "function" ? c(f.value) : c; + + if (this.graphType === "points") { + const pointSize = this.config.pointSize || 3; + const px = x + width / 2; + IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, { + "fillStyle": color, + "strokeStyle": color + }); + } else { + let height = y - y0; + + if (Math.abs(height) < 1) { + height = height < 0 ? -1 : 1; + } + + const pixelEnd = x + width; + + if (pixelEnd > lastPixelEnd || f.value >= 0 && f.value > lastValue || f.value < 0 && f.value < lastNegValue) { + IGVGraphics.fillRect(ctx, x, y0, width, height, { + fillStyle: color + }); + } + + lastValue = f.value; + lastPixelEnd = pixelEnd; + } + } // If the track includes negative values draw a baseline + + + if (this.dataRange.min < 0) { + const basepx = this.dataRange.max / (this.dataRange.max - this.dataRange.min) * options.pixelHeight; + IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, { + strokeStyle: baselineColor + }); + } + } + } // Draw guidelines + + + if (this.config.hasOwnProperty('guideLines')) { + for (let line of this.config.guideLines) { + if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) { + let y = yScale(line.y); + let props = { + 'strokeStyle': line['color'], + 'strokeWidth': 2 + }; + if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props); + } + } + } + } + + popupData(clickState, features) { + // We use the featureCache property rather than method to avoid async load. If the + // feature is not already loaded this won't work, but the user wouldn't be mousing over it either. + features = this.clickedFeatures(clickState, features); + + if (features && features.length > 0) { + const genomicLocation = clickState.genomicLocation; + const popupData = []; // Sort features based on distance from click + + features.sort(function (a, b) { + const distA = Math.abs((a.start + a.end) / 2 - genomicLocation); + const distB = Math.abs((b.start + b.end) / 2 - genomicLocation); + return distA - distB; + }); // Display closest 10 + + const displayFeatures = features.length > 10 ? features.slice(0, 10) : features; // Resort in ascending order + + displayFeatures.sort(function (a, b) { + return a.start - b.start; + }); + + for (let selectedFeature of displayFeatures) { + if (selectedFeature) { + if (popupData.length > 0) { + popupData.push("
"); + } + + 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 []; + } + } + + supportsWholeGenome() { + if (typeof this.featureSource.supportsWholeGenome === 'function') { + return this.featureSource.supportsWholeGenome(); + } else { + return false; + } + } + /** + * 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"; + this.isLog = config.isLog; + this.displayMode = config.displayMode || "EXPANDED"; // EXPANDED | SQUISHED -- TODO perhaps set his based on sample count + + 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 + }); // Color table for mutation (mut and maf) tracks + + 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) : + + + this.featureSource = FeatureSource(this.config, this.browser.genome); + this.initialSort = config.sort; + } + + async postInit() { + if (typeof this.featureSource.getHeader === "function") { + this.header = await this.featureSource.getHeader(); + } // Set properties from track line + + + if (this.header) { + this.setTrackProperties(this.header); + } + } + + menuItemList() { + const menuItems = []; + const lut = { + "SQUISHED": "Squish", + "EXPANDED": "Expand", + "FILL": "Fill" + }; + menuItems.push("
"); + menuItems.push("Sample Height:"); + const displayOptions = this.type === 'seg' ? ["SQUISHED", "EXPANDED", "FILL"] : ["SQUISHED", "EXPANDED"]; + + for (let displayMode of displayOptions) { + menuItems.push({ + object: createCheckbox$1(lut[displayMode], displayMode === this.displayMode), + click: () => { + this.displayMode = displayMode; + this.config.displayMode = displayMode; + this.trackView.checkContentHeight(); + this.trackView.repaintViews(); + } + }); + } + + 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, 0, pixelWidth, pixelHeight, { + 'fillStyle': "rgb(255, 255, 255)" + }); + + if (features && features.length > 0) { + this.checkForLog(features); // New segments could conceivably add new samples + + this.updateSampleKeys(features); // Create a map for fast id -> row lookup + + const samples = {}; + this.sampleKeys.forEach(function (id, index) { + samples[id] = index; + }); + let border; + + switch (this.displayMode) { + case "FILL": + this.sampleHeight = pixelHeight / this.sampleKeys.length; + border = 0; + break; + + case "SQUISHED": + this.sampleHeight = this.squishedRowHeight; + border = 0; + break; + + default: + // EXPANDED + this.sampleHeight = this.expandedRowHeight; + border = 1; + } + + const rowHeight = this.sampleHeight; // this.featureMap = new Map() + + for (let segment of features) { + segment.pixelRect = undefined; // !important, reset this in case segment is not drawn + } + + const pixelBottom = pixelTop + pixelHeight; + const bpEnd = bpStart + pixelWidth * bpPerPixel + 1; + const xScale = bpPerPixel; + this.sampleYStart = undefined; + + for (let f of features) { + if (f.end < bpStart) continue; + if (f.start > bpEnd) break; + const sampleKey = f.sampleKey || f.sample; + f.row = samples[sampleKey]; + const y = f.row * rowHeight + border; + + if (undefined === this.sampleYStart) { + this.sampleYStart = y; + } + + const bottom = y + rowHeight; + + if (bottom < pixelTop || y > pixelBottom) { + continue; + } + + const segmentStart = Math.max(f.start, bpStart); // const segmentStart = segment.start; + + let x = Math.round((segmentStart - bpStart) / xScale); + const segmentEnd = Math.min(f.end, bpEnd); // const segmentEnd = segment.end; + + const x1 = Math.round((segmentEnd - bpStart) / xScale); + let w = Math.max(1, x1 - x); + let color; + let h; + + if (this.color) { + if (typeof this.color === "function") { + color = this.color(f); + } else { + color = this.color; + } + } else if ("mut" === this.type) { + color = this.colorTable.getColor(f.value.toLowerCase()); + 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. + * + * @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(); // self.trackView.$viewport.scrollTop(0); + } + + clickedFeatures(clickState, features) { + const allFeatures = super.clickedFeatures(clickState, features); + return filterByRow(allFeatures, clickState.y); + + function filterByRow(features, y) { + return features.filter(function (feature) { + const rect = feature.pixelRect; + return rect && y >= rect.y && y <= rect.y + rect.h; + }); + } + } + + popupData(clickState, featureList) { + featureList = this.clickedFeatures(clickState); + const items = []; + + for (let feature of featureList) { + if (items.length > 0) { + items.push("
"); + } + + if (typeof feature.popupData === 'function') { + const data = feature.popupData(); + Array.prototype.push.apply(items, data); + } else { + const filteredProperties = new Set(['chr', 'start', 'end', 'sample', 'value', 'row', 'color', 'sampleKey', 'uniqueSampleKey', 'sampleId', 'chromosome', 'uniquePatientKey']); // hack for whole genome features, which save the original feature as "_f" + + const f = feature._f || feature; + items.push({ + name: 'Sample', + value: f.sample + }); + const valueString = this.type === 'seg' ? numberFormatter$1(f.value) : f.value; + items.push({ + name: 'Value', + value: valueString + }); + const locus = `${f.chr}:${numberFormatter$1(f.start + 1)}-${numberFormatter$1(f.end)}`; + items.push({ + name: 'Locus', + value: locus + }); + items.push('
'); + + for (let property of Object.keys(f)) { + if (!filteredProperties.has(property) && isSimpleType(f[property])) { + items.push({ + name: property, + value: f[property] + }); + } + } + } + } + + 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.getCachedFeatures(); + 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; + } + }]; + } + + 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. + */ + + class MergedTrack extends TrackBase { + constructor(config, browser) { + super(config, browser); + } + + init(config) { + if (!config.tracks) { + throw Error("Error: no tracks defined for merged track" + config); + } + + super.init(config); + } + + async postInit() { + this.tracks = []; + + for (let tconf of this.config.tracks) { + if (!tconf.type) inferTrackType(tconf); + 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); + } + } + + Object.defineProperty(this, "height", { + get() { + return this._height; + }, + + set(h) { + this._height = h; + + for (let t of this.tracks) { + t.height = h; + t.config.height = h; + } + } + + }); + this.height = this.config.height || 100; + } + + async getFeatures(chr, bpStart, bpEnd, bpPerPixel) { + const promises = this.tracks.map(t => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel)); + return Promise.all(promises); + } + + draw(options) { + var i, len, mergedFeatures, trackOptions, dataRange; + mergedFeatures = options.features; // Array of feature arrays, 1 for each track + + dataRange = autoscale(options.referenceFrame.chr, mergedFeatures); //IGVGraphics.fillRect(options.context, 0, options.pixelTop, options.pixelWidth, options.pixelHeight, {'fillStyle': "rgb(255, 255, 255)"}); + + for (i = 0, len = this.tracks.length; i < len; i++) { + trackOptions = Object.assign({}, options); + trackOptions.features = mergedFeatures[i]; + this.tracks[i].dataRange = dataRange; + this.tracks[i].draw(trackOptions); + } + } + + paintAxis(ctx, pixelWidth, pixelHeight) { + var i, len, autoscale, track; + autoscale = true; // Hardcoded for now + + for (i = 0, len = this.tracks.length; i < len; i++) { + track = this.tracks[i]; + + if (typeof track.paintAxis === 'function') { + track.paintAxis(ctx, pixelWidth, pixelHeight); + if (autoscale) break; + } + } + } + + popupData(clickState, features) { + const featuresArray = features || clickState.viewport.getCachedFeatures(); + + 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) { + var min = 0, + max = -Number.MAX_VALUE; + // if (chr === 'all') { + // allValues = []; + // featureArrays.forEach(function (features) { + // features.forEach(function (f) { + // if (!Number.isNaN(f.value)) { + // allValues.push(f.value); + // } + // }); + // }); + // + // min = Math.min(0, IGVMath.percentile(allValues, .1)); + // max = IGVMath.percentile(allValues, 99.9); + // + // } + // else { + + featureArrays.forEach(function (features, i) { + features.forEach(function (f) { + if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) { + min = Math.min(min, f.value); + max = Math.max(max, f.value); + } + }); + }); // } + + return { + min: min, + max: max + }; + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2016 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 + } + + 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 + } + } + + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2014 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + const isString$3 = isString$6; + const hashCode = hashCode$1; + + class BamAlignmentRow { + constructor() { + this.alignments = []; + this.score = undefined; + } + + findAlignment(genomicLocation) { + const alignmentContains = (a, genomicLocation) => { + return genomicLocation >= a.start && genomicLocation < a.start + a.lengthOnRef; + }; // find single alignment that overlaps sort location + + + let centerAlignment; + + for (let i = 0; i < this.alignments.length; i++) { + const a = this.alignments[i]; + + if (genomicLocation >= a.start && genomicLocation < a.start + a.lengthOnRef) { + if (a.paired) { + if (a.firstAlignment && alignmentContains(a.firstAlignment, genomicLocation)) { + centerAlignment = a.firstAlignment; + } else if (a.secondAlignment && alignmentContains(a.secondAlignment, genomicLocation)) { + centerAlignment = a.secondAlignment; + } + } else { + centerAlignment = a; + } + + break; + } + } + + return centerAlignment; + } + + updateScore(options, alignmentContainer) { + this.score = this.calculateScore(options, alignmentContainer); + } + + calculateScore({ + position, + option, + direction, + tag + }, alignmentContainer) { + if (!option) option = "BASE"; + const alignment = this.findAlignment(position); + + if (undefined === alignment) { + return Number.MAX_VALUE * (direction ? 1 : -1); + } + + let mate; + + switch (option) { + case "NUCLEOTIDE": + case "BASE": + { + return calculateBaseScore(alignment, alignmentContainer, position); + } + + case "STRAND": + return alignment.strand ? 1 : -1; + + case "START": + return alignment.start; + + case "TAG": + { + const tagValue = alignment.tags()[tag]; + + if (tagValue !== undefined) { + return isString$3(tagValue) ? hashCode(tagValue) : tagValue; + } else { + return Number.MAX_VALUE; + } + } + + case "READ_NAME": + return hashCode(alignment.readName); + + case "INSERT_SIZE": + return -Math.abs(alignment.fragmentLength); + + case "GAP_SIZE": + return -alignment.gapSizeAt(position); + + case "MATE_CHR": + mate = alignment.mate; + + if (!mate) { + return Number.MAX_VALUE; + } else { + if (mate.chr === alignment.chr) { + return Number.MAX_VALUE - 1; + } else { + return hashCode(mate.chr); + } + } + + case "MQ": + return alignment.mq === undefined ? Number.MAX_VALUE : -alignment.mq; + + default: + return Number.MAX_VALUE; + } + + function calculateBaseScore(alignment, alignmentContainer, genomicLocation) { + let reference; + const idx = Math.floor(genomicLocation) - alignmentContainer.start; + + if (idx < alignmentContainer.sequence.length) { + reference = alignmentContainer.sequence.charAt(idx); + } + + if (!reference) { + return 0; + } + + const base = alignment.readBaseAt(genomicLocation); + const quality = alignment.readBaseQualityAt(genomicLocation); + const coverageMap = alignmentContainer.coverageMap; + const coverageMapIndex = Math.floor(genomicLocation - coverageMap.bpStart); + const coverage = coverageMap.coverage[coverageMapIndex]; // Insertions. These are additive with base scores as they occur between bases, so you can have a + // base mismatch AND an insertion + + let baseScore = 0; + + if (alignment.insertions) { + for (let ins of alignment.insertions) { + if (ins.start === genomicLocation) { + baseScore = -coverage.ins; + } + } + } + + if (!base) { + // Either deletion or skipped (splice junction) + const delCount = coverage.del; + + if (delCount > 0) { + baseScore -= delCount; + } else if (baseScore === 0) { + // Don't modify insertion score, if any + baseScore = 1; + } + } else { + reference = reference.toUpperCase(); + + if ('N' === base && baseScore === 0) { + baseScore = 2; + } else if ((reference === base || '=' === base) && baseScore === 0) { + baseScore = 4 - quality / 1000; + } else if ("X" === base || reference !== base) { + const count = coverage["pos" + base] + coverage["neg" + base]; + baseScore -= count + quality / 1000; + } + } + + return baseScore; + } + } + + } + + function canBePaired(alignment) { + return alignment.isPaired() && alignment.mate && alignment.isMateMapped() && alignment.chr === alignment.mate.chr && (alignment.isFirstOfPair() || alignment.isSecondOfPair()) && !(alignment.isSecondary() || alignment.isSupplementary()); + } + + function pairAlignments(rows) { + const pairCache = {}; + const result = []; + + for (let row of rows) { + for (let alignment of row.alignments) { + if (canBePaired(alignment)) { + let pairedAlignment = pairCache[alignment.readName]; + + if (pairedAlignment) { + pairedAlignment.setSecondAlignment(alignment); + pairCache[alignment.readName] = undefined; // Don't need to track this anymore. + } else { + pairedAlignment = new PairedAlignment(alignment); + pairCache[alignment.readName] = pairedAlignment; + result.push(pairedAlignment); + } + } else { + result.push(alignment); + } + } + } + + return result; + } + + function unpairAlignments(rows) { + const result = []; + + for (let row of rows) { + for (let alignment of row.alignments) { + if (alignment instanceof PairedAlignment) { + if (alignment.firstAlignment) result.push(alignment.firstAlignment); // shouldn't need the null test + + if (alignment.secondAlignment) result.push(alignment.secondAlignment); + } else { + result.push(alignment); + } + } + } + + return result; + } + + function packAlignmentRows(alignments, start, end, showSoftClips) { + if (!alignments) { + return undefined; + } else if (alignments.length === 0) { + return []; + } else { + alignments.sort(function (a, b) { + return showSoftClips ? a.scStart - b.scStart : a.start - b.start; + }); // bucketStart = Math.max(start, alignments[0].start); + + const firstAlignment = alignments[0]; + let bucketStart = Math.max(start, showSoftClips ? firstAlignment.scStart : firstAlignment.start); + let nextStart = bucketStart; + const bucketList = []; + + for (let alignment of alignments) { + //var buckListIndex = Math.max(0, alignment.start - bucketStart); + const s = showSoftClips ? alignment.scStart : alignment.start; + const buckListIndex = Math.max(0, s - bucketStart); + + if (bucketList[buckListIndex] === undefined) { + bucketList[buckListIndex] = []; + } + + bucketList[buckListIndex].push(alignment); + } + + let allocatedCount = 0; + let lastAllocatedCount = 0; + const packedAlignmentRows = []; + const alignmentSpace = 2; + + try { + while (allocatedCount < alignments.length) { + const alignmentRow = new BamAlignmentRow(); + + while (nextStart <= end) { + let bucket = undefined; + let index; + + while (!bucket && nextStart <= end) { + index = nextStart - bucketStart; + + if (bucketList[index] === undefined) { + ++nextStart; // No alignments at this index + } else { + bucket = bucketList[index]; + } + } // while (bucket) + + + if (!bucket) { + break; + } + + const alignment = bucket.pop(); + + if (0 === bucket.length) { + bucketList[index] = undefined; + } + + alignmentRow.alignments.push(alignment); + nextStart = showSoftClips ? alignment.scStart + alignment.scLengthOnRef + alignmentSpace : alignment.start + alignment.lengthOnRef + alignmentSpace; + ++allocatedCount; + } // while (nextStart) + + + if (alignmentRow.alignments.length > 0) { + packedAlignmentRows.push(alignmentRow); + } + + nextStart = bucketStart; + if (allocatedCount === lastAllocatedCount) break; // Protect from infinite loops + + lastAllocatedCount = allocatedCount; + } // while (allocatedCount) + + } catch (e) { + console.error(e); + throw e; + } + + return packedAlignmentRows; + } + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2016 University of California San Diego + * Author: Jim Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + class AlignmentContainer { + 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(); + }; + + this.pairedEndStats = new PairedEndStats(); + } + + push(alignment) { + if (this.filter(alignment) === false) return; + + if (alignment.isPaired()) { + this.pairedEndStats.push(alignment); + } + + 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; + this.pairedEndStats.compute(); + } + + 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); + } + + allAlignments() { + const alignments = []; + + for (let row of this.packedAlignmentRows) { + for (let alignment of row.alignments) { + alignments.push(alignment); + } + } + + return alignments; + } + + getMax(start, end) { + return this.coverageMap.getMax(start, end); + } + + } + + class DownsampleBucket { + constructor(start, end, alignmentContainer) { + this.start = start; + this.end = end; + this.alignments = []; + this.downsampledCount = 0; + this.samplingDepth = alignmentContainer.samplingDepth; + this.pairsSupported = alignmentContainer.pairsSupported; + this.downsampledReads = alignmentContainer.downsampledReads; + this.pairsCache = alignmentContainer.pairsCache; + } + + addAlignment(alignment) { + var idx, replacedAlignment, pairedAlignment; + + if (this.pairsSupported && canBePaired(alignment)) { + pairedAlignment = this.pairsCache[alignment.readName]; + + if (pairedAlignment) { + // Not subject to downsampling, just update the existing alignment + pairedAlignment.setSecondAlignment(alignment); + this.pairsCache[alignment.readName] = undefined; // Don't need to track this anymore. NOTE: Don't "delete", causes runtime performance issues + + return; + } + } + + if (this.alignments.length < this.samplingDepth) { + if (this.pairsSupported && canBePaired(alignment)) { + // First alignment in a pair + pairedAlignment = new PairedAlignment(alignment); + this.paired = true; + this.pairsCache[alignment.readName] = pairedAlignment; + this.alignments.push(pairedAlignment); + } else { + this.alignments.push(alignment); + } + } else { + idx = Math.floor(Math.random() * (this.samplingDepth + this.downsampledCount - 1)); + + if (idx < this.samplingDepth) { + // Keep the new item + // idx = Math.floor(Math.random() * (this.alignments.length - 1)); + replacedAlignment = this.alignments[idx]; // To be replaced + + if (this.pairsSupported && canBePaired(alignment)) { + if (this.pairsCache[replacedAlignment.readName] !== undefined) { + this.pairsCache[replacedAlignment.readName] = undefined; + } + + pairedAlignment = new PairedAlignment(alignment); + this.paired = true; + this.pairsCache[alignment.readName] = pairedAlignment; + this.alignments[idx] = pairedAlignment; + } else { + this.alignments[idx] = alignment; + } + + this.downsampledReads.add(replacedAlignment.readName); + } else { + this.downsampledReads.add(alignment.readName); + } + + this.downsampledCount++; + } + } + + } + + class CoverageMap { + constructor(chr, start, end, alleleFreqThreshold) { + this.chr = chr; + this.bpStart = start; + this.length = end - start; + this.coverage = new Array(this.length); + this.maximum = 0; + this.threshold = alleleFreqThreshold; + this.qualityWeight = true; + } + /** + * Return the maximum coverage value between start and end. This is used for autoscaling. + * @param start + * @param end + */ + + + getMax(start, end) { + let max = 0; + const len = this.coverage.length; + + for (let i = 0; i < len; i++) { + const pos = this.bpStart + i; + if (pos > end) break; + const cov = this.coverage[i]; + + if (pos >= start && cov) { + max = Math.max(max, cov.total); + } + } + + return max; + } + + incCounts(alignment) { + var self = this; + + if (alignment.blocks === undefined) { + incBlockCount(alignment); + } else { + alignment.blocks.forEach(function (block) { + incBlockCount(block); + }); + } + + if (alignment.gaps) { + for (let del of alignment.gaps) { + if (del.type === 'D') { + const offset = del.start - self.bpStart; + + for (let i = offset; i < offset + del.len; i++) { + if (i < 0) continue; + + if (!this.coverage[i]) { + this.coverage[i] = new Coverage(self.threshold); + } + + this.coverage[i].del++; + } + } + } + } + + if (alignment.insertions) { + for (let del of alignment.insertions) { + const i = del.start - this.bpStart; + if (i < 0) continue; + + if (!this.coverage[i]) { + this.coverage[i] = new Coverage(self.threshold); + } + + this.coverage[i].ins++; + } + } + + function incBlockCount(block) { + if ('S' === block.type) return; + const seq = alignment.seq; + const qual = alignment.qual; + const seqOffset = block.seqOffset; + + for (let i = block.start - self.bpStart, j = 0; j < block.len; i++, j++) { + if (!self.coverage[i]) { + self.coverage[i] = new Coverage(self.threshold); + } + + const base = seq == undefined ? "N" : seq.charAt(seqOffset + j); + const key = alignment.strand ? "pos" + base : "neg" + base; + const q = qual && seqOffset + j < qual.length ? qual[seqOffset + j] : 30; + self.coverage[i][key] += 1; + self.coverage[i]["qual" + base] += q; + self.coverage[i].total += 1; + self.coverage[i].qual += q; + self.maximum = Math.max(self.coverage[i].total, self.maximum); + } + } + } + + } + + class Coverage { + constructor(alleleThreshold) { + this.qualityWeight = true; + this.posA = 0; + this.negA = 0; + this.posT = 0; + this.negT = 0; + this.posC = 0; + this.negC = 0; + this.posG = 0; + this.negG = 0; + this.posN = 0; + this.negN = 0; + this.pos = 0; + this.neg = 0; + this.qualA = 0; + this.qualT = 0; + this.qualC = 0; + this.qualG = 0; + this.qualN = 0; + this.qual = 0; + this.total = 0; + this.del = 0; + this.ins = 0; + this.threshold = alleleThreshold; + } + + isMismatch(refBase) { + const threshold = this.threshold * (this.qualityWeight && this.qual ? this.qual : this.total); + let mismatchQualitySum = 0; + + for (let base of ["A", "T", "C", "G"]) { + if (base !== refBase) { + mismatchQualitySum += this.qualityWeight && this.qual ? this["qual" + base] : this["pos" + base] + this["neg" + base]; + } + } + + return mismatchQualitySum >= threshold; + } + + } + + class DownsampledInterval { + constructor(start, end, counts) { + this.start = start; + this.end = end; + this.counts = counts; + } + + popupData(genomicLocation) { + return [{ + name: "start", + value: this.start + 1 + }, { + name: "end", + value: this.end + }, { + name: "# downsampled:", + value: this.counts + }]; + } + + } + + class PairedEndStats { + constructor(lowerPercentile, upperPercentile) { + this.totalCount = 0; + this.frCount = 0; + this.rfCount = 0; + this.ffCount = 0; + this.sumF = 0; + this.sumF2 = 0; //this.lp = lowerPercentile === undefined ? 0.005 : lowerPercentile; + //this.up = upperPercentile === undefined ? 0.995 : upperPercentile; + //this.digest = new Digest(); + } + + push(alignment) { + if (alignment.isProperPair()) { + var fragmentLength = Math.abs(alignment.fragmentLength); //this.digest.push(fragmentLength); + + this.sumF += fragmentLength; + this.sumF2 += fragmentLength * fragmentLength; + 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++; + } + } + + compute() { + if (this.totalCount > 100) { + 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"; + var fMean = this.sumF / this.totalCount; + var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount)); + this.lowerFragmentLength = fMean - 3 * stdDev; + this.upperFragmentLength = fMean + 3 * stdDev; //this.lowerFragmentLength = this.digest.percentile(this.lp); + //this.upperFragmentLength = this.digest.percentile(this.up); + //this.digest = 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. + */ + 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 + }; + /** + * 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; + } + + 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; + + 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; + } + } + /** + * 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 + }); + nameValues.push({ + name: 'Cigar', + value: this.cigar + }); + nameValues.push({ + name: 'Mapped', + value: yesNo(this.isMapped()) + }); + 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 + + } + + nameValues.push("
"); + const tagDict = this.tags(); + let isFirst = true; + + for (let key in tagDict) { + if (tagDict.hasOwnProperty(key)) { + if (isFirst) { + nameValues.push({ + name: key, + value: tagDict[key], + borderTop: true + }); + isFirst = false; + } else { + nameValues.push({ + name: key, + value: tagDict[key] + }); + } + } + } + + 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; + } + + } + + 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 readInt$1(ba, offset) { + return ba[offset + 3] << 24 | ba[offset + 2] << 16 | ba[offset + 1] << 8 | ba[offset]; + } + + function readShort(ba, offset) { + return ba[offset + 1] << 8 | ba[offset]; + } + + function readFloat(ba, offset) { + const dataView = new DataView(ba.buffer); + return dataView.getFloat32(offset); + } + + function readInt8(ba, offset) { + const dataView = new DataView(ba.buffer); + return dataView.getInt8(offset); + } + + function readUInt8(ba, offset) { + const dataView = new DataView(ba.buffer); + return dataView.getUint8(offset); + } + + /* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 The Regents of the University of California + * Author: Jim Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + /** + * Created by jrobinso on 4/5/18. + */ + class AlignmentBlock { + constructor(b) { + if (b) { + Object.assign(this, b); + } + } + + seqIndexAt(genomicLocation) { + return Math.floor(genomicLocation) - this.start + this.seqOffset; + } + + } + + class BamFilter { + constructor(options) { + if (!options) options = {}; + this.vendorFailed = options.vendorFailed === undefined ? true : options.vendorFailed; + this.duplicates = options.duplicates === undefined ? true : options.duplicates; + this.secondary = options.secondary || false; + this.supplementary = options.supplementary || false; + this.mqThreshold = options.mqThreshold === undefined ? 0 : options.mqThreshold; + + if (options.readgroups) { + this.readgroups = new Set(options.readgroups); + } + } + + pass(alignment) { + if (this.vendorFailed && alignment.isFailsVendorQualityCheck()) return false; + if (this.duplicates && alignment.isDuplicate()) return false; + if (this.secondary && alignment.isSecondary()) return false; + if (this.supplementary && alignment.isSupplementary()) return false; + if (alignment.mq < this.mqThreshold) return false; + + if (this.readgroups) { + var rg = alignment.tags()['RG']; + return this.readgroups.has(rg); + } + + return true; + } + + } + + /** + * This code is based on the Biodalliance BAM reader by Thomas Down, 2011 + * + * https://github.com/dasmoth/dalliance/blob/master/js/bam.js + */ + + const SEQ_DECODER = ['=', 'A', 'C', 'x', 'G', 'x', 'x', 'x', 'T', 'x', 'x', 'x', 'x', 'x', 'x', 'N']; + const CIGAR_DECODER = ['M', 'I', 'D', 'N', 'S', 'H', 'P', '=', 'X', '?', '?', '?', '?', '?', '?', '?']; + const READ_STRAND_FLAG$1 = 0x10; + const MATE_STRAND_FLAG$1 = 0x20; + const BAM1_MAGIC_BYTES = new Uint8Array([0x42, 0x41, 0x4d, 0x01]); // BAM\1 + + const BAM1_MAGIC_NUMBER = readInt(BAM1_MAGIC_BYTES, 0); + const DEFAULT_ALLELE_FREQ_THRESHOLD = 0.2; + const DEFAULT_SAMPLING_WINDOW_SIZE = 100; + const DEFAULT_SAMPLING_DEPTH = 500; + const MAXIMUM_SAMPLING_DEPTH = 10000; + const BamUtils = { + readHeader: function (url, options, genome) { + return igvxhr.loadArrayBuffer(url, options).then(function (compressedBuffer) { + var header, unc, uncba; + unc = unbgzf(compressedBuffer); + uncba = unc; + header = BamUtils.decodeBamHeader(uncba, genome); + return header; + }); + }, + + /** + * + * @param ba bytes to decode as a UInt8Array + * @param genome optional igv genome object + * @returns {{ magicNumer: number, size: number, chrNames: Array, chrToIndex: ({}|*), chrAliasTable: ({}|*) }} + */ + decodeBamHeader: function (ba, genome) { + var magic, samHeaderLen, samHeader, chrToIndex, chrNames, chrAliasTable, alias; + magic = readInt(ba, 0); + + if (magic !== BAM1_MAGIC_NUMBER) { + throw new Error('BAM header errror: bad magic number. This could be caused by either a corrupt or missing file.'); + } + + samHeaderLen = readInt(ba, 4); + samHeader = ''; + + for (var i = 0; i < samHeaderLen; ++i) { + samHeader += String.fromCharCode(ba[i + 8]); + } + + var nRef = readInt(ba, samHeaderLen + 8); + var p = samHeaderLen + 12; + chrToIndex = {}; + chrNames = []; + chrAliasTable = {}; + + for (i = 0; i < nRef; ++i) { + var lName = readInt(ba, p); + var name = ''; + + for (var j = 0; j < lName - 1; ++j) { + name += String.fromCharCode(ba[p + 4 + j]); + } + + readInt(ba, p + lName + 4); //dlog(name + ': ' + lRef); + + chrToIndex[name] = i; + chrNames[i] = name; + + if (genome) { + alias = genome.getChromosomeName(name); + chrAliasTable[alias] = name; + } + + p = p + 8 + lName; + } + + return { + magicNumber: magic, + size: p, + chrNames: chrNames, + chrToIndex: chrToIndex, + chrAliasTable: chrAliasTable + }; + }, + bam_tag2cigar: function (ba, block_end, seq_offset, lseq, al, cigarArray) { + function type2size(x) { + if (x === 'C' || x === 'c' || x === 'A') return 1;else if (x === 'S' || x === 's') return 2;else if (x === 'I' || x === 'i' || x === 'f') return 4;else return 0; + } // test if the real CIGAR is encoded in a CG:B,I tag + + + if (cigarArray.length !== 1 || al.start < 0) return false; + var p = seq_offset + (lseq + 1 >> 1) + lseq; + + while (p + 4 < block_end) { + var tag = String.fromCharCode(ba[p]) + String.fromCharCode(ba[p + 1]); + if (tag === 'CG') break; + var type = String.fromCharCode(ba[p + 2]); + + if (type === 'B') { + // the binary array type + type = String.fromCharCode(ba[p + 3]); + var size = type2size(type); + var len = readInt(ba, p + 4); + p += 8 + size * len; + } else if (type === 'Z' || type === 'H') { + // 0-terminated string + p += 3; + + while (ba[p++] !== 0) {} + } else { + // other atomic types + p += 3 + type2size(type); + } + } + + if (p >= block_end) return false; // no CG tag + + if (String.fromCharCode(ba[p + 2]) !== 'B' || String.fromCharCode(ba[p + 3]) !== 'I') return false; // not of type B,I + // now we know the real CIGAR length and its offset in the binary array + + var cigar_len = readInt(ba, p + 4); + var cigar_offset = p + 8; // 4 for "CGBI" and 4 for length + + if (cigar_offset + cigar_len * 4 > block_end) return false; // out of bound + // decode CIGAR + + var cigar = ''; + var lengthOnRef = 0; + cigarArray.length = 0; // empty the old array + + p = cigar_offset; + + for (var k = 0; k < cigar_len; ++k, p += 4) { + var cigop = readInt(ba, p); + var opLen = cigop >> 4; + var opLtr = CIGAR_DECODER[cigop & 0xf]; + if (opLtr === 'M' || opLtr === 'EQ' || opLtr === 'X' || opLtr === 'D' || opLtr === 'N' || opLtr === '=') lengthOnRef += opLen; + cigar = cigar + opLen + opLtr; + cigarArray.push({ + len: opLen, + ltr: opLtr + }); + } // update alignment record. We are not updating bin, as apparently it is not used. + + + al.cigar = cigar; + al.lengthOnRef = lengthOnRef; + return true; + }, + + /** + * + * @param ba bytes to decode as an UInt8Array + * @param offset offset position of ba array to start decoding + * @param alignmentContainer container to receive the decoded alignments + * @param min minimum genomic position + * @param max maximum genomic position + * @param chrIdx chromosome index + * @param chrNames array of chromosome names + * @param filter a BamFilter object + * + * @return true if we have moved beyond the right end of the genomic range. + */ + decodeBamRecords: function (ba, offset, alignmentContainer, chrNames, chrIdx, min, max, filter) { + while (offset < ba.length) { + const blockSize = readInt(ba, offset); + const blockEnd = offset + blockSize + 4; + const alignment = new BamAlignment(); + const refID = readInt(ba, offset + 4); + const pos = readInt(ba, offset + 8); + + if (blockEnd > ba.length) { + return; + } + + if (refID < 0) { + offset = blockEnd; + continue; // unmapped read + } else if (chrIdx !== undefined && (refID > chrIdx || pos > max)) { + return true; // off right edge, we're done + } else if (chrIdx !== undefined && refID < chrIdx) { + offset = blockEnd; + continue; // ref ID to left of start, not sure this is possible + } + + const bin_mq_nl = readInt(ba, offset + 12); + const mq = (bin_mq_nl & 0xff00) >> 8; + const nl = bin_mq_nl & 0xff; + const flag_nc = readInt(ba, offset + 16); + const flag = (flag_nc & 0xffff0000) >> 16; + const nc = flag_nc & 0xffff; + const lseq = readInt(ba, offset + 20); + const mateChrIdx = readInt(ba, offset + 24); + const matePos = readInt(ba, offset + 28); + const tlen = 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 = tlen; + 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$3(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) { + 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(''); + } + } + }; + /** + * 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; + } + } + + 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; + } + + const isString$2 = isString$6; + /** + * 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 = isString$2(config.url) && config.url.startsWith("data:"); + BamUtils.setReaderDefaults(this, config); + } // Return an alignment container + + + async readAlignments(chr, bpStart, bpEnd) { + this.genome; + + 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.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold); + + 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(this, 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); + } + } + } + + } + + 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; // Todo - deal with Picard convention. WHY DOES THERE HAVE TO BE 2? + + this.baiPath = config.indexURL; + + if (!this.baiPath && config.indexed !== false && !(this.bamPath instanceof File)) { + this.baiPath = inferIndexPath(this.bamPath, "bai"); + console.error(`Warning: no indexURL specified for ${this.config.url}. Guessing ${this.baiPath}`); + } + + BamUtils.setReaderDefaults(this, config); + } + + async readAlignments(chr, bpStart, bpEnd) { + const chrToIndex = await this.getChrIndex(); + const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr; + const chrId = chrToIndex[queryChr]; + const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config.samplingWindowSize, this.config.samplingDepth, this.config.pairsSupported, this.config.alleleFreqThreshold); + + if (chrId === undefined) { + return alignmentContainer; + } else { + const bamIndex = await this.getIndex(); + const chunks = bamIndex.blocksForRange(chrId, bpStart, bpEnd); + + if (!chunks || chunks.length === 0) { + return alignmentContainer; + } + + for (let c of chunks) { + let lastBlockSize; + + if (c.maxv.offset === 0) { + lastBlockSize = 0; // Don't need to read the last block. + } else { + const bsizeOptions = buildOptions(this.config, { + range: { + start: c.maxv.block, + size: 26 + } + }); + const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions); + lastBlockSize = bgzBlockSize(abuffer); + } + + const fetchMin = c.minv.block; + const fetchMax = c.maxv.block + lastBlockSize; + const range = { + start: fetchMin, + size: fetchMax - fetchMin + 1 + }; + const compressed = await igvxhr.loadArrayBuffer(this.bamPath, buildOptions(this.config, { + range: range + })); + var ba = unbgzf(compressed); //new Uint8Array(BGZip.unbgzf(compressed)); //, c.maxv.block - c.minv.block + 1)); + + const done = BamUtils.decodeBamRecords(ba, c.minv.offset, alignmentContainer, this.indexToChr, chrId, bpStart, bpEnd, this.filter); + + if (done) { + break; + } + } + + alignmentContainer.finish(); + return alignmentContainer; + } + } + + async getHeader() { + if (!this.header) { + const genome = this.genome; + const index = await this.getIndex(); + let len; + + if (index.firstAlignmentBlock) { + const bsizeOptions = buildOptions(this.config, { + range: { + start: index.firstAlignmentBlock, + size: 26 + } + }); + const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions); + const bsize = bgzBlockSize(abuffer); + len = index.firstAlignmentBlock + 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 = {}; + const chrAliasTable = {}; + config.sources.sequences.forEach(function (chr) { + bamReaders[chr] = null; // Placeholder + + if (genome) { + const alias = genome.getChromosomeName(chr); + chrAliasTable[alias] = chr; + } + }); + this.chrAliasTable = chrAliasTable; + this.bamReaders = bamReaders; + BamUtils.setReaderDefaults(this, config); + } + + async readAlignments(chr, start, end) { + this.genome; + const self = this; + const queryChr = self.chrAliasTable.hasOwnProperty(chr) ? self.chrAliasTable[chr] : chr; + + if (!this.bamReaders.hasOwnProperty(queryChr) || "none" === this.bamReaders[queryChr]) { + return Promise.resolve(new AlignmentContainer(chr, start, end)); + } else { + let reader = self.bamReaders[queryChr]; + let tmp; + + if (!reader) { + tmp = { + url: self.config.sources.url.replace("$CHR", queryChr) + }; + + if (self.config.sources.indexURL) { + tmp.indexURL = self.config.sources.indexURL.replace("$CHR", queryChr); + } + + const bamConfig = Object.assign(self.config, tmp); + reader = new BamReader(bamConfig); + self.bamReaders[queryChr] = reader; + } + + return reader.readAlignments(queryChr, start, end).catch(function (error) { + console.error(error); + const str = `Error reading BAM or index file for: ${tmp ? tmp.url : ''}`; + Alert.presentAlert(new Error(str)); + self.bamReaders[queryChr] = "none"; + return new AlignmentContainer(chr, start, end); // Empty alignment container + }); + } + } + + } + + /* + * 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.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold); + 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.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold); + BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end); + alignmentContainer.finish(); + return alignmentContainer; + } + + } + + var $propertyIsEnumerable = {}.propertyIsEnumerable; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + + var getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor; // Nashorn ~ JDK8 bug + + var NASHORN_BUG = getOwnPropertyDescriptor$1 && !$propertyIsEnumerable.call({ + 1: 2 + }, 1); // `Object.prototype.propertyIsEnumerable` method implementation + // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable + + var f$3 = NASHORN_BUG ? function propertyIsEnumerable(V) { + var descriptor = getOwnPropertyDescriptor$1(this, V); + return !!descriptor && descriptor.enumerable; + } : $propertyIsEnumerable; + var objectPropertyIsEnumerable = { + f: f$3 + }; + + var split = ''.split; // fallback for non-array-like ES3 and non-enumerable old V8 strings + + var indexedObject = fails(function () { + // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346 + // eslint-disable-next-line no-prototype-builtins -- safe + return !Object('z').propertyIsEnumerable(0); + }) ? function (it) { + return classofRaw(it) == 'String' ? split.call(it, '') : Object(it); + } : Object; + + var toIndexedObject = function (it) { + return indexedObject(requireObjectCoercible(it)); + }; + + var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; // `Object.getOwnPropertyDescriptor` method + // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor + + var f$2 = descriptors ? $getOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) { + O = toIndexedObject(O); + P = toPrimitive(P, true); + if (ie8DomDefine) try { + return $getOwnPropertyDescriptor(O, P); + } catch (error) { + /* empty */ + } + if (has$1(O, P)) return createPropertyDescriptor(!objectPropertyIsEnumerable.f.call(O, P), O[P]); + }; + var objectGetOwnPropertyDescriptor = { + f: f$2 + }; + + var max = Math.max; + var min = Math.min; // Helper for a popular repeating case of the spec: + // Let integer be ? ToInteger(index). + // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length). + + var toAbsoluteIndex = function (index, length) { + var integer = toInteger(index); + return integer < 0 ? max(integer + length, 0) : min(integer, length); + }; + + var createMethod = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = toIndexedObject($this); + var length = toLength(O.length); + var index = toAbsoluteIndex(fromIndex, length); + var value; // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare -- NaN check + + if (IS_INCLUDES && el != el) while (length > index) { + value = O[index++]; // eslint-disable-next-line no-self-compare -- NaN check + + if (value != value) return true; // Array#indexOf ignores holes, Array#includes - not + } else for (; length > index; index++) { + if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0; + } + return !IS_INCLUDES && -1; + }; + }; + + var arrayIncludes = { + // `Array.prototype.includes` method + // https://tc39.es/ecma262/#sec-array.prototype.includes + includes: createMethod(true), + // `Array.prototype.indexOf` method + // https://tc39.es/ecma262/#sec-array.prototype.indexof + indexOf: createMethod(false) + }; + + var indexOf = arrayIncludes.indexOf; + + var objectKeysInternal = function (object, names) { + var O = toIndexedObject(object); + var i = 0; + var result = []; + var key; + + for (key in O) !has$1(hiddenKeys$1, key) && has$1(O, key) && result.push(key); // Don't enum bug & hidden keys + + + while (names.length > i) if (has$1(O, key = names[i++])) { + ~indexOf(result, key) || result.push(key); + } + + return result; + }; + + // IE8- don't enum bug keys + var enumBugKeys = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']; + + var hiddenKeys = enumBugKeys.concat('length', 'prototype'); // `Object.getOwnPropertyNames` method + // https://tc39.es/ecma262/#sec-object.getownpropertynames + // eslint-disable-next-line es/no-object-getownpropertynames -- safe + + var f$1 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) { + return objectKeysInternal(O, hiddenKeys); + }; + + var objectGetOwnPropertyNames = { + f: f$1 + }; + + // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe + var f = Object.getOwnPropertySymbols; + var objectGetOwnPropertySymbols = { + f: f + }; + + var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) { + var keys = objectGetOwnPropertyNames.f(anObject(it)); + var getOwnPropertySymbols = objectGetOwnPropertySymbols.f; + return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys; + }; + + var copyConstructorProperties = function (target, source) { + var keys = ownKeys(source); + var defineProperty = objectDefineProperty.f; + var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f; + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!has$1(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key)); + } + }; + + var replacement = /#|\.prototype\./; + + var isForced = function (feature, detection) { + var value = data[normalize(feature)]; + return value == POLYFILL ? true : value == NATIVE ? false : typeof detection == 'function' ? fails(detection) : !!detection; + }; + + var normalize = isForced.normalize = function (string) { + return String(string).replace(replacement, '.').toLowerCase(); + }; + + var data = isForced.data = {}; + var NATIVE = isForced.NATIVE = 'N'; + var POLYFILL = isForced.POLYFILL = 'P'; + var isForced_1 = isForced; + + var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f; + /* + options.target - name of the target object + options.global - target is the global object + options.stat - export as static methods of target + options.proto - export as prototype methods of target + options.real - real prototype method for the `pure` version + options.forced - export even if the native feature is available + options.bind - bind methods to the target, required for the `pure` version + options.wrap - wrap constructors to preventing global pollution, required for the `pure` version + options.unsafe - use the simple assignment of property instead of delete + defineProperty + options.sham - add a flag to not completely full polyfills + options.enumerable - export as enumerable property + options.noTargetGet - prevent calling a getter on target + */ + + var _export = function (options, source) { + var TARGET = options.target; + var GLOBAL = options.global; + var STATIC = options.stat; + var FORCED, target, key, targetProperty, sourceProperty, descriptor; + + if (GLOBAL) { + target = global$1; + } else if (STATIC) { + target = global$1[TARGET] || setGlobal(TARGET, {}); + } else { + target = (global$1[TARGET] || {}).prototype; + } + + if (target) for (key in source) { + sourceProperty = source[key]; + + if (options.noTargetGet) { + descriptor = getOwnPropertyDescriptor(target, key); + targetProperty = descriptor && descriptor.value; + } else targetProperty = target[key]; + + FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced); // contained in target + + if (!FORCED && targetProperty !== undefined) { + if (typeof sourceProperty === typeof targetProperty) continue; + copyConstructorProperties(sourceProperty, targetProperty); + } // add a flag to not completely full polyfills + + + if (options.sham || targetProperty && targetProperty.sham) { + createNonEnumerableProperty(sourceProperty, 'sham', true); + } // extend global + + + redefine(target, key, sourceProperty, options); + } + }; + + var functionBindContext = function (fn, that, length) { + aFunction(fn); + if (that === undefined) return fn; + + switch (length) { + case 0: + return function () { + return fn.call(that); + }; + + case 1: + return function (a) { + return fn.call(that, a); + }; + + case 2: + return function (a, b) { + return fn.call(that, a, b); + }; + + case 3: + return function (a, b, c) { + return fn.call(that, a, b, c); + }; + } + + return function () { + return fn.apply(that, arguments); + }; + }; + + var html = getBuiltIn('document', 'documentElement'); + + var engineIsIos = /(?:iphone|ipod|ipad).*applewebkit/i.test(engineUserAgent); + + var engineIsNode = classofRaw(global$1.process) == 'process'; + + var location = global$1.location; + var set = global$1.setImmediate; + var clear = global$1.clearImmediate; + var process$1 = global$1.process; + var MessageChannel$1 = global$1.MessageChannel; + var Dispatch = global$1.Dispatch; + var counter = 0; + var queue = {}; + var ONREADYSTATECHANGE = 'onreadystatechange'; + var defer, channel, port; + + var run = function (id) { + // eslint-disable-next-line no-prototype-builtins -- safe + if (queue.hasOwnProperty(id)) { + var fn = queue[id]; + delete queue[id]; + fn(); + } + }; + + var runner = function (id) { + return function () { + run(id); + }; + }; + + var listener = function (event) { + run(event.data); + }; + + var post = function (id) { + // old engines have not location.origin + global$1.postMessage(id + '', location.protocol + '//' + location.host); + }; // Node.js 0.9+ & IE10+ has setImmediate, otherwise: + + + if (!set || !clear) { + set = function setImmediate(fn) { + var args = []; + var i = 1; + + while (arguments.length > i) args.push(arguments[i++]); + + queue[++counter] = function () { + // eslint-disable-next-line no-new-func -- spec requirement + (typeof fn == 'function' ? fn : Function(fn)).apply(undefined, args); + }; + + defer(counter); + return counter; + }; + + clear = function clearImmediate(id) { + delete queue[id]; + }; // Node.js 0.8- + + + if (engineIsNode) { + defer = function (id) { + process$1.nextTick(runner(id)); + }; // Sphere (JS game engine) Dispatch API + + } else if (Dispatch && Dispatch.now) { + defer = function (id) { + Dispatch.now(runner(id)); + }; // Browsers with MessageChannel, includes WebWorkers + // except iOS - https://github.com/zloirock/core-js/issues/624 + + } else if (MessageChannel$1 && !engineIsIos) { + channel = new MessageChannel$1(); + port = channel.port2; + channel.port1.onmessage = listener; + defer = functionBindContext(port.postMessage, port, 1); // Browsers with postMessage, skip WebWorkers + // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' + } else if (global$1.addEventListener && typeof postMessage == 'function' && !global$1.importScripts && location && location.protocol !== 'file:' && !fails(post)) { + defer = post; + global$1.addEventListener('message', listener, false); // IE8- + } else if (ONREADYSTATECHANGE in documentCreateElement('script')) { + defer = function (id) { + html.appendChild(documentCreateElement('script'))[ONREADYSTATECHANGE] = function () { + html.removeChild(this); + run(id); + }; + }; // Rest old browsers + + } else { + defer = function (id) { + setTimeout(runner(id), 0); + }; + } + } + + var task = { + set: set, + clear: clear + }; + + var FORCED = !global$1.setImmediate || !global$1.clearImmediate; // http://w3c.github.io/setImmediate/ + + _export({ + global: true, + bind: true, + enumerable: true, + forced: FORCED + }, { + // `setImmediate` method + // http://w3c.github.io/setImmediate/#si-setImmediate + setImmediate: task.set, + // `clearImmediate` method + // http://w3c.github.io/setImmediate/#si-clearImmediate + clearImmediate: task.clear + }); + + const eval2 = eval; + + const gmodCRAM = function (t) { + var e = {}; + + function r(n) { + if (e[n]) return e[n].exports; + var i = e[n] = { + i: n, + l: !1, + exports: {} + }; + return t[n].call(i.exports, i, i.exports, r), i.l = !0, i.exports; + } + + return r.m = t, r.c = e, r.d = function (t, e, n) { + r.o(t, e) || Object.defineProperty(t, e, { + enumerable: !0, + get: n + }); + }, r.r = function (t) { + "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, { + value: "Module" + }), Object.defineProperty(t, "__esModule", { + value: !0 + }); + }, r.t = function (t, e) { + if (1 & e && (t = r(t)), 8 & e) return t; + if (4 & e && "object" == typeof t && t && t.__esModule) return t; + var n = Object.create(null); + if (r.r(n), Object.defineProperty(n, "default", { + enumerable: !0, + value: t + }), 2 & e && "string" != typeof t) for (var i in t) r.d(n, i, function (e) { + return t[e]; + }.bind(null, i)); + return n; + }, r.n = function (t) { + var e = t && t.__esModule ? function () { + return t.default; + } : function () { + return t; + }; + return r.d(e, "a", e), e; + }, r.o = function (t, e) { + return Object.prototype.hasOwnProperty.call(t, e); + }, r.p = "", r(r.s = 163); + }([function (t, e, r) { + + var n = r(13), + i = r(81).f, + o = r(107), + a = r(3), + s = r(15), + u = r(20), + f = r(24), + c = function (t) { + var e = function (e, r, n) { + if (this instanceof t) { + switch (arguments.length) { + case 0: + return new t(); + + case 1: + return new t(e); + + case 2: + return new t(e, r); + } + + return new t(e, r, n); + } + + return t.apply(this, arguments); + }; + + return e.prototype = t.prototype, e; + }; + + t.exports = function (t, e) { + var r, + l, + h, + d, + p, + g, + v, + y, + m = t.target, + b = t.global, + _ = t.stat, + w = t.proto, + x = b ? n : _ ? n[m] : (n[m] || {}).prototype, + E = b ? a : a[m] || (a[m] = {}), + S = E.prototype; + + for (h in e) r = !o(b ? h : m + (_ ? "." : "#") + h, t.forced) && x && f(x, h), p = E[h], r && (g = t.noTargetGet ? (y = i(x, h)) && y.value : x[h]), d = r && g ? g : e[h], r && typeof p == typeof d || (v = t.bind && r ? s(d, n) : t.wrap && r ? c(d) : w && "function" == typeof d ? s(Function.call, d) : d, (t.sham || d && d.sham || p && p.sham) && u(v, "sham", !0), E[h] = v, w && (f(a, l = m + "Prototype") || u(a, l, {}), a[l][h] = d, t.real && S && !S[h] && u(S, h, d))); + }; + }, function (t, e) { + t.exports = function (t) { + return t && t.__esModule ? t : { + default: t + }; + }; + }, function (t, e, r) { + var n = r(14); + + t.exports = function (t) { + if (!n(t)) throw TypeError(String(t) + " is not an object"); + return t; + }; + }, function (t, e) { + t.exports = {}; + }, function (t, e) { + t.exports = !0; + }, function (t, e) { + t.exports = function (t) { + if ("function" != typeof t) throw TypeError(String(t) + " is not a function"); + return t; + }; + }, function (t, e, r) { + var n = r(13), + i = r(61), + o = r(64), + a = r(110), + s = n.Symbol, + u = i("wks"); + + t.exports = function (t) { + return u[t] || (u[t] = a && s[t] || (a ? s : o)("Symbol." + t)); + }; + }, function (t, e) { + t.exports = function (t, e) { + if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function"); + }; + }, function (t, e, r) { + var n = r(2), + i = r(119), + o = r(29), + a = r(15), + s = r(90), + u = r(120), + f = function (t, e) { + this.stopped = t, this.result = e; + }; + + (t.exports = function (t, e, r, c, l) { + var h, + d, + p, + g, + v, + y, + m = a(e, r, c ? 2 : 1); + if (l) h = t;else { + if ("function" != typeof (d = s(t))) throw TypeError("Target is not iterable"); + + if (i(d)) { + for (p = 0, g = o(t.length); g > p; p++) if ((v = c ? m(n(y = t[p])[0], y[1]) : m(t[p])) && v instanceof f) return v; + + return new f(!1); + } + + h = d.call(t); + } + + for (; !(y = h.next()).done;) if ((v = u(h, m, y.value, c)) && v instanceof f) return v; + + return new f(!1); + }).stop = function (t) { + return new f(!0, t); + }; + }, function (t, e, r) { + + var n = r(1), + i = n(r(7)), + o = n(r(32)), + a = n(r(27)), + s = n(r(33)), + u = n(r(265)), + f = function (t) { + function e() { + return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments)); + } + + return (0, s.default)(e, t), e; + }((0, u.default)(Error)), + c = function (t) { + function e() { + return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments)); + } + + return (0, s.default)(e, t), e; + }((0, u.default)(Error)), + l = function (t) { + function e() { + return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments)); + } + + return (0, s.default)(e, t), e; + }(f), + h = function (t) { + function e() { + return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments)); + } + + return (0, s.default)(e, t), e; + }(l), + d = function (t) { + function e() { + return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments)); + } + + return (0, s.default)(e, t), e; + }(f), + p = function (t) { + function e() { + return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments)); + } + + return (0, s.default)(e, t), e; + }(f); + + t.exports = { + CramBufferOverrunError: h, + CramMalformedError: l, + CramUnimplementedError: c, + CramSizeLimitError: d, + CramArgumentError: p + }; + }, function (t, e, r) { + var n = r(196); + + function i(t, e) { + for (var r = 0; r < e.length; r++) { + var i = e[r]; + i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), n(t, i.key, i); + } + } + + t.exports = function (t, e, r) { + return e && i(t.prototype, e), r && i(t, r), t; + }; + }, function (t, e, r) { + var n = r(3), + i = r(24), + o = r(95), + a = r(21).f; + + t.exports = function (t) { + var e = n.Symbol || (n.Symbol = {}); + i(e, t) || a(e, t, { + value: o.f(t) + }); + }; + }, function (t, e) { + t.exports = function (t) { + try { + return !!t(); + } catch (t) { + return !0; + } + }; + }, function (t, e, r) { + (function (e) { + var r = "object", + n = function (t) { + return t && t.Math == Math && t; + }; + + t.exports = n(typeof globalThis == r && globalThis) || n(typeof window == r && window) || n(typeof self == r && self) || n(typeof e == r && e) || Function("return this")(); + }).call(this, r(23)); + }, function (t, e) { + t.exports = function (t) { + return "object" == typeof t ? null !== t : "function" == typeof t; + }; + }, function (t, e, r) { + var n = r(5); + + t.exports = function (t, e, r) { + if (n(t), void 0 === e) return t; + + switch (r) { + case 0: + return function () { + return t.call(e); + }; + + case 1: + return function (r) { + return t.call(e, r); + }; + + case 2: + return function (r, n) { + return t.call(e, r, n); + }; + + case 3: + return function (r, n, i) { + return t.call(e, r, n, i); + }; + } + + return function () { + return t.apply(e, arguments); + }; + }; + }, function (t, e, r) { + t.exports = r(167); + }, function (t, e, r) { + var n = r(12); + t.exports = !n(function () { + return 7 != Object.defineProperty({}, "a", { + get: function () { + return 7; + } + }).a; + }); + }, function (t, e, r) { + var n = r(3); + + t.exports = function (t) { + return n[t + "Prototype"]; + }; + }, function (t, e, r) { + + (function (t) { + /*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ + var n = r(165), + i = r(166), + o = r(105); + + function a() { + return u.TYPED_ARRAY_SUPPORT ? 2147483647 : 1073741823; + } + + function s(t, e) { + if (a() < e) throw new RangeError("Invalid typed array length"); + return u.TYPED_ARRAY_SUPPORT ? (t = new Uint8Array(e)).__proto__ = u.prototype : (null === t && (t = new u(e)), t.length = e), t; + } + + function u(t, e, r) { + if (!(u.TYPED_ARRAY_SUPPORT || this instanceof u)) return new u(t, e, r); + + if ("number" == typeof t) { + if ("string" == typeof e) throw new Error("If encoding is specified then the first argument must be a string"); + return l(this, t); + } + + return f(this, t, e, r); + } + + function f(t, e, r, n) { + if ("number" == typeof e) throw new TypeError('"value" argument must not be a number'); + return "undefined" != typeof ArrayBuffer && e instanceof ArrayBuffer ? function (t, e, r, n) { + if (e.byteLength, r < 0 || e.byteLength < r) throw new RangeError("'offset' is out of bounds"); + if (e.byteLength < r + (n || 0)) throw new RangeError("'length' is out of bounds"); + e = void 0 === r && void 0 === n ? new Uint8Array(e) : void 0 === n ? new Uint8Array(e, r) : new Uint8Array(e, r, n); + u.TYPED_ARRAY_SUPPORT ? (t = e).__proto__ = u.prototype : t = h(t, e); + return t; + }(t, e, r, n) : "string" == typeof e ? function (t, e, r) { + "string" == typeof r && "" !== r || (r = "utf8"); + if (!u.isEncoding(r)) throw new TypeError('"encoding" must be a valid string encoding'); + var n = 0 | p(e, r), + i = (t = s(t, n)).write(e, r); + i !== n && (t = t.slice(0, i)); + return t; + }(t, e, r) : function (t, e) { + if (u.isBuffer(e)) { + var r = 0 | d(e.length); + return 0 === (t = s(t, r)).length ? t : (e.copy(t, 0, 0, r), t); + } + + if (e) { + if ("undefined" != typeof ArrayBuffer && e.buffer instanceof ArrayBuffer || "length" in e) return "number" != typeof e.length || (n = e.length) != n ? s(t, 0) : h(t, e); + if ("Buffer" === e.type && o(e.data)) return h(t, e.data); + } + + var n; + throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object."); + }(t, e); + } + + function c(t) { + if ("number" != typeof t) throw new TypeError('"size" argument must be a number'); + if (t < 0) throw new RangeError('"size" argument must not be negative'); + } + + function l(t, e) { + if (c(e), t = s(t, e < 0 ? 0 : 0 | d(e)), !u.TYPED_ARRAY_SUPPORT) for (var r = 0; r < e; ++r) t[r] = 0; + return t; + } + + function h(t, e) { + var r = e.length < 0 ? 0 : 0 | d(e.length); + t = s(t, r); + + for (var n = 0; n < r; n += 1) t[n] = 255 & e[n]; + + return t; + } + + function d(t) { + if (t >= a()) throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x" + a().toString(16) + " bytes"); + return 0 | t; + } + + function p(t, e) { + if (u.isBuffer(t)) return t.length; + if ("undefined" != typeof ArrayBuffer && "function" == typeof ArrayBuffer.isView && (ArrayBuffer.isView(t) || t instanceof ArrayBuffer)) return t.byteLength; + "string" != typeof t && (t = "" + t); + var r = t.length; + if (0 === r) return 0; + + for (var n = !1;;) switch (e) { + case "ascii": + case "latin1": + case "binary": + return r; + + case "utf8": + case "utf-8": + case void 0: + return D(t).length; + + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return 2 * r; + + case "hex": + return r >>> 1; + + case "base64": + return q(t).length; + + default: + if (n) return D(t).length; + e = ("" + e).toLowerCase(), n = !0; + } + } + + function g(t, e, r) { + var n = !1; + if ((void 0 === e || e < 0) && (e = 0), e > this.length) return ""; + if ((void 0 === r || r > this.length) && (r = this.length), r <= 0) return ""; + if ((r >>>= 0) <= (e >>>= 0)) return ""; + + for (t || (t = "utf8");;) switch (t) { + case "hex": + return O(this, e, r); + + case "utf8": + case "utf-8": + return A(this, e, r); + + case "ascii": + return T(this, e, r); + + case "latin1": + case "binary": + return R(this, e, r); + + case "base64": + return k(this, e, r); + + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return I(this, e, r); + + default: + if (n) throw new TypeError("Unknown encoding: " + t); + t = (t + "").toLowerCase(), n = !0; + } + } + + function v(t, e, r) { + var n = t[e]; + t[e] = t[r], t[r] = n; + } + + function y(t, e, r, n, i) { + if (0 === t.length) return -1; + + if ("string" == typeof r ? (n = r, r = 0) : r > 2147483647 ? r = 2147483647 : r < -2147483648 && (r = -2147483648), r = +r, isNaN(r) && (r = i ? 0 : t.length - 1), r < 0 && (r = t.length + r), r >= t.length) { + if (i) return -1; + r = t.length - 1; + } else if (r < 0) { + if (!i) return -1; + r = 0; + } + + if ("string" == typeof e && (e = u.from(e, n)), u.isBuffer(e)) return 0 === e.length ? -1 : m(t, e, r, n, i); + if ("number" == typeof e) return e &= 255, u.TYPED_ARRAY_SUPPORT && "function" == typeof Uint8Array.prototype.indexOf ? i ? Uint8Array.prototype.indexOf.call(t, e, r) : Uint8Array.prototype.lastIndexOf.call(t, e, r) : m(t, [e], r, n, i); + throw new TypeError("val must be string, number or Buffer"); + } + + function m(t, e, r, n, i) { + var o, + a = 1, + s = t.length, + u = e.length; + + if (void 0 !== n && ("ucs2" === (n = String(n).toLowerCase()) || "ucs-2" === n || "utf16le" === n || "utf-16le" === n)) { + if (t.length < 2 || e.length < 2) return -1; + a = 2, s /= 2, u /= 2, r /= 2; + } + + function f(t, e) { + return 1 === a ? t[e] : t.readUInt16BE(e * a); + } + + if (i) { + var c = -1; + + for (o = r; o < s; o++) if (f(t, o) === f(e, -1 === c ? 0 : o - c)) { + if (-1 === c && (c = o), o - c + 1 === u) return c * a; + } else -1 !== c && (o -= o - c), c = -1; + } else for (r + u > s && (r = s - u), o = r; o >= 0; o--) { + for (var l = !0, h = 0; h < u; h++) if (f(t, o + h) !== f(e, h)) { + l = !1; + break; + } + + if (l) return o; + } + + return -1; + } + + function b(t, e, r, n) { + r = Number(r) || 0; + var i = t.length - r; + n ? (n = Number(n)) > i && (n = i) : n = i; + var o = e.length; + if (o % 2 != 0) throw new TypeError("Invalid hex string"); + n > o / 2 && (n = o / 2); + + for (var a = 0; a < n; ++a) { + var s = parseInt(e.substr(2 * a, 2), 16); + if (isNaN(s)) return a; + t[r + a] = s; + } + + return a; + } + + function _(t, e, r, n) { + return Z(D(e, t.length - r), t, r, n); + } + + function w(t, e, r, n) { + return Z(function (t) { + for (var e = [], r = 0; r < t.length; ++r) e.push(255 & t.charCodeAt(r)); + + return e; + }(e), t, r, n); + } + + function x(t, e, r, n) { + return w(t, e, r, n); + } + + function E(t, e, r, n) { + return Z(q(e), t, r, n); + } + + function S(t, e, r, n) { + return Z(function (t, e) { + for (var r, n, i, o = [], a = 0; a < t.length && !((e -= 2) < 0); ++a) r = t.charCodeAt(a), n = r >> 8, i = r % 256, o.push(i), o.push(n); + + return o; + }(e, t.length - r), t, r, n); + } + + function k(t, e, r) { + return 0 === e && r === t.length ? n.fromByteArray(t) : n.fromByteArray(t.slice(e, r)); + } + + function A(t, e, r) { + r = Math.min(t.length, r); + + for (var n = [], i = e; i < r;) { + var o, + a, + s, + u, + f = t[i], + c = null, + l = f > 239 ? 4 : f > 223 ? 3 : f > 191 ? 2 : 1; + if (i + l <= r) switch (l) { + case 1: + f < 128 && (c = f); + break; + + case 2: + 128 == (192 & (o = t[i + 1])) && (u = (31 & f) << 6 | 63 & o) > 127 && (c = u); + break; + + case 3: + o = t[i + 1], a = t[i + 2], 128 == (192 & o) && 128 == (192 & a) && (u = (15 & f) << 12 | (63 & o) << 6 | 63 & a) > 2047 && (u < 55296 || u > 57343) && (c = u); + break; + + case 4: + o = t[i + 1], a = t[i + 2], s = t[i + 3], 128 == (192 & o) && 128 == (192 & a) && 128 == (192 & s) && (u = (15 & f) << 18 | (63 & o) << 12 | (63 & a) << 6 | 63 & s) > 65535 && u < 1114112 && (c = u); + } + null === c ? (c = 65533, l = 1) : c > 65535 && (c -= 65536, n.push(c >>> 10 & 1023 | 55296), c = 56320 | 1023 & c), n.push(c), i += l; + } + + return function (t) { + var e = t.length; + if (e <= C) return String.fromCharCode.apply(String, t); + var r = "", + n = 0; + + for (; n < e;) r += String.fromCharCode.apply(String, t.slice(n, n += C)); + + return r; + }(n); + } + + e.Buffer = u, e.SlowBuffer = function (t) { + +t != t && (t = 0); + return u.alloc(+t); + }, e.INSPECT_MAX_BYTES = 50, u.TYPED_ARRAY_SUPPORT = void 0 !== t.TYPED_ARRAY_SUPPORT ? t.TYPED_ARRAY_SUPPORT : function () { + try { + var t = new Uint8Array(1); + return t.__proto__ = { + __proto__: Uint8Array.prototype, + foo: function () { + return 42; + } + }, 42 === t.foo() && "function" == typeof t.subarray && 0 === t.subarray(1, 1).byteLength; + } catch (t) { + return !1; + } + }(), e.kMaxLength = a(), u.poolSize = 8192, u._augment = function (t) { + return t.__proto__ = u.prototype, t; + }, u.from = function (t, e, r) { + return f(null, t, e, r); + }, u.TYPED_ARRAY_SUPPORT && (u.prototype.__proto__ = Uint8Array.prototype, u.__proto__ = Uint8Array, "undefined" != typeof Symbol && Symbol.species && u[Symbol.species] === u && Object.defineProperty(u, Symbol.species, { + value: null, + configurable: !0 + })), u.alloc = function (t, e, r) { + return function (t, e, r, n) { + return c(e), e <= 0 ? s(t, e) : void 0 !== r ? "string" == typeof n ? s(t, e).fill(r, n) : s(t, e).fill(r) : s(t, e); + }(null, t, e, r); + }, u.allocUnsafe = function (t) { + return l(null, t); + }, u.allocUnsafeSlow = function (t) { + return l(null, t); + }, u.isBuffer = function (t) { + return !(null == t || !t._isBuffer); + }, u.compare = function (t, e) { + if (!u.isBuffer(t) || !u.isBuffer(e)) throw new TypeError("Arguments must be Buffers"); + if (t === e) return 0; + + for (var r = t.length, n = e.length, i = 0, o = Math.min(r, n); i < o; ++i) if (t[i] !== e[i]) { + r = t[i], n = e[i]; + break; + } + + return r < n ? -1 : n < r ? 1 : 0; + }, u.isEncoding = function (t) { + switch (String(t).toLowerCase()) { + case "hex": + case "utf8": + case "utf-8": + case "ascii": + case "latin1": + case "binary": + case "base64": + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return !0; + + default: + return !1; + } + }, u.concat = function (t, e) { + if (!o(t)) throw new TypeError('"list" argument must be an Array of Buffers'); + if (0 === t.length) return u.alloc(0); + var r; + if (void 0 === e) for (e = 0, r = 0; r < t.length; ++r) e += t[r].length; + var n = u.allocUnsafe(e), + i = 0; + + for (r = 0; r < t.length; ++r) { + var a = t[r]; + if (!u.isBuffer(a)) throw new TypeError('"list" argument must be an Array of Buffers'); + a.copy(n, i), i += a.length; + } + + return n; + }, u.byteLength = p, u.prototype._isBuffer = !0, u.prototype.swap16 = function () { + var t = this.length; + if (t % 2 != 0) throw new RangeError("Buffer size must be a multiple of 16-bits"); + + for (var e = 0; e < t; e += 2) v(this, e, e + 1); + + return this; + }, u.prototype.swap32 = function () { + var t = this.length; + if (t % 4 != 0) throw new RangeError("Buffer size must be a multiple of 32-bits"); + + for (var e = 0; e < t; e += 4) v(this, e, e + 3), v(this, e + 1, e + 2); + + return this; + }, u.prototype.swap64 = function () { + var t = this.length; + if (t % 8 != 0) throw new RangeError("Buffer size must be a multiple of 64-bits"); + + for (var e = 0; e < t; e += 8) v(this, e, e + 7), v(this, e + 1, e + 6), v(this, e + 2, e + 5), v(this, e + 3, e + 4); + + return this; + }, u.prototype.toString = function () { + var t = 0 | this.length; + return 0 === t ? "" : 0 === arguments.length ? A(this, 0, t) : g.apply(this, arguments); + }, u.prototype.equals = function (t) { + if (!u.isBuffer(t)) throw new TypeError("Argument must be a Buffer"); + return this === t || 0 === u.compare(this, t); + }, u.prototype.inspect = function () { + var t = "", + r = e.INSPECT_MAX_BYTES; + return this.length > 0 && (t = this.toString("hex", 0, r).match(/.{2}/g).join(" "), this.length > r && (t += " ... ")), ""; + }, u.prototype.compare = function (t, e, r, n, i) { + if (!u.isBuffer(t)) throw new TypeError("Argument must be a Buffer"); + if (void 0 === e && (e = 0), void 0 === r && (r = t ? t.length : 0), void 0 === n && (n = 0), void 0 === i && (i = this.length), e < 0 || r > t.length || n < 0 || i > this.length) throw new RangeError("out of range index"); + if (n >= i && e >= r) return 0; + if (n >= i) return -1; + if (e >= r) return 1; + if (this === t) return 0; + + for (var o = (i >>>= 0) - (n >>>= 0), a = (r >>>= 0) - (e >>>= 0), s = Math.min(o, a), f = this.slice(n, i), c = t.slice(e, r), l = 0; l < s; ++l) if (f[l] !== c[l]) { + o = f[l], a = c[l]; + break; + } + + return o < a ? -1 : a < o ? 1 : 0; + }, u.prototype.includes = function (t, e, r) { + return -1 !== this.indexOf(t, e, r); + }, u.prototype.indexOf = function (t, e, r) { + return y(this, t, e, r, !0); + }, u.prototype.lastIndexOf = function (t, e, r) { + return y(this, t, e, r, !1); + }, u.prototype.write = function (t, e, r, n) { + if (void 0 === e) n = "utf8", r = this.length, e = 0;else if (void 0 === r && "string" == typeof e) n = e, r = this.length, e = 0;else { + if (!isFinite(e)) throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported"); + e |= 0, isFinite(r) ? (r |= 0, void 0 === n && (n = "utf8")) : (n = r, r = void 0); + } + var i = this.length - e; + if ((void 0 === r || r > i) && (r = i), t.length > 0 && (r < 0 || e < 0) || e > this.length) throw new RangeError("Attempt to write outside buffer bounds"); + n || (n = "utf8"); + + for (var o = !1;;) switch (n) { + case "hex": + return b(this, t, e, r); + + case "utf8": + case "utf-8": + return _(this, t, e, r); + + case "ascii": + return w(this, t, e, r); + + case "latin1": + case "binary": + return x(this, t, e, r); + + case "base64": + return E(this, t, e, r); + + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return S(this, t, e, r); + + default: + if (o) throw new TypeError("Unknown encoding: " + n); + n = ("" + n).toLowerCase(), o = !0; + } + }, u.prototype.toJSON = function () { + return { + type: "Buffer", + data: Array.prototype.slice.call(this._arr || this, 0) + }; + }; + var C = 4096; + + function T(t, e, r) { + var n = ""; + r = Math.min(t.length, r); + + for (var i = e; i < r; ++i) n += String.fromCharCode(127 & t[i]); + + return n; + } + + function R(t, e, r) { + var n = ""; + r = Math.min(t.length, r); + + for (var i = e; i < r; ++i) n += String.fromCharCode(t[i]); + + return n; + } + + function O(t, e, r) { + var n = t.length; + (!e || e < 0) && (e = 0), (!r || r < 0 || r > n) && (r = n); + + for (var i = "", o = e; o < r; ++o) i += U(t[o]); + + return i; + } + + function I(t, e, r) { + for (var n = t.slice(e, r), i = "", o = 0; o < n.length; o += 2) i += String.fromCharCode(n[o] + 256 * n[o + 1]); + + return i; + } + + function L(t, e, r) { + if (t % 1 != 0 || t < 0) throw new RangeError("offset is not uint"); + if (t + e > r) throw new RangeError("Trying to access beyond buffer length"); + } + + function B(t, e, r, n, i, o) { + if (!u.isBuffer(t)) throw new TypeError('"buffer" argument must be a Buffer instance'); + if (e > i || e < o) throw new RangeError('"value" argument is out of bounds'); + if (r + n > t.length) throw new RangeError("Index out of range"); + } + + function P(t, e, r, n) { + e < 0 && (e = 65535 + e + 1); + + for (var i = 0, o = Math.min(t.length - r, 2); i < o; ++i) t[r + i] = (e & 255 << 8 * (n ? i : 1 - i)) >>> 8 * (n ? i : 1 - i); + } + + function F(t, e, r, n) { + e < 0 && (e = 4294967295 + e + 1); + + for (var i = 0, o = Math.min(t.length - r, 4); i < o; ++i) t[r + i] = e >>> 8 * (n ? i : 3 - i) & 255; + } + + function N(t, e, r, n, i, o) { + if (r + n > t.length) throw new RangeError("Index out of range"); + if (r < 0) throw new RangeError("Index out of range"); + } + + function M(t, e, r, n, o) { + return o || N(t, 0, r, 4), i.write(t, e, r, n, 23, 4), r + 4; + } + + function j(t, e, r, n, o) { + return o || N(t, 0, r, 8), i.write(t, e, r, n, 52, 8), r + 8; + } + + u.prototype.slice = function (t, e) { + var r, + n = this.length; + if ((t = ~~t) < 0 ? (t += n) < 0 && (t = 0) : t > n && (t = n), (e = void 0 === e ? n : ~~e) < 0 ? (e += n) < 0 && (e = 0) : e > n && (e = n), e < t && (e = t), u.TYPED_ARRAY_SUPPORT) (r = this.subarray(t, e)).__proto__ = u.prototype;else { + var i = e - t; + r = new u(i, void 0); + + for (var o = 0; o < i; ++o) r[o] = this[o + t]; + } + return r; + }, u.prototype.readUIntLE = function (t, e, r) { + t |= 0, e |= 0, r || L(t, e, this.length); + + for (var n = this[t], i = 1, o = 0; ++o < e && (i *= 256);) n += this[t + o] * i; + + return n; + }, u.prototype.readUIntBE = function (t, e, r) { + t |= 0, e |= 0, r || L(t, e, this.length); + + for (var n = this[t + --e], i = 1; e > 0 && (i *= 256);) n += this[t + --e] * i; + + return n; + }, u.prototype.readUInt8 = function (t, e) { + return e || L(t, 1, this.length), this[t]; + }, u.prototype.readUInt16LE = function (t, e) { + return e || L(t, 2, this.length), this[t] | this[t + 1] << 8; + }, u.prototype.readUInt16BE = function (t, e) { + return e || L(t, 2, this.length), this[t] << 8 | this[t + 1]; + }, u.prototype.readUInt32LE = function (t, e) { + return e || L(t, 4, this.length), (this[t] | this[t + 1] << 8 | this[t + 2] << 16) + 16777216 * this[t + 3]; + }, u.prototype.readUInt32BE = function (t, e) { + return e || L(t, 4, this.length), 16777216 * this[t] + (this[t + 1] << 16 | this[t + 2] << 8 | this[t + 3]); + }, u.prototype.readIntLE = function (t, e, r) { + t |= 0, e |= 0, r || L(t, e, this.length); + + for (var n = this[t], i = 1, o = 0; ++o < e && (i *= 256);) n += this[t + o] * i; + + return n >= (i *= 128) && (n -= Math.pow(2, 8 * e)), n; + }, u.prototype.readIntBE = function (t, e, r) { + t |= 0, e |= 0, r || L(t, e, this.length); + + for (var n = e, i = 1, o = this[t + --n]; n > 0 && (i *= 256);) o += this[t + --n] * i; + + return o >= (i *= 128) && (o -= Math.pow(2, 8 * e)), o; + }, u.prototype.readInt8 = function (t, e) { + return e || L(t, 1, this.length), 128 & this[t] ? -1 * (255 - this[t] + 1) : this[t]; + }, u.prototype.readInt16LE = function (t, e) { + e || L(t, 2, this.length); + var r = this[t] | this[t + 1] << 8; + return 32768 & r ? 4294901760 | r : r; + }, u.prototype.readInt16BE = function (t, e) { + e || L(t, 2, this.length); + var r = this[t + 1] | this[t] << 8; + return 32768 & r ? 4294901760 | r : r; + }, u.prototype.readInt32LE = function (t, e) { + return e || L(t, 4, this.length), this[t] | this[t + 1] << 8 | this[t + 2] << 16 | this[t + 3] << 24; + }, u.prototype.readInt32BE = function (t, e) { + return e || L(t, 4, this.length), this[t] << 24 | this[t + 1] << 16 | this[t + 2] << 8 | this[t + 3]; + }, u.prototype.readFloatLE = function (t, e) { + return e || L(t, 4, this.length), i.read(this, t, !0, 23, 4); + }, u.prototype.readFloatBE = function (t, e) { + return e || L(t, 4, this.length), i.read(this, t, !1, 23, 4); + }, u.prototype.readDoubleLE = function (t, e) { + return e || L(t, 8, this.length), i.read(this, t, !0, 52, 8); + }, u.prototype.readDoubleBE = function (t, e) { + return e || L(t, 8, this.length), i.read(this, t, !1, 52, 8); + }, u.prototype.writeUIntLE = function (t, e, r, n) { + (t = +t, e |= 0, r |= 0, n) || B(this, t, e, r, Math.pow(2, 8 * r) - 1, 0); + var i = 1, + o = 0; + + for (this[e] = 255 & t; ++o < r && (i *= 256);) this[e + o] = t / i & 255; + + return e + r; + }, u.prototype.writeUIntBE = function (t, e, r, n) { + (t = +t, e |= 0, r |= 0, n) || B(this, t, e, r, Math.pow(2, 8 * r) - 1, 0); + var i = r - 1, + o = 1; + + for (this[e + i] = 255 & t; --i >= 0 && (o *= 256);) this[e + i] = t / o & 255; + + return e + r; + }, u.prototype.writeUInt8 = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 1, 255, 0), u.TYPED_ARRAY_SUPPORT || (t = Math.floor(t)), this[e] = 255 & t, e + 1; + }, u.prototype.writeUInt16LE = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 2, 65535, 0), u.TYPED_ARRAY_SUPPORT ? (this[e] = 255 & t, this[e + 1] = t >>> 8) : P(this, t, e, !0), e + 2; + }, u.prototype.writeUInt16BE = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 2, 65535, 0), u.TYPED_ARRAY_SUPPORT ? (this[e] = t >>> 8, this[e + 1] = 255 & t) : P(this, t, e, !1), e + 2; + }, u.prototype.writeUInt32LE = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 4, 4294967295, 0), u.TYPED_ARRAY_SUPPORT ? (this[e + 3] = t >>> 24, this[e + 2] = t >>> 16, this[e + 1] = t >>> 8, this[e] = 255 & t) : F(this, t, e, !0), e + 4; + }, u.prototype.writeUInt32BE = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 4, 4294967295, 0), u.TYPED_ARRAY_SUPPORT ? (this[e] = t >>> 24, this[e + 1] = t >>> 16, this[e + 2] = t >>> 8, this[e + 3] = 255 & t) : F(this, t, e, !1), e + 4; + }, u.prototype.writeIntLE = function (t, e, r, n) { + if (t = +t, e |= 0, !n) { + var i = Math.pow(2, 8 * r - 1); + B(this, t, e, r, i - 1, -i); + } + + var o = 0, + a = 1, + s = 0; + + for (this[e] = 255 & t; ++o < r && (a *= 256);) t < 0 && 0 === s && 0 !== this[e + o - 1] && (s = 1), this[e + o] = (t / a >> 0) - s & 255; + + return e + r; + }, u.prototype.writeIntBE = function (t, e, r, n) { + if (t = +t, e |= 0, !n) { + var i = Math.pow(2, 8 * r - 1); + B(this, t, e, r, i - 1, -i); + } + + var o = r - 1, + a = 1, + s = 0; + + for (this[e + o] = 255 & t; --o >= 0 && (a *= 256);) t < 0 && 0 === s && 0 !== this[e + o + 1] && (s = 1), this[e + o] = (t / a >> 0) - s & 255; + + return e + r; + }, u.prototype.writeInt8 = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 1, 127, -128), u.TYPED_ARRAY_SUPPORT || (t = Math.floor(t)), t < 0 && (t = 255 + t + 1), this[e] = 255 & t, e + 1; + }, u.prototype.writeInt16LE = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 2, 32767, -32768), u.TYPED_ARRAY_SUPPORT ? (this[e] = 255 & t, this[e + 1] = t >>> 8) : P(this, t, e, !0), e + 2; + }, u.prototype.writeInt16BE = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 2, 32767, -32768), u.TYPED_ARRAY_SUPPORT ? (this[e] = t >>> 8, this[e + 1] = 255 & t) : P(this, t, e, !1), e + 2; + }, u.prototype.writeInt32LE = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 4, 2147483647, -2147483648), u.TYPED_ARRAY_SUPPORT ? (this[e] = 255 & t, this[e + 1] = t >>> 8, this[e + 2] = t >>> 16, this[e + 3] = t >>> 24) : F(this, t, e, !0), e + 4; + }, u.prototype.writeInt32BE = function (t, e, r) { + return t = +t, e |= 0, r || B(this, t, e, 4, 2147483647, -2147483648), t < 0 && (t = 4294967295 + t + 1), u.TYPED_ARRAY_SUPPORT ? (this[e] = t >>> 24, this[e + 1] = t >>> 16, this[e + 2] = t >>> 8, this[e + 3] = 255 & t) : F(this, t, e, !1), e + 4; + }, u.prototype.writeFloatLE = function (t, e, r) { + return M(this, t, e, !0, r); + }, u.prototype.writeFloatBE = function (t, e, r) { + return M(this, t, e, !1, r); + }, u.prototype.writeDoubleLE = function (t, e, r) { + return j(this, t, e, !0, r); + }, u.prototype.writeDoubleBE = function (t, e, r) { + return j(this, t, e, !1, r); + }, u.prototype.copy = function (t, e, r, n) { + if (r || (r = 0), n || 0 === n || (n = this.length), e >= t.length && (e = t.length), e || (e = 0), n > 0 && n < r && (n = r), n === r) return 0; + if (0 === t.length || 0 === this.length) return 0; + if (e < 0) throw new RangeError("targetStart out of bounds"); + if (r < 0 || r >= this.length) throw new RangeError("sourceStart out of bounds"); + if (n < 0) throw new RangeError("sourceEnd out of bounds"); + n > this.length && (n = this.length), t.length - e < n - r && (n = t.length - e + r); + var i, + o = n - r; + if (this === t && r < e && e < n) for (i = o - 1; i >= 0; --i) t[i + e] = this[i + r];else if (o < 1e3 || !u.TYPED_ARRAY_SUPPORT) for (i = 0; i < o; ++i) t[i + e] = this[i + r];else Uint8Array.prototype.set.call(t, this.subarray(r, r + o), e); + return o; + }, u.prototype.fill = function (t, e, r, n) { + if ("string" == typeof t) { + if ("string" == typeof e ? (n = e, e = 0, r = this.length) : "string" == typeof r && (n = r, r = this.length), 1 === t.length) { + var i = t.charCodeAt(0); + i < 256 && (t = i); + } + + if (void 0 !== n && "string" != typeof n) throw new TypeError("encoding must be a string"); + if ("string" == typeof n && !u.isEncoding(n)) throw new TypeError("Unknown encoding: " + n); + } else "number" == typeof t && (t &= 255); + + if (e < 0 || this.length < e || this.length < r) throw new RangeError("Out of range index"); + if (r <= e) return this; + var o; + if (e >>>= 0, r = void 0 === r ? this.length : r >>> 0, t || (t = 0), "number" == typeof t) for (o = e; o < r; ++o) this[o] = t;else { + var a = u.isBuffer(t) ? t : D(new u(t, n).toString()), + s = a.length; + + for (o = 0; o < r - e; ++o) this[o + e] = a[o % s]; + } + return this; + }; + var z = /[^+\/0-9A-Za-z-_]/g; + + function U(t) { + return t < 16 ? "0" + t.toString(16) : t.toString(16); + } + + function D(t, e) { + var r; + e = e || 1 / 0; + + for (var n = t.length, i = null, o = [], a = 0; a < n; ++a) { + if ((r = t.charCodeAt(a)) > 55295 && r < 57344) { + if (!i) { + if (r > 56319) { + (e -= 3) > -1 && o.push(239, 191, 189); + continue; + } + + if (a + 1 === n) { + (e -= 3) > -1 && o.push(239, 191, 189); + continue; + } + + i = r; + continue; + } + + if (r < 56320) { + (e -= 3) > -1 && o.push(239, 191, 189), i = r; + continue; + } + + r = 65536 + (i - 55296 << 10 | r - 56320); + } else i && (e -= 3) > -1 && o.push(239, 191, 189); + + if (i = null, r < 128) { + if ((e -= 1) < 0) break; + o.push(r); + } else if (r < 2048) { + if ((e -= 2) < 0) break; + o.push(r >> 6 | 192, 63 & r | 128); + } else if (r < 65536) { + if ((e -= 3) < 0) break; + o.push(r >> 12 | 224, r >> 6 & 63 | 128, 63 & r | 128); + } else { + if (!(r < 1114112)) throw new Error("Invalid code point"); + if ((e -= 4) < 0) break; + o.push(r >> 18 | 240, r >> 12 & 63 | 128, r >> 6 & 63 | 128, 63 & r | 128); + } + } + + return o; + } + + function q(t) { + return n.toByteArray(function (t) { + if ((t = function (t) { + return t.trim ? t.trim() : t.replace(/^\s+|\s+$/g, ""); + }(t).replace(z, "")).length < 2) return ""; + + for (; t.length % 4 != 0;) t += "="; + + return t; + }(t)); + } + + function Z(t, e, r, n) { + for (var i = 0; i < n && !(i + r >= e.length || i >= t.length); ++i) e[i + r] = t[i]; + + return i; + } + }).call(this, r(23)); + }, function (t, e, r) { + var n = r(17), + i = r(21), + o = r(43); + t.exports = n ? function (t, e, r) { + return i.f(t, e, o(1, r)); + } : function (t, e, r) { + return t[e] = r, t; + }; + }, function (t, e, r) { + var n = r(17), + i = r(106), + o = r(2), + a = r(62), + s = Object.defineProperty; + e.f = n ? s : function (t, e, r) { + if (o(t), e = a(e, !0), o(r), i) try { + return s(t, e, r); + } catch (t) {} + if ("get" in r || "set" in r) throw TypeError("Accessors not supported"); + return "value" in r && (t[e] = r.value), t; + }; + }, function (t, e, r) { + var n = r(49); + + t.exports = function (t) { + return Object(n(t)); + }; + }, function (t, e) { + var r; + + r = function () { + return this; + }(); + + try { + r = r || new Function("return this")(); + } catch (t) { + "object" == typeof window && (r = window); + } + + t.exports = r; + }, function (t, e) { + var r = {}.hasOwnProperty; + + t.exports = function (t, e) { + return r.call(t, e); + }; + }, function (t, e, r) { + var n = r(4), + i = r(142); + t.exports = n ? i : function (t) { + return Map.prototype.entries.call(t); + }; + }, function (t, e, r) { + var n = r(3), + i = r(13), + o = function (t) { + return "function" == typeof t ? t : void 0; + }; + + t.exports = function (t, e) { + return arguments.length < 2 ? o(n[t]) || o(i[t]) : n[t] && n[t][e] || i[t] && i[t][e]; + }; + }, function (t, e, r) { + var n = r(255), + i = r(139); + + function o(e) { + return t.exports = o = i ? n : function (t) { + return t.__proto__ || n(t); + }, o(e); + } + + t.exports = o; + }, function (t, e, r) { + var n = r(60), + i = r(49); + + t.exports = function (t) { + return n(i(t)); + }; + }, function (t, e, r) { + var n = r(83), + i = Math.min; + + t.exports = function (t) { + return t > 0 ? i(n(t), 9007199254740991) : 0; + }; + }, function (t, e, r) { + t.exports = r(184); + }, function (t, e, r) { + var n = r(185); + + function i(t, e, r, i, o, a, s) { + try { + var u = t[a](s), + f = u.value; + } catch (t) { + return void r(t); + } + + u.done ? e(f) : n.resolve(f).then(i, o); + } + + t.exports = function (t) { + return function () { + var e = this, + r = arguments; + return new n(function (n, o) { + var a = t.apply(e, r); + + function s(t) { + i(a, n, o, s, u, "next", t); + } + + function u(t) { + i(a, n, o, s, u, "throw", t); + } + + s(void 0); + }); + }; + }; + }, function (t, e, r) { + var n = r(225), + i = r(254); + + t.exports = function (t, e) { + return !e || "object" !== n(e) && "function" != typeof e ? i(t) : e; + }; + }, function (t, e, r) { + var n = r(140), + i = r(96); + + t.exports = function (t, e) { + if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); + t.prototype = n(e && e.prototype, { + constructor: { + value: t, + writable: !0, + configurable: !0 + } + }), e && i(t, e); + }; + }, function (t, e) { + var r = {}.toString; + + t.exports = function (t) { + return r.call(t).slice(8, -1); + }; + }, function (t, e) { + t.exports = {}; + }, function (t, e, r) { + var n = r(21).f, + i = r(20), + o = r(24), + a = r(174), + s = r(6)("toStringTag"), + u = a !== {}.toString; + + t.exports = function (t, e, r, f) { + if (t) { + var c = r ? t : t.prototype; + o(c, s) || n(c, s, { + configurable: !0, + value: e + }), f && u && i(c, "toString", a); + } + }; + }, function (t, e, r) { + t.exports = r(181); + }, function (t, e) { + var r, + n, + i = t.exports = {}; + + function o() { + throw new Error("setTimeout has not been defined"); + } + + function a() { + throw new Error("clearTimeout has not been defined"); + } + + function s(t) { + if (r === setTimeout) return setTimeout(t, 0); + if ((r === o || !r) && setTimeout) return r = setTimeout, setTimeout(t, 0); + + try { + return r(t, 0); + } catch (e) { + try { + return r.call(null, t, 0); + } catch (e) { + return r.call(this, t, 0); + } + } + } + + !function () { + try { + r = "function" == typeof setTimeout ? setTimeout : o; + } catch (t) { + r = o; + } + + try { + n = "function" == typeof clearTimeout ? clearTimeout : a; + } catch (t) { + n = a; + } + }(); + var u, + f = [], + c = !1, + l = -1; + + function h() { + c && u && (c = !1, u.length ? f = u.concat(f) : l = -1, f.length && d()); + } + + function d() { + if (!c) { + var t = s(h); + c = !0; + + for (var e = f.length; e;) { + for (u = f, f = []; ++l < e;) u && u[l].run(); + + l = -1, e = f.length; + } + + u = null, c = !1, function (t) { + if (n === clearTimeout) return clearTimeout(t); + if ((n === a || !n) && clearTimeout) return n = clearTimeout, clearTimeout(t); + + try { + n(t); + } catch (e) { + try { + return n.call(null, t); + } catch (e) { + return n.call(this, t); + } + } + }(t); + } + } + + function p(t, e) { + this.fun = t, this.array = e; + } + + function g() {} + + i.nextTick = function (t) { + var e = new Array(arguments.length - 1); + if (arguments.length > 1) for (var r = 1; r < arguments.length; r++) e[r - 1] = arguments[r]; + f.push(new p(t, e)), 1 !== f.length || c || s(d); + }, p.prototype.run = function () { + this.fun.apply(null, this.array); + }, i.title = "browser", i.browser = !0, i.env = {}, i.argv = [], i.version = "", i.versions = {}, i.on = g, i.addListener = g, i.once = g, i.off = g, i.removeListener = g, i.removeAllListeners = g, i.emit = g, i.prependListener = g, i.prependOnceListener = g, i.listeners = function (t) { + return []; + }, i.binding = function (t) { + throw new Error("process.binding is not supported"); + }, i.cwd = function () { + return "/"; + }, i.chdir = function (t) { + throw new Error("process.chdir is not supported"); + }, i.umask = function () { + return 0; + }; + }, function (t, e, r) { + + var n = r(70), + i = Object.keys || function (t) { + var e = []; + + for (var r in t) e.push(r); + + return e; + }; + + t.exports = l; + var o = r(57); + o.inherits = r(47); + var a = r(127), + s = r(93); + o.inherits(l, a); + + for (var u = i(s.prototype), f = 0; f < u.length; f++) { + var c = u[f]; + l.prototype[c] || (l.prototype[c] = s.prototype[c]); + } + + function l(t) { + if (!(this instanceof l)) return new l(t); + a.call(this, t), s.call(this, t), t && !1 === t.readable && (this.readable = !1), t && !1 === t.writable && (this.writable = !1), this.allowHalfOpen = !0, t && !1 === t.allowHalfOpen && (this.allowHalfOpen = !1), this.once("end", h); + } + + function h() { + this.allowHalfOpen || this._writableState.ended || n.nextTick(d, this); + } + + function d(t) { + t.end(); + } + + Object.defineProperty(l.prototype, "writableHighWaterMark", { + enumerable: !1, + get: function () { + return this._writableState.highWaterMark; + } + }), Object.defineProperty(l.prototype, "destroyed", { + get: function () { + return void 0 !== this._readableState && void 0 !== this._writableState && this._readableState.destroyed && this._writableState.destroyed; + }, + set: function (t) { + void 0 !== this._readableState && void 0 !== this._writableState && (this._readableState.destroyed = t, this._writableState.destroyed = t); + } + }), l.prototype._destroy = function (t, e) { + this.push(null), this.end(), n.nextTick(e, t); + }; + }, function (t, e, r) { + + var n = r(1), + i = n(r(7)), + o = n(r(10)), + a = r(9).CramBufferOverrunError, + s = { + int: !0, + byte: !0, + long: !0, + byteArray: !0, + byteArrayBlock: !0 + }, + u = function () { + function t() { + var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, + r = arguments.length > 1 ? arguments[1] : void 0; + if ((0, i.default)(this, t), this.parameters = e, this.dataType = r, !r) throw new TypeError("must provide a data type to codec constructor"); + if (!s[r]) throw new TypeError("invalid data type ".concat(r)); + } + + return (0, o.default)(t, [{ + key: "_getBits", + value: function (t, e, r) { + var n = 0; + if (e.bytePosition + (7 - e.bitPosition + r) / 8 > t.length) throw new a("read error during decoding. the file seems to be truncated."); + + for (var i = r; i; i -= 1) n <<= 1, n |= t[e.bytePosition] >> e.bitPosition & 1, e.bitPosition -= 1, e.bitPosition < 0 && (e.bytePosition += 1), e.bitPosition &= 7; + + return n; + } + }]), t; + }(); + + t.exports = u; + }, function (t, e, r) { + r(168); + var n = r(176), + i = r(13), + o = r(20), + a = r(35), + s = r(6)("toStringTag"); + + for (var u in n) { + var f = i[u], + c = f && f.prototype; + c && !c[s] && o(c, s, u), a[u] = a.Array; + } + }, function (t, e, r) { + var n, + i, + o, + a = r(169), + s = r(13), + u = r(14), + f = r(20), + c = r(24), + l = r(63), + h = r(50), + d = s.WeakMap; + + if (a) { + var p = new d(), + g = p.get, + v = p.has, + y = p.set; + n = function (t, e) { + return y.call(p, t, e), e; + }, i = function (t) { + return g.call(p, t) || {}; + }, o = function (t) { + return v.call(p, t); + }; + } else { + var m = l("state"); + h[m] = !0, n = function (t, e) { + return f(t, m, e), e; + }, i = function (t) { + return c(t, m) ? t[m] : {}; + }, o = function (t) { + return c(t, m); + }; + } + + t.exports = { + set: n, + get: i, + has: o, + enforce: function (t) { + return o(t) ? i(t) : n(t, {}); + }, + getterFor: function (t) { + return function (e) { + var r; + if (!u(e) || (r = i(e)).type !== t) throw TypeError("Incompatible receiver, " + t + " required"); + return r; + }; + } + }; + }, function (t, e) { + t.exports = function (t, e) { + return { + enumerable: !(1 & t), + configurable: !(2 & t), + writable: !(4 & t), + value: e + }; + }; + }, function (t, e, r) { + var n = r(2), + i = r(173), + o = r(85), + a = r(50), + s = r(112), + u = r(79), + f = r(63)("IE_PROTO"), + c = function () {}, + l = function () { + var t, + e = u("iframe"), + r = o.length; + + for (e.style.display = "none", s.appendChild(e), e.src = String("javascript:"), (t = e.contentWindow.document).open(), t.write("