0) toSkip = top_offset;\n\n while (--i >= lo0 && k > 0) {\n if (filters.zero(j = index[i])) {\n if(toSkip > 0) {\n //skip matching row\n --toSkip;\n } else {\n array.push(data[j]);\n --k;\n }\n }\n }\n\n if(iterable){\n for(i = 0; i < iterablesEmptyRows.length && k > 0; i++) {\n // Add row with empty iterable column at the end\n if(filters.zero(j = iterablesEmptyRows[i])) {\n if(toSkip > 0) {\n //skip matching row\n --toSkip;\n } else {\n array.push(data[j]);\n --k;\n }\n }\n }\n }\n\n return array;\n }\n\n // Returns the bottom K selected records based on this dimension's order.\n // Note: observes this dimension's filter, unlike group and groupAll.\n function bottom(k, bottom_offset) {\n var array = [],\n i,\n j,\n toSkip = 0;\n\n if(bottom_offset && bottom_offset > 0) toSkip = bottom_offset;\n\n if(iterable) {\n // Add row with empty iterable column at the top\n for(i = 0; i < iterablesEmptyRows.length && k > 0; i++) {\n if(filters.zero(j = iterablesEmptyRows[i])) {\n if(toSkip > 0) {\n //skip matching row\n --toSkip;\n } else {\n array.push(data[j]);\n --k;\n }\n }\n }\n }\n\n i = lo0;\n\n while (i < hi0 && k > 0) {\n if (filters.zero(j = index[i])) {\n if(toSkip > 0) {\n //skip matching row\n --toSkip;\n } else {\n array.push(data[j]);\n --k;\n }\n }\n i++;\n }\n\n return array;\n }\n\n // Adds a new group to this dimension, using the specified key function.\n function group(key) {\n var group = {\n top: top,\n all: all,\n reduce: reduce,\n reduceCount: reduceCount,\n reduceSum: reduceSum,\n order: order,\n orderNatural: orderNatural,\n size: size,\n dispose: dispose,\n remove: dispose // for backwards-compatibility\n };\n\n // Ensure that this group will be removed when the dimension is removed.\n dimensionGroups.push(group);\n\n var groups, // array of {key, value}\n groupIndex, // object id ↦ group id\n groupWidth = 8,\n groupCapacity = capacity(groupWidth),\n k = 0, // cardinality\n select,\n heap,\n reduceAdd,\n reduceRemove,\n reduceInitial,\n update = cr_null,\n reset = cr_null,\n resetNeeded = true,\n groupAll = key === cr_null,\n n0old;\n\n if (arguments.length < 1) key = cr_identity;\n\n // The group listens to the crossfilter for when any dimension changes, so\n // that it can update the associated reduce values. It must also listen to\n // the parent dimension for when data is added, and compute new keys.\n filterListeners.push(update);\n indexListeners.push(add);\n removeDataListeners.push(removeData);\n\n // Incorporate any existing data into the grouping.\n add(values, index, 0, n);\n\n // Incorporates the specified new values into this group.\n // This function is responsible for updating groups and groupIndex.\n function add(newValues, newIndex, n0, n1) {\n\n if(iterable) {\n n0old = n0\n n0 = values.length - newValues.length\n n1 = newValues.length;\n }\n\n var oldGroups = groups,\n reIndex = iterable ? [] : cr_index(k, groupCapacity),\n add = reduceAdd,\n remove = reduceRemove,\n initial = reduceInitial,\n k0 = k, // old cardinality\n i0 = 0, // index of old group\n i1 = 0, // index of new record\n j, // object id\n g0, // old group\n x0, // old key\n x1, // new key\n g, // group to add\n x; // key of group to add\n\n // If a reset is needed, we don't need to update the reduce values.\n if (resetNeeded) add = initial = cr_null;\n if (resetNeeded) remove = initial = cr_null;\n\n // Reset the new groups (k is a lower bound).\n // Also, make sure that groupIndex exists and is long enough.\n groups = new Array(k), k = 0;\n if(iterable){\n groupIndex = k0 ? groupIndex : [];\n }\n else{\n groupIndex = k0 > 1 ? xfilterArray.arrayLengthen(groupIndex, n) : cr_index(n, groupCapacity);\n }\n\n\n // Get the first old key (x0 of g0), if it exists.\n if (k0) x0 = (g0 = oldGroups[0]).key;\n\n // Find the first new key (x1), skipping NaN keys.\n while (i1 < n1 && !((x1 = key(newValues[i1])) >= x1)) ++i1;\n\n // While new keys remain…\n while (i1 < n1) {\n\n // Determine the lesser of the two current keys; new and old.\n // If there are no old keys remaining, then always add the new key.\n if (g0 && x0 <= x1) {\n g = g0, x = x0;\n\n // Record the new index of the old group.\n reIndex[i0] = k;\n\n // Retrieve the next old key.\n g0 = oldGroups[++i0];\n if (g0) x0 = g0.key;\n } else {\n g = {key: x1, value: initial()}, x = x1;\n }\n\n // Add the lesser group.\n groups[k] = g;\n\n // Add any selected records belonging to the added group, while\n // advancing the new key and populating the associated group index.\n\n while (x1 <= x) {\n j = newIndex[i1] + (iterable ? n0old : n0)\n\n\n if(iterable){\n if(groupIndex[j]){\n groupIndex[j].push(k)\n }\n else{\n groupIndex[j] = [k]\n }\n }\n else{\n groupIndex[j] = k;\n }\n\n // Always add new values to groups. Only remove when not in filter.\n // This gives groups full information on data life-cycle.\n g.value = add(g.value, data[j], true);\n if (!filters.zeroExcept(j, offset, zero)) g.value = remove(g.value, data[j], false);\n if (++i1 >= n1) break;\n x1 = key(newValues[i1]);\n }\n\n groupIncrement();\n }\n\n // Add any remaining old groups that were greater th1an all new keys.\n // No incremental reduce is needed; these groups have no new records.\n // Also record the new index of the old group.\n while (i0 < k0) {\n groups[reIndex[i0] = k] = oldGroups[i0++];\n groupIncrement();\n }\n\n\n // Fill in gaps with empty arrays where there may have been rows with empty iterables\n if(iterable){\n for (var index1 = 0; index1 < n; index1++) {\n if(!groupIndex[index1]){\n groupIndex[index1] = [];\n }\n }\n }\n\n // If we added any new groups before any old groups,\n // update the group index of all the old records.\n if(k > i0){\n if(iterable){\n for (i0 = 0; i0 < n0old; ++i0) {\n for (index1 = 0; index1 < groupIndex[i0].length; index1++) {\n groupIndex[i0][index1] = reIndex[groupIndex[i0][index1]];\n }\n }\n }\n else{\n for (i0 = 0; i0 < n0; ++i0) {\n groupIndex[i0] = reIndex[groupIndex[i0]];\n }\n }\n }\n\n // Modify the update and reset behavior based on the cardinality.\n // If the cardinality is less than or equal to one, then the groupIndex\n // is not needed. If the cardinality is zero, then there are no records\n // and therefore no groups to update or reset. Note that we also must\n // change the registered listener to point to the new method.\n j = filterListeners.indexOf(update);\n if (k > 1 || iterable) {\n update = updateMany;\n reset = resetMany;\n } else {\n if (!k && groupAll) {\n k = 1;\n groups = [{key: null, value: initial()}];\n }\n if (k === 1) {\n update = updateOne;\n reset = resetOne;\n } else {\n update = cr_null;\n reset = cr_null;\n }\n groupIndex = null;\n }\n filterListeners[j] = update;\n\n // Count the number of added groups,\n // and widen the group index as needed.\n function groupIncrement() {\n if(iterable){\n k++\n return\n }\n if (++k === groupCapacity) {\n reIndex = xfilterArray.arrayWiden(reIndex, groupWidth <<= 1);\n groupIndex = xfilterArray.arrayWiden(groupIndex, groupWidth);\n groupCapacity = capacity(groupWidth);\n }\n }\n }\n\n function removeData(reIndex) {\n if (k > 1 || iterable) {\n var oldK = k,\n oldGroups = groups,\n seenGroups = cr_index(oldK, oldK),\n i,\n i0,\n j;\n\n // Filter out non-matches by copying matching group index entries to\n // the beginning of the array.\n if (!iterable) {\n for (i = 0, j = 0; i < n; ++i) {\n if (reIndex[i] !== REMOVED_INDEX) {\n seenGroups[groupIndex[j] = groupIndex[i]] = 1;\n ++j;\n }\n }\n } else {\n for (i = 0, j = 0; i < n; ++i) {\n if (reIndex[i] !== REMOVED_INDEX) {\n groupIndex[j] = groupIndex[i];\n for (i0 = 0; i0 < groupIndex[j].length; i0++) {\n seenGroups[groupIndex[j][i0]] = 1;\n }\n ++j;\n }\n }\n }\n\n // Reassemble groups including only those groups that were referred\n // to by matching group index entries. Note the new group index in\n // seenGroups.\n groups = [], k = 0;\n for (i = 0; i < oldK; ++i) {\n if (seenGroups[i]) {\n seenGroups[i] = k++;\n groups.push(oldGroups[i]);\n }\n }\n\n if (k > 1 || iterable) {\n // Reindex the group index using seenGroups to find the new index.\n if (!iterable) {\n for (i = 0; i < j; ++i) groupIndex[i] = seenGroups[groupIndex[i]];\n } else {\n for (i = 0; i < j; ++i) {\n for (i0 = 0; i0 < groupIndex[i].length; ++i0) {\n groupIndex[i][i0] = seenGroups[groupIndex[i][i0]];\n }\n }\n }\n } else {\n groupIndex = null;\n }\n filterListeners[filterListeners.indexOf(update)] = k > 1 || iterable\n ? (reset = resetMany, update = updateMany)\n : k === 1 ? (reset = resetOne, update = updateOne)\n : reset = update = cr_null;\n } else if (k === 1) {\n if (groupAll) return;\n for (var index3 = 0; index3 < n; ++index3) if (reIndex[index3] !== REMOVED_INDEX) return;\n groups = [], k = 0;\n filterListeners[filterListeners.indexOf(update)] =\n update = reset = cr_null;\n }\n }\n\n // Reduces the specified selected or deselected records.\n // This function is only used when the cardinality is greater than 1.\n // notFilter indicates a crossfilter.add/remove operation.\n function updateMany(filterOne, filterOffset, added, removed, notFilter) {\n\n if ((filterOne === one && filterOffset === offset) || resetNeeded) return;\n\n var i,\n j,\n k,\n n,\n g;\n\n if(iterable){\n // Add the added values.\n for (i = 0, n = added.length; i < n; ++i) {\n if (filters.zeroExcept(k = added[i], offset, zero)) {\n for (j = 0; j < groupIndex[k].length; j++) {\n g = groups[groupIndex[k][j]];\n g.value = reduceAdd(g.value, data[k], false, j);\n }\n }\n }\n\n // Remove the removed values.\n for (i = 0, n = removed.length; i < n; ++i) {\n if (filters.onlyExcept(k = removed[i], offset, zero, filterOffset, filterOne)) {\n for (j = 0; j < groupIndex[k].length; j++) {\n g = groups[groupIndex[k][j]];\n g.value = reduceRemove(g.value, data[k], notFilter, j);\n }\n }\n }\n return;\n }\n\n // Add the added values.\n for (i = 0, n = added.length; i < n; ++i) {\n if (filters.zeroExcept(k = added[i], offset, zero)) {\n g = groups[groupIndex[k]];\n g.value = reduceAdd(g.value, data[k], false);\n }\n }\n\n // Remove the removed values.\n for (i = 0, n = removed.length; i < n; ++i) {\n if (filters.onlyExcept(k = removed[i], offset, zero, filterOffset, filterOne)) {\n g = groups[groupIndex[k]];\n g.value = reduceRemove(g.value, data[k], notFilter);\n }\n }\n }\n\n // Reduces the specified selected or deselected records.\n // This function is only used when the cardinality is 1.\n // notFilter indicates a crossfilter.add/remove operation.\n function updateOne(filterOne, filterOffset, added, removed, notFilter) {\n if ((filterOne === one && filterOffset === offset) || resetNeeded) return;\n\n var i,\n k,\n n,\n g = groups[0];\n\n // Add the added values.\n for (i = 0, n = added.length; i < n; ++i) {\n if (filters.zeroExcept(k = added[i], offset, zero)) {\n g.value = reduceAdd(g.value, data[k], false);\n }\n }\n\n // Remove the removed values.\n for (i = 0, n = removed.length; i < n; ++i) {\n if (filters.onlyExcept(k = removed[i], offset, zero, filterOffset, filterOne)) {\n g.value = reduceRemove(g.value, data[k], notFilter);\n }\n }\n }\n\n // Recomputes the group reduce values from scratch.\n // This function is only used when the cardinality is greater than 1.\n function resetMany() {\n var i,\n j,\n g;\n\n // Reset all group values.\n for (i = 0; i < k; ++i) {\n groups[i].value = reduceInitial();\n }\n\n // We add all records and then remove filtered records so that reducers\n // can build an 'unfiltered' view even if there are already filters in\n // place on other dimensions.\n if(iterable){\n for (i = 0; i < n; ++i) {\n for (j = 0; j < groupIndex[i].length; j++) {\n g = groups[groupIndex[i][j]];\n g.value = reduceAdd(g.value, data[i], true, j);\n }\n }\n for (i = 0; i < n; ++i) {\n if (!filters.zeroExcept(i, offset, zero)) {\n for (j = 0; j < groupIndex[i].length; j++) {\n g = groups[groupIndex[i][j]];\n g.value = reduceRemove(g.value, data[i], false, j);\n }\n }\n }\n return;\n }\n\n for (i = 0; i < n; ++i) {\n g = groups[groupIndex[i]];\n g.value = reduceAdd(g.value, data[i], true);\n }\n for (i = 0; i < n; ++i) {\n if (!filters.zeroExcept(i, offset, zero)) {\n g = groups[groupIndex[i]];\n g.value = reduceRemove(g.value, data[i], false);\n }\n }\n }\n\n // Recomputes the group reduce values from scratch.\n // This function is only used when the cardinality is 1.\n function resetOne() {\n var i,\n g = groups[0];\n\n // Reset the singleton group values.\n g.value = reduceInitial();\n\n // We add all records and then remove filtered records so that reducers\n // can build an 'unfiltered' view even if there are already filters in\n // place on other dimensions.\n for (i = 0; i < n; ++i) {\n g.value = reduceAdd(g.value, data[i], true);\n }\n\n for (i = 0; i < n; ++i) {\n if (!filters.zeroExcept(i, offset, zero)) {\n g.value = reduceRemove(g.value, data[i], false);\n }\n }\n }\n\n // Returns the array of group values, in the dimension's natural order.\n function all() {\n if (resetNeeded) reset(), resetNeeded = false;\n return groups;\n }\n\n // Returns a new array containing the top K group values, in reduce order.\n function top(k) {\n var top = select(all(), 0, groups.length, k);\n return heap.sort(top, 0, top.length);\n }\n\n // Sets the reduce behavior for this group to use the specified functions.\n // This method lazily recomputes the reduce values, waiting until needed.\n function reduce(add, remove, initial) {\n reduceAdd = add;\n reduceRemove = remove;\n reduceInitial = initial;\n resetNeeded = true;\n return group;\n }\n\n // A convenience method for reducing by count.\n function reduceCount() {\n return reduce(xfilterReduce.reduceIncrement, xfilterReduce.reduceDecrement, cr_zero);\n }\n\n // A convenience method for reducing by sum(value).\n function reduceSum(value) {\n return reduce(xfilterReduce.reduceAdd(value), xfilterReduce.reduceSubtract(value), cr_zero);\n }\n\n // Sets the reduce order, using the specified accessor.\n function order(value) {\n select = xfilterHeapselect.by(valueOf);\n heap = xfilterHeap.by(valueOf);\n function valueOf(d) { return value(d.value); }\n return group;\n }\n\n // A convenience method for natural ordering by reduce value.\n function orderNatural() {\n return order(cr_identity);\n }\n\n // Returns the cardinality of this group, irrespective of any filters.\n function size() {\n return k;\n }\n\n // Removes this group and associated event listeners.\n function dispose() {\n var i = filterListeners.indexOf(update);\n if (i >= 0) filterListeners.splice(i, 1);\n i = indexListeners.indexOf(add);\n if (i >= 0) indexListeners.splice(i, 1);\n i = removeDataListeners.indexOf(removeData);\n if (i >= 0) removeDataListeners.splice(i, 1);\n i = dimensionGroups.indexOf(group);\n if (i >= 0) dimensionGroups.splice(i, 1);\n return group;\n }\n\n return reduceCount().orderNatural();\n }\n\n // A convenience function for generating a singleton group.\n function groupAll() {\n var g = group(cr_null), all = g.all;\n delete g.all;\n delete g.top;\n delete g.order;\n delete g.orderNatural;\n delete g.size;\n g.value = function() { return all()[0].value; };\n return g;\n }\n\n // Removes this dimension and associated groups and event listeners.\n function dispose() {\n dimensionGroups.forEach(function(group) { group.dispose(); });\n var i = dataListeners.indexOf(preAdd);\n if (i >= 0) dataListeners.splice(i, 1);\n i = dataListeners.indexOf(postAdd);\n if (i >= 0) dataListeners.splice(i, 1);\n i = removeDataListeners.indexOf(removeData);\n if (i >= 0) removeDataListeners.splice(i, 1);\n filters.masks[offset] &= zero;\n return filterAll();\n }\n\n return dimension;\n }\n\n // A convenience method for groupAll on a dummy dimension.\n // This implementation can be optimized since it always has cardinality 1.\n function groupAll() {\n var group = {\n reduce: reduce,\n reduceCount: reduceCount,\n reduceSum: reduceSum,\n value: value,\n dispose: dispose,\n remove: dispose // for backwards-compatibility\n };\n\n var reduceValue,\n reduceAdd,\n reduceRemove,\n reduceInitial,\n resetNeeded = true;\n\n // The group listens to the crossfilter for when any dimension changes, so\n // that it can update the reduce value. It must also listen to the parent\n // dimension for when data is added.\n filterListeners.push(update);\n dataListeners.push(add);\n\n // For consistency; actually a no-op since resetNeeded is true.\n add(data, 0, n);\n\n // Incorporates the specified new values into this group.\n function add(newData, n0) {\n var i;\n\n if (resetNeeded) return;\n\n // Cycle through all the values.\n for (i = n0; i < n; ++i) {\n\n // Add all values all the time.\n reduceValue = reduceAdd(reduceValue, data[i], true);\n\n // Remove the value if filtered.\n if (!filters.zero(i)) {\n reduceValue = reduceRemove(reduceValue, data[i], false);\n }\n }\n }\n\n // Reduces the specified selected or deselected records.\n function update(filterOne, filterOffset, added, removed, notFilter) {\n var i,\n k,\n n;\n\n if (resetNeeded) return;\n\n // Add the added values.\n for (i = 0, n = added.length; i < n; ++i) {\n if (filters.zero(k = added[i])) {\n reduceValue = reduceAdd(reduceValue, data[k], notFilter);\n }\n }\n\n // Remove the removed values.\n for (i = 0, n = removed.length; i < n; ++i) {\n if (filters.only(k = removed[i], filterOffset, filterOne)) {\n reduceValue = reduceRemove(reduceValue, data[k], notFilter);\n }\n }\n }\n\n // Recomputes the group reduce value from scratch.\n function reset() {\n var i;\n\n reduceValue = reduceInitial();\n\n // Cycle through all the values.\n for (i = 0; i < n; ++i) {\n\n // Add all values all the time.\n reduceValue = reduceAdd(reduceValue, data[i], true);\n\n // Remove the value if it is filtered.\n if (!filters.zero(i)) {\n reduceValue = reduceRemove(reduceValue, data[i], false);\n }\n }\n }\n\n // Sets the reduce behavior for this group to use the specified functions.\n // This method lazily recomputes the reduce value, waiting until needed.\n function reduce(add, remove, initial) {\n reduceAdd = add;\n reduceRemove = remove;\n reduceInitial = initial;\n resetNeeded = true;\n return group;\n }\n\n // A convenience method for reducing by count.\n function reduceCount() {\n return reduce(xfilterReduce.reduceIncrement, xfilterReduce.reduceDecrement, cr_zero);\n }\n\n // A convenience method for reducing by sum(value).\n function reduceSum(value) {\n return reduce(xfilterReduce.reduceAdd(value), xfilterReduce.reduceSubtract(value), cr_zero);\n }\n\n // Returns the computed reduce value.\n function value() {\n if (resetNeeded) reset(), resetNeeded = false;\n return reduceValue;\n }\n\n // Removes this group and associated event listeners.\n function dispose() {\n var i = filterListeners.indexOf(update);\n if (i >= 0) filterListeners.splice(i, 1);\n i = dataListeners.indexOf(add);\n if (i >= 0) dataListeners.splice(i, 1);\n return group;\n }\n\n return reduceCount();\n }\n\n // Returns the number of records in this crossfilter, irrespective of any filters.\n function size() {\n return n;\n }\n\n // Returns the raw row data contained in this crossfilter\n function all(){\n return data;\n }\n\n // Returns row data with all dimension filters applied, except for filters in ignore_dimensions\n function allFiltered(ignore_dimensions) {\n var array = [],\n i = 0,\n mask = maskForDimensions(ignore_dimensions || []);\n\n for (i = 0; i < n; i++) {\n if (filters.zeroExceptMask(i, mask)) {\n array.push(data[i]);\n }\n }\n\n return array;\n }\n\n function onChange(cb){\n if(typeof cb !== 'function'){\n /* eslint no-console: 0 */\n console.warn('onChange callback parameter must be a function!');\n return;\n }\n callbacks.push(cb);\n return function(){\n callbacks.splice(callbacks.indexOf(cb), 1);\n };\n }\n\n function triggerOnChange(eventName){\n for (var i = 0; i < callbacks.length; i++) {\n callbacks[i](eventName);\n }\n }\n\n return arguments.length\n ? add(arguments[0])\n : crossfilter;\n}\n\n// Returns an array of size n, big enough to store ids up to m.\nfunction cr_index(n, m) {\n return (m < 0x101\n ? xfilterArray.array8 : m < 0x10001\n ? xfilterArray.array16\n : xfilterArray.array32)(n);\n}\n\n// Constructs a new array of size n, with sequential values from 0 to n - 1.\nfunction cr_range(n) {\n var range = cr_index(n, n);\n for (var i = -1; ++i < n;) range[i] = i;\n return range;\n}\n\nfunction capacity(w) {\n return w === 8\n ? 0x100 : w === 16\n ? 0x10000\n : 0x100000000;\n}\n","import {d3} from './lib';\nimport crossfilter from 'crossfilter2';\n\n/* Decompresses ideogram's annotations for crossfilter initialization\nBy default, annotations are clustered by chromosome, e.g.\n[\n {\"chr\": \"1\", \"annots\": [{\"from\": 100, \"to\", 101, \"chr\": \"1\", ...}, ...]},\n {\"chr\": \"2\", \"annots\": [{\"from\": 500, \"to\", 501, \"chr\": \"2\", ...}, ...]},\n ...\n]\nThis method flattens that structure to e.g.\n[\n {\"from\": 100, \"to\": 101, \"chr\": \"1\", ...},\n ...\n {\"from\": 500, \"to\": 501, \"chr\": \"2\", ...},\n]\nSee also: packAnnots\n*/\nfunction unpackAnnots() {\n var chr, annots, i,\n unpackedAnnots = [],\n ideo = this,\n chrs = ideo.annots;\n\n for (i = 0; i < chrs.length; i++) {\n chr = chrs[i];\n annots = chr.annots;\n unpackedAnnots = unpackedAnnots.concat(annots);\n }\n\n return unpackedAnnots;\n}\n\n/*\n Compresses annots back to default state. Inverse of unpackAnnots.\n*/\nfunction packAnnots(unpackedAnnots) {\n var chr, annot, i,\n annots = [],\n ideo = this,\n chrs = ideo.annots;\n\n for (chr in chrs) {\n annots.push({chr: chrs[chr].chr, annots: []});\n }\n\n for (i = 0; i < unpackedAnnots.length; i++) {\n annot = unpackedAnnots[i];\n annots[annot.chrIndex].annots.push(annot);\n }\n\n return annots;\n}\n\n/*\n Initializes crossfilter. Needed for client-side filtering.\n More: https://github.com/square/crossfilter/wiki/API-Reference\n*/\nfunction initCrossFilter() {\n var i, facet,\n ideo = this,\n keys = ideo.rawAnnots.keys;\n\n ideo.unpackedAnnots = ideo.unpackAnnots();\n ideo.crossfilter = crossfilter(ideo.unpackedAnnots);\n\n ideo.annotsByFacet = {};\n ideo.facets = keys.slice(3, keys.length);\n\n for (i = 0; i < ideo.facets.length; i++) {\n facet = ideo.facets[i];\n ideo.annotsByFacet[facet] =\n ideo.crossfilter.dimension(function(d) {\n return d[facet];\n });\n }\n\n if ('filterSelections' in ideo) {\n ideo.filterAnnots(ideo.filterSelections);\n }\n\n ideo.filteredAnnots = ideo.annots;\n}\n\nfunction getFilteredResults(selections, ideo) {\n var fn, i, facet, results, filter,\n counts = {};\n\n if (Object.keys(selections).length === 0) {\n results = ideo.unpackedAnnots;\n } else {\n for (i = 0; i < ideo.facets.length; i++) {\n facet = ideo.facets[i];\n if (facet in selections) {\n filter = selections[facet];\n if (Array.isArray(filter)) {\n fn = function(d) {\n // Filter is numeric range\n if (filter.length === 2) {\n // [min, max]\n return filter[0] <= d && d < filter[1];\n } else if (filter.length === 4) {\n // [min1, max1, min2, max2]\n return (\n filter[0] <= d && d < filter[1] ||\n filter[2] <= d && d < filter[3]\n );\n }\n };\n } else {\n fn = function(d) {\n // Filter is set of categories\n return (d in filter);\n };\n }\n } else {\n fn = null;\n }\n ideo.annotsByFacet[facet].filter(fn);\n counts[facet] = ideo.annotsByFacet[facet].group().top(Infinity);\n }\n results = ideo.annotsByFacet[facet].top(Infinity);\n }\n\n return [results, counts];\n}\n\n/*\n Filters annotations based on the given selections.\n \"selections\" is an object of objects, e.g.\n\n {\n \"tissue-type\": { <-- a facet\n \"cerebral-cortex\": 1, <-- a filter; \"1\" means it is selected\n \"liver\": 1\n },\n \"gene-type\": {\n mirna\": 1\n }\n }\n\n Translation:\n select where:\n (tissue-type is cerebral-cortex OR liver) and (gene-type is mirna)\n\n TODO:\n * Filter counts\n * Integrate server-side filtering for very large datasets\n*/\nfunction filterAnnots(selections) {\n var i, facet, results, counts,\n t0 = Date.now(),\n ideo = this;\n\n ideo.filterSelections = selections;\n [results, counts] = getFilteredResults(selections, ideo);\n\n for (i < 0; i < ideo.facets.length; i++) {\n ideo.annotsByFacet[facet].filterAll(); // clear filters\n }\n\n results = ideo.packAnnots(results);\n\n delete ideo.maxAnnotsPerBar;\n delete ideo.maxAnnotsPerBarAllChrs;\n\n ideo.filteredAnnots = results;\n\n d3.selectAll(ideo.selector + ' polygon.annot').remove();\n ideo.drawAnnots(results);\n\n console.log('Time in filterAnnots: ' + (Date.now() - t0) + ' ms');\n\n return counts;\n}\n\nexport {unpackAnnots, packAnnots, initCrossFilter, filterAnnots};\n","function getPixelAndOtherData(bands, chr, hasBands, ideo) {\n var i, band, csLength, width, maxLength,\n pxStop = 0,\n taxid = chr.id.split('-')[1],\n cs = ideo.coordinateSystem,\n chrHeight = ideo.config.chrHeight;\n\n for (i = 0; i < bands.length; i++) {\n band = bands[i];\n csLength = band[cs].stop - band[cs].start;\n\n // If ideogram is rotated (and thus showing only one chromosome),\n // then set its width independent of the longest chromosome in this\n // genome.\n if (ideo._layout._isRotated) {\n width = chrHeight * csLength / chr.length;\n } else {\n if (ideo.config.chromosomeScale === 'relative') {\n maxLength = ideo.maxLength[taxid][cs];\n } else {\n maxLength = ideo.maxLength[cs];\n }\n width = chrHeight * chr.length / maxLength * csLength / chr.length;\n }\n bands[i].px = {start: pxStop, stop: pxStop + width, width: width};\n\n pxStop = bands[i].px.stop;\n\n if (hasBands && band.stain === 'acen' && band.name[0] === 'p') {\n chr.pcenIndex = i;\n }\n }\n return [bands, chr, pxStop];\n}\n\n/**\n * TODO:\n * A chromosome-level scale property is likely\n * nonsensical for any chromosomes that have cytogenetic band data.\n * Different bands tend to have ratios between number of base pairs\n * and physical length.\n *\n * However, a chromosome-level scale property is likely\n * necessary for chromosomes that do not have band data.\n *\n * This needs further review.\n */\nfunction getChrScale(chr, hasBands, ideo) {\n var chrHeight = ideo.config.chrHeight,\n chrLength = chr.length,\n maxLength = ideo.maxLength,\n taxid = chr.id.split('-')[1],\n scale = {};\n\n scale.bp = chrHeight / maxLength.bp;\n\n if (ideo.config.multiorganism === true) {\n // chr.scale.bp = band.iscn.stop / band.bp.stop;\n if (ideo.config.chromosomeScale === 'relative') {\n scale.iscn = chrHeight * chrLength / maxLength[taxid].bp;\n scale.bp = chrHeight / maxLength[taxid].bp;\n } else {\n scale.iscn = chrHeight * chrLength / maxLength.bp;\n }\n } else if (hasBands) {\n scale.iscn = chrHeight / maxLength.iscn;\n }\n\n return scale;\n}\n\nfunction getChromosomePixels(chr) {\n var bands, chrHeight, pxStop, hasBands, maxLength,\n taxid = chr.id.split('-')[1],\n ideo = this;\n\n bands = chr.bands;\n chrHeight = ideo.config.chrHeight;\n pxStop = 0;\n hasBands = (typeof bands !== 'undefined');\n\n if (hasBands) {\n [bands, chr, pxStop] = getPixelAndOtherData(bands, chr, hasBands, ideo);\n } else {\n if (ideo.config.chromosomeScale === 'relative') {\n maxLength = ideo.maxLength[taxid][ideo.coordinateSystem];\n } else {\n maxLength = ideo.maxLength[ideo.coordinateSystem];\n }\n pxStop = chrHeight * chr.length / maxLength;\n }\n\n chr.width = pxStop;\n chr.scale = getChrScale(chr, hasBands, ideo);\n chr.bands = bands;\n\n return chr;\n}\n\nfunction getChrModelScaffold(chr, bands, chrName, ideo) {\n var hasBands = (typeof bands !== 'undefined');\n\n if (hasBands) {\n const lastBand = bands[bands.length - 1];\n chr.name = chrName;\n chr.length = lastBand[ideo.coordinateSystem].stop;\n\n // Accounts for case where this chromosome\n chr.bpLength = lastBand.bp.stop;\n\n chr.type = 'nuclear';\n } else {\n chr = chrName;\n }\n\n return chr;\n}\n\n/**\n * Encountered when processing an assembly that has chromosomes with\n * centromere data, but this chromosome does not.\n * Example: chromosome F1 in Felis catus.\n */\nfunction deleteExtraneousBands(chr, hasBands) {\n if (hasBands && chr.bands.length === 1) {\n delete chr.bands;\n }\n return chr;\n}\n\nfunction getCentromerePosition(hasBands, bands) {\n\n if (hasBands === false) return '';\n\n // As with Macaca mulatta chromosome Y\n const firstBand = bands[0];\n const lastBand = bands.slice(-1)[0];\n const chrLength = lastBand.bp.stop - firstBand.bp.start;\n const smallLength = chrLength/20;\n\n if (\n // As with almost all mouse chromosome, chimpanzee chr22\n firstBand.name[0] === 'p' && bands[1].name[0] === 'q' &&\n firstBand.bp.stop - firstBand.bp.start < smallLength\n ) {\n return 'telocentric-p';\n }\n\n const penultimateBand = bands.slice(-2)[0];\n\n if (\n penultimateBand.name[0] === 'p' && lastBand.name[0] === 'q' &&\n lastBand.bp.stop - lastBand.bp.start < smallLength\n ) {\n // As with Macaca mulatta chromosome Y\n return 'telocentric-q';\n }\n\n return '';\n}\n\n/**\n * Generates a model object for each chromosome containing information on\n * its name, DOM ID, length in base pairs or ISCN coordinates, cytogenetic\n * bands, centromere position, etc.\n */\nfunction getChromosomeModel(bands, chrName, taxid, chrIndex) {\n var hasBands, org,\n chr = {},\n ideo = this;\n\n hasBands = (typeof bands !== 'undefined');\n\n chr = getChrModelScaffold(chr, bands, chrName, ideo);\n\n chr.chrIndex = chrIndex;\n chr.id = 'chr' + chr.name + '-' + taxid;\n\n if (ideo.config.fullChromosomeLabels === true) {\n org = this.organisms[taxid];\n chr.name = org.scientificName + ' chr' + chr.name;\n }\n\n chr.bands = bands;\n chr = ideo.getChromosomePixels(chr);\n chr.centromerePosition = getCentromerePosition(hasBands, bands);\n\n chr = deleteExtraneousBands(chr, hasBands);\n\n return chr;\n}\n\nexport {getChromosomeModel, getChromosomePixels};\n","// import {getSettings, handleSettingsHeaderClick} from './settings-ui';\nimport version from '../version';\nimport {downloadPng} from '../lib';\n\nconst style = `\n `;\n\n// eslint-disable-next-line max-len\nconst gearIcon = '';\n// Font Awesome Free 5.2.0 by @fontawesome - https://fontawesome.com\n// License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)\n\nfunction deactivate(items) {\n items.forEach(item => {item.classList.remove('active');});\n}\n\nfunction closeTools() {\n const toolHeaders = document.querySelectorAll('#tools > ul > li');\n deactivate(toolHeaders);\n const itemsToClose =\n document.querySelectorAll('.ideo-modal, .ideo-tool-panel');\n itemsToClose.forEach(item => {item.remove();});\n\n document.querySelector('#tools').style.display = 'none';\n}\n\n/**\n * As needed, hide tool panels that are triggered by hovering\n */\nfunction handleHideForHoverables(trigger, tool, toolHeader, toolHeaders) {\n if (trigger === 'mouseenter') {\n\n // Hide panel when hover leaves tool header, if new target element\n // is part of the tools UI (and not the panel itself)\n toolHeader.addEventListener('mouseleave', event => {\n const toElement = event.toElement;\n const toId = toElement.id;\n const panelElement = document.querySelector('.ideo-tool-panel');\n const toolsElement = document.querySelector('#tools');\n if (\n toolsElement.contains(toElement) &&\n panelElement && !panelElement.contains(toElement) &&\n toId !== tool\n ) {\n deactivate(toolHeaders);\n panelElement.remove();\n }\n });\n }\n}\n\n/** Determine action that should trigger a tool panel to display */\nfunction getTrigger(toolHeader) {\n const shouldHover =\n Array.from(toolHeader.classList).includes('ideo-tool-hover');\n const trigger = shouldHover ? 'mouseenter' : 'click';\n return trigger;\n}\n\n/** Shows clicked tool as active, displays resulting panel */\nfunction handleToolClick(ideo) {\n const toolHeaders = document.querySelectorAll('#tools > ul > li');\n\n toolHeaders.forEach(toolHeader => {\n const trigger = getTrigger(toolHeader);\n\n toolHeader.addEventListener(trigger, event => {\n\n // Show only clicked tool header as active\n deactivate(toolHeaders);\n toolHeader.classList += ' active';\n\n const tool = toolHeader.id.split('-')[0];\n const panel = getPanel(tool, ideo);\n\n if (trigger === 'mouseenter') {\n toolHeader.insertAdjacentHTML('beforeend', panel);\n handleHideForHoverables(trigger, tool, toolHeader, toolHeaders);\n\n if (tool === 'download') {\n document.querySelector('#download-image')\n .addEventListener('click', event => {\n closeTools();\n downloadPng(ideo);\n });\n\n document.querySelector('#download-annots')\n .addEventListener('click', event => {\n const element = document.querySelector('#download-annots');\n const classes = Array.from(element.classList);\n if (classes.includes('ideo-disabled') === false) {\n closeTools();\n ideo.downloadAnnotations();\n }\n });\n }\n } else {\n document.querySelector('#gear').insertAdjacentHTML('beforeend', panel);\n }\n });\n });\n\n // Upon clicking \"close\" (x), remove tools UI\n document.querySelectorAll('#close').forEach(closeButton => {\n closeButton.addEventListener('click', () => {closeTools();});\n });\n\n}\n\nfunction handleGearClick(ideo) {\n document.querySelector('#gear')\n .addEventListener('click', event => {\n var options = document.querySelector('#tools');\n if (options.style.display === 'none') {\n options.style.display = '';\n hideOnClickOutside();\n } else {\n options.style.display = 'none';\n closeTools();\n }\n });\n\n handleToolClick(ideo);\n\n // handleSettingsHeaderClick(ideo);\n}\n\nfunction showGearOnIdeogramHover(ideo) {\n const container = document.querySelector(ideo.selector);\n const gear = document.querySelector('#gear');\n const panel = document.querySelector('#tools');\n\n container.addEventListener('mouseover', () => gear.style.display = '');\n container.addEventListener('mouseout', () => {\n // Hide gear only if panel is not shown\n if (panel.style.display === 'none') {\n gear.style.display = 'none';\n }\n });\n\n gear.addEventListener('mouseover', () => gear.style.display = '');\n}\n\nfunction getPanel(tool, ideo) {\n var panel;\n // if (tool === 'settings') panel = getSettings();\n if (tool === 'download') panel = getDownload(ideo);\n if (tool === 'about') panel = getAbout();\n return panel.trim();\n}\n\nfunction getDownload(ideo) {\n\n const numAnnots = document.querySelectorAll('.annot').length;\n const annotsClass = (numAnnots > 0) ? '' : 'ideo-disabled';\n\n return `\n \n
Image\n Annotations\n \n `;\n}\n\nfunction getAbout() {\n const ideogramLink = `\n \n Ideogram.js`;\n const closeButton = 'x';\n return `\n \n ${ideogramLink}, version ${version} ${closeButton}
\n Chromosome visualization for the web\n
`;\n}\n\nfunction hideOnClickOutside(selector) {\n const elements = document.querySelectorAll('#gear, #tools');\n const outsideClickListener = event => {\n let clickedOutsideCount = 0;\n elements.forEach((element) => {\n if (!element.contains(event.target)) {\n clickedOutsideCount += 1;\n }\n });\n if (clickedOutsideCount === elements.length) {\n closeTools();\n removeClickListener();\n }\n };\n\n const removeClickListener = () => {\n document.removeEventListener('click', outsideClickListener);\n };\n\n document.addEventListener('click', outsideClickListener);\n}\n\nfunction initTools(ideo) {\n\n const triangle = '▸';\n\n const toolsHtml = `\n ${style}\n ${gearIcon}
\n `;\n\n\n document.querySelector(ideo.selector)\n .insertAdjacentHTML('beforebegin', toolsHtml);\n\n handleGearClick(ideo);\n\n showGearOnIdeogramHover(ideo);\n}\n\nexport {initTools};\n\n","/* eslint-disable no-use-before-define */\n\nexport class ModelAdapter {\n\n constructor(model) {\n this._model = model;\n this._class = 'ModelAdapter';\n }\n\n static getInstance(model) {\n if (model.bands) {\n return new ModelAdapter(model);\n } else {\n return new ModelNoBandsAdapter(model);\n }\n }\n\n getModel() {\n return this._model;\n }\n\n getCssClass() {\n return '';\n }\n}\n\nexport class ModelNoBandsAdapter extends ModelAdapter {\n\n constructor(model) {\n super(model);\n this._class = 'ModelNoBandsAdapter';\n }\n\n getModel() {\n this._model.bands = [];\n\n const isMT = this._model.name === 'MT'; // Is mitochondrial chromosome\n const width = this._model.width;\n\n if (width > 1 || isMT) {\n // Add single band to bands array\n this._model.bands.push({\n name: 'q',\n px: {\n start: 0,\n stop: width,\n width: width\n },\n bp: {\n start: 1,\n stop: this._model.bpLength\n },\n iscn: {\n start: 1,\n stop: this._model.length\n }\n });\n }\n\n return this._model;\n }\n\n getCssClass() {\n return 'noBands';\n }\n\n}\n","import {Ploidy} from './ploidy';\n\nexport class Color {\n\n constructor(config) {\n // Ideogram config\n this._config = config;\n this._ploidy = new Ploidy(this._config);\n }\n\n getArmColor(chrSetIndex, chrIndex, armIndex) {\n if (this._config.armColors) {\n return this._config.armColors[armIndex];\n } else if (this._config.ancestors) {\n return this._getPolyploidArmColor(chrSetIndex, chrIndex, armIndex);\n } else {\n return null;\n }\n }\n\n getBorderColor(chrSetIndex, chrIndex, armIndex) {\n const config = this._config;\n const color = config.chrBorderColor ? config.chrBorderColor : '#000';\n if (chrIndex < config.ploidy) {\n return color;\n } else if (this._ploidy.exists(chrSetIndex, chrIndex, armIndex)) {\n return color;\n } else {\n return '#fff';\n }\n }\n\n getFillColor() {\n const config = this._config;\n if (!config.chrFillColor) return '#AAA';\n const color = config.chrFillColor;\n if (typeof color === 'string') {\n return {arm: color, centromere: ''};\n } else {\n return color;\n };\n }\n\n _getPolyploidArmColor(chrSetIndex, chrIndex, armIndex) {\n if (!this._ploidy.exists(chrSetIndex, chrIndex, armIndex)) {\n return 'transparent';\n } else {\n var ancestor =\n this._ploidy.getAncestor(chrSetIndex, chrIndex, armIndex);\n return this._config.ancestors[ancestor];\n }\n }\n\n}\n","export class Range {\n\n /**\n * Chromosome range.\n * @public\n * @class\n * @param {Object} data - range data.\n * @param {Integer} data.chr - chromosome index.\n * @param {Integer[]} [data.ploidy] - array which controls on which\n * chromosomes range should appear in case\n * of ploidy.\n * @param {Integer} data.start - range start.\n * @param {Integer} data.stop - range end.\n * @param {String} data.color - range color.\n */\n constructor(data) {\n this._data = data;\n this.start = data.start;\n this.stop = data.stop;\n this.length = this.stop - this.start;\n }\n\n getColor(chrIndex) {\n if (!('ploidy' in this._data)) {\n return this._getColor(chrIndex);\n } else if ('ploidy' in this._data && this._data.ploidy[chrIndex]) {\n return this._getColor(chrIndex);\n } else {\n return 'transparent';\n }\n }\n\n _getColor(chrIndex) {\n if (Array.isArray(this._data.color)) {\n return this._data.color[chrIndex];\n } else {\n return this._data.color;\n }\n }\n\n}\n","/* eslint-disable no-use-before-define */\nimport {Color} from './../color';\nimport {Range} from './../range';\n\nexport class Chromosome {\n\n constructor(adapter, config, ideo) {\n this._adapter = adapter;\n this._model = this._adapter.getModel();\n this._config = config;\n this._ideo = ideo;\n this._color = new Color(this._config);\n this._bumpCoefficient = 5;\n }\n\n /**\n * Factory method\n */\n static getInstance(adapter, config, ideo) {\n const centromerePosition = adapter.getModel().centromerePosition;\n if (centromerePosition === 'telocentric-p') {\n return new TelocentricPChromosome(adapter, config, ideo);\n } else if (centromerePosition === 'telocentric-q') {\n return new TelocentricQChromosome(adapter, config, ideo);\n } else {\n return new MetacentricChromosome(adapter, config, ideo);\n }\n }\n\n _addPArmShape(clipPath, isPArmRendered) {\n if (isPArmRendered) {\n return clipPath.concat(this._getPArmShape());\n } else {\n return clipPath;\n }\n }\n\n _addQArmShape(clipPath, isQArmRendered) {\n if (isQArmRendered) {\n return clipPath.concat(this._getQArmShape());\n } else {\n return clipPath;\n }\n }\n\n /**\n * Append bands container and apply clip-path to it\n */\n render(container, chrSetIndex, chrIndex) {\n\n var self, isPArmRendered, isQArmRendered, clipPath, opacity, fill,\n isFullyBanded;\n\n self = this;\n\n container = container.append('g')\n .attr('class', 'bands')\n .attr('clip-path',\n 'url(#' + this._model.id + '-chromosome-set-clippath)'\n );\n\n // Render chromosome arms\n isPArmRendered = this._renderArm(container, chrSetIndex, chrIndex, 'p');\n isQArmRendered = this._renderArm(container, chrSetIndex, chrIndex, 'q');\n\n // Render range set\n this._renderRangeSet(container, chrSetIndex, chrIndex);\n\n // Push arms shape string into clipPath array\n clipPath = [];\n clipPath = this._addPArmShape(clipPath, isPArmRendered);\n clipPath = this._addQArmShape(clipPath, isQArmRendered);\n\n opacity = '0';\n fill = '';\n isFullyBanded = this.isFullyBanded();\n if (\n 'ancestors' in this._ideo.config &&\n !('rangeSet' in this._ideo.config)\n ) {\n // E.g. diploid human genome (with translucent overlay)\n fill = self._color.getArmColor(chrSetIndex, chrIndex, 0);\n if (isFullyBanded) {\n opacity = '0.5';\n }\n } else if (isFullyBanded) {\n // E.g. mouse reference genome\n opacity = null;\n fill = 'transparent';\n } else if (!('ancestors' in this._ideo.config)) {\n // E.g. chimpanzee assembly Pan_tro 3.0\n opacity = '1';\n }\n\n let centromereFill;\n if (this._ideo.config.chrFillColor) {\n const fillColor = self._color.getFillColor();\n fill = fillColor.arm;\n centromereFill = fillColor.centromere;\n }\n\n // Render chromosome border\n container.append('g')\n .attr('class', 'chromosome-border')\n .selectAll('path')\n .data(clipPath)\n .enter()\n .append('path')\n .attr('fill', fill)\n .style('fill-opacity', opacity)\n .style('fill', function(d) {\n if (d.class === 'acen' && centromereFill) {\n return centromereFill;\n }\n })\n .attr('stroke', function(d, i) {\n return self._color.getBorderColor(chrSetIndex, chrIndex, i);\n })\n .attr('stroke-width', function(d) {\n return ('strokeWidth' in d ? d.strokeWidth : 1);\n })\n .attr('d', function(d) {\n return d.path;\n }).attr('class', function(d) {\n return d.class;\n });\n\n return clipPath;\n }\n\n _renderRangeSet(container, chrSetIndex, chrIndex) {\n\n var self, rangeSet, rangesContainer, ideo;\n\n if (!('rangeSet' in this._config)) {\n return;\n }\n\n rangeSet = this._config.rangeSet.filter(function(range) {\n return range.chr - 1 === chrSetIndex;\n }).map(function(range) {\n return new Range(range);\n });\n\n rangesContainer = container.append('g').attr('class', 'range-set');\n\n self = this;\n ideo = self._ideo;\n\n rangesContainer.selectAll('rect.range')\n .data(rangeSet)\n .enter()\n .append('rect')\n .attr('class', 'range')\n .attr('x', function(range) {\n return ideo.convertBpToPx(self._model, range.start);\n }).attr('y', 0)\n .attr('width', function(range) {\n return ideo.convertBpToPx(self._model, range.length);\n }).attr('height', this._config.chrWidth)\n .style('fill', function(range) {\n return range.getColor(chrIndex);\n });\n }\n\n /**\n * Get chromosome's shape main values\n */\n _getShapeData() {\n\n var firstQBand, i, lastBand, rightTerminalPosition;\n\n // First q band from bands sequence\n for (i = 0; i < this._model.bands.length; i++) {\n if (this._model.bands[i].name[0] === 'q') {\n firstQBand = this._model.bands[i];\n break;\n }\n }\n\n // Chromosome's right position\n lastBand = this._model.bands.length - 1;\n rightTerminalPosition = this._model.bands[lastBand].px.stop;\n\n // Properties description:\n // x1 - left terminal start position\n // x2 - centromere position\n // x3 - right terminal end position\n // w - chromosome width\n // b - bump size\n return {\n x1: 0,\n x2: firstQBand ? firstQBand.px.start : rightTerminalPosition,\n x3: rightTerminalPosition,\n w: this._config.chrWidth,\n b: this._config.chrWidth / this._bumpCoefficient\n };\n }\n\n _getPArmShape() {\n var d = this._getShapeData(),\n x = d.x2 - d.b;\n\n if (this.isFullyBanded() || 'ancestors' in this._ideo.config) {\n // Encountered when chromosome has any of:\n // - One placeholder \"band\", e.g. pig genome GCF_000003025.5\n // - Many (> 2) bands, e.g. human reference genome\n // - Ancestor colors in ploidy configuration, as in ploidy-basic.html\n return {\n class: '',\n path:\n 'M' + d.b + ',0 ' +\n 'L' + x + ',0 ' +\n 'Q' + (d.x2 + d.b) + ',' + (d.w / 2) + ',' + x + ',' + d.w + ' ' +\n 'L' + d.b + ',' + d.w + ' ' +\n 'Q-' + d.b + ',' + (d.w / 2) + ',' + d.b + ',0'\n };\n } else {\n // e.g. chimpanzee assembly Pan_tro 3.0\n return [{\n class: '',\n path:\n 'M' + d.b + ',0 ' +\n 'L' + (x - 2) + ',0 ' +\n 'L' + (x - 2) + ',' + d.w + ' ' +\n 'L' + d.b + ',' + d.w + ' ' +\n 'Q-' + d.b + ',' + (d.w / 2) + ',' + d.b + ',0'\n }, {\n class: 'acen',\n path:\n 'M' + x + ',0 ' +\n 'Q' + (d.x2 + d.b) + ',' + (d.w / 2) + ',' + x + ',' + d.w + ' ' +\n 'L' + x + ',' + d.w + ' ' +\n 'L' + (x - 2) + ',' + d.w + ' ' +\n 'L' + (x - 2) + ',0'\n }];\n }\n }\n\n _getQArmShape() {\n var d = this._getShapeData(),\n x = d.x3 - d.b,\n x2b = d.x2 + d.b;\n\n if (this.isFullyBanded() || 'ancestors' in this._ideo.config) {\n return {\n class: '',\n path:\n 'M' + x2b + ',0 ' +\n 'L' + x + ',0 ' +\n 'Q' + (d.x3 + d.b) + ',' + (d.w / 2) + ',' + x + ',' + d.w + ' ' +\n 'L' + x2b + ',' + d.w + ' ' +\n 'Q' + (d.x2 - d.b) + ',' + (d.w / 2) + ',' + x2b + ',0'\n };\n } else {\n // e.g. chimpanzee assembly Pan_tro 3.0\n return [{\n path:\n 'M' + x2b + ',0 ' +\n 'L' + x + ',0 ' +\n 'Q' + (d.x3 + d.b) + ',' + (d.w / 2) + ',' + x + ',' + d.w + ' ' +\n 'L' + x2b + ',' + d.w + ' ' +\n 'L' + x2b + ',0'\n }, {\n class: 'acen',\n path:\n 'M' + x2b + ',0' +\n 'Q' + (d.x2 - d.b) + ',' + (d.w / 2) + ',' + x2b + ',' + d.w + ' ' +\n 'L' + x2b + ',' + d.w +\n 'L' + (x2b + 2) + ',' + d.w +\n 'L' + (x2b + 2) + ',0'\n }];\n }\n }\n\n isFullyBanded() {\n return (\n this._model.bands &&\n (this._model.bands.length !== 2 || this._model.bands[0].name[0] === 'q')\n );\n }\n\n /**\n * Render arm bands\n */\n _renderBands(container, chrSetIndex, chrIndex, bands, arm) {\n\n var self, armIndex, fill;\n\n self = this;\n armIndex = arm === 'p' ? 0 : 1;\n fill = '';\n\n if ('ancestors' in self._ideo.config && !(self.isFullyBanded())) {\n fill = self._color.getArmColor(chrSetIndex, chrIndex, armIndex);\n }\n\n container.selectAll('path.band.' + arm)\n .data(bands)\n .enter()\n .append('path')\n .attr('id', function(d) {\n return self._model.id + '-' + d.name.replace('.', '-');\n })\n .attr('class', function(d) {\n return 'band ' + arm + '-band ' + d.stain;\n })\n .attr('d', function(d) {\n var start, length;\n\n start = self._ideo.round(d.px.start);\n length = self._ideo.round(d.px.width);\n\n return 'M ' + start + ', 0' +\n 'l ' + length + ' 0 ' +\n 'l 0 ' + self._config.chrWidth + ' ' +\n 'l -' + length + ' 0 z';\n })\n .style('fill', fill);\n }\n\n /**\n * Render a chromosome arm.\n * Returns boolean indicating if any bands were rendered.\n */\n _renderArm(container, chrSetIndex, chrIndex, arm) {\n var bands = this._model.bands.filter(function(band) {\n return band.name[0] === arm;\n });\n\n this._renderBands(container, chrSetIndex, chrIndex, bands, arm);\n\n return Boolean(bands.length);\n }\n}\n\nexport class MetacentricChromosome extends Chromosome {\n\n constructor(model, config, ideo) {\n super(model, config, ideo);\n this._class = 'MetacentricChromosome';\n }\n}\n\nexport class TelocentricPChromosome extends Chromosome {\n\n constructor(model, config, ideo) {\n // alert('p')\n super(model, config, ideo);\n this._class = 'TelocentricPChromosome';\n this._pArmOffset = 3;\n }\n\n _addPArmShape(clipPath) {\n return clipPath.concat(this._getPArmShape());\n }\n\n _getPArmShape() {\n // Properties description:\n // x1 - left terminal start position\n // x2 - centromere position\n // x3 - right terminal end position\n // w - chromosome width\n // b - bump size\n var d = this._getShapeData();\n d.o = this._pArmOffset;\n\n return [{\n class: 'acen',\n path: 'M' + (d.x2 + 2) + ',1' +\n 'L' + (d.x2 + d.o + 3.25) + ',1 ' +\n 'L' + (d.x2 + d.o + 3.25) + ',' + (d.w - 1) + ' ' +\n 'L' + (d.x2 + 2) + ',' + (d.w - 1)\n }, {\n class: 'gpos66',\n path: 'M' + (d.x2 - d.o + 5) + ',0' +\n 'L' + (d.x2 - d.o + 3) + ',0 ' +\n 'L' + (d.x2 - d.o + 3) + ',' + d.w + ' ' +\n 'L' + (d.x2 - d.o + 5) + ',' + d.w,\n strokeWidth: 0.5\n }];\n }\n\n _getQArmShape() {\n // Properties description:\n // x1 - left terminal start position\n // x2 - centromere position\n // x3 - right terminal end position\n // w - chromosome width\n // b - bump size\n var d = this._getShapeData(),\n x = d.x3 - d.b,\n o = this._pArmOffset + 3;\n\n return {\n class: '',\n path:\n 'M' + (d.x2 + o) + ',0 ' +\n 'L' + x + ',0 ' +\n 'Q' + (d.x3 + d.b) + ',' + (d.w / 2) + ',' + x + ',' + d.w + ' ' +\n 'L' + (d.x2 + o) + ',' + d.w\n };\n }\n}\n\nexport class TelocentricQChromosome extends Chromosome {\n\n constructor(model, config, ideo) {\n // alert('q')\n super(model, config, ideo);\n this._class = 'TelocentricQChromosome';\n this._qArmOffset = 3;\n }\n\n _getPArmShape() {\n // Properties description:\n // x1 - left terminal start position\n // x2 - centromere position\n // x3 - right terminal end position\n // w - chromosome width\n // b - bump size\n\n var d = this._getShapeData(),\n x = d.x3 - d.b,\n o = this._qArmOffset;\n\n return {\n class: '',\n path:\n // 'M1,0, ' +\n 'M' + (d.x2 + o) + ',0 ' +\n 'L' + (x + o) + ',0 ' +\n 'L' + (x + o) + ',' + d.w + ' ' +\n 'L' + d.b + ',' + d.w + ' ' +\n 'Q-' + d.b + ',' + (d.w / 2) + ',' + d.b + ',0'\n };\n }\n\n _addQArmShape(clipPath) {\n return clipPath.concat(this._getQArmShape());\n }\n\n _getQArmShape() {\n // Properties description:\n // x1 - left terminal start position\n // x2 - centromere position\n // x3 - right terminal end position\n // w - chromosome width\n // b - bump size\n var d = this._getShapeData();\n d.o = this._qArmOffset;\n\n return [{\n class: 'acen',\n path: 'M' + (d.x2 + 2) + ',1 ' +\n 'L' + (d.x2 + d.o + 3.25) + ',1 ' +\n 'L' + (d.x2 + d.o + 3.25) + ',' + (d.w - 1) + ' ' +\n 'L' + (d.x2 + 2) + ',' + (d.w - 1)\n }, {\n class: 'gpos66',\n path: 'M' + (d.x2 + d.o + 5) + ',0 ' +\n 'L' + (d.x2 + d.o + 3) + ',0 ' +\n 'L' + (d.x2 + d.o + 3) + ',' + d.w + ' ' +\n 'L' + (d.x2 + d.o + 5) + ',' + d.w,\n strokeWidth: 0.5\n }];\n }\n}\n","import {d3} from '../lib';\nimport {initTools} from '../tools/tools';\nimport {ModelAdapter} from '../model-adapter';\nimport {Chromosome} from './chromosome';\n\n/**\n * Adds a copy of a chromosome (i.e. a homologous chromosome, homolog) to DOM\n *\n * @param chrModel\n * @param chrIndex\n * @param homologIndex\n * @param container\n */\nfunction appendHomolog(chrModel, chrIndex, homologIndex, container) {\n\n var homologOffset, chromosome, shape, defs, adapter;\n\n defs = d3.select(this.selector + ' defs');\n // Get chromosome model adapter class\n adapter = ModelAdapter.getInstance(chrModel);\n\n // How far this copy of the chromosome is from another\n homologOffset = homologIndex * this.config.chrMargin;\n\n // Append chromosome's container\n chromosome = container\n .append('g')\n .attr('id', chrModel.id)\n .attr('class', 'chromosome ' + adapter.getCssClass())\n .attr('transform', 'translate(0, ' + homologOffset + ')');\n\n // Render chromosome\n shape = Chromosome.getInstance(adapter, this.config, this)\n .render(chromosome, chrIndex, homologIndex);\n\n d3.select('#' + chrModel.id + '-chromosome-set-clippath').remove();\n\n defs.append('clipPath')\n .attr('id', chrModel.id + '-chromosome-set-clippath')\n .selectAll('path')\n .data(shape)\n .enter()\n .append('path')\n .attr('d', function(d) {return d.path;})\n .attr('class', function(d) {return d.class;});\n\n\n if (chrModel.width < 1) {\n d3.select('#' + chrModel.id + ' .bands').style('opacity', 0);\n }\n}\n\n/**\n * Renders all the bands and outlining boundaries of a chromosome.\n */\nfunction drawChromosome(chrModel) {\n var chrIndex, container, numChrsInSet, transform, homologIndex,\n chrSetSelector;\n\n chrIndex = chrModel.chrIndex;\n\n transform = this._layout.getChromosomeSetTranslate(chrIndex);\n\n chrSetSelector = this.selector + ' #' + chrModel.id + '-chromosome-set';\n\n d3.selectAll(chrSetSelector + ' g').remove();\n\n container = d3.select(chrSetSelector);\n\n if (container.nodes().length === 0) {\n // Append chromosome set container\n container = d3.select(this.selector)\n .append('g')\n .attr('class', 'chromosome-set')\n .attr('transform', transform)\n .attr('id', chrModel.id + '-chromosome-set');\n }\n\n if (\n 'sex' in this.config &&\n this.config.ploidy === 2 &&\n this.sexChromosomes.index === chrIndex\n ) {\n this.drawSexChromosomes(container, chrIndex);\n return;\n }\n\n numChrsInSet = 1;\n if (this.config.ploidy > 1) {\n numChrsInSet = this._ploidy.getChromosomesNumber(chrIndex);\n }\n\n for (homologIndex = 0; homologIndex < numChrsInSet; homologIndex++) {\n this.appendHomolog(chrModel, chrIndex, homologIndex, container);\n }\n}\n\n/**\n * Rotates a chromosome 90 degrees and shows or hides all other chromosomes\n * Useful for focusing or defocusing a particular chromosome\n */\nfunction rotateAndToggleDisplay(chrElement) {\n var chrName, chrModel, chrIndex;\n\n this.unhighlight();\n\n // Do nothing if taxid not defined. But it should be defined.\n // To fix that bug we should have a way to find chromosome set number.\n if (!this.config.taxid) return;\n\n chrName = chrElement.id.split('-')[0].replace('chr', '');\n chrModel = this.chromosomes[this.config.taxid][chrName];\n chrIndex = chrModel.chrIndex;\n\n this._layout.rotate(chrIndex, chrIndex, chrElement);\n}\n\nfunction setOverflowScroll() {\n var ideo, config, ideoWidth, ideoInnerWrap, ideoMiddleWrap, ideoSvg,\n ploidy, ploidyPad;\n\n ideo = this;\n config = ideo.config;\n\n ideoSvg = d3.select(config.container + ' svg#_ideogram');\n ideoInnerWrap = d3.select(config.container + ' #_ideogramInnerWrap');\n ideoMiddleWrap = d3.select(config.container + ' #_ideogramMiddleWrap');\n\n ploidy = config.ploidy;\n if (ploidy === 1) {\n ploidyPad = ploidy;\n } else {\n ploidyPad = ploidy * 1.12;\n }\n\n let annotHeight = 0;\n if ('annotationsLayout' in config) {\n annotHeight = config.annotationHeight * config.numAnnotTracks;\n }\n\n if (\n config.orientation === 'vertical' &&\n config.perspective !== 'comparative' &&\n config.geometry !== 'collinear'\n ) {\n ideoWidth =\n (ideo.numChromosomes) *\n (config.chrWidth + config.chrMargin + annotHeight);\n } else {\n return;\n }\n\n if (config.annotationsLayout === 'heatmap-2d') {\n return;\n }\n\n ideoWidth = Math.ceil(ideoWidth * ploidyPad / config.rows);\n if (ideo._layout._class === 'SmallLayout') ideoWidth += 100;\n\n ideoWidth += 35; // Account for settings gear\n\n // Ensures absolutely-positioned elements, e.g. heatmap overlaps, display\n // properly if ideogram container also has position: absolute\n ideoMiddleWrap.style('height', ideo._layout.getHeight() + 'px');\n\n ideoInnerWrap\n .style('max-width', ideoWidth + 'px')\n .style('overflow-x', 'scroll')\n .style('position', 'absolute');\n\n ideoSvg.style('min-width', (ideoWidth - 5) + 'px');\n\n if (ideo.config.showTools) {\n initTools(ideo);\n }\n}\n\nexport {\n appendHomolog, drawChromosome, rotateAndToggleDisplay, setOverflowScroll\n};\n","import {d3} from '../lib';\n\nfunction getChrSetLabelLines(d, i, ideo) {\n var lines;\n if (d.name.indexOf(' ') === -1) {\n lines = [d.name];\n } else {\n lines = d.name.match(/^(.*)\\s+([^\\s]+)$/).slice(1).reverse();\n }\n\n if (\n 'sex' in ideo.config &&\n ideo.config.ploidy === 2 &&\n i === ideo.sexChromosomes.index\n ) {\n if (ideo.config.sex === 'male') {\n lines = ['XY'];\n } else {\n lines = ['XX'];\n }\n }\n\n return lines;\n}\n\nfunction renderChromosomeSetLabel(d, i, textElement, ideo) {\n // Get label lines\n var lines = getChrSetLabelLines(d, i, ideo);\n\n // Render label lines\n d3.select(textElement).selectAll('tspan')\n .data(lines)\n .enter()\n .append('tspan')\n .attr('dy', function(d, i) {\n return i * -1.2 + 'em';\n })\n .attr('x', ideo._layout.getChromosomeSetLabelXPosition())\n .attr('class', function(a, i) {\n var fullLabels = ideo.config.fullChromosomeLabels;\n return i === 1 && fullLabels ? 'italic' : null;\n })\n .text(String);\n}\n\nfunction appendChromosomeSetLabels(ideo) {\n var layout = ideo._layout;\n\n d3.selectAll(ideo.selector + ' .chromosome-set')\n .insert('text', ':first-child')\n .data(ideo.chromosomesArray)\n .attr('class', layout.getChromosomeLabelClass())\n .attr('transform', layout.getChromosomeSetLabelTranslate())\n .attr('x', layout.getChromosomeSetLabelXPosition())\n .attr('y', function(d, i) {\n return layout.getChromosomeSetLabelYPosition(i);\n })\n .attr('text-anchor', layout.getChromosomeSetLabelAnchor())\n .each(function(d, i) {\n renderChromosomeSetLabel(d, i, this, ideo);\n });\n}\n\nfunction appendChromosomeLabels(ideo) {\n var layout = ideo._layout;\n\n d3.selectAll(ideo.selector + ' .chromosome-set')\n .each(function(a, chrSetIndex) {\n d3.select(this).selectAll('.chromosome')\n .append('text')\n .attr('class', 'chrLabel')\n .attr('transform', layout.getChromosomeSetLabelTranslate())\n .attr('x', function(d, i) {\n return layout.getChromosomeLabelXPosition(i);\n })\n .attr('y', function(d, i) {\n return layout.getChromosomeLabelYPosition(i);\n })\n .text(function(d, chrIndex) {\n return ideo._ploidy.getAncestor(chrSetIndex, chrIndex);\n })\n .attr('text-anchor', 'middle');\n });\n}\n\n/**\n * Draws labels for each chromosome, e.g. \"1\", \"2\", \"X\".\n * If ideogram configuration has 'fullChromosomeLabels: True',\n * then labels includes name of taxon, which can help when\n * depicting orthologs.\n */\nfunction drawChromosomeLabels() {\n var ideo = this;\n appendChromosomeSetLabels(ideo);\n appendChromosomeLabels(ideo);\n}\n\nfunction getLabelPositionAttrs(scale) {\n var x, y, scaleSvg;\n\n if (\n typeof (scale) !== 'undefined' &&\n scale.hasOwnProperty('x') &&\n !(scale.x === 1 && scale.y === 1)\n ) {\n scaleSvg = 'scale(' + scale.x + ',' + scale.y + ')';\n x = -6;\n y = (scale === '' ? -16 : -14);\n } else {\n x = -8;\n y = -16;\n scale = {x: 1, y: 1};\n scaleSvg = '';\n }\n\n return {x: x, y: y, scaleSvg: scaleSvg, scale: scale};\n}\n\nfunction updateChrIndex(chrIndex, config) {\n if (config.numAnnotTracks > 1 || config.orientation === '') chrIndex -= 1;\n return chrIndex;\n}\n\nfunction rotateVerticalChromosomeLabels(chr, chrIndex, labelPosAttrs, ideo) {\n var chrMargin2, chrMargin, y,\n config = ideo.config;\n\n chrIndex = updateChrIndex(chrIndex, config);\n\n chrMargin2 = -4;\n if (config.showBandLabels === true) {\n chrMargin2 = config.chrMargin + config.chrWidth + 26;\n }\n\n chrMargin = config.chrMargin * chrIndex;\n if (config.numAnnotTracks > 1 === false) chrMargin += 1;\n\n y = chrMargin + chrMargin2;\n\n chr.selectAll('text.chrLabel')\n .attr('transform', labelPosAttrs.scaleSvg)\n .selectAll('tspan')\n .attr('x', labelPosAttrs.x)\n .attr('y', y);\n}\n\nfunction rotateHorizontalChromosomeLabels(chr, chrIndex, labelPosAttrs, ideo) {\n var chrMargin, chrMargin2, tracksHeight, x,\n config = ideo.config;\n\n chrMargin2 = -config.chrWidth - 2;\n if (config.showBandLabels === true) chrMargin2 = config.chrMargin + 8;\n\n tracksHeight = config.annotTracksHeight;\n if (config.annotationsLayout !== 'overlay') tracksHeight *= 2;\n\n chrMargin = config.chrMargin * chrIndex;\n x = -(chrMargin + chrMargin2) + 3 + tracksHeight;\n x /= labelPosAttrs.scale.x;\n\n chr.selectAll('text.chrLabel')\n .attr('transform', 'rotate(-90)' + labelPosAttrs.scaleSvg)\n .selectAll('tspan')\n .attr('x', x)\n .attr('y', labelPosAttrs.y);\n}\n\n/**\n * Rotates chromosome labels by 90 degrees, e.g. upon clicking a chromosome.\n */\nfunction rotateChromosomeLabels(chr, chrIndex, orientation, scale) {\n var labelPosAttrs,\n ideo = this;\n\n chrIndex -= 1;\n\n labelPosAttrs = getLabelPositionAttrs(scale);\n\n if (orientation === 'vertical' || orientation === '') {\n rotateVerticalChromosomeLabels(chr, chrIndex, labelPosAttrs, ideo);\n } else {\n rotateHorizontalChromosomeLabels(chr, chrIndex, labelPosAttrs, ideo);\n }\n}\n\nexport {drawChromosomeLabels, rotateChromosomeLabels};\n","// DEFLATE is a complex format; to read this code, you should probably check the RFC first:\n// https://tools.ietf.org/html/rfc1951\n// You may also wish to take a look at the guide I made about this program:\n// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad\n// Some of the following code is similar to that of UZIP.js:\n// https://github.com/photopea/UZIP.js\n// However, the vast majority of the codebase has diverged from UZIP.js to increase performance and reduce bundle size.\n// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint\n// is better for memory in most engines (I *think*).\nvar ch2 = {};\nvar wk = (function (c, id, msg, transfer, cb) {\n var w = new Worker(ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([\n c + ';addEventListener(\"error\",function(e){e=e.error;postMessage({$e$:[e.message,e.code,e.stack]})})'\n ], { type: 'text/javascript' }))));\n w.onmessage = function (e) {\n var d = e.data, ed = d.$e$;\n if (ed) {\n var err = new Error(ed[0]);\n err['code'] = ed[1];\n err.stack = ed[2];\n cb(err, null);\n }\n else\n cb(null, d);\n };\n w.postMessage(msg, transfer);\n return w;\n});\n\n// aliases for shorter compressed code (most minifers don't do this)\nvar u8 = Uint8Array, u16 = Uint16Array, u32 = Uint32Array;\n// fixed length extra bits\nvar fleb = new u8([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, /* unused */ 0, 0, /* impossible */ 0]);\n// fixed distance extra bits\n// see fleb note\nvar fdeb = new u8([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, /* unused */ 0, 0]);\n// code length index map\nvar clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n// get base, reverse index map from extra bits\nvar freb = function (eb, start) {\n var b = new u16(31);\n for (var i = 0; i < 31; ++i) {\n b[i] = start += 1 << eb[i - 1];\n }\n // numbers here are at max 18 bits\n var r = new u32(b[30]);\n for (var i = 1; i < 30; ++i) {\n for (var j = b[i]; j < b[i + 1]; ++j) {\n r[j] = ((j - b[i]) << 5) | i;\n }\n }\n return [b, r];\n};\nvar _a = freb(fleb, 2), fl = _a[0], revfl = _a[1];\n// we can ignore the fact that the other numbers are wrong; they never happen anyway\nfl[28] = 258, revfl[258] = 28;\nvar _b = freb(fdeb, 0), fd = _b[0], revfd = _b[1];\n// map of value to reverse (assuming 16 bits)\nvar rev = new u16(32768);\nfor (var i = 0; i < 32768; ++i) {\n // reverse table algorithm from SO\n var x = ((i & 0xAAAA) >>> 1) | ((i & 0x5555) << 1);\n x = ((x & 0xCCCC) >>> 2) | ((x & 0x3333) << 2);\n x = ((x & 0xF0F0) >>> 4) | ((x & 0x0F0F) << 4);\n rev[i] = (((x & 0xFF00) >>> 8) | ((x & 0x00FF) << 8)) >>> 1;\n}\n// create huffman tree from u8 \"map\": index -> code length for code index\n// mb (max bits) must be at most 15\n// TODO: optimize/split up?\nvar hMap = (function (cd, mb, r) {\n var s = cd.length;\n // index\n var i = 0;\n // u16 \"map\": index -> # of codes with bit length = index\n var l = new u16(mb);\n // length of cd must be 288 (total # of codes)\n for (; i < s; ++i) {\n if (cd[i])\n ++l[cd[i] - 1];\n }\n // u16 \"map\": index -> minimum code for bit length = index\n var le = new u16(mb);\n for (i = 0; i < mb; ++i) {\n le[i] = (le[i - 1] + l[i - 1]) << 1;\n }\n var co;\n if (r) {\n // u16 \"map\": index -> number of actual bits, symbol for code\n co = new u16(1 << mb);\n // bits to remove for reverser\n var rvb = 15 - mb;\n for (i = 0; i < s; ++i) {\n // ignore 0 lengths\n if (cd[i]) {\n // num encoding both symbol and bits read\n var sv = (i << 4) | cd[i];\n // free bits\n var r_1 = mb - cd[i];\n // start value\n var v = le[cd[i] - 1]++ << r_1;\n // m is end value\n for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {\n // every 16 bit value starting with the code yields the same result\n co[rev[v] >>> rvb] = sv;\n }\n }\n }\n }\n else {\n co = new u16(s);\n for (i = 0; i < s; ++i) {\n if (cd[i]) {\n co[i] = rev[le[cd[i] - 1]++] >>> (15 - cd[i]);\n }\n }\n }\n return co;\n});\n// fixed length tree\nvar flt = new u8(288);\nfor (var i = 0; i < 144; ++i)\n flt[i] = 8;\nfor (var i = 144; i < 256; ++i)\n flt[i] = 9;\nfor (var i = 256; i < 280; ++i)\n flt[i] = 7;\nfor (var i = 280; i < 288; ++i)\n flt[i] = 8;\n// fixed distance tree\nvar fdt = new u8(32);\nfor (var i = 0; i < 32; ++i)\n fdt[i] = 5;\n// fixed length map\nvar flm = /*#__PURE__*/ hMap(flt, 9, 0), flrm = /*#__PURE__*/ hMap(flt, 9, 1);\n// fixed distance map\nvar fdm = /*#__PURE__*/ hMap(fdt, 5, 0), fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);\n// find max of array\nvar max = function (a) {\n var m = a[0];\n for (var i = 1; i < a.length; ++i) {\n if (a[i] > m)\n m = a[i];\n }\n return m;\n};\n// read d, starting at bit p and mask with m\nvar bits = function (d, p, m) {\n var o = (p / 8) | 0;\n return ((d[o] | (d[o + 1] << 8)) >> (p & 7)) & m;\n};\n// read d, starting at bit p continuing for at least 16 bits\nvar bits16 = function (d, p) {\n var o = (p / 8) | 0;\n return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >> (p & 7));\n};\n// get end of byte\nvar shft = function (p) { return ((p + 7) / 8) | 0; };\n// typed array slice - allows garbage collector to free original reference,\n// while being more compatible than .slice\nvar slc = function (v, s, e) {\n if (s == null || s < 0)\n s = 0;\n if (e == null || e > v.length)\n e = v.length;\n // can't use .constructor in case user-supplied\n var n = new (v.BYTES_PER_ELEMENT == 2 ? u16 : v.BYTES_PER_ELEMENT == 4 ? u32 : u8)(e - s);\n n.set(v.subarray(s, e));\n return n;\n};\n/**\n * Codes for errors generated within this library\n */\nexport var FlateErrorCode = {\n UnexpectedEOF: 0,\n InvalidBlockType: 1,\n InvalidLengthLiteral: 2,\n InvalidDistance: 3,\n StreamFinished: 4,\n NoStreamHandler: 5,\n InvalidHeader: 6,\n NoCallback: 7,\n InvalidUTF8: 8,\n ExtraFieldTooLong: 9,\n InvalidDate: 10,\n FilenameTooLong: 11,\n StreamFinishing: 12,\n InvalidZipData: 13,\n UnknownCompressionMethod: 14\n};\n// error codes\nvar ec = [\n 'unexpected EOF',\n 'invalid block type',\n 'invalid length/literal',\n 'invalid distance',\n 'stream finished',\n 'no stream handler',\n ,\n 'no callback',\n 'invalid UTF-8 data',\n 'extra field too long',\n 'date not in range 1980-2099',\n 'filename too long',\n 'stream finishing',\n 'invalid zip data'\n // determined by unknown compression method\n];\n;\nvar err = function (ind, msg, nt) {\n var e = new Error(msg || ec[ind]);\n e.code = ind;\n if (Error.captureStackTrace)\n Error.captureStackTrace(e, err);\n if (!nt)\n throw e;\n return e;\n};\n// expands raw DEFLATE data\nvar inflt = function (dat, buf, st) {\n // source length\n var sl = dat.length;\n if (!sl || (st && st.f && !st.l))\n return buf || new u8(0);\n // have to estimate size\n var noBuf = !buf || st;\n // no state\n var noSt = !st || st.i;\n if (!st)\n st = {};\n // Assumes roughly 33% compression ratio average\n if (!buf)\n buf = new u8(sl * 3);\n // ensure buffer can fit at least l elements\n var cbuf = function (l) {\n var bl = buf.length;\n // need to increase size to fit\n if (l > bl) {\n // Double or set to necessary, whichever is greater\n var nbuf = new u8(Math.max(bl * 2, l));\n nbuf.set(buf);\n buf = nbuf;\n }\n };\n // last chunk bitpos bytes\n var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;\n // total bits\n var tbts = sl * 8;\n do {\n if (!lm) {\n // BFINAL - this is only 1 when last chunk is next\n final = bits(dat, pos, 1);\n // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman\n var type = bits(dat, pos + 1, 3);\n pos += 3;\n if (!type) {\n // go to end of byte boundary\n var s = shft(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = s + l;\n if (t > sl) {\n if (noSt)\n err(0);\n break;\n }\n // ensure size\n if (noBuf)\n cbuf(bt + l);\n // Copy over uncompressed data\n buf.set(dat.subarray(s, t), bt);\n // Get new bitpos, update byte count\n st.b = bt += l, st.p = pos = t * 8, st.f = final;\n continue;\n }\n else if (type == 1)\n lm = flrm, dm = fdrm, lbt = 9, dbt = 5;\n else if (type == 2) {\n // literal lengths\n var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;\n var tl = hLit + bits(dat, pos + 5, 31) + 1;\n pos += 14;\n // length+distance tree\n var ldt = new u8(tl);\n // code length tree\n var clt = new u8(19);\n for (var i = 0; i < hcLen; ++i) {\n // use index map to get real code\n clt[clim[i]] = bits(dat, pos + i * 3, 7);\n }\n pos += hcLen * 3;\n // code lengths bits\n var clb = max(clt), clbmsk = (1 << clb) - 1;\n // code lengths map\n var clm = hMap(clt, clb, 1);\n for (var i = 0; i < tl;) {\n var r = clm[bits(dat, pos, clbmsk)];\n // bits read\n pos += r & 15;\n // symbol\n var s = r >>> 4;\n // code length to copy\n if (s < 16) {\n ldt[i++] = s;\n }\n else {\n // copy count\n var c = 0, n = 0;\n if (s == 16)\n n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];\n else if (s == 17)\n n = 3 + bits(dat, pos, 7), pos += 3;\n else if (s == 18)\n n = 11 + bits(dat, pos, 127), pos += 7;\n while (n--)\n ldt[i++] = c;\n }\n }\n // length tree distance tree\n var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);\n // max length bits\n lbt = max(lt);\n // max dist bits\n dbt = max(dt);\n lm = hMap(lt, lbt, 1);\n dm = hMap(dt, dbt, 1);\n }\n else\n err(1);\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n }\n // Make sure the buffer can hold this + the largest possible addition\n // Maximum chunk size (practically, theoretically infinite) is 2^17;\n if (noBuf)\n cbuf(bt + 131072);\n var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;\n var lpos = pos;\n for (;; lpos = pos) {\n // bits read, code\n var c = lm[bits16(dat, pos) & lms], sym = c >>> 4;\n pos += c & 15;\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n if (!c)\n err(2);\n if (sym < 256)\n buf[bt++] = sym;\n else if (sym == 256) {\n lpos = pos, lm = null;\n break;\n }\n else {\n var add = sym - 254;\n // no extra bits needed if less\n if (sym > 264) {\n // index\n var i = sym - 257, b = fleb[i];\n add = bits(dat, pos, (1 << b) - 1) + fl[i];\n pos += b;\n }\n // dist\n var d = dm[bits16(dat, pos) & dms], dsym = d >>> 4;\n if (!d)\n err(3);\n pos += d & 15;\n var dt = fd[dsym];\n if (dsym > 3) {\n var b = fdeb[dsym];\n dt += bits16(dat, pos) & ((1 << b) - 1), pos += b;\n }\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n if (noBuf)\n cbuf(bt + 131072);\n var end = bt + add;\n for (; bt < end; bt += 4) {\n buf[bt] = buf[bt - dt];\n buf[bt + 1] = buf[bt + 1 - dt];\n buf[bt + 2] = buf[bt + 2 - dt];\n buf[bt + 3] = buf[bt + 3 - dt];\n }\n bt = end;\n }\n }\n st.l = lm, st.p = lpos, st.b = bt, st.f = final;\n if (lm)\n final = 1, st.m = lbt, st.d = dm, st.n = dbt;\n } while (!final);\n return bt == buf.length ? buf : slc(buf, 0, bt);\n};\n// starting at p, write the minimum number of bits that can hold v to d\nvar wbits = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) | 0;\n d[o] |= v;\n d[o + 1] |= v >>> 8;\n};\n// starting at p, write the minimum number of bits (>8) that can hold v to d\nvar wbits16 = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) | 0;\n d[o] |= v;\n d[o + 1] |= v >>> 8;\n d[o + 2] |= v >>> 16;\n};\n// creates code lengths from a frequency table\nvar hTree = function (d, mb) {\n // Need extra info to make a tree\n var t = [];\n for (var i = 0; i < d.length; ++i) {\n if (d[i])\n t.push({ s: i, f: d[i] });\n }\n var s = t.length;\n var t2 = t.slice();\n if (!s)\n return [et, 0];\n if (s == 1) {\n var v = new u8(t[0].s + 1);\n v[t[0].s] = 1;\n return [v, 1];\n }\n t.sort(function (a, b) { return a.f - b.f; });\n // after i2 reaches last ind, will be stopped\n // freq must be greater than largest possible number of symbols\n t.push({ s: -1, f: 25001 });\n var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;\n t[0] = { s: -1, f: l.f + r.f, l: l, r: r };\n // efficient algorithm from UZIP.js\n // i0 is lookbehind, i2 is lookahead - after processing two low-freq\n // symbols that combined have high freq, will start processing i2 (high-freq,\n // non-composite) symbols instead\n // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/\n while (i1 != s - 1) {\n l = t[t[i0].f < t[i2].f ? i0++ : i2++];\n r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];\n t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };\n }\n var maxSym = t2[0].s;\n for (var i = 1; i < s; ++i) {\n if (t2[i].s > maxSym)\n maxSym = t2[i].s;\n }\n // code lengths\n var tr = new u16(maxSym + 1);\n // max bits in tree\n var mbt = ln(t[i1 - 1], tr, 0);\n if (mbt > mb) {\n // more algorithms from UZIP.js\n // TODO: find out how this code works (debt)\n // ind debt\n var i = 0, dt = 0;\n // left cost\n var lft = mbt - mb, cst = 1 << lft;\n t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });\n for (; i < s; ++i) {\n var i2_1 = t2[i].s;\n if (tr[i2_1] > mb) {\n dt += cst - (1 << (mbt - tr[i2_1]));\n tr[i2_1] = mb;\n }\n else\n break;\n }\n dt >>>= lft;\n while (dt > 0) {\n var i2_2 = t2[i].s;\n if (tr[i2_2] < mb)\n dt -= 1 << (mb - tr[i2_2]++ - 1);\n else\n ++i;\n }\n for (; i >= 0 && dt; --i) {\n var i2_3 = t2[i].s;\n if (tr[i2_3] == mb) {\n --tr[i2_3];\n ++dt;\n }\n }\n mbt = mb;\n }\n return [new u8(tr), mbt];\n};\n// get the max length and assign length codes\nvar ln = function (n, l, d) {\n return n.s == -1\n ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))\n : (l[n.s] = d);\n};\n// length codes generation\nvar lc = function (c) {\n var s = c.length;\n // Note that the semicolon was intentional\n while (s && !c[--s])\n ;\n var cl = new u16(++s);\n // ind num streak\n var cli = 0, cln = c[0], cls = 1;\n var w = function (v) { cl[cli++] = v; };\n for (var i = 1; i <= s; ++i) {\n if (c[i] == cln && i != s)\n ++cls;\n else {\n if (!cln && cls > 2) {\n for (; cls > 138; cls -= 138)\n w(32754);\n if (cls > 2) {\n w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);\n cls = 0;\n }\n }\n else if (cls > 3) {\n w(cln), --cls;\n for (; cls > 6; cls -= 6)\n w(8304);\n if (cls > 2)\n w(((cls - 3) << 5) | 8208), cls = 0;\n }\n while (cls--)\n w(cln);\n cls = 1;\n cln = c[i];\n }\n }\n return [cl.subarray(0, cli), s];\n};\n// calculate the length of output from tree, code lengths\nvar clen = function (cf, cl) {\n var l = 0;\n for (var i = 0; i < cl.length; ++i)\n l += cf[i] * cl[i];\n return l;\n};\n// writes a fixed block\n// returns the new bit pos\nvar wfblk = function (out, pos, dat) {\n // no need to write 00 as type: TypedArray defaults to 0\n var s = dat.length;\n var o = shft(pos + 2);\n out[o] = s & 255;\n out[o + 1] = s >>> 8;\n out[o + 2] = out[o] ^ 255;\n out[o + 3] = out[o + 1] ^ 255;\n for (var i = 0; i < s; ++i)\n out[o + i + 4] = dat[i];\n return (o + 4 + s) * 8;\n};\n// writes a block\nvar wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {\n wbits(out, p++, final);\n ++lf[256];\n var _a = hTree(lf, 15), dlt = _a[0], mlb = _a[1];\n var _b = hTree(df, 15), ddt = _b[0], mdb = _b[1];\n var _c = lc(dlt), lclt = _c[0], nlc = _c[1];\n var _d = lc(ddt), lcdt = _d[0], ndc = _d[1];\n var lcfreq = new u16(19);\n for (var i = 0; i < lclt.length; ++i)\n lcfreq[lclt[i] & 31]++;\n for (var i = 0; i < lcdt.length; ++i)\n lcfreq[lcdt[i] & 31]++;\n var _e = hTree(lcfreq, 7), lct = _e[0], mlcb = _e[1];\n var nlcc = 19;\n for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)\n ;\n var flen = (bl + 5) << 3;\n var ftlen = clen(lf, flt) + clen(df, fdt) + eb;\n var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + (2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18]);\n if (flen <= ftlen && flen <= dtlen)\n return wfblk(out, p, dat.subarray(bs, bs + bl));\n var lm, ll, dm, dl;\n wbits(out, p, 1 + (dtlen < ftlen)), p += 2;\n if (dtlen < ftlen) {\n lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;\n var llm = hMap(lct, mlcb, 0);\n wbits(out, p, nlc - 257);\n wbits(out, p + 5, ndc - 1);\n wbits(out, p + 10, nlcc - 4);\n p += 14;\n for (var i = 0; i < nlcc; ++i)\n wbits(out, p + 3 * i, lct[clim[i]]);\n p += 3 * nlcc;\n var lcts = [lclt, lcdt];\n for (var it = 0; it < 2; ++it) {\n var clct = lcts[it];\n for (var i = 0; i < clct.length; ++i) {\n var len = clct[i] & 31;\n wbits(out, p, llm[len]), p += lct[len];\n if (len > 15)\n wbits(out, p, (clct[i] >>> 5) & 127), p += clct[i] >>> 12;\n }\n }\n }\n else {\n lm = flm, ll = flt, dm = fdm, dl = fdt;\n }\n for (var i = 0; i < li; ++i) {\n if (syms[i] > 255) {\n var len = (syms[i] >>> 18) & 31;\n wbits16(out, p, lm[len + 257]), p += ll[len + 257];\n if (len > 7)\n wbits(out, p, (syms[i] >>> 23) & 31), p += fleb[len];\n var dst = syms[i] & 31;\n wbits16(out, p, dm[dst]), p += dl[dst];\n if (dst > 3)\n wbits16(out, p, (syms[i] >>> 5) & 8191), p += fdeb[dst];\n }\n else {\n wbits16(out, p, lm[syms[i]]), p += ll[syms[i]];\n }\n }\n wbits16(out, p, lm[256]);\n return p + ll[256];\n};\n// deflate options (nice << 13) | chain\nvar deo = /*#__PURE__*/ new u32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);\n// empty\nvar et = /*#__PURE__*/ new u8(0);\n// compresses data into a raw DEFLATE buffer\nvar dflt = function (dat, lvl, plvl, pre, post, lst) {\n var s = dat.length;\n var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);\n // writing to this writes to the output buffer\n var w = o.subarray(pre, o.length - post);\n var pos = 0;\n if (!lvl || s < 8) {\n for (var i = 0; i <= s; i += 65535) {\n // end\n var e = i + 65535;\n if (e >= s) {\n // write final block\n w[pos >> 3] = lst;\n }\n pos = wfblk(w, pos + 1, dat.subarray(i, e));\n }\n }\n else {\n var opt = deo[lvl - 1];\n var n = opt >>> 13, c = opt & 8191;\n var msk_1 = (1 << plvl) - 1;\n // prev 2-byte val map curr 2-byte val map\n var prev = new u16(32768), head = new u16(msk_1 + 1);\n var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;\n var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };\n // 24576 is an arbitrary number of maximum symbols per block\n // 424 buffer for last block\n var syms = new u32(25000);\n // length/literal freq distance freq\n var lf = new u16(288), df = new u16(32);\n // l/lcnt exbits index l/lind waitdx bitpos\n var lc_1 = 0, eb = 0, i = 0, li = 0, wi = 0, bs = 0;\n for (; i < s; ++i) {\n // hash value\n // deopt when i > s - 3 - at end, deopt acceptable\n var hv = hsh(i);\n // index mod 32768 previous index mod\n var imod = i & 32767, pimod = head[hv];\n prev[imod] = pimod;\n head[hv] = imod;\n // We always should modify head and prev, but only add symbols if\n // this data is not yet processed (\"wait\" for wait index)\n if (wi <= i) {\n // bytes remaining\n var rem = s - i;\n if ((lc_1 > 7000 || li > 24576) && rem > 423) {\n pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);\n li = lc_1 = eb = 0, bs = i;\n for (var j = 0; j < 286; ++j)\n lf[j] = 0;\n for (var j = 0; j < 30; ++j)\n df[j] = 0;\n }\n // len dist chain\n var l = 2, d = 0, ch_1 = c, dif = (imod - pimod) & 32767;\n if (rem > 2 && hv == hsh(i - dif)) {\n var maxn = Math.min(n, rem) - 1;\n var maxd = Math.min(32767, i);\n // max possible length\n // not capped at dif because decompressors implement \"rolling\" index population\n var ml = Math.min(258, rem);\n while (dif <= maxd && --ch_1 && imod != pimod) {\n if (dat[i + l] == dat[i + l - dif]) {\n var nl = 0;\n for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)\n ;\n if (nl > l) {\n l = nl, d = dif;\n // break out early when we reach \"nice\" (we are satisfied enough)\n if (nl > maxn)\n break;\n // now, find the rarest 2-byte sequence within this\n // length of literals and search for that instead.\n // Much faster than just using the start\n var mmd = Math.min(dif, nl - 2);\n var md = 0;\n for (var j = 0; j < mmd; ++j) {\n var ti = (i - dif + j + 32768) & 32767;\n var pti = prev[ti];\n var cd = (ti - pti + 32768) & 32767;\n if (cd > md)\n md = cd, pimod = ti;\n }\n }\n }\n // check the previous match\n imod = pimod, pimod = prev[imod];\n dif += (imod - pimod + 32768) & 32767;\n }\n }\n // d will be nonzero only when a match was found\n if (d) {\n // store both dist and len data in one Uint32\n // Make sure this is recognized as a len/dist with 28th bit (2^28)\n syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];\n var lin = revfl[l] & 31, din = revfd[d] & 31;\n eb += fleb[lin] + fdeb[din];\n ++lf[257 + lin];\n ++df[din];\n wi = i + l;\n ++lc_1;\n }\n else {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n }\n }\n pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);\n // this is the easiest way to avoid needing to maintain state\n if (!lst && pos & 7)\n pos = wfblk(w, pos + 1, et);\n }\n return slc(o, 0, pre + shft(pos) + post);\n};\n// CRC32 table\nvar crct = /*#__PURE__*/ (function () {\n var t = new Int32Array(256);\n for (var i = 0; i < 256; ++i) {\n var c = i, k = 9;\n while (--k)\n c = ((c & 1) && -306674912) ^ (c >>> 1);\n t[i] = c;\n }\n return t;\n})();\n// CRC32\nvar crc = function () {\n var c = -1;\n return {\n p: function (d) {\n // closures have awful performance\n var cr = c;\n for (var i = 0; i < d.length; ++i)\n cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);\n c = cr;\n },\n d: function () { return ~c; }\n };\n};\n// Alder32\nvar adler = function () {\n var a = 1, b = 0;\n return {\n p: function (d) {\n // closures have awful performance\n var n = a, m = b;\n var l = d.length | 0;\n for (var i = 0; i != l;) {\n var e = Math.min(i + 2655, l);\n for (; i < e; ++i)\n m += n += d[i];\n n = (n & 65535) + 15 * (n >> 16), m = (m & 65535) + 15 * (m >> 16);\n }\n a = n, b = m;\n },\n d: function () {\n a %= 65521, b %= 65521;\n return (a & 255) << 24 | (a >>> 8) << 16 | (b & 255) << 8 | (b >>> 8);\n }\n };\n};\n;\n// deflate with opts\nvar dopt = function (dat, opt, pre, post, st) {\n return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : (12 + opt.mem), pre, post, !st);\n};\n// Walmart object spread\nvar mrg = function (a, b) {\n var o = {};\n for (var k in a)\n o[k] = a[k];\n for (var k in b)\n o[k] = b[k];\n return o;\n};\n// worker clone\n// This is possibly the craziest part of the entire codebase, despite how simple it may seem.\n// The only parameter to this function is a closure that returns an array of variables outside of the function scope.\n// We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.\n// We will return an object mapping of true variable name to value (basically, the current scope as a JS object).\n// The reason we can't just use the original variable names is minifiers mangling the toplevel scope.\n// This took me three weeks to figure out how to do.\nvar wcln = function (fn, fnStr, td) {\n var dt = fn();\n var st = fn.toString();\n var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/\\s+/g, '').split(',');\n for (var i = 0; i < dt.length; ++i) {\n var v = dt[i], k = ks[i];\n if (typeof v == 'function') {\n fnStr += ';' + k + '=';\n var st_1 = v.toString();\n if (v.prototype) {\n // for global objects\n if (st_1.indexOf('[native code]') != -1) {\n var spInd = st_1.indexOf(' ', 8) + 1;\n fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));\n }\n else {\n fnStr += st_1;\n for (var t in v.prototype)\n fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();\n }\n }\n else\n fnStr += st_1;\n }\n else\n td[k] = v;\n }\n return [fnStr, td];\n};\nvar ch = [];\n// clone bufs\nvar cbfs = function (v) {\n var tl = [];\n for (var k in v) {\n if (v[k].buffer) {\n tl.push((v[k] = new v[k].constructor(v[k])).buffer);\n }\n }\n return tl;\n};\n// use a worker to execute code\nvar wrkr = function (fns, init, id, cb) {\n var _a;\n if (!ch[id]) {\n var fnStr = '', td_1 = {}, m = fns.length - 1;\n for (var i = 0; i < m; ++i)\n _a = wcln(fns[i], fnStr, td_1), fnStr = _a[0], td_1 = _a[1];\n ch[id] = wcln(fns[m], fnStr, td_1);\n }\n var td = mrg({}, ch[id][1]);\n return wk(ch[id][0] + ';onmessage=function(e){for(var k in e.data)self[k]=e.data[k];onmessage=' + init.toString() + '}', id, td, cbfs(td), cb);\n};\n// base async inflate fn\nvar bInflt = function () { return [u8, u16, u32, fleb, fdeb, clim, fl, fd, flrm, fdrm, rev, ec, hMap, max, bits, bits16, shft, slc, err, inflt, inflateSync, pbf, gu8]; };\nvar bDflt = function () { return [u8, u16, u32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]; };\n// gzip extra\nvar gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };\n// gunzip extra\nvar guze = function () { return [gzs, gzl]; };\n// zlib extra\nvar zle = function () { return [zlh, wbytes, adler]; };\n// unzlib extra\nvar zule = function () { return [zlv]; };\n// post buf\nvar pbf = function (msg) { return postMessage(msg, [msg.buffer]); };\n// get u8\nvar gu8 = function (o) { return o && o.size && new u8(o.size); };\n// async helper\nvar cbify = function (dat, opts, fns, init, id, cb) {\n var w = wrkr(fns, init, id, function (err, dat) {\n w.terminate();\n cb(err, dat);\n });\n w.postMessage([dat, opts], opts.consume ? [dat.buffer] : []);\n return function () { w.terminate(); };\n};\n// auto stream\nvar astrm = function (strm) {\n strm.ondata = function (dat, final) { return postMessage([dat, final], [dat.buffer]); };\n return function (ev) { return strm.push(ev.data[0], ev.data[1]); };\n};\n// async stream attach\nvar astrmify = function (fns, strm, opts, init, id) {\n var t;\n var w = wrkr(fns, init, id, function (err, dat) {\n if (err)\n w.terminate(), strm.ondata.call(strm, err);\n else {\n if (dat[1])\n w.terminate();\n strm.ondata.call(strm, err, dat[0], dat[1]);\n }\n });\n w.postMessage(opts);\n strm.push = function (d, f) {\n if (!strm.ondata)\n err(5);\n if (t)\n strm.ondata(err(4, 0, 1), null, !!f);\n w.postMessage([d, t = f], [d.buffer]);\n };\n strm.terminate = function () { w.terminate(); };\n};\n// read 2 bytes\nvar b2 = function (d, b) { return d[b] | (d[b + 1] << 8); };\n// read 4 bytes\nvar b4 = function (d, b) { return (d[b] | (d[b + 1] << 8) | (d[b + 2] << 16) | (d[b + 3] << 24)) >>> 0; };\nvar b8 = function (d, b) { return b4(d, b) + (b4(d, b + 4) * 4294967296); };\n// write bytes\nvar wbytes = function (d, b, v) {\n for (; v; ++b)\n d[b] = v, v >>>= 8;\n};\n// gzip header\nvar gzh = function (c, o) {\n var fn = o.filename;\n c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3; // assume Unix\n if (o.mtime != 0)\n wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));\n if (fn) {\n c[3] = 8;\n for (var i = 0; i <= fn.length; ++i)\n c[i + 10] = fn.charCodeAt(i);\n }\n};\n// gzip footer: -8 to -4 = CRC, -4 to -0 is length\n// gzip start\nvar gzs = function (d) {\n if (d[0] != 31 || d[1] != 139 || d[2] != 8)\n err(6, 'invalid gzip data');\n var flg = d[3];\n var st = 10;\n if (flg & 4)\n st += d[10] | (d[11] << 8) + 2;\n for (var zs = (flg >> 3 & 1) + (flg >> 4 & 1); zs > 0; zs -= !d[st++])\n ;\n return st + (flg & 2);\n};\n// gzip length\nvar gzl = function (d) {\n var l = d.length;\n return ((d[l - 4] | d[l - 3] << 8 | d[l - 2] << 16) | (d[l - 1] << 24)) >>> 0;\n};\n// gzip header length\nvar gzhl = function (o) { return 10 + ((o.filename && (o.filename.length + 1)) || 0); };\n// zlib header\nvar zlh = function (c, o) {\n var lv = o.level, fl = lv == 0 ? 0 : lv < 6 ? 1 : lv == 9 ? 3 : 2;\n c[0] = 120, c[1] = (fl << 6) | (fl ? (32 - 2 * fl) : 1);\n};\n// zlib valid\nvar zlv = function (d) {\n if ((d[0] & 15) != 8 || (d[0] >>> 4) > 7 || ((d[0] << 8 | d[1]) % 31))\n err(6, 'invalid zlib data');\n if (d[1] & 32)\n err(6, 'invalid zlib data: preset dictionaries not supported');\n};\nfunction AsyncCmpStrm(opts, cb) {\n if (!cb && typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n return opts;\n}\n// zlib footer: -4 to -0 is Adler32\n/**\n * Streaming DEFLATE compression\n */\nvar Deflate = /*#__PURE__*/ (function () {\n function Deflate(opts, cb) {\n if (!cb && typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n this.o = opts || {};\n }\n Deflate.prototype.p = function (c, f) {\n this.ondata(dopt(c, this.o, 0, 0, !f), f);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Deflate.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n this.d = final;\n this.p(chunk, final || false);\n };\n return Deflate;\n}());\nexport { Deflate };\n/**\n * Asynchronous streaming DEFLATE compression\n */\nvar AsyncDeflate = /*#__PURE__*/ (function () {\n function AsyncDeflate(opts, cb) {\n astrmify([\n bDflt,\n function () { return [astrm, Deflate]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Deflate(ev.data);\n onmessage = astrm(strm);\n }, 6);\n }\n return AsyncDeflate;\n}());\nexport { AsyncDeflate };\nexport function deflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n ], function (ev) { return pbf(deflateSync(ev.data[0], ev.data[1])); }, 0, cb);\n}\n/**\n * Compresses data with DEFLATE without any wrapper\n * @param data The data to compress\n * @param opts The compression options\n * @returns The deflated version of the data\n */\nexport function deflateSync(data, opts) {\n return dopt(data, opts || {}, 0, 0);\n}\n/**\n * Streaming DEFLATE decompression\n */\nvar Inflate = /*#__PURE__*/ (function () {\n /**\n * Creates an inflation stream\n * @param cb The callback to call whenever data is inflated\n */\n function Inflate(cb) {\n this.s = {};\n this.p = new u8(0);\n this.ondata = cb;\n }\n Inflate.prototype.e = function (c) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n var l = this.p.length;\n var n = new u8(l + c.length);\n n.set(this.p), n.set(c, l), this.p = n;\n };\n Inflate.prototype.c = function (final) {\n this.d = this.s.i = final || false;\n var bts = this.s.b;\n var dt = inflt(this.p, this.o, this.s);\n this.ondata(slc(dt, bts, this.s.b), this.d);\n this.o = slc(dt, this.s.b - 32768), this.s.b = this.o.length;\n this.p = slc(this.p, (this.s.p / 8) | 0), this.s.p &= 7;\n };\n /**\n * Pushes a chunk to be inflated\n * @param chunk The chunk to push\n * @param final Whether this is the final chunk\n */\n Inflate.prototype.push = function (chunk, final) {\n this.e(chunk), this.c(final);\n };\n return Inflate;\n}());\nexport { Inflate };\n/**\n * Asynchronous streaming DEFLATE decompression\n */\nvar AsyncInflate = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous inflation stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncInflate(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n function () { return [astrm, Inflate]; }\n ], this, 0, function () {\n var strm = new Inflate();\n onmessage = astrm(strm);\n }, 7);\n }\n return AsyncInflate;\n}());\nexport { AsyncInflate };\nexport function inflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt\n ], function (ev) { return pbf(inflateSync(ev.data[0], gu8(ev.data[1]))); }, 1, cb);\n}\n/**\n * Expands DEFLATE data with no wrapper\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function inflateSync(data, out) {\n return inflt(data, out);\n}\n// before you yell at me for not just using extends, my reason is that TS inheritance is hard to workerize.\n/**\n * Streaming GZIP compression\n */\nvar Gzip = /*#__PURE__*/ (function () {\n function Gzip(opts, cb) {\n this.c = crc();\n this.l = 0;\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gzip.prototype.push = function (chunk, final) {\n Deflate.prototype.push.call(this, chunk, final);\n };\n Gzip.prototype.p = function (c, f) {\n this.c.p(c);\n this.l += c.length;\n var raw = dopt(c, this.o, this.v && gzhl(this.o), f && 8, !f);\n if (this.v)\n gzh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 8, this.c.d()), wbytes(raw, raw.length - 4, this.l);\n this.ondata(raw, f);\n };\n return Gzip;\n}());\nexport { Gzip };\n/**\n * Asynchronous streaming GZIP compression\n */\nvar AsyncGzip = /*#__PURE__*/ (function () {\n function AsyncGzip(opts, cb) {\n astrmify([\n bDflt,\n gze,\n function () { return [astrm, Deflate, Gzip]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Gzip(ev.data);\n onmessage = astrm(strm);\n }, 8);\n }\n return AsyncGzip;\n}());\nexport { AsyncGzip };\nexport function gzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n gze,\n function () { return [gzipSync]; }\n ], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);\n}\n/**\n * Compresses data with GZIP\n * @param data The data to compress\n * @param opts The compression options\n * @returns The gzipped version of the data\n */\nexport function gzipSync(data, opts) {\n if (!opts)\n opts = {};\n var c = crc(), l = data.length;\n c.p(data);\n var d = dopt(data, opts, gzhl(opts), 8), s = d.length;\n return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;\n}\n/**\n * Streaming GZIP decompression\n */\nvar Gunzip = /*#__PURE__*/ (function () {\n /**\n * Creates a GUNZIP stream\n * @param cb The callback to call whenever data is inflated\n */\n function Gunzip(cb) {\n this.v = 1;\n Inflate.call(this, cb);\n }\n /**\n * Pushes a chunk to be GUNZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gunzip.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n var s = this.p.length > 3 ? gzs(this.p) : 4;\n if (s >= this.p.length && !final)\n return;\n this.p = this.p.subarray(s), this.v = 0;\n }\n if (final) {\n if (this.p.length < 8)\n err(6, 'invalid gzip data');\n this.p = this.p.subarray(0, -8);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Gunzip;\n}());\nexport { Gunzip };\n/**\n * Asynchronous streaming GZIP decompression\n */\nvar AsyncGunzip = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous GUNZIP stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncGunzip(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n guze,\n function () { return [astrm, Inflate, Gunzip]; }\n ], this, 0, function () {\n var strm = new Gunzip();\n onmessage = astrm(strm);\n }, 9);\n }\n return AsyncGunzip;\n}());\nexport { AsyncGunzip };\nexport function gunzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt,\n guze,\n function () { return [gunzipSync]; }\n ], function (ev) { return pbf(gunzipSync(ev.data[0])); }, 3, cb);\n}\n/**\n * Expands GZIP data\n * @param data The data to decompress\n * @param out Where to write the data. GZIP already encodes the output size, so providing this doesn't save memory.\n * @returns The decompressed version of the data\n */\nexport function gunzipSync(data, out) {\n return inflt(data.subarray(gzs(data), -8), out || new u8(gzl(data)));\n}\n/**\n * Streaming Zlib compression\n */\nvar Zlib = /*#__PURE__*/ (function () {\n function Zlib(opts, cb) {\n this.c = adler();\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be zlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Zlib.prototype.push = function (chunk, final) {\n Deflate.prototype.push.call(this, chunk, final);\n };\n Zlib.prototype.p = function (c, f) {\n this.c.p(c);\n var raw = dopt(c, this.o, this.v && 2, f && 4, !f);\n if (this.v)\n zlh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 4, this.c.d());\n this.ondata(raw, f);\n };\n return Zlib;\n}());\nexport { Zlib };\n/**\n * Asynchronous streaming Zlib compression\n */\nvar AsyncZlib = /*#__PURE__*/ (function () {\n function AsyncZlib(opts, cb) {\n astrmify([\n bDflt,\n zle,\n function () { return [astrm, Deflate, Zlib]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Zlib(ev.data);\n onmessage = astrm(strm);\n }, 10);\n }\n return AsyncZlib;\n}());\nexport { AsyncZlib };\nexport function zlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n zle,\n function () { return [zlibSync]; }\n ], function (ev) { return pbf(zlibSync(ev.data[0], ev.data[1])); }, 4, cb);\n}\n/**\n * Compress data with Zlib\n * @param data The data to compress\n * @param opts The compression options\n * @returns The zlib-compressed version of the data\n */\nexport function zlibSync(data, opts) {\n if (!opts)\n opts = {};\n var a = adler();\n a.p(data);\n var d = dopt(data, opts, 2, 4);\n return zlh(d, opts), wbytes(d, d.length - 4, a.d()), d;\n}\n/**\n * Streaming Zlib decompression\n */\nvar Unzlib = /*#__PURE__*/ (function () {\n /**\n * Creates a Zlib decompression stream\n * @param cb The callback to call whenever data is inflated\n */\n function Unzlib(cb) {\n this.v = 1;\n Inflate.call(this, cb);\n }\n /**\n * Pushes a chunk to be unzlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzlib.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n if (this.p.length < 2 && !final)\n return;\n this.p = this.p.subarray(2), this.v = 0;\n }\n if (final) {\n if (this.p.length < 4)\n err(6, 'invalid zlib data');\n this.p = this.p.subarray(0, -4);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Unzlib;\n}());\nexport { Unzlib };\n/**\n * Asynchronous streaming Zlib decompression\n */\nvar AsyncUnzlib = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous Zlib decompression stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncUnzlib(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n zule,\n function () { return [astrm, Inflate, Unzlib]; }\n ], this, 0, function () {\n var strm = new Unzlib();\n onmessage = astrm(strm);\n }, 11);\n }\n return AsyncUnzlib;\n}());\nexport { AsyncUnzlib };\nexport function unzlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt,\n zule,\n function () { return [unzlibSync]; }\n ], function (ev) { return pbf(unzlibSync(ev.data[0], gu8(ev.data[1]))); }, 5, cb);\n}\n/**\n * Expands Zlib data\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function unzlibSync(data, out) {\n return inflt((zlv(data), data.subarray(2, -4)), out);\n}\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzip as compress, AsyncGzip as AsyncCompress };\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzipSync as compressSync, Gzip as Compress };\n/**\n * Streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar Decompress = /*#__PURE__*/ (function () {\n /**\n * Creates a decompression stream\n * @param cb The callback to call whenever data is decompressed\n */\n function Decompress(cb) {\n this.G = Gunzip;\n this.I = Inflate;\n this.Z = Unzlib;\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Decompress.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (!this.s) {\n if (this.p && this.p.length) {\n var n = new u8(this.p.length + chunk.length);\n n.set(this.p), n.set(chunk, this.p.length);\n }\n else\n this.p = chunk;\n if (this.p.length > 2) {\n var _this_1 = this;\n var cb = function () { _this_1.ondata.apply(_this_1, arguments); };\n this.s = (this.p[0] == 31 && this.p[1] == 139 && this.p[2] == 8)\n ? new this.G(cb)\n : ((this.p[0] & 15) != 8 || (this.p[0] >> 4) > 7 || ((this.p[0] << 8 | this.p[1]) % 31))\n ? new this.I(cb)\n : new this.Z(cb);\n this.s.push(this.p, final);\n this.p = null;\n }\n }\n else\n this.s.push(chunk, final);\n };\n return Decompress;\n}());\nexport { Decompress };\n/**\n * Asynchronous streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar AsyncDecompress = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous decompression stream\n * @param cb The callback to call whenever data is decompressed\n */\n function AsyncDecompress(cb) {\n this.G = AsyncGunzip;\n this.I = AsyncInflate;\n this.Z = AsyncUnzlib;\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncDecompress.prototype.push = function (chunk, final) {\n Decompress.prototype.push.call(this, chunk, final);\n };\n return AsyncDecompress;\n}());\nexport { AsyncDecompress };\nexport function decompress(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzip(data, opts, cb)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflate(data, opts, cb)\n : unzlib(data, opts, cb);\n}\n/**\n * Expands compressed GZIP, Zlib, or raw DEFLATE data, automatically detecting the format\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function decompressSync(data, out) {\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzipSync(data, out)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflateSync(data, out)\n : unzlibSync(data, out);\n}\n// flatten a directory structure\nvar fltn = function (d, p, t, o) {\n for (var k in d) {\n var val = d[k], n = p + k, op = o;\n if (Array.isArray(val))\n op = mrg(o, val[1]), val = val[0];\n if (val instanceof u8)\n t[n] = [val, op];\n else {\n t[n += '/'] = [new u8(0), op];\n fltn(val, n, t, o);\n }\n }\n};\n// text encoder\nvar te = typeof TextEncoder != 'undefined' && /*#__PURE__*/ new TextEncoder();\n// text decoder\nvar td = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();\n// text decoder stream\nvar tds = 0;\ntry {\n td.decode(et, { stream: true });\n tds = 1;\n}\ncatch (e) { }\n// decode UTF8\nvar dutf8 = function (d) {\n for (var r = '', i = 0;;) {\n var c = d[i++];\n var eb = (c > 127) + (c > 223) + (c > 239);\n if (i + eb > d.length)\n return [r, slc(d, i - 1)];\n if (!eb)\n r += String.fromCharCode(c);\n else if (eb == 3) {\n c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63)) - 65536,\n r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));\n }\n else if (eb & 1)\n r += String.fromCharCode((c & 31) << 6 | (d[i++] & 63));\n else\n r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63));\n }\n};\n/**\n * Streaming UTF-8 decoding\n */\nvar DecodeUTF8 = /*#__PURE__*/ (function () {\n /**\n * Creates a UTF-8 decoding stream\n * @param cb The callback to call whenever data is decoded\n */\n function DecodeUTF8(cb) {\n this.ondata = cb;\n if (tds)\n this.t = new TextDecoder();\n else\n this.p = et;\n }\n /**\n * Pushes a chunk to be decoded from UTF-8 binary\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n DecodeUTF8.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n final = !!final;\n if (this.t) {\n this.ondata(this.t.decode(chunk, { stream: true }), final);\n if (final) {\n if (this.t.decode().length)\n err(8);\n this.t = null;\n }\n return;\n }\n if (!this.p)\n err(4);\n var dat = new u8(this.p.length + chunk.length);\n dat.set(this.p);\n dat.set(chunk, this.p.length);\n var _a = dutf8(dat), ch = _a[0], np = _a[1];\n if (final) {\n if (np.length)\n err(8);\n this.p = null;\n }\n else\n this.p = np;\n this.ondata(ch, final);\n };\n return DecodeUTF8;\n}());\nexport { DecodeUTF8 };\n/**\n * Streaming UTF-8 encoding\n */\nvar EncodeUTF8 = /*#__PURE__*/ (function () {\n /**\n * Creates a UTF-8 decoding stream\n * @param cb The callback to call whenever data is encoded\n */\n function EncodeUTF8(cb) {\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be encoded to UTF-8\n * @param chunk The string data to push\n * @param final Whether this is the last chunk\n */\n EncodeUTF8.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n this.ondata(strToU8(chunk), this.d = final || false);\n };\n return EncodeUTF8;\n}());\nexport { EncodeUTF8 };\n/**\n * Converts a string into a Uint8Array for use with compression/decompression methods\n * @param str The string to encode\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless decoding a binary string.\n * @returns The string encoded in UTF-8/Latin-1 binary\n */\nexport function strToU8(str, latin1) {\n if (latin1) {\n var ar_1 = new u8(str.length);\n for (var i = 0; i < str.length; ++i)\n ar_1[i] = str.charCodeAt(i);\n return ar_1;\n }\n if (te)\n return te.encode(str);\n var l = str.length;\n var ar = new u8(str.length + (str.length >> 1));\n var ai = 0;\n var w = function (v) { ar[ai++] = v; };\n for (var i = 0; i < l; ++i) {\n if (ai + 5 > ar.length) {\n var n = new u8(ai + 8 + ((l - i) << 1));\n n.set(ar);\n ar = n;\n }\n var c = str.charCodeAt(i);\n if (c < 128 || latin1)\n w(c);\n else if (c < 2048)\n w(192 | (c >> 6)), w(128 | (c & 63));\n else if (c > 55295 && c < 57344)\n c = 65536 + (c & 1023 << 10) | (str.charCodeAt(++i) & 1023),\n w(240 | (c >> 18)), w(128 | ((c >> 12) & 63)), w(128 | ((c >> 6) & 63)), w(128 | (c & 63));\n else\n w(224 | (c >> 12)), w(128 | ((c >> 6) & 63)), w(128 | (c & 63));\n }\n return slc(ar, 0, ai);\n}\n/**\n * Converts a Uint8Array to a string\n * @param dat The data to decode to string\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless encoding to binary string.\n * @returns The original UTF-8/Latin-1 string\n */\nexport function strFromU8(dat, latin1) {\n if (latin1) {\n var r = '';\n for (var i = 0; i < dat.length; i += 16384)\n r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));\n return r;\n }\n else if (td)\n return td.decode(dat);\n else {\n var _a = dutf8(dat), out = _a[0], ext = _a[1];\n if (ext.length)\n err(8);\n return out;\n }\n}\n;\n// deflate bit flag\nvar dbf = function (l) { return l == 1 ? 3 : l < 6 ? 2 : l == 9 ? 1 : 0; };\n// skip local zip header\nvar slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); };\n// read zip header\nvar zh = function (d, b, z) {\n var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20);\n var _a = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2];\n return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off];\n};\n// read zip64 extra field\nvar z64e = function (d, b) {\n for (; b2(d, b) != 1; b += 4 + b2(d, b + 2))\n ;\n return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)];\n};\n// extra field length\nvar exfl = function (ex) {\n var le = 0;\n if (ex) {\n for (var k in ex) {\n var l = ex[k].length;\n if (l > 65535)\n err(9);\n le += l + 4;\n }\n }\n return le;\n};\n// write zip header\nvar wzh = function (d, b, f, fn, u, c, ce, co) {\n var fl = fn.length, ex = f.extra, col = co && co.length;\n var exl = exfl(ex);\n wbytes(d, b, ce != null ? 0x2014B50 : 0x4034B50), b += 4;\n if (ce != null)\n d[b++] = 20, d[b++] = f.os;\n d[b] = 20, b += 2; // spec compliance? what's that?\n d[b++] = (f.flag << 1) | (c == null && 8), d[b++] = u && 8;\n d[b++] = f.compression & 255, d[b++] = f.compression >> 8;\n var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;\n if (y < 0 || y > 119)\n err(10);\n wbytes(d, b, (y << 25) | ((dt.getMonth() + 1) << 21) | (dt.getDate() << 16) | (dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() >>> 1)), b += 4;\n if (c != null) {\n wbytes(d, b, f.crc);\n wbytes(d, b + 4, c);\n wbytes(d, b + 8, f.size);\n }\n wbytes(d, b + 12, fl);\n wbytes(d, b + 14, exl), b += 16;\n if (ce != null) {\n wbytes(d, b, col);\n wbytes(d, b + 6, f.attrs);\n wbytes(d, b + 10, ce), b += 14;\n }\n d.set(fn, b);\n b += fl;\n if (exl) {\n for (var k in ex) {\n var exf = ex[k], l = exf.length;\n wbytes(d, b, +k);\n wbytes(d, b + 2, l);\n d.set(exf, b + 4), b += 4 + l;\n }\n }\n if (col)\n d.set(co, b), b += col;\n return b;\n};\n// write zip footer (end of central directory)\nvar wzf = function (o, b, c, d, e) {\n wbytes(o, b, 0x6054B50); // skip disk\n wbytes(o, b + 8, c);\n wbytes(o, b + 10, c);\n wbytes(o, b + 12, d);\n wbytes(o, b + 16, e);\n};\n/**\n * A pass-through stream to keep data uncompressed in a ZIP archive.\n */\nvar ZipPassThrough = /*#__PURE__*/ (function () {\n /**\n * Creates a pass-through stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n */\n function ZipPassThrough(filename) {\n this.filename = filename;\n this.c = crc();\n this.size = 0;\n this.compression = 0;\n }\n /**\n * Processes a chunk and pushes to the output stream. You can override this\n * method in a subclass for custom behavior, but by default this passes\n * the data through. You must call this.ondata(err, chunk, final) at some\n * point in this method.\n * @param chunk The chunk to process\n * @param final Whether this is the last chunk\n */\n ZipPassThrough.prototype.process = function (chunk, final) {\n this.ondata(null, chunk, final);\n };\n /**\n * Pushes a chunk to be added. If you are subclassing this with a custom\n * compression algorithm, note that you must push data from the source\n * file only, pre-compression.\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n ZipPassThrough.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n this.c.p(chunk);\n this.size += chunk.length;\n if (final)\n this.crc = this.c.d();\n this.process(chunk, final || false);\n };\n return ZipPassThrough;\n}());\nexport { ZipPassThrough };\n// I don't extend because TypeScript extension adds 1kB of runtime bloat\n/**\n * Streaming DEFLATE compression for ZIP archives. Prefer using AsyncZipDeflate\n * for better performance\n */\nvar ZipDeflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n * @param opts The compression options\n */\n function ZipDeflate(filename, opts) {\n var _this_1 = this;\n if (!opts)\n opts = {};\n ZipPassThrough.call(this, filename);\n this.d = new Deflate(opts, function (dat, final) {\n _this_1.ondata(null, dat, final);\n });\n this.compression = 8;\n this.flag = dbf(opts.level);\n }\n ZipDeflate.prototype.process = function (chunk, final) {\n try {\n this.d.push(chunk, final);\n }\n catch (e) {\n this.ondata(e, null, final);\n }\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n ZipDeflate.prototype.push = function (chunk, final) {\n ZipPassThrough.prototype.push.call(this, chunk, final);\n };\n return ZipDeflate;\n}());\nexport { ZipDeflate };\n/**\n * Asynchronous streaming DEFLATE compression for ZIP archives\n */\nvar AsyncZipDeflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n * @param opts The compression options\n */\n function AsyncZipDeflate(filename, opts) {\n var _this_1 = this;\n if (!opts)\n opts = {};\n ZipPassThrough.call(this, filename);\n this.d = new AsyncDeflate(opts, function (err, dat, final) {\n _this_1.ondata(err, dat, final);\n });\n this.compression = 8;\n this.flag = dbf(opts.level);\n this.terminate = this.d.terminate;\n }\n AsyncZipDeflate.prototype.process = function (chunk, final) {\n this.d.push(chunk, final);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncZipDeflate.prototype.push = function (chunk, final) {\n ZipPassThrough.prototype.push.call(this, chunk, final);\n };\n return AsyncZipDeflate;\n}());\nexport { AsyncZipDeflate };\n// TODO: Better tree shaking\n/**\n * A zippable archive to which files can incrementally be added\n */\nvar Zip = /*#__PURE__*/ (function () {\n /**\n * Creates an empty ZIP archive to which files can be added\n * @param cb The callback to call whenever data for the generated ZIP archive\n * is available\n */\n function Zip(cb) {\n this.ondata = cb;\n this.u = [];\n this.d = 1;\n }\n /**\n * Adds a file to the ZIP archive\n * @param file The file stream to add\n */\n Zip.prototype.add = function (file) {\n var _this_1 = this;\n if (!this.ondata)\n err(5);\n // finishing or finished\n if (this.d & 2)\n this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, false);\n else {\n var f = strToU8(file.filename), fl_1 = f.length;\n var com = file.comment, o = com && strToU8(com);\n var u = fl_1 != file.filename.length || (o && (com.length != o.length));\n var hl_1 = fl_1 + exfl(file.extra) + 30;\n if (fl_1 > 65535)\n this.ondata(err(11, 0, 1), null, false);\n var header = new u8(hl_1);\n wzh(header, 0, file, f, u);\n var chks_1 = [header];\n var pAll_1 = function () {\n for (var _i = 0, chks_2 = chks_1; _i < chks_2.length; _i++) {\n var chk = chks_2[_i];\n _this_1.ondata(null, chk, false);\n }\n chks_1 = [];\n };\n var tr_1 = this.d;\n this.d = 0;\n var ind_1 = this.u.length;\n var uf_1 = mrg(file, {\n f: f,\n u: u,\n o: o,\n t: function () {\n if (file.terminate)\n file.terminate();\n },\n r: function () {\n pAll_1();\n if (tr_1) {\n var nxt = _this_1.u[ind_1 + 1];\n if (nxt)\n nxt.r();\n else\n _this_1.d = 1;\n }\n tr_1 = 1;\n }\n });\n var cl_1 = 0;\n file.ondata = function (err, dat, final) {\n if (err) {\n _this_1.ondata(err, dat, final);\n _this_1.terminate();\n }\n else {\n cl_1 += dat.length;\n chks_1.push(dat);\n if (final) {\n var dd = new u8(16);\n wbytes(dd, 0, 0x8074B50);\n wbytes(dd, 4, file.crc);\n wbytes(dd, 8, cl_1);\n wbytes(dd, 12, file.size);\n chks_1.push(dd);\n uf_1.c = cl_1, uf_1.b = hl_1 + cl_1 + 16, uf_1.crc = file.crc, uf_1.size = file.size;\n if (tr_1)\n uf_1.r();\n tr_1 = 1;\n }\n else if (tr_1)\n pAll_1();\n }\n };\n this.u.push(uf_1);\n }\n };\n /**\n * Ends the process of adding files and prepares to emit the final chunks.\n * This *must* be called after adding all desired files for the resulting\n * ZIP file to work properly.\n */\n Zip.prototype.end = function () {\n var _this_1 = this;\n if (this.d & 2) {\n this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, true);\n return;\n }\n if (this.d)\n this.e();\n else\n this.u.push({\n r: function () {\n if (!(_this_1.d & 1))\n return;\n _this_1.u.splice(-1, 1);\n _this_1.e();\n },\n t: function () { }\n });\n this.d = 3;\n };\n Zip.prototype.e = function () {\n var bt = 0, l = 0, tl = 0;\n for (var _i = 0, _a = this.u; _i < _a.length; _i++) {\n var f = _a[_i];\n tl += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0);\n }\n var out = new u8(tl + 22);\n for (var _b = 0, _c = this.u; _b < _c.length; _b++) {\n var f = _c[_b];\n wzh(out, bt, f, f.f, f.u, f.c, l, f.o);\n bt += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0), l += f.b;\n }\n wzf(out, bt, this.u.length, tl, l);\n this.ondata(null, out, true);\n this.d = 2;\n };\n /**\n * A method to terminate any internal workers used by the stream. Subsequent\n * calls to add() will fail.\n */\n Zip.prototype.terminate = function () {\n for (var _i = 0, _a = this.u; _i < _a.length; _i++) {\n var f = _a[_i];\n f.t();\n }\n this.d = 2;\n };\n return Zip;\n}());\nexport { Zip };\nexport function zip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n var r = {};\n fltn(data, '', r, opts);\n var k = Object.keys(r);\n var lft = k.length, o = 0, tot = 0;\n var slft = lft, files = new Array(lft);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var cbd = function (a, b) {\n mt(function () { cb(a, b); });\n };\n mt(function () { cbd = cb; });\n var cbf = function () {\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n tot = 0;\n for (var i = 0; i < slft; ++i) {\n var f = files[i];\n try {\n var l = f.c.length;\n wzh(out, tot, f, f.f, f.u, l);\n var badd = 30 + f.f.length + exfl(f.extra);\n var loc = tot + badd;\n out.set(f.c, loc);\n wzh(out, o, f, f.f, f.u, l, tot, f.m), o += 16 + badd + (f.m ? f.m.length : 0), tot = loc + l;\n }\n catch (e) {\n return cbd(e, null);\n }\n }\n wzf(out, o, files.length, cdl, oe);\n cbd(null, out);\n };\n if (!lft)\n cbf();\n var _loop_1 = function (i) {\n var fn = k[i];\n var _a = r[fn], file = _a[0], p = _a[1];\n var c = crc(), size = file.length;\n c.p(file);\n var f = strToU8(fn), s = f.length;\n var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n var exl = exfl(p.extra);\n var compression = p.level == 0 ? 0 : 8;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cbd(e, null);\n }\n else {\n var l = d.length;\n files[i] = mrg(p, {\n size: size,\n crc: c.d(),\n c: d,\n f: f,\n m: m,\n u: s != fn.length || (m && (com.length != ms)),\n compression: compression\n });\n o += 30 + s + exl + l;\n tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n if (!--lft)\n cbf();\n }\n };\n if (s > 65535)\n cbl(err(11, 0, 1), null);\n if (!compression)\n cbl(null, file);\n else if (size < 160000) {\n try {\n cbl(null, deflateSync(file, p));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(deflate(file, p, cbl));\n };\n // Cannot use lft because it can decrease\n for (var i = 0; i < slft; ++i) {\n _loop_1(i);\n }\n return tAll;\n}\n/**\n * Synchronously creates a ZIP file. Prefer using `zip` for better performance\n * with more than one file.\n * @param data The directory structure for the ZIP archive\n * @param opts The main options, merged with per-file options\n * @returns The generated ZIP archive\n */\nexport function zipSync(data, opts) {\n if (!opts)\n opts = {};\n var r = {};\n var files = [];\n fltn(data, '', r, opts);\n var o = 0;\n var tot = 0;\n for (var fn in r) {\n var _a = r[fn], file = _a[0], p = _a[1];\n var compression = p.level == 0 ? 0 : 8;\n var f = strToU8(fn), s = f.length;\n var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n var exl = exfl(p.extra);\n if (s > 65535)\n err(11);\n var d = compression ? deflateSync(file, p) : file, l = d.length;\n var c = crc();\n c.p(file);\n files.push(mrg(p, {\n size: file.length,\n crc: c.d(),\n c: d,\n f: f,\n m: m,\n u: s != fn.length || (m && (com.length != ms)),\n o: o,\n compression: compression\n }));\n o += 30 + s + exl + l;\n tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n }\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n for (var i = 0; i < files.length; ++i) {\n var f = files[i];\n wzh(out, f.o, f, f.f, f.u, f.c.length);\n var badd = 30 + f.f.length + exfl(f.extra);\n out.set(f.c, f.o + badd);\n wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);\n }\n wzf(out, o, files.length, cdl, oe);\n return out;\n}\n/**\n * Streaming pass-through decompression for ZIP archives\n */\nvar UnzipPassThrough = /*#__PURE__*/ (function () {\n function UnzipPassThrough() {\n }\n UnzipPassThrough.prototype.push = function (data, final) {\n this.ondata(null, data, final);\n };\n UnzipPassThrough.compression = 0;\n return UnzipPassThrough;\n}());\nexport { UnzipPassThrough };\n/**\n * Streaming DEFLATE decompression for ZIP archives. Prefer AsyncZipInflate for\n * better performance.\n */\nvar UnzipInflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE decompression that can be used in ZIP archives\n */\n function UnzipInflate() {\n var _this_1 = this;\n this.i = new Inflate(function (dat, final) {\n _this_1.ondata(null, dat, final);\n });\n }\n UnzipInflate.prototype.push = function (data, final) {\n try {\n this.i.push(data, final);\n }\n catch (e) {\n this.ondata(e, null, final);\n }\n };\n UnzipInflate.compression = 8;\n return UnzipInflate;\n}());\nexport { UnzipInflate };\n/**\n * Asynchronous streaming DEFLATE decompression for ZIP archives\n */\nvar AsyncUnzipInflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE decompression that can be used in ZIP archives\n */\n function AsyncUnzipInflate(_, sz) {\n var _this_1 = this;\n if (sz < 320000) {\n this.i = new Inflate(function (dat, final) {\n _this_1.ondata(null, dat, final);\n });\n }\n else {\n this.i = new AsyncInflate(function (err, dat, final) {\n _this_1.ondata(err, dat, final);\n });\n this.terminate = this.i.terminate;\n }\n }\n AsyncUnzipInflate.prototype.push = function (data, final) {\n if (this.i.terminate)\n data = slc(data, 0);\n this.i.push(data, final);\n };\n AsyncUnzipInflate.compression = 8;\n return AsyncUnzipInflate;\n}());\nexport { AsyncUnzipInflate };\n/**\n * A ZIP archive decompression stream that emits files as they are discovered\n */\nvar Unzip = /*#__PURE__*/ (function () {\n /**\n * Creates a ZIP decompression stream\n * @param cb The callback to call whenever a file in the ZIP archive is found\n */\n function Unzip(cb) {\n this.onfile = cb;\n this.k = [];\n this.o = {\n 0: UnzipPassThrough\n };\n this.p = et;\n }\n /**\n * Pushes a chunk to be unzipped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzip.prototype.push = function (chunk, final) {\n var _this_1 = this;\n if (!this.onfile)\n err(5);\n if (!this.p)\n err(4);\n if (this.c > 0) {\n var len = Math.min(this.c, chunk.length);\n var toAdd = chunk.subarray(0, len);\n this.c -= len;\n if (this.d)\n this.d.push(toAdd, !this.c);\n else\n this.k[0].push(toAdd);\n chunk = chunk.subarray(len);\n if (chunk.length)\n return this.push(chunk, final);\n }\n else {\n var f = 0, i = 0, is = void 0, buf = void 0;\n if (!this.p.length)\n buf = chunk;\n else if (!chunk.length)\n buf = this.p;\n else {\n buf = new u8(this.p.length + chunk.length);\n buf.set(this.p), buf.set(chunk, this.p.length);\n }\n var l = buf.length, oc = this.c, add = oc && this.d;\n var _loop_2 = function () {\n var _a;\n var sig = b4(buf, i);\n if (sig == 0x4034B50) {\n f = 1, is = i;\n this_1.d = null;\n this_1.c = 0;\n var bf = b2(buf, i + 6), cmp_1 = b2(buf, i + 8), u = bf & 2048, dd = bf & 8, fnl = b2(buf, i + 26), es = b2(buf, i + 28);\n if (l > i + 30 + fnl + es) {\n var chks_3 = [];\n this_1.k.unshift(chks_3);\n f = 2;\n var sc_1 = b4(buf, i + 18), su_1 = b4(buf, i + 22);\n var fn_1 = strFromU8(buf.subarray(i + 30, i += 30 + fnl), !u);\n if (sc_1 == 4294967295) {\n _a = dd ? [-2] : z64e(buf, i), sc_1 = _a[0], su_1 = _a[1];\n }\n else if (dd)\n sc_1 = -1;\n i += es;\n this_1.c = sc_1;\n var d_1;\n var file_1 = {\n name: fn_1,\n compression: cmp_1,\n start: function () {\n if (!file_1.ondata)\n err(5);\n if (!sc_1)\n file_1.ondata(null, et, true);\n else {\n var ctr = _this_1.o[cmp_1];\n if (!ctr)\n file_1.ondata(err(14, 'unknown compression type ' + cmp_1, 1), null, false);\n d_1 = sc_1 < 0 ? new ctr(fn_1) : new ctr(fn_1, sc_1, su_1);\n d_1.ondata = function (err, dat, final) { file_1.ondata(err, dat, final); };\n for (var _i = 0, chks_4 = chks_3; _i < chks_4.length; _i++) {\n var dat = chks_4[_i];\n d_1.push(dat, false);\n }\n if (_this_1.k[0] == chks_3 && _this_1.c)\n _this_1.d = d_1;\n else\n d_1.push(et, true);\n }\n },\n terminate: function () {\n if (d_1 && d_1.terminate)\n d_1.terminate();\n }\n };\n if (sc_1 >= 0)\n file_1.size = sc_1, file_1.originalSize = su_1;\n this_1.onfile(file_1);\n }\n return \"break\";\n }\n else if (oc) {\n if (sig == 0x8074B50) {\n is = i += 12 + (oc == -2 && 8), f = 3, this_1.c = 0;\n return \"break\";\n }\n else if (sig == 0x2014B50) {\n is = i -= 4, f = 3, this_1.c = 0;\n return \"break\";\n }\n }\n };\n var this_1 = this;\n for (; i < l - 4; ++i) {\n var state_1 = _loop_2();\n if (state_1 === \"break\")\n break;\n }\n this.p = et;\n if (oc < 0) {\n var dat = f ? buf.subarray(0, is - 12 - (oc == -2 && 8) - (b4(buf, is - 16) == 0x8074B50 && 4)) : buf.subarray(0, i);\n if (add)\n add.push(dat, !!f);\n else\n this.k[+(f == 2)].push(dat);\n }\n if (f & 2)\n return this.push(buf.subarray(i), final);\n this.p = buf.subarray(i);\n }\n if (final) {\n if (this.c)\n err(13);\n this.p = null;\n }\n };\n /**\n * Registers a decoder with the stream, allowing for files compressed with\n * the compression type provided to be expanded correctly\n * @param decoder The decoder constructor\n */\n Unzip.prototype.register = function (decoder) {\n this.o[decoder.compression] = decoder;\n };\n return Unzip;\n}());\nexport { Unzip };\nvar mt = typeof queueMicrotask == 'function' ? queueMicrotask : typeof setTimeout == 'function' ? setTimeout : function (fn) { fn(); };\nexport function unzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var files = {};\n var cbd = function (a, b) {\n mt(function () { cb(a, b); });\n };\n mt(function () { cbd = cb; });\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558) {\n cbd(err(13, 0, 1), null);\n return tAll;\n }\n }\n ;\n var lft = b2(data, e + 8);\n if (lft) {\n var c = lft;\n var o = b4(data, e + 16);\n var z = o == 4294967295;\n if (z) {\n e = b4(data, e - 12);\n if (b4(data, e) != 0x6064B50) {\n cbd(err(13, 0, 1), null);\n return tAll;\n }\n c = lft = b4(data, e + 32);\n o = b4(data, e + 48);\n }\n var fltr = opts && opts.filter;\n var _loop_3 = function (i) {\n var _a = zh(data, o, z), c_1 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cbd(e, null);\n }\n else {\n if (d)\n files[fn] = d;\n if (!--lft)\n cbd(null, files);\n }\n };\n if (!fltr || fltr({\n name: fn,\n size: sc,\n originalSize: su,\n compression: c_1\n })) {\n if (!c_1)\n cbl(null, slc(data, b, b + sc));\n else if (c_1 == 8) {\n var infl = data.subarray(b, b + sc);\n if (sc < 320000) {\n try {\n cbl(null, inflateSync(infl, new u8(su)));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(inflate(infl, { size: su }, cbl));\n }\n else\n cbl(err(14, 'unknown compression type ' + c_1, 1), null);\n }\n else\n cbl(null, null);\n };\n for (var i = 0; i < c; ++i) {\n _loop_3(i);\n }\n }\n else\n cbd(null, {});\n return tAll;\n}\n/**\n * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better\n * performance with more than one file.\n * @param data The raw compressed ZIP file\n * @param opts The ZIP extraction options\n * @returns The decompressed files\n */\nexport function unzipSync(data, opts) {\n var files = {};\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558)\n err(13);\n }\n ;\n var c = b2(data, e + 8);\n if (!c)\n return {};\n var o = b4(data, e + 16);\n var z = o == 4294967295;\n if (z) {\n e = b4(data, e - 12);\n if (b4(data, e) != 0x6064B50)\n err(13);\n c = b4(data, e + 32);\n o = b4(data, e + 48);\n }\n var fltr = opts && opts.filter;\n for (var i = 0; i < c; ++i) {\n var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n if (!fltr || fltr({\n name: fn,\n size: sc,\n originalSize: su,\n compression: c_2\n })) {\n if (!c_2)\n files[fn] = slc(data, b, b + sc);\n else if (c_2 == 8)\n files[fn] = inflateSync(data.subarray(b, b + sc), new u8(su));\n else\n err(14, 'unknown compression type ' + c_2);\n }\n }\n return files;\n}\n","/** Get time in milliseconds between a start time (t0) and now */\nfunction timeDiff(t0) {\n return Math.round(performance.now() - t0);\n}\n\n/** Initialize performance analysis settings */\nfunction initAnalyzeRelatedGenes(ideo) {\n ideo.time = {\n rg: { // times for related genes\n t0: performance.now()\n }\n };\n if ('_didRelatedGenesFirstPlot' in ideo) {\n delete ideo._didRelatedGenesFirstPlot;\n }\n}\n\nfunction getRelatedGenesByType() {\n const ideo = this;\n const relatedGenes = ideo.annotDescriptions.annots;\n\n const related = Object.values(relatedGenes).slice();\n\n const paralogous = related.filter(r => {\n return r.type && r.type.includes('paralogous');\n });\n const interacting = related.filter(r => {\n return r.type && r.type.includes('interacting gene');\n });\n const searched = Object.entries(relatedGenes).filter(entry => {\n return entry[1].type && entry[1].type.includes('searched gene');\n })[0][0];\n\n return {related, paralogous, interacting, searched};\n}\n\nfunction getRelatedGenesTooltipAnalytics(annot) {\n const ideo = this;\n\n const timeSincePrevTooltip = performance.now() - ideo.time.prevTooltipOff;\n const prevAnnotDomId = ideo.time.prevTooltipAnnotDomId;\n\n if (timeSincePrevTooltip < 300 && annot.domId === prevAnnotDomId) {\n return null;\n }\n\n const tooltipGene = annot.name;\n\n // e.g. \"interacting gene\" -> \"interacting\"\n const tooltipRelatedType =\n ideo.annotDescriptions.annots[annot.name].type.split(' ')[0];\n\n const countsByType = getCountsByType(ideo);\n\n const analytics = Object.assign(\n {tooltipGene, tooltipRelatedType}, countsByType\n );\n\n return analytics;\n}\n\n/** Compute granular related genes plotting analytics */\nfunction analyzePlotTimes(type, ideo) {\n // Paralogs and interacting genes:\n // http://localhost:8080/examples/vanilla/related-genes?q=RAD51\n //\n // No paralogs:\n // http://localhost:8080/examples/vanilla/related-genes?q=BRCA1&org=mus-musculus\n //\n // No interacting genes:\n // http://localhost:8080/examples/vanilla/related-genes?q=DMC1\n //\n // No paralogs, no interacting genes:\n // http://localhost:8080/examples/vanilla/related-genes?q=BRCA1&org=macaca-mulatta\n\n\n const otherTypes = {\n paralogous: 'interacting',\n interacting: 'paralogous'\n };\n const related = ideo.getRelatedGenesByType();\n const otherType = otherTypes[type];\n const numThisRelated = related[type].length;\n const numOtherRelated = related[otherType] ? related[otherType].length : 0;\n\n if (!ideo._didRelatedGenesFirstPlot) {\n // 1st of 2 attempted plot logs\n ideo._didRelatedGenesFirstPlot = true;\n\n ideo.time.rg.totalFirstPlot = timeDiff(ideo.time.rg.t0);\n\n if (numThisRelated > 0) {\n ideo.time.rg.timestampFirstPlot = performance.now();\n ideo._relatedGenesFirstPlotType = type;\n }\n } else {\n // 2nd of 2 attempted plot logs\n if (numThisRelated > 0 && numOtherRelated > 0) {\n // Paralogs and interacting genes were found, e.g. human RAD51\n const timestampFirstPlot = ideo.time.rg.timestampFirstPlot;\n ideo.time.rg.totalLastPlotDiff = timeDiff(timestampFirstPlot);\n } else if (numThisRelated > 0 && numOtherRelated === 0) {\n // Other attempt did not plot, and this did, so log this as 1st\n // Often seen when no interacting genes found, e.g. human DMC1\n ideo.time.rg.timestampFirstPlot = performance.now();\n ideo.time.rg.totalFirstPlot = timeDiff(ideo.time.rg.t0);\n ideo._relatedGenesFirstPlotType = type;\n ideo.time.rg.totalLastPlotDiff = 0;\n\n } else if (numThisRelated === 0 && numOtherRelated > 0) {\n // This attempt did not plot, the other did, so log 1st plot as also last\n // Often seen when no paralogs found, e.g. mouse BRCA1\n ideo.time.rg.totalLastPlotDiff = 0;\n } else {\n // No related genes found, so note only the searched gene is plotted\n // Example: Macaca mulatta BRCA1\n ideo._relatedGenesFirstPlotType = 'searched';\n ideo.time.rg.totalLastPlotDiff = 0;\n }\n }\n}\n\nfunction getCountsByType(ideo) {\n const related = ideo.getRelatedGenesByType();\n\n const numRelatedGenes = related['related'].length;\n const numParalogs = related['paralogous'].length;\n const numInteractingGenes = related['interacting'].length;\n const searchedGene = related['searched'];\n\n return {\n numRelatedGenes, numParalogs, numInteractingGenes, searchedGene\n };\n}\n\n/** Summarizes number and kind of related genes, performance, etc. */\nfunction analyzeRelatedGenes(ideo) {\n\n const countsByType = getCountsByType(ideo);\n\n const timeTotal = ideo.time.rg.total;\n const timeTotalFirstPlot = ideo.time.rg.totalFirstPlot;\n const timeTotalLastPlotDiff = ideo.time.rg.totalLastPlotDiff;\n const timeParalogs = ideo.time.rg.paralogs;\n const timeInteractingGenes = ideo.time.rg.interactions;\n const timeSearchedGene = ideo.time.rg.searchedGene;\n const firstPlotType = ideo._relatedGenesFirstPlotType;\n\n const analytics = Object.assign({\n firstPlotType,\n timeTotal, timeTotalFirstPlot, timeTotalLastPlotDiff,\n timeSearchedGene, timeInteractingGenes, timeParalogs\n }, countsByType);\n\n ideo.relatedGenesAnalytics = analytics;\n}\n\nexport {\n initAnalyzeRelatedGenes, analyzePlotTimes, analyzeRelatedGenes, timeDiff,\n getRelatedGenesByType, getRelatedGenesTooltipAnalytics\n};\n","/**\n * @fileoverview Fetch cached gene data: name, position, etc.\n *\n * Gene cache eliminates needing to fetch names and positions of genes from\n * third-party APIs at runtime. It achieves this by fetching a static file\n * containing gene data upon initializing Ideogram.\n *\n * Use cases:\n *\n * - test if a given string is a gene name, e.g. for gene search\n * - find genomic position of a given gene (or all genes)\n */\n\nimport {slug, getEarlyTaxid} from './lib';\nimport {organismMetadata} from './init/organism-metadata';\nimport version from './version';\n\nlet perfTimes;\n\n/** Get URL for gene cache file */\nfunction getCacheUrl(orgName, cacheDir, ideo) {\n const organism = slug(orgName);\n\n if (!cacheDir) {\n const splitDataDir = ideo.config.dataDir.split('/');\n const dataIndex = splitDataDir.indexOf('data');\n const baseDir = splitDataDir.slice(0, dataIndex).join('/') + '/data/';\n cacheDir = baseDir + 'cache/';\n }\n\n const cacheUrl = cacheDir + organism + '-genes.tsv';\n\n return cacheUrl;\n}\n\n/**\n * Convert pre-annotation arrays to annotation objects\n * sorted by genomic position.\n */\nfunction parseAnnots(preAnnots) {\n const chromosomes = {};\n\n for (let i = 0; i < preAnnots.length; i++) {\n const [chromosome, start, stop, ensemblId, gene] = preAnnots[i];\n\n if (!(chromosome in chromosomes)) {\n chromosomes[chromosome] = {chr: chromosome, annots: []};\n } else {\n const annot = {name: gene, start, stop, ensemblId};\n chromosomes[chromosome].annots.push(annot);\n }\n }\n\n const annotsSortedByPosition = {};\n\n Object.entries(chromosomes).forEach(([chr, annotsByChr]) => {\n annotsSortedByPosition[chr] = {\n chr,\n annots: annotsByChr.annots.sort((a, b) => a.start - b.start)\n };\n });\n\n return annotsSortedByPosition;\n}\n\n/**\n * Build full Ensembl ID from prefix (e.g. ENSG) and slim ID (e.g. 223972)\n *\n * Example output ID: ENSG00000223972\n * */\nfunction getEnsemblId(ensemblPrefix, slimEnsemblId) {\n\n // C. elegans (prefix: WBGene) has special IDs, e.g. WBGene00197333\n const padLength = ensemblPrefix === 'WBGene' ? 8 : 11;\n\n // Zero-pad the slim ID, e.g. 223972 -> 00000223972\n const zeroPaddedId = slimEnsemblId.padStart(padLength, '0');\n\n return ensemblPrefix + zeroPaddedId;\n}\n\n/** Parse a gene cache TSV file, return array of useful transforms */\nfunction parseCache(rawTsv, orgName) {\n const names = [];\n const nameCaseMap = {};\n const namesById = {};\n const idsByName = {};\n const lociByName = {};\n const lociById = {};\n const preAnnots = [];\n let ensemblPrefix;\n\n let t0 = performance.now();\n const lines = rawTsv.split(/\\r\\n|\\n/);\n perfTimes.rawTsvSplit = Math.round(performance.now() - t0);\n\n t0 = performance.now();\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === '') continue; // Skip empty lines\n if (line[0] === '#') {\n if (line.slice(0, 9) === '## prefix') {\n ensemblPrefix = line.split('prefix: ')[1];\n }\n continue;\n }\n const [\n chromosome, rawStart, rawLength, slimEnsemblId, gene\n ] = line.trim().split(/\\t/);\n const start = parseInt(rawStart);\n const stop = start + parseInt(rawLength);\n const ensemblId = getEnsemblId(ensemblPrefix, slimEnsemblId);\n preAnnots.push([chromosome, start, stop, ensemblId, gene]);\n const locus = [chromosome, start, stop];\n\n names.push(gene);\n nameCaseMap[gene.toLowerCase()] = gene;\n namesById[ensemblId] = gene;\n idsByName[gene] = ensemblId;\n lociByName[gene] = locus;\n lociById[ensemblId] = locus;\n };\n const t1 = performance.now();\n perfTimes.parseCacheLoop = Math.round(t1 - t0);\n\n const sortedAnnots = parseAnnots(preAnnots);\n perfTimes.parseAnnots = Math.round(performance.now() - t1);\n\n return [\n names, nameCaseMap, namesById, idsByName, lociByName, lociById,\n sortedAnnots\n ];\n}\n\n/** Get organism's metadata fields */\nfunction parseOrgMetadata(orgName) {\n const taxid = getEarlyTaxid(orgName);\n return organismMetadata[taxid];\n}\n\n/** Reports if current organism has a gene cache */\nfunction hasGeneCache(orgName) {\n const metadata = parseOrgMetadata(orgName);\n return (metadata.hasGeneCache && metadata.hasGeneCache === true);\n}\n\nasync function cacheFetch(url) {\n const response = await Ideogram.cache.match(url);\n if (typeof response === 'undefined') {\n // If cache miss, then fetch and add response to cache\n await Ideogram.cache.add(url);\n }\n return await Ideogram.cache.match(url);\n}\n\n/**\n * Fetch cached gene data, transform it usefully, and set it as ideo prop\n */\nexport default async function initGeneCache(orgName, ideo, cacheDir=null) {\n\n const startTime = performance.now();\n perfTimes = {};\n\n // Skip initialization if files needed to make cache don't exist\n if (!hasGeneCache(orgName)) return;\n\n // Skip initialization if cache is already populated\n if (Ideogram.geneCache && Ideogram.geneCache[orgName]) {\n // Simplify chief use case, i.e. for single organism\n ideo.geneCache = Ideogram.geneCache[orgName];\n return;\n }\n\n if (!Ideogram.geneCache) {\n Ideogram.geneCache = {};\n }\n\n Ideogram.cache = await caches.open(`ideogram-${version}`);\n\n const cacheUrl = getCacheUrl(orgName, cacheDir, ideo);\n\n const fetchStartTime = performance.now();\n const response = await cacheFetch(cacheUrl);\n const data = await response.text();\n const fetchEndTime = performance.now();\n perfTimes.fetch = Math.round(fetchEndTime - fetchStartTime);\n\n const [\n interestingNames, nameCaseMap, namesById, idsByName,\n lociByName, lociById, sortedAnnots\n ] = parseCache(data, orgName);\n perfTimes.parseCache = Math.round(performance.now() - fetchEndTime);\n\n ideo.geneCache = {\n interestingNames, // Array ordered by general or scholarly interest\n nameCaseMap, // Maps of lowercase gene names to proper gene names\n namesById,\n idsByName,\n lociByName, // Object of gene positions, keyed by gene name\n lociById,\n sortedAnnots // Ideogram annotations sorted by genomic position\n };\n Ideogram.geneCache[orgName] = ideo.geneCache;\n\n if (ideo.config.debug) {\n perfTimes.total = Math.round(performance.now() - startTime);\n console.log('perfTimes in initGeneCache:', perfTimes);\n }\n}\n","import {decompressSync, strFromU8} from 'fflate';\n\n// Definitions for ArrowHead values in WikiPathways GPML\n//\n// See also: https://discover.nci.nih.gov/mim/formal_mim_spec.pdf\nconst interactionArrowMap = {\n 'Arrow': ['acts on', 'acted on by'],\n 'TBar': ['inhibits', 'inhibited by'],\n 'mim-binding': ['binds', 'binds'],\n 'mim-catalysis': ['catalyzes', 'catalyzed by'],\n 'mim-cleavage': ['cleaves', 'cleaved by'],\n 'mim-conversion': ['converts', 'converted by'],\n // 'mim-covalent-bond': ['covalently binds',\n // 'mim-gap': 'MimGap',\n 'mim-inhibition': ['inhibits', 'inhibited by'],\n 'mim-modification': ['modifies', 'modified by'],\n 'mim-necessary-stimulation':\n ['necessarily stimulates', 'necessarily stimulated by'],\n 'mim-stimulation': ['stimulates', 'stimulated by'],\n 'mim-transcription-translation':\n ['transcribes / translates', 'transcribed / translated by']\n};\n\n// Which interactions types to show first, if showing multiple\nconst rankedInteractionTypes = [\n 'transcribe',\n 'cleave',\n 'convert',\n 'bind',\n 'modifie',\n 'catalyze',\n 'necessarily stimulate',\n 'inhibit',\n 'stimulate',\n 'act'\n];\n\nexport function sortInteractionTypes(a, b) {\n const ranks = {};\n for (let i = 0; i < rankedInteractionTypes.length; i++) {\n const rankedIxnType = rankedInteractionTypes[i];\n if (rankedIxnType.includes(a)) ranks.a = i;\n if (rankedIxnType.includes(b)) ranks.b = i;\n }\n return ranks.b - ranks.a;\n}\n\n/** Determine if all given interactions in *one* pathway are same */\nfunction determineIxnsInPathwayAreSame(ixns, ixnTypeReference) {\n let isRefMatch = true;\n let thisIsSame = true;\n\n if (ixns.length === 0) return {isRefMatch, thisIsSame};\n\n const thisIxnTypeReference = ixns[0].ixnType.toLowerCase();\n ixns.forEach(ixn => {\n const ixnType = ixn.ixnType.toLowerCase();\n if (ixnType !== ixnTypeReference) {\n isRefMatch = false;\n }\n if (ixnType !== thisIxnTypeReference) {\n thisIsSame = false;\n }\n });\n return {isRefMatch, thisIsSame};\n}\n\n/**\n * Return first valid interaction type from interactions-by-pathway object\n */\nfunction getIxnTypeReference(ixnsByPwid) {\n const ixnTypeReference = Object.values(ixnsByPwid).find(ixns => {\n return ixns.length > 0 && 'ixnType' in ixns[0];\n })[0].ixnType.toLowerCase();\n\n return ixnTypeReference;\n}\n\n/**\n * Determine whether all given interactions in all given pathways are the same\n */\nfunction setIsSame(enrichedIxns) {\n let isSame = true;\n const ixnsByPwid = enrichedIxns.ixnsByPwid;\n\n const ixnTypeReference = getIxnTypeReference(ixnsByPwid);\n\n Object.entries(ixnsByPwid).map(([pwid, ixns]) => {\n const {isRefMatch, thisIsSame} =\n determineIxnsInPathwayAreSame(ixns, ixnTypeReference);\n if (!thisIsSame || !isRefMatch) {\n isSame = false;\n }\n enrichedIxns.isSameByPwid[pwid] = thisIsSame;\n });\n enrichedIxns.isSame = isSame;\n\n return enrichedIxns;\n}\n\n/**\n * If interactions aren't all exactly the same, then they are often still\n * directionally equivalent.\n *\n * E.g. if gene A both \"modifies\" and \"converts\" gene B, then we can summarize\n * that as gene A \"acts on\" gene B, rather than completely reverting to saying\n * gene A \"interacts with\" gene B.\n *\n */\nfunction summarizeByDirection(enrichedIxns) {\n\n let isDirectionSame = true;\n\n const leftTypes = []; // \"Acts on\" types\n const rightTypes = []; // \"Acted on by\" types\n Object.values(interactionArrowMap).forEach(directedTypes => {\n rightTypes.push(directedTypes[0]);\n leftTypes.push(directedTypes[1]);\n });\n\n const right = 'Acts on';\n const left = 'Acted on by';\n\n const ixnsByPwid = enrichedIxns.ixnsByPwid;\n const firstIxnType = getIxnTypeReference(ixnsByPwid);\n const isRight = rightTypes.includes(firstIxnType);\n const directionReference = isRight ? right : left;\n\n Object.entries(ixnsByPwid).map(([pwid, ixns]) => {\n let isPwDirectionSame = true;\n if (ixns.length > 0) {\n const pwFirstIxnType = ixns[0].ixnType.toLowerCase();\n const pwIsRight = rightTypes.includes(pwFirstIxnType);\n const pwDirectionReference = pwIsRight ? right : left;\n ixns.forEach(ixn => {\n const ixnType = ixn.ixnType.toLowerCase();\n const thisIsRight = rightTypes.includes(ixnType);\n const direction = thisIsRight ? right : left;\n enrichedIxns.directionsByPwid[pwid] = direction;\n if (direction !== directionReference) {\n isDirectionSame = false;\n }\n if (direction !== pwDirectionReference) {\n isPwDirectionSame = false;\n }\n });\n }\n enrichedIxns.isDirectionSameByPwid[pwid] = isPwDirectionSame;\n\n });\n\n enrichedIxns.isDirectionSame = isDirectionSame;\n if (isDirectionSame === true) {\n enrichedIxns.direction = directionReference;\n }\n\n return enrichedIxns;\n}\n\n/**\n * Summarize interactions by direction\n *\n * @param {String} gene Interacting gene\n * @param {Array} pathwayIds List of WikiPathways IDs\n * @param {Object} ideo Ideogram instance object\n * @returns\n */\nexport function summarizeInteractions(gene, pathwayIds, ideo) {\n let summary = null;\n\n const ixnsByPwid = detailAllInteractions(gene, pathwayIds, ideo);\n\n console.log('gene')\n console.log(gene)\n console.log('ixnsByPwid')\n console.log(ixnsByPwid)\n console.log('pathwayIds')\n console.log(pathwayIds)\n console.log('pathwayIds[0]')\n console.log(pathwayIds[0])\n\n const ixns = ixnsByPwid[pathwayIds[0]];\n\n if (ixns.length > 0) {\n let enrichedIxns = {\n ixnsByPwid,\n isSameByPwid: {}, // If pathway has all same interaction types\n isSame: null, // If above is true for all pathways\n isDirectionSameByPwid: {}, // If pathway has same ixn direction\n isDirectionSame: null, // If above is true for all pathways\n directionsByPwid: {}\n };\n enrichedIxns = setIsSame(enrichedIxns);\n\n if (enrichedIxns.isSame) {\n const ixnType = ixns[0].ixnType;\n const newIxn = ixnType;\n summary = newIxn;\n } else {\n\n enrichedIxns = summarizeByDirection(enrichedIxns);\n\n if (enrichedIxns.isDirectionSame) {\n summary = enrichedIxns.direction;\n } else {\n summary = 'Interacts with';\n }\n }\n }\n\n\n // if (direction !== null) {\n // summary = direction;\n // }\n // const pwidsByIxnType = {};\n // Object.entries(ixns).map(([k, v]) => {\n // if (!pwidsByIxnType[v.ixnType]) {\n // pwidsByIxnType[v.ixnType] = [v.pathwayId];\n // } else {\n // pwidsByIxnType[v.ixnType].push([v.pathwayId]);\n // }\n // });\n\n // console.log('pwidsByIxnType')\n // console.log(pwidsByIxnType)\n // const tpArray = Object.entries(pwidsByIxnType);\n // const sortedIndices = sortInteractionTypes(tpArray.map(tp => tp[0]));\n // const sortedTpArray =\n // sortedIndices.map(sortedIndex => tpArray[sortedIndex]);\n\n // console.log('sortedTpArray')\n // console.log(sortedTpArray)\n return summary;\n}\n\n/**\n * Get detailInteractions results for multiple pathways\n *\n * @param gene Interacting gene\n * @param pathwayIds List of WikiPathways IDs\n * @ideo ideo Ideogram instance object\n */\nexport function detailAllInteractions(gene, pathwayIds, ideo) {\n const ixnsByPwid = {};\n\n pathwayIds.map(pathwayId => {\n const ixns = detailInteractions(gene, pathwayId, ideo);\n\n ixnsByPwid[pathwayId] = ixns;\n });\n return ixnsByPwid;\n}\n\n/** Get IDs and data element objects for searched or interacting gene */\nfunction getMatches(gpml, label) {\n\n const nodes = Array.from(gpml.querySelectorAll(\n `DataNode[TextLabel=\"${label}\"]`\n ));\n\n const genes = nodes.map(node => {\n return {\n type: 'node',\n matchedLabel: label,\n textLabel: node.getAttribute('TextLabel'),\n graphId: node.getAttribute('GraphId'),\n groupRef: node.getAttribute('GroupRef')\n };\n });\n\n // Get group identifiers\n const geneGraphIds = genes.map(g => g.graphId);\n const geneGroupRefs = genes.map(g => g.groupRef);\n const groupSelectors =\n geneGroupRefs.map(ggr => `Group[GroupId=\"${ggr}\"]`).join(',');\n\n let geneGroups = [];\n if (groupSelectors !== '') {\n const groups = gpml.querySelectorAll(groupSelectors);\n geneGroups = Array.from(groups).map(group => {\n return {\n type: 'group',\n matchedLabel: label,\n graphId: group.getAttribute('GraphId'),\n groupId: group.getAttribute('GroupId')\n };\n });\n }\n\n const geneGroupGraphIds = geneGroups.map(g => g.graphId);\n const matchingGraphIds = geneGraphIds.concat(geneGroupGraphIds);\n\n const elements = genes.concat(geneGroups);\n\n return [matchingGraphIds, elements];\n}\n\n/**\n * Request compressed GPML files, which contain detailed interaction data, e.g.\n * https://cdn.jsdelivr.net/npm/ixn/WP3982.xml.gz\n *\n * For more easily readable versions, see also:\n * - https://www.wikipathways.org/index.php?title=Pathway:WP3982&action=edit\n * - https://www.wikipathways.org//wpi/wpi.php?action=downloadFile&type=gpml&pwTitle=Pathway:WP3982\n *\n * GPML (Graphical Pathway Markup Language) data encodes detailed interaction\n * data for biochemical pathways.\n */\nexport function fetchGpmls(ideo) {\n\n const pathwayIdsByInteractingGene = {};\n Object.entries(ideo.annotDescriptions.annots)\n .forEach(([annotName, descObj]) => {\n if ('type' in descObj && descObj.type.includes('interacting gene')) {\n pathwayIdsByInteractingGene[annotName] = descObj.pathwayIds;\n }\n });\n\n const gpmlsByInteractingGene = {};\n Object.entries(pathwayIdsByInteractingGene)\n .forEach(([ixnGene, pathwayIds]) => {\n gpmlsByInteractingGene[ixnGene] = {};\n pathwayIds.map(async pathwayId => {\n const pathwayFile = `${pathwayId}.xml.gz`;\n const gpmlUrl = `https://cdn.jsdelivr.net/npm/ixn2/${pathwayFile}`;\n const response = await fetch(gpmlUrl);\n const blob = await response.blob();\n const uint8Array = new Uint8Array(await blob.arrayBuffer());\n const rawGpml = strFromU8(decompressSync(uint8Array));\n\n const gpml = new DOMParser().parseFromString(rawGpml, 'text/xml');\n\n gpmlsByInteractingGene[ixnGene][pathwayId] = gpml;\n });\n });\n\n ideo.gpmlsByInteractingGene = gpmlsByInteractingGene;\n}\n\n/**\n * Get interaction object from a GPML graphics XML element\n *\n * This interaction object connects the searched gene and interacting gene.\n */\nfunction parseInteractionGraphic(graphic, graphIds) {\n let interaction = null;\n\n const {searchedGeneGraphIds, matchingGraphIds} = graphIds;\n\n const endGraphRefs = [];\n let numMatchingPoints = 0;\n let isConnectedToSourceGene = false;\n let ixnType = null;\n let searchedGeneIndex = null;\n\n Array.from(graphic.children).forEach(child => {\n if (child.nodeName !== 'Point') return;\n const point = child;\n const graphRef = point.getAttribute('GraphRef');\n if (graphRef === null) return;\n\n if (matchingGraphIds.includes(graphRef)) {\n numMatchingPoints += 1;\n endGraphRefs.push(graphRef);\n\n if (searchedGeneGraphIds.includes(graphRef)) {\n isConnectedToSourceGene = true;\n }\n\n if (point.getAttribute('ArrowHead')) {\n const arrowHead = point.getAttribute('ArrowHead');\n const isStart = searchedGeneGraphIds.includes(graphRef);\n if (searchedGeneIndex === null) {\n searchedGeneIndex = isStart ? 0 : 1;\n }\n ixnType = interactionArrowMap[arrowHead][isStart ? 0 : 1];\n }\n }\n });\n\n if (numMatchingPoints >= 2 && isConnectedToSourceGene) {\n if (searchedGeneIndex === null) {\n ixnType = 'interacts with';\n }\n ixnType = ixnType[0].toUpperCase() + ixnType.slice(1);\n const interactionGraphId = graphic.parentNode.getAttribute('GraphId');\n interaction = {\n 'interactionId': interactionGraphId,\n 'endIds': endGraphRefs,\n ixnType\n };\n }\n\n return interaction;\n}\n\n/**\n * Fetch GPML for pathway and find ID of Interaction between two genes,\n * and the ID of the two DataNodes for each of those interactions.\n *\n * WikiPathways SVG isn't detailed enough to reliably determine the specific\n * interaction elements relating two genes, given only the gene symbols. This\n * fetches augmented GPML data for the pathway, and queries it to get only\n * interactions between the two genes.\n */\nexport function detailInteractions(interactingGene, pathwayId, ideo) {\n\n // Get pathway's GPML, which contains detailed interaction data\n const gpml = ideo.gpmlsByInteractingGene[interactingGene][pathwayId];\n\n // Get symbol of the searched gene, e.g. \"PTEN\"\n const searchedGene =\n Object.entries(ideo.annotDescriptions.annots)\n .find(([k, v]) => v.type === 'searched gene')[0];\n\n // Gets IDs and elements for searched gene and interacting gene, and,\n // if they're in any groups, the IDs of those groups\n const [searchedGeneGraphIds, se] = getMatches(gpml, searchedGene);\n const [interactingGeneGraphIds, ie] = getMatches(gpml, interactingGene);\n\n const elements = {\n searchedGene: se,\n interactingGene: ie\n };\n\n const matchingGraphIds =\n searchedGeneGraphIds.concat(interactingGeneGraphIds);\n const graphIds = {searchedGeneGraphIds, matchingGraphIds};\n\n // Get interaction objects that connect the searched and interacting genes\n const interactions = [];\n const graphicsXml = gpml.querySelectorAll('Interaction Graphics');\n Array.from(graphicsXml).forEach(graphic => {\n const interaction = parseInteractionGraphic(graphic, graphIds);\n if (interaction !== null) {\n interaction.elements = elements;\n interactions.push(interaction);\n }\n });\n\n return interactions;\n}\n\n// export async function fetchInteractionDiagram(annot, descObj, ideo) {\n// // Fetch raw SVG for pathway diagram\n// const pathwayId = descObj.pathwayIds[0];\n// // const baseUrl = 'https://eweitz.github.io/cachome/wikipathways/';\n// const baseUrl = 'https://cachome.github.io/wikipathways/';\n// // const baseUrl = 'http://localhost/wikipathways/data/';\n// const diagramUrl = baseUrl + pathwayId + '.svg';\n// const response = await fetch(diagramUrl);\n// if (response.ok) {\n\n// // console.log('searchedGene', searchedGene)\n\n// const ixns = await detailInteractions(annot.name, pathwayId, ideo);\n\n// let selectors = `[name=${annot.name}]`;\n// let searchedGeneIndex = 0;\n// let interactingGeneIndex;\n// if (ixns.length > 0) {\n// selectors = ixns[0].endIds.map(id => '#' + id).join(',');\n// searchedGeneIndex = ixns[0].searchedGeneIndex;\n// interactingGeneIndex = (searchedGeneIndex === 0) ? 1 : 0;\n// }\n// // https://webservice.wikipathways.org/findInteractions?query=ACE2&format=json\n\n// const rawDiagram = await response.text();\n\n// const pathwayDiagram =\n// `${rawDiagram}
`;\n\n// annot.displayName += pathwayDiagram;\n\n// document.querySelector('#_ideogramTooltip').innerHTML =\n// annot.displayName;\n\n// Ideogram.d3.select('svg.Diagram')\n// .attr('width', 350)\n// .attr('height', 300);\n\n// const viewport = document.querySelector('.svg-pan-zoom_viewport');\n// viewport.removeAttribute('style');\n// viewport.removeAttribute('transform');\n\n// const matches = document.querySelectorAll(selectors);\n// console.log('matches', matches)\n// const match0 = matches[searchedGeneIndex]\n// const m0 = match0.getCTM();\n// const m0Rect = match0.getBoundingClientRect();\n// const m0Box = match0.getBBox();\n// const m0MinX = m0.e/m0.a;\n// const m0MinY = m0.f/m0.d;\n\n// let minX = m0MinX;\n// let minY = m0MinY;\n// let width;\n// let height;\n\n// width = 350;\n// height = 300;\n\n// // matches[0].children[0].setAttribute('fill', '#F55');\n// match0.children[0].style.fill = '#f55';\n\n// if (matches.length > 1) {\n// // console.log('matches.length > 1')\n// // matches[1].children[0].setAttribute('fill', '#C4C');\n// const match1 = matches[interactingGeneIndex];\n// // console.log('match1')\n// // console.log(match1)\n// match1.children[0].style.fill = '#c4c';\n// const m1 = matches[1].getCTM();\n// const m1Rect = matches[1].getBoundingClientRect();\n// const m1Box = matches[1].getBBox();\n// console.log('m0', m0)\n// console.log('m1', m1)\n// const m1MinX = m1.e/m1.a;\n// const m1MinY = m1.f/m1.d;\n// // const m1MinX = m1.e/m1.a + m1Rect.width;\n// // const m1MinY = m1.f/m1.d - m1Rect.height;\n// if (m1MinX < m0MinX) minX = m1MinX;\n// if (m1MinY < m0MinY) minY = m1MinY;\n\n// let pairWidth = 0;\n// if (m0Rect.left < m1Rect.left) {\n// // pairWidth = m1Rect.right - m0Rect.left;\n// width += m1Box.width + 40;\n// }\n\n// // width += pairWidth;\n\n// // console.log('m0Rect', m0Rect)\n// // console.log('m1Rect', m1Rect)\n// // console.log('m1MinX', m1MinX)\n// // console.log('m0MinX', m0MinX)\n// // console.log('m1MinY', m1MinY)\n// // console.log('m0MinY', m0MinY)\n// // console.log('pairWidth', pairWidth)\n// // console.log('width', width)\n// // width += Math.abs(m1MinX - m0MinX);\n// // height += Math.abs(m1MinY - m0MinY);\n\n// // minX -= 100;\n// // minY -= 100;\n\n// // minX -= 150;\n// // minY -= 150;\n// } else {\n// minX -= 150;\n// minY -= 150;\n// }\n\n// minX = Math.round(minX);\n// minY = Math.round(minY);\n// width = Math.round(width);\n// height = Math.round(height);\n\n// const viewBox = `${minX} ${minY} ${width} ${height}`;\n// console.log('viewBox', viewBox);\n// document.querySelector('svg.Diagram').setAttribute('viewBox', viewBox);\n\n// }\n// }\n","/**\n * @fileoverview Kit used in \"Related genes\" example\n *\n * This file simplifies client code for reusing a \"related genes\" ideogram --\n * which finds and displays related genes for a searched gene.\n *\n * Related genes here are either \"interacting genes\" or \"paralogs\".\n * Interacting genes are genes immediately upstream or downstream of the\n * searched gene in a biochemical pathway. Paralogs are evolutionarily\n * similar genes in the same species.\n *\n * Data sources:\n * - Interacting genes: WikiPathways\n * - Paralogs: Ensembl\n * - Genomic coordinates: Ensembl, via MyGene.info\n *\n * Features provided by this module help users discover and explore genes\n * related to their gene of interest.\n *\n * The reference implementation is available at:\n * https://eweitz.github.io/ideogram/related-genes\n */\n\nimport {decompressSync, strFromU8} from 'fflate';\n\nimport {\n initAnalyzeRelatedGenes, analyzePlotTimes, analyzeRelatedGenes, timeDiff,\n getRelatedGenesByType, getRelatedGenesTooltipAnalytics\n} from './analyze-related-genes';\n\nimport {writeLegend} from '../annotations/legend';\nimport {getAnnotDomId} from '../annotations/process';\nimport {applyRankCutoff, sortAnnotsByRank} from '../annotations/labels';\nimport {getDir} from '../lib';\nimport initGeneCache from '../gene-cache';\nimport {fetchGpmls, summarizeInteractions} from './wikipathways';\n\n/** Sets DOM IDs for ideo.relatedAnnots; needed to associate labels */\nfunction setRelatedAnnotDomIds(ideo) {\n const updated = [];\n\n const sortedChrNames = ideo.chromosomesArray.map((chr) => {\n return chr.name;\n });\n\n // Arrange related annots by chromosome\n const annotsByChr = {};\n ideo.relatedAnnots.forEach((annot) => {\n if (annot.chr in annotsByChr) {\n annotsByChr[annot.chr].push(annot);\n } else {\n annotsByChr[annot.chr] = [annot];\n }\n });\n\n // Sort related annots by relevance within each chromosome\n const relevanceSortedAnnotsNamesByChr = {};\n Object.entries(annotsByChr).map(([chr, annots]) => {\n\n // Reverse-sort, so first annots are drawn last, and thus at top layer\n annots.sort((a, b) => ideo.annotSortFunction(a, b));\n\n const annotNames = annots.map((annot) => annot.name).reverse();\n relevanceSortedAnnotsNamesByChr[chr] = annotNames;\n });\n\n ideo.relatedAnnots.forEach((annot) => {\n const chr = annot.chr;\n\n // Annots have DOM IDs keyed by chromosome index and annotation index.\n // We reconstruct those here using structures built in two blocks above.\n const chrIndex = sortedChrNames.indexOf(chr);\n const annotIndex =\n relevanceSortedAnnotsNamesByChr[chr].indexOf(annot.name);\n\n annot.domId = getAnnotDomId(chrIndex, annotIndex);\n updated.push(annot);\n });\n\n ideo.relatedAnnots = updated;\n}\n\n/**\n * Determines if interaction node might be a gene\n *\n * Some interaction nodes are biological processes; this filters out many.\n * Filtering these out makes downstream queries faster.\n *\n * ixn {Object} Interaction from WikiPathways\n * gene {Object} Gene from MyGene.info\n */\nfunction maybeGeneSymbol(ixn, gene) {\n return (\n ixn !== '' &&\n !ixn.includes(' ') &&\n !ixn.includes('/') && // e.g. Akt/PKB\n ixn.toLowerCase() !== gene.name.toLowerCase()\n );\n}\n\n// /** Helpful for debugging race conditions caused by concurrency */\n// const sleep = (delay) => {\n// new Promise((resolve) => setTimeout(resolve, delay));\n// }\n\n/** Reports if interaction node is a gene and not previously seen */\nfunction isInteractionRelevant(rawIxn, gene, nameId, seenNameIds, ideo) {\n let isGeneSymbol;\n if ('geneCache' in ideo && gene.name) {\n isGeneSymbol = rawIxn.toLowerCase() in ideo.geneCache.nameCaseMap;\n } else {\n isGeneSymbol = maybeGeneSymbol(rawIxn, gene);\n }\n\n return isGeneSymbol && !(nameId in seenNameIds);\n}\n\n/**\n * Retrieves interacting genes from WikiPathways API\n *\n * Docs:\n * https://webservice.wikipathways.org/ui/\n * https://www.wikipathways.org/index.php/Help:WikiPathways_Webservice/API\n *\n * Examples:\n * https://webservice.wikipathways.org/findInteractions?query=ACE2&format=json\n * https://webservice.wikipathways.org/findInteractions?query=RAD51&format=json\n */\nasync function fetchInteractions(gene, ideo) {\n const ixns = {};\n const seenNameIds = {};\n const orgNameSimple = ideo.config.organism.replace(/-/g, ' ');\n const upperGene = gene.name.toUpperCase();\n // const queryString = `?query=${gene.name}&format=json`;\n // const url =\n // `https://webservice.wikipathways.org/findInteractions${queryString}`;\n // const url = `http://localhost:8080/dist/data/cache/${gene.name}.json.gz`;\n const url = `https://cdn.jsdelivr.net/npm/ixn2/${upperGene}.json.gz`;\n\n // await sleep(3000);\n\n const response = await fetch(url);\n // const data = await response.json();\n\n let data = {result: []};\n\n if (response.ok) {\n const blob = await response.blob();\n const uint8Array = new Uint8Array(await blob.arrayBuffer());\n data = JSON.parse(strFromU8(decompressSync(uint8Array)));\n }\n\n console.log('data')\n console.log(data)\n\n // For each interaction, get nodes immediately upstream and downstream.\n // Filter out pathway nodes that are definitely not gene symbols, then\n // group pathways by gene symbol. Each interacting gene can have\n // multiple pathways.\n data.result.forEach(interaction => {\n if (interaction.species.toLowerCase() === orgNameSimple) {\n const right = interaction.fields.right.values;\n const left = interaction.fields.left.values;\n // let mediator = [];\n // if ('mediator' in interaction.fields) {\n // mediator = interaction.fields.mediator.values;\n // console.log('mediator', mediator)\n // }\n // const rawIxns = right.concat(left, mediator);\n const rawIxns = right.concat(left);\n const name = interaction.name;\n const id = interaction.id;\n\n // rawIxns can contain multiple genes, e.g. when\n // a group (i.e. a complex or a set of paralogs)\n // interacts with the searched gene\n const wrappedRawIxns = rawIxns.map(rawIxn => {\n return {name: rawIxn, color: ''};\n });\n const sortedRawIxns =\n sortAnnotsByRank(wrappedRawIxns, ideo).map(i => i.name);\n\n console.log('sortedRawIxns')\n console.log(sortedRawIxns)\n\n sortedRawIxns.forEach(rawIxn => {\n\n // Prevent overwriting searched gene. Occurs with e.g. human CD4\n if (rawIxn.includes(gene.name)) return;\n\n // if (rawIxn === '') return; // Avoid oddly blank placeholders\n\n const nameId = name + id;\n\n const isRelevant =\n isInteractionRelevant(rawIxn, gene, nameId, seenNameIds, ideo);\n\n if (isRelevant) {\n console.log('id, rawIxn', id, rawIxn)\n seenNameIds[nameId] = 1;\n const ixn = {name, pathwayId: id};\n if (rawIxn in ixns) {\n ixns[rawIxn].push(ixn);\n } else {\n ixns[rawIxn] = [ixn];\n }\n }\n });\n }\n });\n\n return ixns;\n}\n\n/**\n * Queries MyGene.info API, returns parsed JSON\n *\n * Docs:\n * https://docs.mygene.info/en/v3/\n *\n * Example:\n * https://mygene.info/v3/query?q=symbol:cdk2%20OR%20symbol:brca1&species=9606&fields=symbol,genomic_pos,name\n */\nasync function fetchMyGeneInfo(queryString) {\n const myGeneBase = 'https://mygene.info/v3/query';\n const response = await fetch(myGeneBase + queryString + '&size=400');\n const data = await response.json();\n return data;\n}\n\nfunction parseNameAndEnsemblIdFromMgiGene(gene) {\n const name = gene.name;\n const id = gene.genomic_pos.ensemblgene;\n let ensemblId = id;\n if (typeof id === 'undefined') {\n // Encountered in AKT3, when querying related genes for MTOR\n // A 'chr'omosome value containing _ indicates an alt loci scaffold,\n // so ignore that and take the Ensembl ID associated with the\n // first position of a primary chromosome.\n ensemblId =\n gene.genomic_pos.filter(pos => !pos.chr.includes('_'))[0].ensemblgene;\n }\n return {name, ensemblId};\n}\n\n/**\n * Summarizes interactions for a gene\n *\n * This comprises most of the content for tooltips for interacting genes.\n */\nfunction describeInteractions(gene, ixns, searchedGene) {\n const pathwayIds = [];\n const pathwayNames = [];\n let ixnsDescription = '';\n console.log('gene', gene)\n\n console.log('ixns', ixns)\n if (typeof ixns !== 'undefined') {\n // ixns is undefined when querying e.g. CDKN1B in human\n const pathwaysBase = 'https://www.wikipathways.org/index.php/Pathway:';\n const links = ixns.map(ixn => {\n const url = `${pathwaysBase}${ixn.pathwayId}`;\n pathwayIds.push(ixn.pathwayId);\n pathwayNames.push(ixn.name);\n return `${ixn.name}`;\n }).join('
');\n\n ixnsDescription =\n `Interacts with ${searchedGene.name} in:
${links}`;\n }\n\n const {name, ensemblId} = parseNameAndEnsemblIdFromMgiGene(gene);\n const type = 'interacting gene';\n const descriptionObj = {\n description: ixnsDescription,\n ixnsDescription, ensemblId, name, type, pathwayIds, pathwayNames\n };\n return descriptionObj;\n}\n\n/** Throw error when searched gene (e.g. \"Foo\") isn't found */\nfunction throwGeneNotFound(geneSymbol, ideo) {\n const organism = ideo.organismScientificName;\n throw Error(`\"${geneSymbol}\" is not a known gene in ${organism}`);\n}\n\n/**\n * Fetch genes from cache\n * Construct objects that match format of MyGene.info API response\n */\nfunction fetchGenesFromCache(names, type, ideo) {\n const cache = ideo.geneCache;\n const isSymbol = (type === 'symbol');\n const locusMap = isSymbol ? cache.lociByName : cache.lociById;\n const nameMap = isSymbol ? cache.idsByName : cache.namesById;\n\n const hits = names.map(name => {\n\n const nameLc = name.toLowerCase();\n\n if (!locusMap[name] && !cache.nameCaseMap[nameLc]) {\n if (isSymbol) {\n throwGeneNotFound(name, ideo);\n } else {\n return;\n }\n }\n\n // Canonicalize name if it is mistaken in upstream data source.\n // This can sometimes happen in WikiPathways, e.g. when searching\n // interactions for rat Pten, it includes a result for \"PIK3CA\".\n // In that case, this would correct PIK3CA to be Pik3ca.\n if (isSymbol && !locusMap[name] && cache.nameCaseMap[nameLc]) {\n name = cache.nameCaseMap[nameLc];\n }\n\n const locus = locusMap[name];\n const symbol = isSymbol ? name : nameMap[name];\n const ensemblId = isSymbol ? nameMap[name] : name;\n\n const hit = {\n symbol,\n name: '',\n source: 'cache',\n genomic_pos: {\n chr: locus[0],\n start: locus[1],\n end: locus[2],\n ensemblgene: ensemblId\n }\n };\n\n return hit;\n });\n\n const hitsWithGenomicPos = hits.filter(hit => hit !== undefined);\n\n return hitsWithGenomicPos;\n}\n\n/** Fetch genes from cache, or, if needed, from MyGene.info API */\nasync function fetchGenes(names, type, ideo) {\n\n let data;\n\n // Account for single-gene fetch\n if (typeof names === 'string') names = [names];\n\n // Query parameter for MyGene.info API\n const qParam = names.map(name => `${type}:${name.trim()}`).join(' OR ');\n const taxid = ideo.config.taxid;\n\n const queryStringBase = `?q=${qParam}&species=${taxid}&fields=`;\n\n if (ideo.geneCache) {\n const hits = fetchGenesFromCache(names, type, ideo);\n\n // Asynchronously fetch full name, but don't await the response, because\n // full names are only shown upon hovering over an annotation.\n const queryString = `${queryStringBase}symbol,name`;\n data = fetchMyGeneInfo(queryString).then(data => {\n data.hits.forEach((hit) => {\n const symbol = hit.symbol;\n const fullName = hit.name;\n if (symbol in ideo.annotDescriptions.annots) {\n ideo.annotDescriptions.annots[symbol].name = fullName;\n } else {\n ideo.annotDescriptions.annots[symbol] = {name: fullName};\n }\n });\n });\n\n data = {hits, fromGeneCache: true};\n } else {\n // Fetch gene data from MyGene.info\n const queryString = `${queryStringBase}symbol,genomic_pos,name`;\n data = await fetchMyGeneInfo(queryString);\n }\n\n return data;\n}\n\n/**\n * Retrieves position and other data on interacting genes from MyGene.info\n */\nasync function fetchInteractionAnnots(interactions, searchedGene, ideo) {\n\n const annots = [];\n const symbols = Object.keys(interactions);\n\n if (symbols.length === 0) return annots;\n\n const data = await fetchGenes(symbols, 'symbol', ideo);\n\n data.hits.forEach(gene => {\n // If hit lacks position\n // or is same as searched gene (e.g. search for human SRC),\n // then skip processing\n if (\n 'genomic_pos' in gene === false ||\n gene.symbol === searchedGene.name\n ) {\n return;\n }\n\n const annot = parseAnnotFromMgiGene(gene, ideo, 'purple');\n annots.push(annot);\n\n const ixns = interactions[gene.symbol];\n\n const descriptionObj = describeInteractions(gene, ixns, searchedGene);\n\n mergeDescriptions(annot, descriptionObj, ideo);\n });\n\n // Fetch GPML files to use when updating interaction descriptions with\n // refined direction.\n fetchGpmls(ideo);\n\n return annots;\n}\n\n/** Fetch paralog positions from MyGeneInfo */\nasync function fetchParalogPositionsFromMyGeneInfo(\n homologs, searchedGene, ideo\n) {\n const annots = [];\n const ensemblIds = homologs.map(homolog => homolog.id);\n\n const data = await fetchGenes(ensemblIds, 'ensemblgene', ideo);\n\n data.hits.forEach(gene => {\n\n // If hit lacks position, skip processing\n if ('genomic_pos' in gene === false) return;\n if ('name' in gene === false) return;\n\n const annot = parseAnnotFromMgiGene(gene, ideo, 'pink');\n annots.push(annot);\n\n const description = `Paralog of ${searchedGene.name}`;\n const {name, ensemblId} = parseNameAndEnsemblIdFromMgiGene(gene);\n const type = 'paralogous gene';\n const descriptionObj = {description, ensemblId, name, type};\n mergeDescriptions(annot, descriptionObj, ideo);\n });\n\n return annots;\n}\n\n/**\n * Fetch paralogs of searched gene\n */\nasync function fetchParalogs(annot, ideo) {\n const taxid = ideo.config.taxid;\n\n // Fetch paralogs\n const params = `&format=condensed&type=paralogues&target_taxon=${taxid}`;\n const path = `/homology/id/${annot.id}?${params}`;\n const ensemblHomologs = await Ideogram.fetchEnsembl(path);\n const homologs = ensemblHomologs.data[0].homologies;\n\n // Fetch positions of paralogs\n let annots =\n await fetchParalogPositionsFromMyGeneInfo(homologs, annot, ideo);\n\n // Omit genes named like \"AC113554.1\", which is an \"accession.version\".\n // Such accVers are raw and poorly suited here.\n annots = annots.filter(annot => {\n const isAccVer = annot.name.match(/^AC[0-9.]+$/);\n return !isAccVer;\n });\n\n return annots;\n}\n\n/**\n * Filters out placements on alternative loci scaffolds, an advanced\n * genome assembly feature we are not concerned with in ideograms.\n *\n * Example:\n * https://mygene.info/v3/query?q=symbol:PTPRC&species=9606&fields=symbol,genomic_pos,name\n */\nfunction getGenomicPos(gene, ideo) {\n let genomicPos = null;\n if (Array.isArray(gene.genomic_pos)) {\n genomicPos = gene.genomic_pos.filter(pos => {\n return pos.chr in ideo.chromosomes[ideo.config.taxid];\n })[0];\n } else {\n genomicPos = gene.genomic_pos;\n }\n return genomicPos;\n}\n\n/**\n * Transforms MyGene.info (MGI) gene into Ideogram annotation\n */\nfunction parseAnnotFromMgiGene(gene, ideo, color='red') {\n const genomicPos = getGenomicPos(gene, ideo);\n\n const annot = {\n name: gene.symbol,\n chr: genomicPos.chr,\n start: genomicPos.start,\n stop: genomicPos.end,\n id: genomicPos.ensemblgene,\n color\n };\n\n return annot;\n}\n\nfunction moveLegend() {\n const ideoInnerDom = document.querySelector('#_ideogramInnerWrap');\n const decorPad = setRelatedDecorPad({}).legendPad;\n const left = decorPad + 20;\n const legendStyle = `position: absolute; top: 15px; left: ${left}px`;\n const legend = document.querySelector('#_ideogramLegend');\n ideoInnerDom.prepend(legend);\n legend.style = legendStyle;\n}\n\n/** Filter annotations to only include those in configured list */\nfunction applyAnnotsIncludeList(annots, ideo) {\n\n if (ideo.config.annotsInList === 'all') return annots;\n\n const includedAnnots = [];\n annots.forEach(annot => {\n if (ideo.config.annotsInList.includes(annot.name.toLowerCase())) {\n includedAnnots.push(annot);\n }\n });\n return includedAnnots;\n}\n\n/** Fetch and draw interacting genes, return Promise for annots */\nfunction processInteractions(annot, ideo) {\n return new Promise(async (resolve) => {\n const t0 = performance.now();\n\n const interactions = await fetchInteractions(annot, ideo);\n const annots = await fetchInteractionAnnots(interactions, annot, ideo);\n\n ideo.relatedAnnots.push(...annots);\n finishPlotRelatedGenes('interacting', ideo);\n\n ideo.time.rg.interactions = timeDiff(t0);\n\n resolve();\n });\n}\n\n/** Find and draw paralogs, return Promise for annots */\nfunction processParalogs(annot, ideo) {\n return new Promise(async (resolve) => {\n const t0 = performance.now();\n\n const annots = await fetchParalogs(annot, ideo);\n ideo.relatedAnnots.push(...annots);\n finishPlotRelatedGenes('paralogous', ideo);\n\n ideo.time.rg.paralogs = timeDiff(t0);\n\n resolve();\n });\n}\n\n/**\n * Sorts gene names consistently.\n *\n * Might also loosely rank by first-discovered or most prominent\n */\nfunction sortGeneNames(aName, bName) {\n // Rank shorter names above longer names\n if (bName.length !== aName.length) return bName.length - aName.length;\n\n // Rank names of equal length alphabetically\n return [aName, bName].sort().indexOf(aName) === 0 ? 1 : -1;\n}\n\n/** Sorts by relevance of related status */\nfunction sortAnnotsByRelatedStatus(a, b) {\n var aName, bName, aColor, bColor;\n if ('name' in a) {\n // Locally processed annotations\n aName = a.name;\n bName = b.name;\n aColor = a.color;\n bColor = b.color;\n } else {\n // Raw annotations\n [aName, aColor] = [a[0], a[3]];\n [bName, bColor] = [b[0], b[3]];\n }\n\n // Rank red (searched gene) highest\n if (aColor === 'red') return -1;\n if (bColor === 'red') return 1;\n\n // Rank purple (interacting gene) above red (paralogous gene)\n if (aColor === 'purple' && bColor === 'pink') return -1;\n if (bColor === 'purple' && aColor === 'pink') return 1;\n\n return sortGeneNames(aName, bName);\n}\n\nfunction mergeDescriptions(annot, desc, ideo) {\n let mergedDesc;\n const descriptions = ideo.annotDescriptions.annots;\n if (annot.name in descriptions) {\n const otherDesc = descriptions[annot.name];\n mergedDesc = desc;\n if (desc.type === otherDesc.type) return;\n Object.keys(otherDesc).forEach(function(key) {\n if (key in mergedDesc === false) {\n mergedDesc[key] = otherDesc[key];\n }\n });\n // Object.assign({}, descriptions[annot.name]);\n mergedDesc.type += ', ' + otherDesc.type;\n mergedDesc.description += `
${otherDesc.description}`;\n } else {\n mergedDesc = desc;\n }\n\n ideo.annotDescriptions.annots[annot.name] = mergedDesc;\n}\n\nfunction mergeAnnots(unmergedAnnots) {\n\n const seenAnnots = {};\n let mergedAnnots = [];\n\n unmergedAnnots.forEach((annot) => {\n if (annot.name in seenAnnots === false) {\n mergedAnnots.push(annot);\n seenAnnots[annot.name] = 1;\n } else {\n if (annot.color === 'purple') {\n mergedAnnots = mergedAnnots.map((mergedAnnot) => {\n return (annot.name === mergedAnnot.name) ? annot : mergedAnnot;\n });\n }\n }\n });\n\n return mergedAnnots;\n}\n\n/** Filter, sort, draw annots. Move legend. */\nfunction finishPlotRelatedGenes(type, ideo) {\n let annots = ideo.relatedAnnots.slice();\n\n annots = applyAnnotsIncludeList(annots, ideo);\n annots = mergeAnnots(annots);\n annots = applyRankCutoff(annots, 40, ideo);\n ideo.relatedAnnots = mergeAnnots(annots);\n ideo.relatedAnnots = applyRankCutoff(annots, 40, ideo);\n annots.sort(sortAnnotsByRelatedStatus);\n ideo.relatedAnnots.sort(sortAnnotsByRelatedStatus);\n\n if (annots.length > 1 && ideo.onFindRelatedGenesCallback) {\n ideo.onFindRelatedGenesCallback();\n }\n\n ideo.drawAnnots(annots);\n\n if (ideo.config.showAnnotLabels) {\n setRelatedAnnotDomIds(ideo);\n ideo.fillAnnotLabels(ideo.relatedAnnots);\n }\n\n moveLegend();\n\n analyzePlotTimes(type, ideo);\n}\n\n/** Fetch position of searched gene, return corresponding annotation */\nasync function processSearchedGene(geneSymbol, ideo) {\n const t0 = performance.now();\n\n const data = await fetchGenes(geneSymbol, 'symbol', ideo);\n\n if (data.hits.length === 0) {\n return;\n }\n const gene = data.hits.find(hit => {\n const genomicPos = getGenomicPos(hit, ideo); // omits alt loci\n return genomicPos && genomicPos.ensemblgene;\n });\n const ensemblId = gene.genomic_pos.ensemblgene;\n\n // Assign tooltip content. Much of the content is often retrieved from\n // the gene cache. In that case, all fields except `name` are fetched\n // from cache. Occasionally, e.g. often upon the very first search, no\n // content is yet available from cache.\n let desc = {description: '', ensemblId, type: 'searched gene'};\n if (gene.symbol in ideo.annotDescriptions.annots) {\n // Most content already set via cache.\n // `name` will be set via non-blocking part of `fetchGenes`.\n const oldDesc = ideo.annotDescriptions.annots[gene.symbol];\n desc = Object.assign(oldDesc, desc);\n } else {\n // No content has been set yet via cache. In this case, `gene` already\n // has all the data needed for the searched gene's tooltip content.\n desc.name = gene.name;\n }\n\n ideo.annotDescriptions.annots[gene.symbol] = desc;\n\n const annot = parseAnnotFromMgiGene(gene, ideo);\n\n ideo.relatedAnnots.push(annot);\n\n ideo.time.rg.searchedGene = timeDiff(t0);\n\n return annot;\n}\n\nfunction adjustPlaceAndVisibility(ideo) {\n var ideoContainerDom = document.querySelector(ideo.config.container);\n\n ideoContainerDom.style.visibility = '';\n ideoContainerDom.style.position = 'absolute';\n ideoContainerDom.style.width = '100%';\n\n var ideoInnerDom = document.querySelector('#_ideogramInnerWrap');\n ideoInnerDom.style.position = 'relative';\n ideoInnerDom.style.marginLeft = 'auto';\n ideoInnerDom.style.marginRight = 'auto';\n ideoInnerDom.style.overflowY = 'hidden';\n document.querySelector('#_ideogramMiddleWrap').style.overflowY = 'hidden';\n\n const legendPad = ideo.config.legendPad;\n\n if (typeof ideo.didAdjustIdeogramLegend === 'undefined') {\n // Accounts for moving legend when external content at left or right\n // is variable upon first rendering plotted genes\n\n var ideoDom = document.querySelector('#_ideogram');\n const legendWidth = 160;\n ideoInnerDom.style.maxWidth =\n (\n parseInt(ideoInnerDom.style.maxWidth) +\n legendWidth +\n legendPad\n ) + 'px';\n\n ideoDom.style.minWidth =\n (parseInt(ideoDom.style.minWidth) + legendPad) + 'px';\n ideoDom.style.maxWidth =\n (parseInt(ideoDom.style.minWidth) + legendPad) + 'px';\n ideoDom.style.position = 'relative';\n ideoDom.style.left = legendWidth + 'px';\n\n ideo.didAdjustIdeogramLegend = true;\n }\n}\n\n/**\n * For given gene, finds and draws interacting genes and paralogs\n *\n * @param geneSymbol {String} Gene symbol, e.g. RAD51\n */\nasync function plotRelatedGenes(geneSymbol=null) {\n\n const ideo = this;\n\n ideo.clearAnnotLabels();\n const legend = document.querySelector('#_ideogramLegend');\n if (legend) legend.remove();\n\n if (!geneSymbol) {\n return plotGeneHints(ideo);\n }\n\n ideo.config = setRelatedDecorPad(ideo.config);\n\n const organism = ideo.getScientificName(ideo.config.taxid);\n const version = Ideogram.version;\n const headers = [\n `# Related genes for ${geneSymbol} in ${organism}`,\n `# Generated by Ideogram.js version ${version}, https://github.com/eweitz/ideogram`,\n `# Generated at ${window.location.href}`\n ].join('\\n');\n\n delete ideo.annotDescriptions;\n ideo.annotDescriptions = {headers, annots: {}};\n\n const ideoSel = ideo.selector;\n const annotSel = ideoSel + ' .annot';\n document.querySelectorAll(annotSel).forEach(el => el.remove());\n\n ideo.startHideAnnotTooltipTimeout();\n\n // Refine style\n document.querySelectorAll('.chromosome').forEach(chromosome => {\n chromosome.style.cursor = '';\n });\n\n adjustPlaceAndVisibility(ideo);\n\n ideo.relatedAnnots = [];\n\n // Fetch positon of searched gene\n const annot = await processSearchedGene(geneSymbol, ideo);\n\n if (typeof annot === 'undefined') throwGeneNotFound(geneSymbol, ideo);\n\n ideo.config.legend = relatedLegend;\n writeLegend(ideo);\n moveLegend();\n\n await Promise.all([\n processInteractions(annot, ideo),\n processParalogs(annot, ideo)\n ]);\n\n ideo.time.rg.total = timeDiff(ideo.time.rg.t0);\n\n analyzeRelatedGenes(ideo);\n\n if (ideo.onPlotRelatedGenesCallback) ideo.onPlotRelatedGenesCallback();\n}\n\nfunction getAnnotByName(annotName, ideo) {\n var annotByName;\n ideo.annots.forEach(annotsByChr => {\n annotsByChr.annots.forEach(annot => {\n if (annotName === annot.name) {\n annotByName = annot;\n }\n });\n });\n return annotByName;\n}\n\n/**\n * Handles click within annotation tooltip\n *\n * Makes clicking link in tooltip behave same as clicking annotation\n */\nfunction handleTooltipClick(ideo) {\n const tooltip = document.querySelector('._ideogramTooltip');\n if (!ideo.addedTooltipClickHandler) {\n tooltip.addEventListener('click', () => {\n const geneDom = document.querySelector('#ideo-related-gene');\n const annotName = geneDom.textContent;\n const annot = getAnnotByName(annotName, ideo);\n ideo.onClickAnnot(annot);\n });\n\n // Ensures handler isn't added redundantly. This is used because\n // addEventListener options like {once: true} don't suffice\n ideo.addedTooltipClickHandler = true;\n }\n}\n\n/**\n * Enhance tooltip shown on hovering over gene annotation\n */\nfunction decorateRelatedGene(annot) {\n const ideo = this;\n const descObj = ideo.annotDescriptions.annots[annot.name];\n\n console.log('annot', annot)\n console.log('descObj')\n console.log(descObj)\n if ('type' in descObj && descObj.type.includes('interacting gene')) {\n const pathwayIds = descObj.pathwayIds;\n const summary = summarizeInteractions(annot.name, pathwayIds, ideo);\n if (summary !== null) {\n const oldSummary = 'Interacts with';\n descObj.description =\n descObj.description.replace(oldSummary, summary);\n }\n }\n\n const description =\n descObj.description.length > 0 ? `
${descObj.description}` : '';\n const fullName = descObj.name;\n const style = 'style=\"color: #0366d6; cursor: pointer;\"';\n\n const originalDisplay =\n `${annot.name}
` +\n `${fullName}
` +\n `${description}` +\n `
`;\n\n annot.displayName = originalDisplay;\n\n\n handleTooltipClick(ideo);\n\n return annot;\n}\n\nconst shape = 'triangle';\n\nconst legendHeaderStyle =\n `font-size: 14px; font-weight: bold; font-color: #333;`;\nconst relatedLegend = [{\n name: `\n \n
Related genes
\n
Click gene to search\n
\n `,\n nameHeight: 50,\n rows: [\n {name: 'Interacting gene', color: 'purple', shape: shape},\n {name: 'Paralogous gene', color: 'pink', shape: shape},\n {name: 'Searched gene', color: 'red', shape: shape}\n ]\n}];\n\nconst citedLegend = [{\n name: `\n \n
Highly cited genes
\n
Click gene to search\n
\n `,\n nameHeight: 30,\n rows: []\n}];\n\n/** Sets legendPad for related genes view */\nfunction setRelatedDecorPad(kitConfig) {\n if (kitConfig.showAnnotLabels) {\n kitConfig.legendPad = 70;\n } else {\n kitConfig.legendPad = 30;\n }\n return kitConfig;\n}\n\n/**\n * Wrapper for Ideogram constructor, with generic \"Related genes\" options\n *\n * This function is made available as a static method on Ideogram.\n *\n * @param {Object} config Ideogram configuration object\n */\nfunction _initRelatedGenes(config, annotsInList) {\n\n if (annotsInList !== 'all') {\n annotsInList = annotsInList.map(name => name.toLowerCase());\n }\n\n const kitDefaults = {\n showFullyBanded: false,\n rotatable: false,\n legend: relatedLegend,\n chrBorderColor: '#333',\n chrLabelColor: '#333',\n onWillShowAnnotTooltip: decorateRelatedGene,\n annotsInList: annotsInList,\n showTools: true,\n showAnnotLabels: true,\n relatedGenesMode: 'related'\n };\n\n if ('onWillShowAnnotTooltip' in config) {\n const key = 'onWillShowAnnotTooltip';\n const clientFn = config[key];\n const defaultFunction = kitDefaults[key];\n const newFunction = function(annot) {\n annot = defaultFunction.bind(this)(annot);\n annot = clientFn.bind(this)(annot);\n return annot;\n };\n kitDefaults[key] = newFunction;\n delete config[key];\n }\n\n // Override kit defaults if client specifies otherwise\n let kitConfig = Object.assign(kitDefaults, config);\n\n kitConfig = setRelatedDecorPad(kitConfig);\n\n const ideogram = new Ideogram(kitConfig);\n\n // Called upon completing last plot, including all related genes\n if (config.onPlotRelatedGenes) {\n ideogram.onPlotRelatedGenesCallback = config.onPlotRelatedGenes;\n }\n\n // Called upon 1) finding paralogs, and 2) finding interacting genes\n if (config.onFindRelatedGenes) {\n ideogram.onFindRelatedGenesCallback = config.onFindRelatedGenes;\n }\n\n ideogram.getTooltipAnalytics = getRelatedGenesTooltipAnalytics;\n\n ideogram.annotSortFunction = sortAnnotsByRelatedStatus;\n\n initAnalyzeRelatedGenes(ideogram);\n\n initGeneCache(ideogram.config.organism, ideogram);\n\n return ideogram;\n}\n\nfunction plotGeneHints() {\n const ideo = this;\n\n if (!ideo || 'annotDescriptions' in ideo) return;\n\n ideo.annotDescriptions = {annots: {}};\n\n ideo.flattenAnnots().map((annot) => {\n let description = [];\n if ('significance' in annot && annot.significance !== 'n/a') {\n description.push(annot.significance);\n }\n if ('citations' in annot && annot.citations !== undefined) {\n description.push(annot.citations);\n }\n description = description.join('
');\n ideo.annotDescriptions.annots[annot.name] = {\n description,\n name: annot.fullName\n };\n });\n\n adjustPlaceAndVisibility(ideo);\n moveLegend();\n ideo.fillAnnotLabels();\n const container = ideo.config.container;\n document.querySelector(container).style.visibility = '';\n}\n\n/**\n * Wrapper for Ideogram constructor, with generic \"Related genes\" options\n *\n * This function is made available as a static method on Ideogram.\n *\n * @param {Object} config Ideogram configuration object\n */\nfunction _initGeneHints(config, annotsInList) {\n\n delete config.onPlotRelatedGenes;\n\n if (annotsInList !== 'all') {\n annotsInList = annotsInList.map(name => name.toLowerCase());\n }\n\n const annotsPath =\n getDir('cache/homo-sapiens-top-genes.tsv');\n\n const kitDefaults = {\n showFullyBanded: false,\n rotatable: false,\n legend: citedLegend,\n chrMargin: -4,\n chrBorderColor: '#333',\n chrLabelColor: '#333',\n onWillShowAnnotTooltip: decorateRelatedGene,\n annotsInList: annotsInList,\n showTools: true,\n showAnnotLabels: true,\n onDrawAnnots: plotGeneHints,\n annotationsPath: annotsPath,\n relatedGenesMode: 'hints'\n };\n\n if ('onWillShowAnnotTooltip' in config) {\n const key = 'onWillShowAnnotTooltip';\n const clientFn = config[key];\n const defaultFunction = kitDefaults[key];\n const newFunction = function(annot) {\n annot = defaultFunction.bind(this)(annot);\n annot = clientFn.bind(this)(annot);\n return annot;\n };\n kitDefaults[key] = newFunction;\n delete config[key];\n }\n\n if ('onDrawAnnots' in config) {\n const key = 'onDrawAnnots';\n const clientFn = config[key];\n const defaultFunction = kitDefaults[key];\n const newFunction = function() {\n defaultFunction.bind(this)();\n clientFn.bind(this)();\n };\n kitDefaults[key] = newFunction;\n delete config[key];\n }\n\n // Override kit defaults if client specifies otherwise\n const kitConfig = Object.assign(kitDefaults, config);\n\n if (kitConfig.showAnnotLabels) {\n kitConfig.legendPad = 80;\n } else {\n kitConfig.legendPad = 30;\n }\n\n const ideogram = new Ideogram(kitConfig);\n\n // Called upon completing last plot, including all related genes\n if (config.onPlotRelatedGenes) {\n ideogram.onPlotRelatedGenesCallback = config.onPlotRelatedGenes;\n }\n\n // Called upon 1) finding paralogs, and 2) finding interacting genes\n if (config.onFindRelatedGenes) {\n ideogram.onFindRelatedGenesCallback = config.onFindRelatedGenes;\n }\n\n ideogram.getTooltipAnalytics = getRelatedGenesTooltipAnalytics;\n\n ideogram.annotSortFunction = sortAnnotsByRelatedStatus;\n\n initAnalyzeRelatedGenes(ideogram);\n\n initGeneCache(ideogram.config.organism, ideogram);\n\n return ideogram;\n}\n\nexport {\n _initGeneHints, _initRelatedGenes, plotRelatedGenes, getRelatedGenesByType\n};\n","/**\n * @fileoverview Core module of Ideogram.js, links all other modules\n * This file defines the Ideogram class, its constructor method, and its\n * static methods. All instance methods are defined in other modules.\n *\n */\n\nimport version from './version';\n\nimport {\n configure, initDrawChromosomes, handleRotateOnClick, onLoad,\n init, finishInit, writeContainer\n} from './init/init';\n\nimport {\n onLoadAnnots, onDrawAnnots, processAnnotData, restoreDefaultTracks,\n updateDisplayedTracks, initAnnotSettings, fetchAnnots, drawAnnots,\n getHistogramBars, drawHeatmaps, deserializeAnnotsForHeatmap, fillAnnots,\n drawProcessedAnnots, drawSynteny, startHideAnnotTooltipTimeout,\n showAnnotTooltip, onWillShowAnnotTooltip, setOriginalTrackIndexes,\n afterRawAnnots, onClickAnnot, downloadAnnotations, addAnnotLabel,\n removeAnnotLabel, fillAnnotLabels, clearAnnotLabels, flattenAnnots\n // fadeOutAnnotLabels\n} from './annotations/annotations';\n\nimport {highlight, unhighlight} from './annotations/highlight';\n\nimport {\n esearch, esummary, elink,\n getOrganismFromEutils, getTaxids,\n getAssemblyAndChromosomesFromEutils\n} from './services/services';\n\nimport {\n drawBandLabels, getBandColorGradients, processBandData,\n setBandsToShow, hideUnshownBandLabels, drawBandLabelText, drawBandLabelStalk\n} from './bands/bands';\n\nimport {onBrushMove, onBrushEnd, createBrush} from './brush';\nimport {onCursorMove, createClickCursor} from './cursor';\nimport {drawSexChromosomes, setSexChromosomes} from './sex-chromosomes';\nimport {convertBpToPx, convertPxToBp} from './coordinate-converters';\nimport {\n unpackAnnots, packAnnots, initCrossFilter, filterAnnots\n} from './filter';\n\nimport {\n assemblyIsAccession, getDataDir, round, onDidRotate, getSvg, d3,\n getTaxid, getCommonName, getScientificName, fetch as _fetch,\n isRoman, parseRoman\n} from './lib';\n\nimport {\n getChromosomeModel, getChromosomePixels\n} from './views/chromosome-model';\n\nimport {\n appendHomolog, drawChromosome, rotateAndToggleDisplay, setOverflowScroll\n} from './views/draw-chromosomes';\n\nimport {\n drawChromosomeLabels, rotateChromosomeLabels\n} from './views/chromosome-labels.js';\n\nimport {\n _initGeneHints, _initRelatedGenes, plotRelatedGenes, getRelatedGenesByType\n} from './kit/related-genes';\n\nexport default class Ideogram {\n constructor(config) {\n\n // Functions from init.js\n this.configure = configure;\n this.initDrawChromosomes = initDrawChromosomes;\n this.onLoad = onLoad;\n this.handleRotateOnClick = handleRotateOnClick;\n this.init = init;\n this.finishInit = finishInit;\n this.writeContainer = writeContainer;\n\n // Functions from annotations.js\n this.onLoadAnnots = onLoadAnnots;\n this.onDrawAnnots = onDrawAnnots;\n this.processAnnotData = processAnnotData;\n this.restoreDefaultTracks = restoreDefaultTracks;\n this.updateDisplayedTracks = updateDisplayedTracks;\n this.initAnnotSettings = initAnnotSettings;\n this.fetchAnnots = fetchAnnots;\n this.drawAnnots = drawAnnots;\n this.getHistogramBars = getHistogramBars;\n this.drawHeatmaps = drawHeatmaps;\n this.deserializeAnnotsForHeatmap = deserializeAnnotsForHeatmap;\n this.fillAnnots = fillAnnots;\n this.drawProcessedAnnots = drawProcessedAnnots;\n this.drawSynteny = drawSynteny;\n this.startHideAnnotTooltipTimeout = startHideAnnotTooltipTimeout;\n this.showAnnotTooltip = showAnnotTooltip;\n this.onWillShowAnnotTooltip = onWillShowAnnotTooltip;\n this.onClickAnnot = onClickAnnot;\n this.setOriginalTrackIndexes = setOriginalTrackIndexes;\n this.afterRawAnnots = afterRawAnnots;\n this.downloadAnnotations = downloadAnnotations;\n this.addAnnotLabel = addAnnotLabel;\n this.removeAnnotLabel = removeAnnotLabel;\n // this.fadeOutAnnotLabels = fadeOutAnnotLabels;\n this.fillAnnotLabels = fillAnnotLabels;\n this.clearAnnotLabels = clearAnnotLabels;\n this.flattenAnnots = flattenAnnots;\n\n this.highlight = highlight;\n this.unhighlight = unhighlight;\n\n // Variables and functions from services.js\n this.esearch = esearch;\n this.esummary = esummary;\n this.elink = elink;\n this.getOrganismFromEutils = getOrganismFromEutils;\n this.getTaxids = getTaxids;\n this.getAssemblyAndChromosomesFromEutils =\n getAssemblyAndChromosomesFromEutils;\n\n // Functions from bands.js\n this.drawBandLabels = drawBandLabels;\n this.getBandColorGradients = getBandColorGradients;\n this.processBandData = processBandData;\n this.setBandsToShow = setBandsToShow;\n this.hideUnshownBandLabels = hideUnshownBandLabels;\n this.drawBandLabelText = drawBandLabelText;\n this.drawBandLabelStalk = drawBandLabelStalk;\n\n // Functions from brush.js\n this.onBrushMove = onBrushMove;\n this.onBrushEnd = onBrushEnd;\n this.createBrush = createBrush;\n\n // Functions from cursor.js\n this.createClickCursor = createClickCursor;\n this.onCursorMove = onCursorMove;\n\n // Functions from sex-chromosomes.js\n this.drawSexChromosomes = drawSexChromosomes;\n this.setSexChromosomes = setSexChromosomes;\n\n // Functions from coordinate-converters.js\n this.convertBpToPx = convertBpToPx;\n this.convertPxToBp = convertPxToBp;\n\n // Functions from filter.js\n this.unpackAnnots = unpackAnnots;\n this.packAnnots = packAnnots;\n this.initCrossFilter = initCrossFilter;\n this.filterAnnots = filterAnnots;\n\n // Functions from lib\n this.assemblyIsAccession = assemblyIsAccession;\n this.getDataDir = getDataDir;\n this.round = round;\n this.onDidRotate = onDidRotate;\n this.getSvg = getSvg;\n this.fetch = _fetch;\n this.getTaxid = getTaxid;\n this.getCommonName = getCommonName;\n this.getScientificName = getScientificName;\n\n // Functions from views/chromosome-model.js\n this.getChromosomeModel = getChromosomeModel;\n this.getChromosomePixels = getChromosomePixels;\n\n // Functions from views/chromosome-labels.js\n this.drawChromosomeLabels = drawChromosomeLabels;\n this.rotateChromosomeLabels = rotateChromosomeLabels;\n\n // Functions from views/draw-chromosomes.js\n this.appendHomolog = appendHomolog;\n this.drawChromosome = drawChromosome;\n this.rotateAndToggleDisplay = rotateAndToggleDisplay;\n this.setOverflowScroll = setOverflowScroll;\n\n this.plotRelatedGenes = plotRelatedGenes;\n this.getRelatedGenesByType = getRelatedGenesByType;\n\n this.configure(config);\n }\n\n /**\n * Get the current version of Ideogram.js\n */\n static get version() {\n return version;\n }\n\n /**\n * Enable use of D3 in client apps, via \"d3 = Ideogram.d3\"\n */\n static get d3() {\n return d3;\n }\n\n /**\n * Request data from Ensembl REST API\n * Docs: https://rest.ensembl.org/\n *\n * @param {String} path URL path\n * @param {Object} body POST body\n * @param {String} method HTTP method; 'GET' (default) or 'POST'\n */\n static async fetchEnsembl(path, body = null, method = 'GET') {\n const init = {\n method: method\n };\n if (body !== null) init.body = JSON.stringify(body);\n if (method === 'GET') {\n // Use HTTP parameter, not header, to avoid needless OPTIONS request\n const delimiter = path.includes('&') ? '&' : '?';\n path += delimiter + 'content-type=application/json';\n } else {\n // Method is POST, so content-type must be defined in header\n init.headers = {'Content-Type': 'application/json'};\n }\n\n // const random = Math.random();\n // console.log(random)\n // if (random < 0.5) {\n const response = await fetch(`https://rest.ensembl.org${path}`, init);\n const json = await response.json();\n return json;\n // } else {\n // // Mock error\n // init.headers = {'Content-Type': 'application/json'};\n // const response = await fetch('https://httpstat.us/500/cors', init);\n // const json = await response.json();\n // return json;\n // }\n }\n\n /**\n * Helper for sortChromosomes().\n * Gets names and biological types for diverse chromosome variables\n */\n static getChrSortNamesAndTypes(a, b) {\n var chrAName, chrBName,\n aIsCP, bIsCP, aIsMT, bIsMT, aIsAP, bIsAP, aIsNuclear, bIsNuclear;\n\n if (typeof a === 'string' || 'chr' in a && 'annots' in a) {\n // Chromosome data is from either:\n // - Ideogram static file cache (e.g. homo-sapiens.json)\n // - Ideogram raw annotations\n chrAName = (typeof a === 'string') ? a : a.chr;\n chrBName = (typeof b === 'string') ? b : b.chr;\n\n aIsCP = chrAName === 'CP';\n bIsCP = chrBName === 'CP';\n aIsMT = chrAName === 'MT';\n bIsMT = chrBName === 'MT';\n aIsAP = chrAName === 'AP';\n bIsAP = chrBName === 'AP';\n aIsNuclear = (!aIsCP && !aIsMT && !aIsAP);\n bIsNuclear = (!bIsCP && !bIsMT && !bIsAP);\n } else {\n // Chromosome data is from NCBI E-Utils web API\n chrAName = a.name;\n chrBName = b.name;\n\n aIsCP = a.type === 'chloroplast';\n bIsCP = b.type === 'chloroplast';\n aIsMT = a.type === 'mitochondrion';\n bIsMT = b.type === 'mitochondrion';\n aIsAP = a.type === 'apicoplast';\n bIsAP = b.type === 'apicoplast';\n aIsNuclear = a.type === 'nuclear';\n bIsNuclear = b.type === 'nuclear';\n }\n\n const chrTypes = {\n aIsNuclear, bIsNuclear, aIsCP, bIsCP, aIsMT, bIsMT, aIsAP, bIsAP\n };\n\n return [chrAName, chrBName, chrTypes];\n }\n\n /**\n * Sorts two chromosome objects by type and name\n * - Nuclear chromosomes come before non-nuclear chromosomes.\n * - Among nuclear chromosomes, use \"natural sorting\", e.g.\n * numbers come before letters\n * - Among non-nuclear chromosomes, i.e. \"MT\" (mitochondrial DNA) and\n * \"CP\" (chromoplast DNA), MT comes first\n *\n * @param a Chromosome string or object \"A\"\n * @param b Chromosome string or object \"B\"\n * @returns {Number} JavaScript sort order indicator\n */\n static sortChromosomes(a, b) {\n\n let [chrAName, chrBName, chrTypes] =\n Ideogram.getChrSortNamesAndTypes(a, b);\n\n const {\n aIsNuclear, bIsNuclear, aIsCP, bIsCP, aIsMT, bIsMT, aIsAP, bIsAP\n } = chrTypes;\n\n if (aIsNuclear && bIsNuclear) {\n\n if (isRoman(chrAName) && isRoman(chrBName)) {\n // As in yeast genome\n chrAName = parseRoman(chrAName).toString();\n chrBName = parseRoman(chrBName).toString();\n }\n\n return chrAName.localeCompare(chrBName, 'en', {numeric: true});\n } else if (!aIsNuclear && bIsNuclear) {\n return 1;\n } else if (aIsMT && bIsCP) {\n return 1;\n } else if (aIsCP && bIsMT) {\n return -1;\n } else if (!aIsAP && !aIsMT && !aIsCP && (bIsMT || bIsCP || bIsAP)) {\n return -1;\n }\n }\n\n /**\n * Wrapper for Ideogram constructor, with generic \"Related genes\" options\n *\n * @param {Object} config Ideogram configuration object\n */\n static initRelatedGenes(config, annotsInList='all') {\n return _initRelatedGenes(config, annotsInList);\n }\n\n /**\n * Wrapper for Ideogram constructor, with generic \"Related genes\" options\n *\n * @param {Object} config Ideogram configuration object\n */\n static initGeneHints(config, annotsInList='all') {\n return _initGeneHints(config, annotsInList);\n }\n}\n","var version = '1.34.0';\nexport default version;\n","import Ideogram from './ideogram';\n\n// Enable references to Ideogram when loaded via traditional script tag\nwindow.Ideogram = Ideogram;\n\n// Enable references to Ideogram when imported as an ES6 module\nexport default Ideogram;\n"],"names":["root","factory","exports","module","define","amd","a","i","self","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","none","selector","this","querySelector","x","Array","from","empty","querySelectorAll","matches","childMatcher","node","find","childFirst","firstElementChild","filter","children","update","length","EnterNode","parent","datum","ownerDocument","namespaceURI","_next","_parent","__data__","bindIndex","group","enter","exit","data","groupLength","dataLength","bindKey","keyValue","nodeByKeyValue","Map","keyValues","has","set","delete","ascending","b","NaN","constructor","appendChild","child","insertBefore","next","xhtml","svg","xlink","xml","xmlns","name","prefix","indexOf","slice","namespaces","space","local","attrRemove","removeAttribute","attrRemoveNS","fullname","removeAttributeNS","attrConstant","setAttribute","attrConstantNS","setAttributeNS","attrFunction","v","apply","arguments","attrFunctionNS","defaultView","document","styleRemove","style","removeProperty","styleConstant","priority","setProperty","styleFunction","styleValue","getPropertyValue","getComputedStyle","propertyRemove","propertyConstant","propertyFunction","classArray","string","trim","split","classList","ClassList","_node","_names","getAttribute","classedAdd","names","list","n","add","classedRemove","remove","classedTrue","classedFalse","classedFunction","textRemove","textContent","textConstant","textFunction","htmlRemove","innerHTML","htmlConstant","htmlFunction","raise","nextSibling","parentNode","lower","previousSibling","firstChild","creatorInherit","uri","documentElement","createElement","createElementNS","creatorFixed","namespace","constantNull","removeChild","selection_cloneShallow","clone","cloneNode","selection_cloneDeep","parseTypenames","typenames","map","t","type","onRemove","typename","on","__on","j","m","removeEventListener","listener","options","onAdd","event","contextListener","addEventListener","push","dispatchEvent","params","window","CustomEvent","createEvent","initEvent","bubbles","cancelable","detail","dispatchConstant","dispatchFunction","join","splice","contains","Selection","groups","parents","_groups","_parents","selection","select","subgroups","subnode","subgroup","selectAll","array","arrayAll","selectorAll","selectChild","match","childFind","selectChildren","childrenFilter","matcher","bind","enterGroup","updateGroup","exitGroup","previous","i0","i1","_enter","_exit","sparse","onenter","onupdate","onexit","append","merge","order","Error","groups0","groups1","m0","m1","Math","min","merges","group0","group1","compareDocumentPosition","sort","compare","compareNode","sortgroups","sortgroup","callback","nodes","size","each","attr","getAttributeNS","property","classed","text","html","create","creator","insert","before","deep","dispatch","iterator","responseBlob","response","ok","status","statusText","blob","input","init","fetch","then","responseArrayBuffer","arrayBuffer","EOL","EOF","objectConverter","columns","Function","JSON","stringify","inferColumns","rows","columnSet","forEach","row","column","pad","width","s","delimiter","reFormat","RegExp","DELIMITER","charCodeAt","parseRows","f","N","I","eof","eol","token","c","replace","preformatBody","formatValue","formatRow","Date","date","year","hours","getUTCHours","minutes","getUTCMinutes","seconds","getUTCSeconds","milliseconds","getUTCMilliseconds","isNaN","getUTCFullYear","getUTCMonth","getUTCDate","formatDate","test","parse","convert","object","customConverter","format","concat","formatBody","formatRows","csv","dsv","csvParse","tsv","tsvParse","responseText","dsvParse","undefined","Promise","resolve","reject","image","Image","onerror","onload","src","responseJson","json","parser","DOMParser","parseFromString","noop","_","Dispatch","types","T","copy","that","args","preventDefault","stopImmediatePropagation","view","noevent","__noselect","MozUserSelect","yesdrag","noclick","setTimeout","extend","Color","darker","brighter","reI","reN","reP","reHex","reRgbInteger","reRgbPercent","reRgbaInteger","reRgbaPercent","reHslPercent","reHslaPercent","named","aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkgrey","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkslategrey","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dimgrey","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","green","greenyellow","grey","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightgrey","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightslategrey","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","slategrey","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen","color_formatHex","rgb","formatHex","color_formatRgb","formatRgb","color","l","toLowerCase","exec","parseInt","rgbn","Rgb","rgba","hsla","r","g","rgbConvert","opacity","rgb_formatHex","hex","rgb_formatRgb","max","round","toString","h","Hsl","hslConvert","hsl2rgb","m2","basis","t1","v0","v1","v2","v3","t2","t3","channels","assign","displayable","formatHsl","k","pow","nogamma","d","linear","rgbGamma","y","exponential","gamma","start","end","rgbSpline","spline","colors","genericArray","nb","na","setTime","values","floor","reA","reB","source","am","bm","bs","bi","lastIndex","q","index","number","one","zero","ArrayBuffer","isView","DataView","isArray","valueOf","sourceEvent","currentTarget","ownerSVGElement","createSVGPoint","point","clientX","clientY","matrixTransform","getScreenCTM","inverse","getBoundingClientRect","rect","left","clientLeft","top","clientTop","pageX","pageY","taskHead","taskTail","timeout","interval","clockLast","clockNow","clockSkew","clock","performance","now","setFrame","requestAnimationFrame","clearNow","Timer","_call","_time","timer","delay","time","restart","wake","e","timerFlush","t0","Infinity","sleep","nap","poke","clearTimeout","clearInterval","setInterval","elapsed","stop","TypeError","emptyOn","emptyTween","id","timing","schedules","__transition","tween","state","tick","duration","ease","schedule","active","svgNode","degrees","PI","identity","translateX","translateY","rotate","skewX","scaleX","scaleY","sqrt","atan2","atan","interpolateTransform","pxComma","pxParen","degParen","pop","xa","ya","xb","yb","translate","scale","interpolateTransformCss","DOMMatrix","WebKitCSSMatrix","isIdentity","decompose","interpolateTransformSvg","transform","baseVal","consolidate","matrix","tweenRemove","tween0","tween1","tweenFunction","tweenValue","transition","_id","interpolate","value1","string00","interpolate0","string1","string0","string10","attrInterpolate","attrInterpolateNS","attrTweenNS","_value","attrTween","delayFunction","delayConstant","durationFunction","durationConstant","easeConstant","onFunction","on0","on1","sit","every","styleInterpolate","styleTween","textInterpolate","textTween","_name","newId","selection_prototype","inherit","id0","id1","styleNull","listener0","styleMaybeRemove","removeFunction","easeVarying","cancel","interrupt","defaultTiming","BrushEvent","target","mode","defineProperties","configurable","MODE_DRAG","MODE_SPACE","MODE_HANDLE","MODE_CENTER","abs","number1","number2","X","handles","output","xy","Y","XY","cursors","overlay","w","nw","ne","se","sw","flipX","flipY","signsX","signsY","defaultFilter","ctrlKey","button","defaultExtent","hasAttribute","viewBox","height","defaultTouchable","navigator","maxTouchPoints","__brush","extent","brushSelection","dim","brushX","brushY","touchending","touchable","keys","listeners","handleSize","brush","initialize","handle","redraw","started","touchmoved","touchended","emitter","clean","emit","Emitter","touches","w0","w1","n0","n1","e0","e1","s0","s1","moving","lockX","lockY","metaKey","altKey","signX","signY","W","E","S","dx","dy","shifting","shiftKey","points","identifier","pointer","point0","pts","move","beforestart","moved","ended","keydowned","keyupped","p","changedTouches","cur","keyCode","selection0","selection1","clear","starting","keyModifiers","formatDecimalParts","toExponential","coefficient","prefixExponent","re","formatSpecifier","specifier","FormatSpecifier","fill","align","sign","symbol","comma","precision","exponent","toFixed","toLocaleString","toPrecision","formatRounded","toUpperCase","formatPrefix","prefixes","locale","grouping","thousands","Number","substring","reverse","currencyPrefix","currency","currencySuffix","decimal","numerals","formatNumerals","String","percent","minus","nan","newFormat","formatTypes","suffix","formatType","maybeSuffix","valuePrefix","valueSuffix","valueNegative","out","formatTrim","padding","defaultLocale","step","e10","e5","e2","tickIncrement","count","power","log","LN10","error","delta","lo","hi","mid","ascendingComparator","center","right","ascendingBisect","bisector","bisectRight","unit","normalize","bimap","domain","range","d0","d1","r0","r1","polymap","bisect","clamp","unknown","continuous","untransform","piecewise","rescale","invert","rangeRound","u","transformer","initRange","linearish","ticks","isFinite","ceil","tickFormat","step0","step1","tickStep","precisionPrefix","precisionRound","precisionFixed","nice","prestep","maxIter","staticColors","organismMetadata","commonName","scientificName","assemblies","default","GRCh38","GRCh37","NCBI36","hasGeneCache","GRCm38","MGSCv37","TAIR10","R64","GCA_000002765","d3","d3fetch","d3brush","d3dispatch","d3format","assemblyIsAccession","config","assembly","getDir","dir","script","tmp","ideogramInLeaf","scripts","version","Ideogram","location","pathname","includes","fetchWithRetry","url","isRetry","getDataDir","coord","onDidRotate","chrModel","onDidRotateCallback","getSvg","fetchWithAuth","contentType","headers","Headers","accessToken","Authorization","getTaxid","organism","taxid","organisms","slug","getCommonName","getScientificName","isRoman","parseRoman","val","M","D","C","L","V","reduce","aa","getFont","ideo","family","fontFamily","annotLabelSize","getTextSize","font","context","canvas","getContext","metrics","measureText","actualBoundingBoxRight","actualBoundingBoxLeft","actualBoundingBoxAscent","actualBoundingBoxDescent","scaleLinear","valueof","configuredCss","configure","chromosomesArray","coordinateSystem","maxLength","bp","iscn","chromosomes","numChromosomes","debug","dataDir","container","resolution","orientation","showChromosomeLabels","showNonNuclearChromosomes","chromosomeScale","showTools","ploidy","sexChromosomes","sex","ancestors","P","ploidyDesc","configurePloidy","showBandLabels","showFullyBanded","bandsToShow","bandData","configureBands","chrHeight","configureHeight","chrWidth","configureWidth","geometry","chrMargin","configureMargin","onLoad","onLoadCallback","onLoadAnnots","onLoadAnnotsCallback","onDrawAnnots","onDrawAnnotsCallback","onBrushMove","onBrushMoveCallback","onBrushEnd","onBrushEndCallback","onCursorMove","onCursorMoveCallback","onWillShowAnnotTooltip","onWillShowAnnotTooltipCallback","onClickAnnot","onClickAnnotCallback","configureCallbacks","organismsWithBands","configureOrganisms","bump","adjustedBump","configureBump","chromosome","rotatable","configureSingleChromosome","chrLabelSize","chrLabelColor","configureTextStyle","initAnnotSettings","annotationsLayout","annotTracksHeight","chrSets","yOffsets","annotLabelHeight","demarcateCollinearChromosomes","prevChr","prevWidth","seenTaxids","getYOffsets","chrSet","chrLabelX","adjustedX","orgIndex","taxids","labelSpan","labelGenomes","rearrangeChromosomes","multiorganism","maxHeight","xOffsets","annotHeight","annotationHeight","numAnnotTracks","getXOffsets","adjustedY","chrLabelY","org","maxWidth","collinearizeVerticalChromosomes","processAnnots","rawAnnots","setOriginalTrackIndexes","annotationsDisplayedTracks","annots","updateDisplayedTracks","processAnnotData","filterable","initCrossFilter","drawProcessedAnnots","finishInit","t0A","getTime","confAnnots","annotations","initDrawChromosomes","annotationsPath","checkAnnotData","waitForAndProcessAnnots","chrID","t0C","t1C","hideUnshownBandLabels","console","rotateChromosomeLabels","drawChromosomeLabels","processLabels","createBrush","cursorPosition","createClickCursor","drawAnnots","afterRawAnnots","t1A","reportDebugTimings","setOverflowScroll","collinearizeChromosomes","Ploidy","_config","_description","_normalize","getChromosomesNumber","setIndex","chrSetCode","description","descValue","normalized","existence","_getexistenceArray","getSetSize","chrSetIndex","getAncestor","chrIndex","exists","armIndex","desc","ChromosomeUtil","getLabel","getSetLabel","_ideo","_ploidy","_translate","chrSetMargin","_tickSize","_isRotated","_getLeftMargin","margin","_getYScale","getChromosomeLabels","chrElement","util","labels","getChromosomeBandLabelTranslate","band","tickSize","px","didRotate","chrName","bands","oldWidth","chrSetElement","getChromosomeModel","drawChromosome","handleRotateOnClick","displayedTrackIndexes","drawBandLabels","otherChrs","ideoBounds","labelSelectors","chrHeightOriginal","chrWidthOriginal","annotationHeightOriginal","rotateBack","_layout","rotateForward","elementLength","windowLength","_class","innerWidth","innerHeight","getChromosomeLabelClass","_getAdditionalOffset","numTracks","annotationsNumTracks","_getChromosomeSetSize","getChromosomeSetLabelAnchor","getChromosomeLabelYPosition","getChromosomeSetLabelYPosition","Layout","super","getChromosomeScale","getChromosomeScaleBack","getChromosomeSetTranslate","getHeight","getWidth","getChromosomeBandTickY1","getChromosomeBandTickY2","getChromosomeSetLabelTranslate","getChromosomeBandLabelAnchor","ideoBox","chrBox","getChromosomeSetYTranslate","barWidth","legendPad","prevTranslate","getChromosomeSetLabelXPosition","getChromosomeLabelXPosition","xOffset","lastSetOffset","setSize","chromosomeSetYTranslate","chrs","numChrs","chrsPerRow","yOffset","additionalPadding","writeContainer","fetchAnnots","setPloidy","perspective","PairedLayout","SmallLayout","VerticalLayout","HorizontalLayout","getLayout","svgClass","getContainerSvgClass","getBandColorGradients","writeContainerDom","isOnlyIdeogram","writeTooltipContainer","lastBandDataUrl","getBandUrl","bandDataFileNames","isHeterogameticChromosome","prepareChromosomes","bandsArray","setCoordinateSystem","chrBands","chr","setSexChromosomes","_gotChrModels","element","rotateAndToggleDisplay","getBandFileName","accession","bandFileName","getBandFileNames","bandFileNames","isCustomOrganism","prepareContainer","bandDataUrl","hasNonGenBankAssembly","shouldFetchBands","numBandDataResponses","rawBands","fileNames","fetchedTaxid","fileName","setBandData","fetchBands","processBandData","ideoNext","ideoQueued","ideoWait","containerId","getOrganismFromEutils","getTaxids","initializeTaxids","organismScientificName","promises","all","taxidsAndBandsArrays","taxidAndBandsArray","getBandsAndPrepareContainer","BedParser","bed","parseBed","componentToHex","parseGenomicCoordinates","ucscStyle","parseAnnotFromTsvLine","tsvLine","annot","label","rgbToHex","parseRawAnnots","bedStartIndex","tsvLines","line","TsvParser","parseTsv","getValueColumnIndex","headerLine","header","fullName","significance","citations","tsvStartIndex","citeIndex","fromTo","annotsByChr","reservedTrackKeys","defaultHeatmapColors","getLabels","heatmaps","metadata","trackLabels","inflateThresholds","thresholds","heatmapThresholds","shouldUseThresholdColor","numThresholds","prevThreshold","threshold","getHeatmapAnnotColor","thresholdList","tvNum","thresholdColor","parseFloat","writeCanvases","chrLeft","trackLeft","trackWidth","contextArray","fillCanvasAnnots","demarcateChrs","trackIndex","fillStyle","startPx","fillRect","fillCanvasAnnotValues","ideoMarginTop","add2dAnnotsForChr","omittedAnnots","ra","stopPx","convertBpToPx","shift","startHideTrackLabelTimeout","showTrackLabel","hideTrackLabelTimeout","ideoHeight","drawHeatmaps","annotContainers","prevX","xBump","showChromosomesLabels","labelContainer","markBump","renderTrackLabels","writeTrackLabels","drawHeatmapsCollinear","writeCanvas","drawHeatmaps2d","writeTrackLabelContainer","trackCanvas","firstTrackId","trackBox","labelBox","getTrackLabelOffsets","getNewRawAnnots","heatmapKeyIndexes","newRa","newRas","deserializeAnnotsForHeatmap","rawAnnotsContainer","newRaContainers","heatmapKey","rawAnnotBoxes","raContainer","getNewRawAnnotContainers","reportPerformance","startHideAnnotTooltipTimeout","showAnnotTooltip","hideAnnotTooltipTimeout","content","tooltip","cx","cy","displayName","getContentAndYOffset","renderTooltip","getAnnotDomLabelId","domId","changeAnnotState","labelId","annotId","triggerAnnotEvent","annotElement","parentElement","prevTooltipOff","prevTooltipAnnotDomId","_ideoActiveTimeout","getAnnotByName","annotName","found","thisAnnot","getAnnotLabelLayout","annotDom","annotRect","ideoRect","bottom","addAnnotLabel","backgroundColor","borderColor","layout","didSetLabelStyle","insertAdjacentHTML","renderLabel","sortAnnotsByRank","ranks","geneCache","interestingNames","rank","applyRankCutoff","cutoff","fillAnnotLabels","sortedAnnots","clearAnnotLabels","spacedAnnots","spacedLayouts","flattenAnnots","some","sl","xOverlap","yOverlap","numLabels","relatedGenesMode","removeAnnotLabel","getHistogramBars","chrModels","bars","isFirstGet","histogramScaling","setIdeoHistogramScaling","maxAnnotsPerBar","lastBand","numBins","bar","convertPxToBp","annotationsColor","getRawBars","chrAnnots","barAnnots","barPx","nextBarPx","assignAnnotsToBars","maxAnnotsPerBarAllChrs","barCount","setIdeoMaxAnnotsPerBar","barCountRatio","ideoIsRotated","setProportionalBarHeight","reportGetHistogramBarPerformance","legendStyle","getIcon","icon","shape","getListItems","nameHeight","lineHeight","getLineHeight","writeLegend","legend","nameStyle","lineHeightCss","friendlyAnnots","rawAnnot","parseFriendlyAnnots","parseFriendlyKeys","chrAnnot","numAnnots","warn","warnIfTooManyAnnots","filledAnnots","getChrAnnotNodes","fillAnnots","shapes","triangle","circle","rectangle","getShapes","determineShape","writeTrackAnnots","x1","x2","writeOverlayAnnots","chrWidths","y1","y2","thisChrWidth","getHistogramPoints","writeHistogramAnnots","drawAnnotsByLayoutType","writeSyntenicRegion","syntenies","regionID","activeRegion","others","writeSyntenicRegionPolygons","syntenicRegion","r2","regions","writeSyntenicRegionPolygonsHorizontal","getRegionsR1AndR2","r1Offset","r2Offset","r1ChrDom","r1GenomeHorizontalXOffset","getCTM","r1GenomeVerticalXOffset","r2ChrDom","r2GenomeHorizontalXOffset","r2GenomeVerticalXOffset","writeSyntenicRegionLines","stroke","writeSyntenicRegionLabels","regionId","rangeIds","r1Width","drawSynteny","syntenicRegions","writeSyntenicRegions","drawSyntenyCollinear","drawSyntenyCollinearHorizontal","restoreDefaultTracks","trackIndexes","displayedRawAnnotsByChr","displayedAnnots","getDisplayedRawAnnotsByChr","setAnnotsByChr","setAnnots","trackIndexOriginal","numAvailTracks","getSetAnnotsByChr","colorMap","addAnnot","annotationTracks","annotTrack","addClientAnnot","addSparseServerAnnot","addBasicClientAnnot","getAnnotDomId","annotIndex","addAnnotsForChr","shouldAssignDomId","annotSortFunction","warnOfUndefinedChromosome","addAnnots","unorderedAnnots","orderAnnotContainers","numOmittedTracks","sendTrackAndAnnotWarnings","ExpressionMatrixParser","setRawAnnots","fetchCoordinates","coordinates","parseExpressionMatrix","ensemblData","gene","expressions","cells","downloadAnnotations","annotDescriptions","ensemblId","annotsTsv","annotsHref","encodeURIComponent","evt","MouseEvent","body","initAnnotHeight","localAnnotationsPath","initNumTracksAndBarWidth","initTooltip","onWillAddAnnotLabel","onWillAddAnnotLabelCallback","initAnnotLabel","sortChromosomes","displayedTracks","inflateHeatmaps","seen","duplicates","detectDuplicateChrsInRawAnnots","accumulator","annotsUrl","is2dHeatmap","extension","alert","validateAnnotsUrl","rawAnnotsResponse","chrArray","highlight","chrNames","highlightsHtml","chrId","chrDom","unhighlight","highlightsSelector","getTaxidFromEutils","orgName","taxonomySearch","esearch","esearchresult","idlist","esummary","result","commonname","setAssemblyAndChromosomes","originalChrs","filteredChrs","urlOrg","getAssemblyAndChromosomesFromEutils","chromosomesUrl","promise2","catch","errorMessage","splitBand","asmAndChrTaxidsArray","seenChrs","setTaxidData","asmChrTaxidsArray","isOrganismSupported","ideoOrg","getTaxidsForOrganismsInConfig","tmpChrs","chrsOrgSlugs","orgs","promise","getTaxidFromEutilsPromises","augmentedOrganismMetadata","orgNameAndTaxid","warning","customMetadata","populateNonNativeOrg","orgMetadata","orgFields","prepareTmpChrsAndTaxids","asmAccs","asmAndChrPromises","configOrganisms","sortedTaxids","sortTaxidsByOriginalOrganismOption","taxidInit","getIsMultiorganism","getTaxidsForOrganismsNotInConfig","parseChromosome","genome","cnIndex","subtype","subname","parseMitochondrion","parseChloroplastOrPlastid","parseApicoplast","substr","parseNuclear","getChrNameAndType","slen","assemblyAccession","asmSearchUrl","termStem","getAssemblySearchUrl","asmUid","asmSummaryUrl","fetchAssemblySummary","uids","qs","elink","webenv","linksets","getESearchUrlForChromosomes","esearchUrl","ids","ntSummary","fetchNucleotideSummary","results","seenChrId","parseChromosomes","rejectedReason","getPrevRight","prevLabelXRight","prevHiddenBoxIndex","textOffsets","updateShown","indexesToShow","overlapRight","prevRight","isBefore","hiddenIndex","doSkip","getIndexesToShow","offsets","textsLength","setBandsToShow","selectorsToShow","ithLength","drawBandLabelText","bandsToLabel","drawBandLabelStalk","getChrModels","getStainAndColors","gradients","stain","color1","color2","color3","getGradients","updateLines","lines","getStain","getLineObject","shouldSkipBand","chrsAreList","chrNotInList","thisChr","getBandsArray","bandsByChr","chrLength","setChromosomesByTaxid","chrBandsArray","updateChromosomes","getDelimiterTsvLinesAndInit","parseBands","setChrsByTaxidsWithBands","to","chrLengthBp","bpDomain","pxRange","nameSplit","fromToSplit","refineGenomicCoordinates","cm","getChrModel","getBasePairDomainAndPixelRange","xScale","selectedRegion","setBrush","setSelectedRegion","x0","writeBrush","position","cursorBrush","setCursorPosition","newPosition","offsetX","setCursor","drawSexChromosomes","sexChromosomeIndexes","sciLength","sci","homologIndex","appendHomolog","sexChrs","iscnStart","iscnLength","bpStart","bpStop","bpLength","pxStart","pxLength","getPx","throwBpToPxError","getBp","iscnStop","pxStop","throwPxToBpError","array8","arrayUntyped","array16","array32","arrayLengthen","arrayLengthenUntyped","arrayWiden","arrayWidenUntyped","bitarray","subarrays","masks","Uint8Array","Uint16Array","Uint32Array","lengthen","len","offset","dest","truncate","zeroExcept","zeroExceptMask","mask","only","onlyExcept","onlyOffset","onlyOne","heap_by","heap","sift","by","heapselect_by","queue","bisect_by","reg","path","REMOVED_INDEX","crossfilter","heapselect","permute","filters","removeData","dimension","groupAll","allFiltered","onChange","isElementFiltered","filterListeners","dataListeners","removeDataListeners","callbacks","newData","triggerOnChange","predicate","newIndex","removed","usePred","shouldRemove","index1","index2","index3","index4","maskForDimensions","dimensions","ignore_dimensions","iterable","accessorPath","newValues","iterablesIndexCount","iterablesIndexFilterStatus","refilterFunction","filterValue","filterValuePresent","filterAll","filterRange","filterFunction","filterExact","currentFilter","hasCurrentFilter","top_offset","hi0","toSkip","lo0","iterablesEmptyRows","bottom_offset","orderNatural","dispose","accessor","sortRange","cr_range","A","B","refilter","indexListeners","dimensionGroups","unshift","preAdd","postAdd","newIterablesIndexCount","newIterablesIndexFilterStatus","cr_index","unsortedIndex","sortMap","bounds","lo1","hi1","old_n0","oldValues","oldIndex","oldIterablesIndexFilterStatus","oldiiclength","index5","reIndex","oldDataIndex","filterIndexBounds","filterIndexFunction","added","valueIndexAdded","valueIndexRemoved","newAdded","newRemoved","indexLength","reduceCount","reduceSum","groupIndex","reduceAdd","reduceRemove","reduceInitial","n0old","groupWidth","groupCapacity","capacity","reset","resetNeeded","g0","oldGroups","initial","k0","groupIncrement","updateMany","resetMany","updateOne","resetOne","oldK","seenGroups","filterOne","filterOffset","notFilter","reduceValue","cb","eventName","unpackAnnots","unpackedAnnots","packAnnots","facet","annotsByFacet","facets","filterAnnots","filterSelections","filteredAnnots","selections","counts","fn","getFilteredResults","getChromosomePixels","hasBands","csLength","cs","pcenIndex","getPixelAndOtherData","getChrScale","getChrModelScaffold","fullChromosomeLabels","centromerePosition","firstBand","smallLength","getCentromerePosition","deleteExtraneousBands","deactivate","items","item","closeTools","display","handleToolClick","toolHeaders","toolHeader","trigger","getTrigger","tool","panel","getPanel","toElement","toId","panelElement","handleHideForHoverables","ideoSvg","canvasId","ideoSvgClone","getElementById","ctx","setTransform","imageSmoothingEnabled","XMLSerializer","serializeToString","domUrl","URL","webkitURL","img","svgBlob","Blob","createObjectURL","imgUrl","drawImage","revokeObjectURL","toDataURL","downloadPng","closeButton","initTools","elements","outsideClickListener","clickedOutsideCount","removeClickListener","hideOnClickOutside","handleGearClick","gear","showGearOnIdeogramHover","ModelAdapter","model","_model","ModelNoBandsAdapter","getModel","getCssClass","isMT","getArmColor","armColors","_getPolyploidArmColor","getBorderColor","chrBorderColor","getFillColor","chrFillColor","arm","centromere","ancestor","Range","_data","getColor","_getColor","Chromosome","adapter","_adapter","_color","_bumpCoefficient","TelocentricPChromosome","TelocentricQChromosome","MetacentricChromosome","_addPArmShape","clipPath","isPArmRendered","_getPArmShape","_addQArmShape","isQArmRendered","_getQArmShape","render","isFullyBanded","centromereFill","_renderArm","_renderRangeSet","fillColor","class","strokeWidth","rangeSet","rangesContainer","_getShapeData","firstQBand","rightTerminalPosition","x3","x2b","_renderBands","Boolean","_pArmOffset","_qArmOffset","homologOffset","defs","getInstance","numChrsInSet","chrSetSelector","ideoWidth","ideoInnerWrap","ideoMiddleWrap","ploidyPad","textElement","getChrSetLabelLines","fullLabels","renderChromosomeSetLabel","appendChromosomeSetLabels","appendChromosomeLabels","labelPosAttrs","scaleSvg","getLabelPositionAttrs","chrMargin2","updateChrIndex","rotateVerticalChromosomeLabels","tracksHeight","rotateHorizontalChromosomeLabels","u8","u16","u32","fleb","fdeb","clim","freb","eb","_a","fl","revfl","_b","fd","rev","hMap","cd","mb","co","le","rvb","sv","r_1","flt","fdt","bits","bits16","slc","BYTES_PER_ELEMENT","subarray","ec","err","ind","msg","nt","code","captureStackTrace","inflt","dat","buf","st","noBuf","noSt","cbuf","bl","nbuf","final","pos","bt","lm","dm","lbt","dbt","tbts","flrm","fdrm","hLit","hcLen","tl","ldt","clt","clb","clbmsk","clm","lt","dt","lms","dms","lpos","sym","dsym","et","gunzipSync","flg","zs","gzs","unzlibSync","decompressSync","inflateSync","td","TextDecoder","decode","stream","strFromU8","latin1","fromCharCode","dutf8","timeDiff","initAnalyzeRelatedGenes","rg","_didRelatedGenesFirstPlot","getRelatedGenesByType","relatedGenes","related","paralogous","interacting","searched","entries","entry","getRelatedGenesTooltipAnalytics","timeSincePrevTooltip","prevAnnotDomId","tooltipGene","tooltipRelatedType","countsByType","getCountsByType","numRelatedGenes","numParalogs","numInteractingGenes","searchedGene","perfTimes","getEnsemblId","ensemblPrefix","slimEnsemblId","padLength","padStart","async","initGeneCache","cacheDir","startTime","getEarlyTaxid","parseOrgMetadata","cache","caches","open","cacheUrl","splitDataDir","dataIndex","baseDir","getCacheUrl","fetchStartTime","cacheFetch","fetchEndTime","nameCaseMap","namesById","idsByName","lociByName","lociById","rawTsv","preAnnots","rawTsvSplit","rawStart","rawLength","locus","parseCacheLoop","annotsSortedByPosition","parseAnnots","parseCache","total","queueMicrotask","interactionArrowMap","getIxnTypeReference","ixnsByPwid","ixns","ixnType","getMatches","gpml","genes","matchedLabel","textLabel","graphId","groupRef","geneGraphIds","groupSelectors","ggr","geneGroups","groupId","geneGroupGraphIds","fetchMyGeneInfo","queryString","parseNameAndEnsemblIdFromMgiGene","genomic_pos","ensemblgene","throwGeneNotFound","geneSymbol","fetchGenes","queryStringBase","hits","isSymbol","locusMap","nameMap","nameLc","hit","fetchGenesFromCache","fromGeneCache","getGenomicPos","genomicPos","parseAnnotFromMgiGene","moveLegend","ideoInnerDom","setRelatedDecorPad","prepend","processInteractions","interactions","seenNameIds","orgNameSimple","uint8Array","interaction","species","fields","rawIxns","sortedRawIxns","rawIxn","nameId","isRelevant","isGeneSymbol","ixn","maybeGeneSymbol","isInteractionRelevant","pathwayId","fetchInteractions","symbols","descriptionObj","pathwayIds","pathwayNames","ixnsDescription","pathwaysBase","links","describeInteractions","mergeDescriptions","pathwayIdsByInteractingGene","descObj","gpmlsByInteractingGene","ixnGene","gpmlUrl","rawGpml","fetchGpmls","fetchInteractionAnnots","relatedAnnots","finishPlotRelatedGenes","processParalogs","homologs","fetchEnsembl","homologies","ensemblIds","homolog","fetchParalogPositionsFromMyGeneInfo","fetchParalogs","paralogs","sortAnnotsByRelatedStatus","aName","bName","aColor","bColor","sortGeneNames","mergedDesc","descriptions","otherDesc","mergeAnnots","unmergedAnnots","seenAnnots","mergedAnnots","mergedAnnot","annotsInList","includedAnnots","applyAnnotsIncludeList","onFindRelatedGenesCallback","showAnnotLabels","updated","sortedChrNames","relevanceSortedAnnotsNamesByChr","annotNames","setRelatedAnnotDomIds","otherType","numThisRelated","numOtherRelated","timestampFirstPlot","totalLastPlotDiff","totalFirstPlot","_relatedGenesFirstPlotType","analyzePlotTimes","adjustPlaceAndVisibility","ideoContainerDom","visibility","marginLeft","marginRight","overflowY","didAdjustIdeogramLegend","ideoDom","legendWidth","minWidth","plotRelatedGenes","plotGeneHints","href","annotSel","el","cursor","oldDesc","processSearchedGene","relatedLegend","timeTotal","timeTotalFirstPlot","timeTotalLastPlotDiff","timeParalogs","timeInteractingGenes","timeSearchedGene","firstPlotType","analytics","relatedGenesAnalytics","analyzeRelatedGenes","onPlotRelatedGenesCallback","decorateRelatedGene","summary","interactingGene","searchedGeneGraphIds","interactingGeneGraphIds","ie","matchingGraphIds","graphIds","graphicsXml","graphic","endGraphRefs","numMatchingPoints","isConnectedToSourceGene","searchedGeneIndex","nodeName","graphRef","arrowHead","isStart","parseInteractionGraphic","detailInteractions","detailAllInteractions","enrichedIxns","isSameByPwid","isSame","isDirectionSameByPwid","isDirectionSame","directionsByPwid","ixnTypeReference","pwid","isRefMatch","thisIsSame","thisIxnTypeReference","determineIxnsInPathwayAreSame","setIsSame","leftTypes","rightTypes","directedTypes","firstIxnType","directionReference","isPwDirectionSame","pwFirstIxnType","pwDirectionReference","direction","summarizeByDirection","summarizeInteractions","oldSummary","originalDisplay","addedTooltipClickHandler","annotByName","handleTooltipClick","legendHeaderStyle","citedLegend","kitConfig","eutils","_fetch","method","chrAName","chrBName","aIsCP","bIsCP","aIsMT","bIsMT","aIsAP","bIsAP","aIsNuclear","bIsNuclear","chrTypes","getChrSortNamesAndTypes","localeCompare","numeric","kitDefaults","clientFn","defaultFunction","newFunction","ideogram","onPlotRelatedGenes","onFindRelatedGenes","getTooltipAnalytics","_initRelatedGenes","annotsPath","_initGeneHints"],"sourceRoot":""}