igv 3.3.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/igv.d.ts +665 -0
- package/dist/igv.esm.js +510 -178
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +510 -178
- package/dist/igv.min.js +9 -9
- package/dist/igv.min.js.map +1 -1
- package/package.json +5 -3
package/dist/igv.esm.js
CHANGED
|
@@ -373,7 +373,7 @@ class Dialog {
|
|
|
373
373
|
* @param x
|
|
374
374
|
* @returns {boolean}
|
|
375
375
|
*/
|
|
376
|
-
function isString$
|
|
376
|
+
function isString$3(x) {
|
|
377
377
|
return typeof x === "string" || x instanceof String
|
|
378
378
|
}
|
|
379
379
|
|
|
@@ -473,7 +473,7 @@ function getFilename$2(urlOrFile) {
|
|
|
473
473
|
|
|
474
474
|
if (urlOrFile.name !== undefined) {
|
|
475
475
|
return urlOrFile.name
|
|
476
|
-
} else if (isString$
|
|
476
|
+
} else if (isString$3(urlOrFile)) {
|
|
477
477
|
|
|
478
478
|
let index = urlOrFile.lastIndexOf("/");
|
|
479
479
|
let filename = index < 0 ? urlOrFile : urlOrFile.substr(index + 1);
|
|
@@ -10678,7 +10678,7 @@ const isNumber = function (num) {
|
|
|
10678
10678
|
};
|
|
10679
10679
|
|
|
10680
10680
|
async function getFilename$1(url) {
|
|
10681
|
-
if (isString$
|
|
10681
|
+
if (isString$3(url) && url.startsWith("https://drive.google.com")) {
|
|
10682
10682
|
// This will fail if Google API key is not defined
|
|
10683
10683
|
if (getApiKey() === undefined) {
|
|
10684
10684
|
throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
|
|
@@ -10720,7 +10720,7 @@ function prettyBasePairNumber(raw) {
|
|
|
10720
10720
|
|
|
10721
10721
|
|
|
10722
10722
|
function isDataURL(obj) {
|
|
10723
|
-
return (isString$
|
|
10723
|
+
return (isString$3(obj) && obj.startsWith("data:"))
|
|
10724
10724
|
}
|
|
10725
10725
|
|
|
10726
10726
|
function createColumn(columnContainer, className) {
|
|
@@ -10946,7 +10946,7 @@ function createMenuElements$1(itemList, popover) {
|
|
|
10946
10946
|
return list;
|
|
10947
10947
|
}
|
|
10948
10948
|
|
|
10949
|
-
/*! @license DOMPurify 3.2.
|
|
10949
|
+
/*! @license DOMPurify 3.2.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.6/LICENSE */
|
|
10950
10950
|
|
|
10951
10951
|
const {
|
|
10952
10952
|
entries,
|
|
@@ -11150,7 +11150,7 @@ const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
|
11150
11150
|
const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
|
|
11151
11151
|
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
|
|
11152
11152
|
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
11153
|
-
const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
11153
|
+
const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
11154
11154
|
);
|
|
11155
11155
|
const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
11156
11156
|
const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
|
@@ -11247,7 +11247,7 @@ const _createHooksMap = function _createHooksMap() {
|
|
|
11247
11247
|
function createDOMPurify() {
|
|
11248
11248
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
11249
11249
|
const DOMPurify = root => createDOMPurify(root);
|
|
11250
|
-
DOMPurify.version = '3.2.
|
|
11250
|
+
DOMPurify.version = '3.2.6';
|
|
11251
11251
|
DOMPurify.removed = [];
|
|
11252
11252
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
11253
11253
|
// Not running in a browser, provide a factory function
|
|
@@ -11486,8 +11486,8 @@ function createDOMPurify() {
|
|
|
11486
11486
|
URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
11487
11487
|
DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
|
|
11488
11488
|
FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
11489
|
-
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
|
|
11490
|
-
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
11489
|
+
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
|
|
11490
|
+
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
|
|
11491
11491
|
USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
|
|
11492
11492
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
11493
11493
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
@@ -11852,7 +11852,7 @@ function createDOMPurify() {
|
|
|
11852
11852
|
allowedTags: ALLOWED_TAGS
|
|
11853
11853
|
});
|
|
11854
11854
|
/* Detect mXSS attempts abusing namespace confusion */
|
|
11855
|
-
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
|
|
11855
|
+
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
|
|
11856
11856
|
_forceRemove(currentNode);
|
|
11857
11857
|
return true;
|
|
11858
11858
|
}
|
|
@@ -12004,7 +12004,8 @@ function createDOMPurify() {
|
|
|
12004
12004
|
value: attrValue
|
|
12005
12005
|
} = attr;
|
|
12006
12006
|
const lcName = transformCaseFunc(name);
|
|
12007
|
-
|
|
12007
|
+
const initValue = attrValue;
|
|
12008
|
+
let value = name === 'value' ? initValue : stringTrim(initValue);
|
|
12008
12009
|
/* Execute a hook if present */
|
|
12009
12010
|
hookEvent.attrName = lcName;
|
|
12010
12011
|
hookEvent.attrValue = value;
|
|
@@ -12030,10 +12031,9 @@ function createDOMPurify() {
|
|
|
12030
12031
|
if (hookEvent.forceKeepAttr) {
|
|
12031
12032
|
continue;
|
|
12032
12033
|
}
|
|
12033
|
-
/* Remove attribute */
|
|
12034
|
-
_removeAttribute(name, currentNode);
|
|
12035
12034
|
/* Did the hooks approve of the attribute? */
|
|
12036
12035
|
if (!hookEvent.keepAttr) {
|
|
12036
|
+
_removeAttribute(name, currentNode);
|
|
12037
12037
|
continue;
|
|
12038
12038
|
}
|
|
12039
12039
|
/* Work around a security issue in jQuery 3.0 */
|
|
@@ -12050,6 +12050,7 @@ function createDOMPurify() {
|
|
|
12050
12050
|
/* Is `value` valid for this attribute? */
|
|
12051
12051
|
const lcTag = transformCaseFunc(currentNode.nodeName);
|
|
12052
12052
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
12053
|
+
_removeAttribute(name, currentNode);
|
|
12053
12054
|
continue;
|
|
12054
12055
|
}
|
|
12055
12056
|
/* Handle attributes that require Trusted Types */
|
|
@@ -12070,19 +12071,23 @@ function createDOMPurify() {
|
|
|
12070
12071
|
}
|
|
12071
12072
|
}
|
|
12072
12073
|
/* Handle invalid data-* attribute set by try-catching it */
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12074
|
+
if (value !== initValue) {
|
|
12075
|
+
try {
|
|
12076
|
+
if (namespaceURI) {
|
|
12077
|
+
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
12078
|
+
} else {
|
|
12079
|
+
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
12080
|
+
currentNode.setAttribute(name, value);
|
|
12081
|
+
}
|
|
12082
|
+
if (_isClobbered(currentNode)) {
|
|
12083
|
+
_forceRemove(currentNode);
|
|
12084
|
+
} else {
|
|
12085
|
+
arrayPop(DOMPurify.removed);
|
|
12086
|
+
}
|
|
12087
|
+
} catch (_) {
|
|
12088
|
+
_removeAttribute(name, currentNode);
|
|
12084
12089
|
}
|
|
12085
|
-
}
|
|
12090
|
+
}
|
|
12086
12091
|
}
|
|
12087
12092
|
/* Execute a hook if present */
|
|
12088
12093
|
_executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
|
|
@@ -16903,7 +16908,7 @@ function findFeatureAfterCenter(featureList, center, direction = true) {
|
|
|
16903
16908
|
*/
|
|
16904
16909
|
|
|
16905
16910
|
const fixColor = (colorString) => {
|
|
16906
|
-
if (isString$
|
|
16911
|
+
if (isString$3(colorString)) {
|
|
16907
16912
|
return (colorString.indexOf(",") > 0 && !(colorString.startsWith("rgb(") || colorString.startsWith("rgba("))) ?
|
|
16908
16913
|
`rgb(${colorString})` : colorString
|
|
16909
16914
|
} else {
|
|
@@ -16975,7 +16980,7 @@ class TrackBase {
|
|
|
16975
16980
|
this.name = config.name || config.label;
|
|
16976
16981
|
} else if (isFile(config.url)) {
|
|
16977
16982
|
this.name = config.url.name;
|
|
16978
|
-
} else if (isString$
|
|
16983
|
+
} else if (isString$3(config.url) && !config.url.startsWith("data:")) {
|
|
16979
16984
|
this.name = getFilename$2(config.url);
|
|
16980
16985
|
}
|
|
16981
16986
|
|
|
@@ -20897,7 +20902,7 @@ class FeatureFileReader {
|
|
|
20897
20902
|
* THE SOFTWARE.
|
|
20898
20903
|
*/
|
|
20899
20904
|
|
|
20900
|
-
const isString$
|
|
20905
|
+
const isString$2 = isString$3;
|
|
20901
20906
|
|
|
20902
20907
|
|
|
20903
20908
|
class CustomServiceReader {
|
|
@@ -20936,7 +20941,7 @@ class CustomServiceReader {
|
|
|
20936
20941
|
if (data) {
|
|
20937
20942
|
if (typeof this.config.parser === "function") {
|
|
20938
20943
|
features = this.config.parser(data);
|
|
20939
|
-
} else if (isString$
|
|
20944
|
+
} else if (isString$2(data)) {
|
|
20940
20945
|
features = JSON.parse(data);
|
|
20941
20946
|
} else {
|
|
20942
20947
|
features = data;
|
|
@@ -22337,7 +22342,7 @@ function overlaps(item, chrIdx1, startBase, chrIdx2, endBase) {
|
|
|
22337
22342
|
}
|
|
22338
22343
|
|
|
22339
22344
|
function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
22340
|
-
|
|
22345
|
+
//biggenepred
|
|
22341
22346
|
if ("biginteract" === format || (autoSql && ('chromatinInteract' === autoSql.table || 'interact' === autoSql.table))) {
|
|
22342
22347
|
return decodeInteract
|
|
22343
22348
|
} else {
|
|
@@ -22384,9 +22389,20 @@ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
|
22384
22389
|
const extraStart = definedFieldCount;
|
|
22385
22390
|
for (let i = extraStart; i < fieldCount; i++) {
|
|
22386
22391
|
if (i < autoSql.fields.length) {
|
|
22392
|
+
|
|
22387
22393
|
const name = autoSql.fields[i].name;
|
|
22388
|
-
|
|
22389
|
-
|
|
22394
|
+
|
|
22395
|
+
if (name === "exonFrames") {
|
|
22396
|
+
const frameOffsets = tokens[i - 3].replace(/,$/, '').split(',');
|
|
22397
|
+
for (let i = 0; i < feature.exons.length; i++) {
|
|
22398
|
+
const exon = feature.exons[i];
|
|
22399
|
+
const fo = parseInt(frameOffsets[i]);
|
|
22400
|
+
if (fo != -1) exon.readingFrame = fo;
|
|
22401
|
+
}
|
|
22402
|
+
} else {
|
|
22403
|
+
const value = tokens[i - 3];
|
|
22404
|
+
feature[name] = value;
|
|
22405
|
+
}
|
|
22390
22406
|
}
|
|
22391
22407
|
}
|
|
22392
22408
|
}
|
|
@@ -22432,6 +22448,7 @@ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
|
22432
22448
|
|
|
22433
22449
|
return feature
|
|
22434
22450
|
}
|
|
22451
|
+
|
|
22435
22452
|
}
|
|
22436
22453
|
|
|
22437
22454
|
function findUTRs(exons, cdStart, cdEnd) {
|
|
@@ -23019,14 +23036,14 @@ class BWReader {
|
|
|
23019
23036
|
// TODO -- align all feature attribute names with UCSC, an use specific column
|
|
23020
23037
|
for (let key of Object.keys(f)) {
|
|
23021
23038
|
const v = f[key];
|
|
23022
|
-
if (isString$
|
|
23039
|
+
if (isString$3(v) && v.toLowerCase() === term.toLowerCase()) {
|
|
23023
23040
|
return true
|
|
23024
23041
|
}
|
|
23025
23042
|
}
|
|
23026
23043
|
return false
|
|
23027
23044
|
});
|
|
23028
23045
|
if (matching.length > 0) {
|
|
23029
|
-
return matching.reduce((l, f) => (l.end - l.start) > (f.end - f.start) ? l : f,
|
|
23046
|
+
return matching.reduce((l, f) => (l.end - l.start) > (f.end - f.start) ? l : f, matching[0])
|
|
23030
23047
|
} else {
|
|
23031
23048
|
return undefined
|
|
23032
23049
|
}
|
|
@@ -23317,7 +23334,7 @@ class BWReader {
|
|
|
23317
23334
|
class ZoomLevelHeader {
|
|
23318
23335
|
constructor(index, byteBuffer) {
|
|
23319
23336
|
this.index = index;
|
|
23320
|
-
this.reductionLevel = byteBuffer.
|
|
23337
|
+
this.reductionLevel = byteBuffer.getUInt();
|
|
23321
23338
|
this.reserved = byteBuffer.getInt();
|
|
23322
23339
|
this.dataOffset = byteBuffer.getLong();
|
|
23323
23340
|
this.indexOffset = byteBuffer.getLong();
|
|
@@ -26920,7 +26937,7 @@ function renderAminoAcidSequence(ctx, strand, leftExon, exon, riteExon, bpStart,
|
|
|
26920
26937
|
let aaLetter;
|
|
26921
26938
|
if (undefined === aminoAcidLetter) {
|
|
26922
26939
|
|
|
26923
|
-
if(sequenceInterval.hasSequence(start, end)) {
|
|
26940
|
+
if (sequenceInterval.hasSequence(start, end)) {
|
|
26924
26941
|
|
|
26925
26942
|
const sequence = sequenceInterval.getSequence(start, end);
|
|
26926
26943
|
if (sequence && 3 === sequence.length) {
|
|
@@ -27056,7 +27073,8 @@ function renderFeatureLabel(ctx, feature, featureX, featureX1, featureY, referen
|
|
|
27056
27073
|
try {
|
|
27057
27074
|
ctx.save();
|
|
27058
27075
|
|
|
27059
|
-
|
|
27076
|
+
const labelField = this.config.labelField ? this.config.labelField : 'name';
|
|
27077
|
+
let name = feature[labelField];
|
|
27060
27078
|
if (name === undefined && feature.gene) name = feature.gene.name;
|
|
27061
27079
|
if (name === undefined) name = feature.id || feature.ID;
|
|
27062
27080
|
if (!name || name === '.') return
|
|
@@ -27089,20 +27107,13 @@ function renderFeatureLabel(ctx, feature, featureX, featureX1, featureY, referen
|
|
|
27089
27107
|
if (options.labelAllFeatures || xleft > lastLabelX || selected) {
|
|
27090
27108
|
options.rowLastLabelX[feature.row] = xright;
|
|
27091
27109
|
|
|
27092
|
-
|
|
27093
|
-
|
|
27094
|
-
|
|
27095
|
-
|
|
27096
|
-
|
|
27097
|
-
|
|
27098
|
-
|
|
27099
|
-
ctx.clearRect(
|
|
27100
|
-
centerX - textMetrics.width / 2 - 1,
|
|
27101
|
-
labelY - textMetrics.actualBoundingBoxAscent - 1,
|
|
27102
|
-
textMetrics.width + 2,
|
|
27103
|
-
textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent + 2);
|
|
27104
|
-
IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform);
|
|
27105
|
-
}
|
|
27110
|
+
ctx.clearRect(
|
|
27111
|
+
centerX - textMetrics.width / 2 - 1,
|
|
27112
|
+
labelY - textMetrics.actualBoundingBoxAscent - 1,
|
|
27113
|
+
textMetrics.width + 2,
|
|
27114
|
+
textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent + 2);
|
|
27115
|
+
IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform);
|
|
27116
|
+
|
|
27106
27117
|
}
|
|
27107
27118
|
|
|
27108
27119
|
} finally {
|
|
@@ -27516,7 +27527,11 @@ class FeatureTrack extends TrackBase {
|
|
|
27516
27527
|
// If drawing amino acids fetch cached sequence interval. It is not needed if track does not support AA, but
|
|
27517
27528
|
// costs nothing since only a reference to a cached object is fetched.
|
|
27518
27529
|
if (bpPerPixel < aminoAcidSequenceRenderThreshold) {
|
|
27519
|
-
|
|
27530
|
+
// Restrict the range requested to the limits: 1-chromosome.bpLength
|
|
27531
|
+
const chromosome = this.browser.genome.getChromosome(referenceFrame.chr);
|
|
27532
|
+
const chromosomeEnd = chromosome.bpLength;
|
|
27533
|
+
options.sequenceInterval = this.browser.genome.getSequenceInterval(referenceFrame.chr,
|
|
27534
|
+
bpStart > 0 ? bpStart : 0, bpEnd > chromosomeEnd ? chromosomeEnd : bpEnd);
|
|
27520
27535
|
}
|
|
27521
27536
|
|
|
27522
27537
|
|
|
@@ -27643,7 +27658,7 @@ class FeatureTrack extends TrackBase {
|
|
|
27643
27658
|
fd.name &&
|
|
27644
27659
|
fd.name.toLowerCase() === "name" &&
|
|
27645
27660
|
fd.value &&
|
|
27646
|
-
isString$
|
|
27661
|
+
isString$3(fd.value) &&
|
|
27647
27662
|
!fd.value.startsWith("<")) {
|
|
27648
27663
|
const href = infoURL.replace("$$", feature.name);
|
|
27649
27664
|
fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
|
|
@@ -28287,7 +28302,7 @@ class BlatTrack extends FeatureTrack {
|
|
|
28287
28302
|
async postInit() {
|
|
28288
28303
|
if(!this.featureSource) {
|
|
28289
28304
|
// This will be the case when restoring from a session
|
|
28290
|
-
const db = this.browser.genome.
|
|
28305
|
+
const db = this.browser.genome.ucscID; // TODO -- blat specific property
|
|
28291
28306
|
const url = this.browser.config["blatServerURL"];
|
|
28292
28307
|
const features = await blat({url, userSeq: this.sequence, db});
|
|
28293
28308
|
this._features = features;
|
|
@@ -28304,7 +28319,7 @@ class BlatTrack extends FeatureTrack {
|
|
|
28304
28319
|
if (undefined === this.table) {
|
|
28305
28320
|
|
|
28306
28321
|
const rows = this._features.map(f => [
|
|
28307
|
-
f.chr,
|
|
28322
|
+
this.browser.genome.getChromosomeDisplayName(f.chr),
|
|
28308
28323
|
(f.start + 1),
|
|
28309
28324
|
f.end,
|
|
28310
28325
|
f.strand,
|
|
@@ -28379,7 +28394,7 @@ async function createBlatTrack({sequence, browser, name, title}) {
|
|
|
28379
28394
|
|
|
28380
28395
|
try {
|
|
28381
28396
|
|
|
28382
|
-
const db = browser.genome.
|
|
28397
|
+
const db = browser.genome.ucscID; // TODO -- blat specific property
|
|
28383
28398
|
const url = browser.config["blatServerURL"] || defaultBlatServer;
|
|
28384
28399
|
const features = await blat({url, userSeq: sequence, db});
|
|
28385
28400
|
|
|
@@ -28621,7 +28636,7 @@ class SequenceTrack {
|
|
|
28621
28636
|
}
|
|
28622
28637
|
|
|
28623
28638
|
items.push({
|
|
28624
|
-
label: 'BLAT
|
|
28639
|
+
label: 'BLAT visible sequence',
|
|
28625
28640
|
click: async () => {
|
|
28626
28641
|
let sequence = await this.browser.genome.getSequence(chr, start, end);
|
|
28627
28642
|
if (sequence) {
|
|
@@ -30609,13 +30624,6 @@ class TrackDbHub {
|
|
|
30609
30624
|
this.trackStanzas = trackStanzas;
|
|
30610
30625
|
}
|
|
30611
30626
|
|
|
30612
|
-
findCytobandURL() {
|
|
30613
|
-
for (const t of this.trackStanzas) {
|
|
30614
|
-
if (t.name === "cytoBandIdeo" && t.hasProperty("bigDataUrl")) {
|
|
30615
|
-
return t.getProperty("bigDataUrl")
|
|
30616
|
-
}
|
|
30617
|
-
}
|
|
30618
|
-
}
|
|
30619
30627
|
|
|
30620
30628
|
getSupportedTrackCount() {
|
|
30621
30629
|
let count = 0;
|
|
@@ -30955,6 +30963,7 @@ function parseMetadata(metadata) {
|
|
|
30955
30963
|
|
|
30956
30964
|
const idMappings = new Map([
|
|
30957
30965
|
["hg38", "GCF_000001405.40"],
|
|
30966
|
+
["hg38_1kg", "GCF_000001405.40"],
|
|
30958
30967
|
["mm39", "GCF_000001635.27"],
|
|
30959
30968
|
["mm10", "GCF_000001635.26"],
|
|
30960
30969
|
["bosTau9", "GCF_002263795.1"],
|
|
@@ -30991,12 +31000,16 @@ class Hub {
|
|
|
30991
31000
|
this.genomeStanzas = genomeStanzas;
|
|
30992
31001
|
this.trackStanzas = trackStanzas;
|
|
30993
31002
|
this.groupStanzas = groupStanzas;
|
|
31003
|
+
this.cytobandStanza = null;
|
|
30994
31004
|
this.trackHubMap = new Map();
|
|
30995
31005
|
|
|
30996
31006
|
// trackStanzas will not be null if this is a "onefile" hub
|
|
30997
31007
|
if (trackStanzas) {
|
|
30998
31008
|
const genomeId = genomeStanzas[0].getProperty("genome"); // Assumption here this is a single genome hub
|
|
30999
31009
|
this.trackHubMap.set(genomeId, new TrackDbHub(trackStanzas, groupStanzas));
|
|
31010
|
+
|
|
31011
|
+
// Search for cytoband track. This supports a special but important case -- Genark assembly hubs
|
|
31012
|
+
this.cytobandStanza = this.trackStanzas.find(t => t.name === "cytoBandIdeo" && t.hasProperty("bigDataUrl")) || null;
|
|
31000
31013
|
}
|
|
31001
31014
|
}
|
|
31002
31015
|
|
|
@@ -31098,6 +31111,10 @@ isPcr dynablat-01.soe.ucsc.edu 4040 dynamic GCF/000/186/305/GCF_000186305.1
|
|
|
31098
31111
|
config.twoBitBptURL = genomeStanza.getProperty("twoBitBptUrl");
|
|
31099
31112
|
}
|
|
31100
31113
|
|
|
31114
|
+
if(this.cytobandStanza){
|
|
31115
|
+
config.cytobandBbURL = this.cytobandStanza.getProperty("bigDataUrl");
|
|
31116
|
+
}
|
|
31117
|
+
|
|
31101
31118
|
if (this.hubStanza.hasProperty("longLabel")) {
|
|
31102
31119
|
config.description = this.hubStanza.getProperty("longLabel").replace("/", "\n");
|
|
31103
31120
|
} else {
|
|
@@ -31257,7 +31274,6 @@ isPcr dynablat-01.soe.ucsc.edu 4040 dynamic GCF/000/186/305/GCF_000186305.1
|
|
|
31257
31274
|
if (t.hasProperty("searchTrix")) {
|
|
31258
31275
|
config.trixURL = t.getProperty("searchTrix");
|
|
31259
31276
|
}
|
|
31260
|
-
|
|
31261
31277
|
if (t.hasProperty("group")) {
|
|
31262
31278
|
config._group = t.getProperty("group");
|
|
31263
31279
|
if (this.groupPriorityMap && this.groupPriorityMap.has(config._group)) {
|
|
@@ -31266,6 +31282,12 @@ isPcr dynablat-01.soe.ucsc.edu 4040 dynamic GCF/000/186/305/GCF_000186305.1
|
|
|
31266
31282
|
this.groupPriorityMap.set(config._group, nextPriority);
|
|
31267
31283
|
}
|
|
31268
31284
|
}
|
|
31285
|
+
const labelFields = t.hasProperty("defaultLabelFields") ?
|
|
31286
|
+
t.getProperty("defaultLabelFields") :
|
|
31287
|
+
t.getProperty("labelFields");
|
|
31288
|
+
if (labelFields) {
|
|
31289
|
+
config.labelField = labelFields.split(",")[0];
|
|
31290
|
+
}
|
|
31269
31291
|
|
|
31270
31292
|
return config
|
|
31271
31293
|
}
|
|
@@ -31406,9 +31428,9 @@ async function loadStanzas(url) {
|
|
|
31406
31428
|
}
|
|
31407
31429
|
|
|
31408
31430
|
|
|
31409
|
-
const
|
|
31410
|
-
const key = line.substring(0,
|
|
31411
|
-
let value = line.substring(
|
|
31431
|
+
const index = line.indexOf(' ');
|
|
31432
|
+
const key = line.substring(0, index).trim();
|
|
31433
|
+
let value = line.substring(index + 1).trim();
|
|
31412
31434
|
|
|
31413
31435
|
if (key === "type") {
|
|
31414
31436
|
// The "type" property contains format and sometimes other information. For example, data range
|
|
@@ -31491,8 +31513,8 @@ function getHost(url) {
|
|
|
31491
31513
|
return host
|
|
31492
31514
|
}
|
|
31493
31515
|
|
|
31494
|
-
const DEFAULT_GENOMES_URL = "https://igv.org/genomes/
|
|
31495
|
-
const BACKUP_GENOMES_URL = "https://raw.githubusercontent.com/igvteam/igv-
|
|
31516
|
+
const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes3.json";
|
|
31517
|
+
const BACKUP_GENOMES_URL = "https://raw.githubusercontent.com/igvteam/igv-data/refs/heads/main/genomes/web/genomes.json";
|
|
31496
31518
|
|
|
31497
31519
|
const GenomeUtils = {
|
|
31498
31520
|
|
|
@@ -31517,7 +31539,7 @@ const GenomeUtils = {
|
|
|
31517
31539
|
} catch (error) {
|
|
31518
31540
|
try {
|
|
31519
31541
|
console.error("Error initializing default genomes:", error);
|
|
31520
|
-
const jsonArray = await igvxhr.loadJson(BACKUP_GENOMES_URL, {timeout:
|
|
31542
|
+
const jsonArray = await igvxhr.loadJson(BACKUP_GENOMES_URL, {timeout: 10000});
|
|
31521
31543
|
processJson(jsonArray, table);
|
|
31522
31544
|
} catch (e) {
|
|
31523
31545
|
console.error("Error initializing backup genomes:", error);
|
|
@@ -31547,7 +31569,7 @@ const GenomeUtils = {
|
|
|
31547
31569
|
expandReference: async function (alert, idOrConfig) {
|
|
31548
31570
|
|
|
31549
31571
|
// idOrConfig might be a json string? I'm actually not sure how this arises.
|
|
31550
|
-
if (isString$
|
|
31572
|
+
if (isString$3(idOrConfig) && idOrConfig.startsWith("{")) {
|
|
31551
31573
|
try {
|
|
31552
31574
|
idOrConfig = JSON.parse(idOrConfig);
|
|
31553
31575
|
} catch (e) {
|
|
@@ -31556,7 +31578,7 @@ const GenomeUtils = {
|
|
|
31556
31578
|
}
|
|
31557
31579
|
|
|
31558
31580
|
let genomeID;
|
|
31559
|
-
if (isString$
|
|
31581
|
+
if (isString$3(idOrConfig)) {
|
|
31560
31582
|
genomeID = idOrConfig;
|
|
31561
31583
|
} else if (idOrConfig.genome) {
|
|
31562
31584
|
genomeID = idOrConfig.genome;
|
|
@@ -32502,7 +32524,7 @@ class TrackViewport extends Viewport {
|
|
|
32502
32524
|
let track = this.trackView.track;
|
|
32503
32525
|
const dataList = track.popupData(clickState);
|
|
32504
32526
|
|
|
32505
|
-
const popupClickHandlerResult = this.browser.fireEvent('trackclick', [track, dataList]);
|
|
32527
|
+
const popupClickHandlerResult = this.browser.fireEvent('trackclick', [track, dataList, clickState.genomicLocation]);
|
|
32506
32528
|
|
|
32507
32529
|
let content;
|
|
32508
32530
|
if (undefined === popupClickHandlerResult || true === popupClickHandlerResult) {
|
|
@@ -40992,6 +41014,9 @@ function doSortByAttributes(sampleInfo, sampleKeys) {
|
|
|
40992
41014
|
return true
|
|
40993
41015
|
}
|
|
40994
41016
|
|
|
41017
|
+
/**
|
|
41018
|
+
* Track for segmented copy number, mut, maf and shoebox files.
|
|
41019
|
+
*/
|
|
40995
41020
|
class SegTrack extends TrackBase {
|
|
40996
41021
|
|
|
40997
41022
|
#sortDirections = new Map()
|
|
@@ -41139,7 +41164,10 @@ class SegTrack extends TrackBase {
|
|
|
41139
41164
|
}, e);
|
|
41140
41165
|
}
|
|
41141
41166
|
|
|
41142
|
-
menuItems.push({
|
|
41167
|
+
menuItems.push({
|
|
41168
|
+
element: createElementWithString('<div>Set color scale threshold</div>'),
|
|
41169
|
+
dialog: dialogPresentationHandler
|
|
41170
|
+
});
|
|
41143
41171
|
}
|
|
41144
41172
|
|
|
41145
41173
|
menuItems.push('<hr/>');
|
|
@@ -41170,12 +41198,75 @@ class SegTrack extends TrackBase {
|
|
|
41170
41198
|
|
|
41171
41199
|
getSamples() {
|
|
41172
41200
|
return {
|
|
41173
|
-
names: this.
|
|
41201
|
+
names: this.filteredSampleKeys,
|
|
41174
41202
|
height: this.sampleHeight,
|
|
41175
41203
|
yOffset: 0
|
|
41176
41204
|
}
|
|
41177
41205
|
}
|
|
41178
41206
|
|
|
41207
|
+
/**
|
|
41208
|
+
* Set the sample filter object. This is used to filter samples from the set based on values over a specified
|
|
41209
|
+
* genomic region. The values compared depend on the track data type:
|
|
41210
|
+
* - "seg" and "shoebox" -- average value over the region
|
|
41211
|
+
* - "mut" and "maf" -- count of features overlapping the region
|
|
41212
|
+
*
|
|
41213
|
+
* The method is asynchronous because it may need to fetch data from the server to compute the scores.
|
|
41214
|
+
* Computed scores are stored and used to filter the sample keys on demand.
|
|
41215
|
+
*
|
|
41216
|
+
* @param filterObject
|
|
41217
|
+
* @returns {Promise<void>}
|
|
41218
|
+
*/
|
|
41219
|
+
async setSampleFilter(filterObject) {
|
|
41220
|
+
if (!filterObject) {
|
|
41221
|
+
this.config.filterObject = undefined;
|
|
41222
|
+
this.filterObject = undefined;
|
|
41223
|
+
this.trackView.repaintViews();
|
|
41224
|
+
} else {
|
|
41225
|
+
const filterObjectCopy = Object.assign({}, filterObject);
|
|
41226
|
+
this.config.filterObject = filterObjectCopy;
|
|
41227
|
+
|
|
41228
|
+
filterObject.scores = await this.computeRegionScores(filterObject);
|
|
41229
|
+
this.filterObject = filterObject;
|
|
41230
|
+
this.trackView.checkContentHeight();
|
|
41231
|
+
this.trackView.repaintViews();
|
|
41232
|
+
}
|
|
41233
|
+
// TODO - store filter object in session
|
|
41234
|
+
}
|
|
41235
|
+
|
|
41236
|
+
|
|
41237
|
+
/**
|
|
41238
|
+
* Filter function for sample keys.
|
|
41239
|
+
*
|
|
41240
|
+
* @param sampleKey
|
|
41241
|
+
* @returns {boolean}
|
|
41242
|
+
*/
|
|
41243
|
+
filter(sampleKey) {
|
|
41244
|
+
if (this.filterObject) {
|
|
41245
|
+
const filterObject = this.filterObject;
|
|
41246
|
+
const scores = filterObject.scores;
|
|
41247
|
+
const score = scores[sampleKey];
|
|
41248
|
+
|
|
41249
|
+
if (this.type === 'seg') {
|
|
41250
|
+
if (filterObject.op === '>') {
|
|
41251
|
+
return score > filterObject.value
|
|
41252
|
+
} else if (filterObject.op === '<') {
|
|
41253
|
+
return score < filterObject.value
|
|
41254
|
+
}
|
|
41255
|
+
} else if (this.type === 'mut' || this.type === 'maf') {
|
|
41256
|
+
return 'HAS' === filterObject.op ? score : !score
|
|
41257
|
+
}
|
|
41258
|
+
}
|
|
41259
|
+
// else if (this.config.sampleFilter) {
|
|
41260
|
+
// return this.config.sampleFilter(sampleKey)
|
|
41261
|
+
// }
|
|
41262
|
+
return true
|
|
41263
|
+
}
|
|
41264
|
+
|
|
41265
|
+
get filteredSampleKeys() {
|
|
41266
|
+
return this.sampleKeys.filter(sampleKey => this.filter(sampleKey))
|
|
41267
|
+
}
|
|
41268
|
+
|
|
41269
|
+
|
|
41179
41270
|
async getFeatures(chr, start, end) {
|
|
41180
41271
|
const features = await this.featureSource.getFeatures({chr, start, end});
|
|
41181
41272
|
// New segments could conceivably add new samples
|
|
@@ -41183,8 +41274,10 @@ class SegTrack extends TrackBase {
|
|
|
41183
41274
|
|
|
41184
41275
|
if (this.initialSort) {
|
|
41185
41276
|
const sort = this.initialSort;
|
|
41277
|
+
|
|
41186
41278
|
if (sort.option === undefined || sort.option.toUpperCase() === "VALUE") {
|
|
41187
|
-
|
|
41279
|
+
const sortFeatures = (sort.chr === chr && sort.start >= start && sort.end <= end) ? features : undefined;
|
|
41280
|
+
this.sortByValue(sort, sortFeatures);
|
|
41188
41281
|
} else if ("ATTRIBUTE" === sort.option.toUpperCase() && sort.attribute) {
|
|
41189
41282
|
const sortDirection = "DESC" === sort.direction ? 1 : -1;
|
|
41190
41283
|
this.sortByAttribute(sort.attribute, sortDirection);
|
|
@@ -41199,6 +41292,7 @@ class SegTrack extends TrackBase {
|
|
|
41199
41292
|
|
|
41200
41293
|
IGVGraphics.fillRect(context, 0, pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
|
|
41201
41294
|
|
|
41295
|
+
|
|
41202
41296
|
if (features && features.length > 0) {
|
|
41203
41297
|
|
|
41204
41298
|
this.checkForLog(features);
|
|
@@ -41210,14 +41304,15 @@ class SegTrack extends TrackBase {
|
|
|
41210
41304
|
|
|
41211
41305
|
// Create a map for fast id -> row lookup
|
|
41212
41306
|
const samples = {};
|
|
41213
|
-
|
|
41307
|
+
const filteredKeys = this.filteredSampleKeys;
|
|
41308
|
+
filteredKeys.forEach(function (id, index) {
|
|
41214
41309
|
samples[id] = index;
|
|
41215
41310
|
});
|
|
41216
41311
|
|
|
41217
41312
|
let border;
|
|
41218
41313
|
switch (this.displayMode) {
|
|
41219
41314
|
case "FILL":
|
|
41220
|
-
this.sampleHeight = pixelHeight /
|
|
41315
|
+
this.sampleHeight = pixelHeight / filteredKeys.length;
|
|
41221
41316
|
border = 0;
|
|
41222
41317
|
break
|
|
41223
41318
|
|
|
@@ -41355,7 +41450,7 @@ class SegTrack extends TrackBase {
|
|
|
41355
41450
|
if (!features) return 0
|
|
41356
41451
|
const sampleHeight = ("SQUISHED" === this.displayMode) ? this.squishedRowHeight : this.expandedRowHeight;
|
|
41357
41452
|
this.updateSampleKeys(features);
|
|
41358
|
-
return this.
|
|
41453
|
+
return this.filteredSampleKeys.length * sampleHeight
|
|
41359
41454
|
}
|
|
41360
41455
|
|
|
41361
41456
|
/**
|
|
@@ -41364,16 +41459,38 @@ class SegTrack extends TrackBase {
|
|
|
41364
41459
|
async sortByValue(sort, featureList) {
|
|
41365
41460
|
|
|
41366
41461
|
const chr = sort.chr;
|
|
41462
|
+
const start = sort.position !== undefined ? sort.position - 1 : sort.start;
|
|
41463
|
+
const end = sort.end === undefined ? start + 1 : sort.end;
|
|
41464
|
+
const scores = await this.computeRegionScores({chr, start, end}, featureList);
|
|
41465
|
+
const d2 = (sort.direction === "ASC" ? 1 : -1);
|
|
41466
|
+
|
|
41467
|
+
this.sampleKeys.sort(function (a, b) {
|
|
41468
|
+
let s1 = scores[a];
|
|
41469
|
+
let s2 = scores[b];
|
|
41470
|
+
if (!s1) s1 = d2 * Number.MAX_VALUE;
|
|
41471
|
+
if (!s2) s2 = d2 * Number.MAX_VALUE;
|
|
41472
|
+
if (s1 === s2) return 0
|
|
41473
|
+
else if (s1 > s2) return d2
|
|
41474
|
+
else return d2 * -1
|
|
41475
|
+
});
|
|
41476
|
+
|
|
41477
|
+
this.config.sort = sort;
|
|
41478
|
+
this.trackView.repaintViews();
|
|
41479
|
+
}
|
|
41480
|
+
|
|
41481
|
+
|
|
41482
|
+
async computeRegionScores(filterObject, featureList) {
|
|
41483
|
+
|
|
41484
|
+
const chr = filterObject.chr;
|
|
41367
41485
|
let start, end;
|
|
41368
|
-
if (
|
|
41369
|
-
start =
|
|
41486
|
+
if (filterObject.position) {
|
|
41487
|
+
start = filterObject.position - 1;
|
|
41370
41488
|
end = start + 1;
|
|
41371
41489
|
} else {
|
|
41372
|
-
start =
|
|
41373
|
-
end =
|
|
41490
|
+
start = filterObject.start;
|
|
41491
|
+
end = filterObject.end;
|
|
41374
41492
|
}
|
|
41375
41493
|
|
|
41376
|
-
|
|
41377
41494
|
if (!featureList) {
|
|
41378
41495
|
featureList = await this.featureSource.getFeatures({chr, start, end});
|
|
41379
41496
|
}
|
|
@@ -41382,62 +41499,40 @@ class SegTrack extends TrackBase {
|
|
|
41382
41499
|
this.updateSampleKeys(featureList);
|
|
41383
41500
|
|
|
41384
41501
|
const scores = {};
|
|
41385
|
-
const
|
|
41502
|
+
const bpLength = end - start + 1;
|
|
41503
|
+
|
|
41504
|
+
const mutationTypes = filterObject.value ? new Set(filterObject.value) : undefined;
|
|
41505
|
+
|
|
41506
|
+
for (let segment of featureList) {
|
|
41507
|
+
if (segment.end < start) continue
|
|
41508
|
+
if (segment.start > end) break
|
|
41509
|
+
const sampleKey = segment.sampleKey || segment.sample;
|
|
41510
|
+
|
|
41511
|
+
if ("mut" === this.type) {
|
|
41512
|
+
if (mutationTypes) {
|
|
41513
|
+
const mutationType = segment.getAttribute("Variant_Classification");
|
|
41514
|
+
if (mutationTypes.has(mutationType)) {
|
|
41515
|
+
// Just count features overlapping region per sample
|
|
41516
|
+
scores[sampleKey] = (scores[sampleKey] || 0) + 1;
|
|
41517
|
+
}
|
|
41518
|
+
} else {
|
|
41519
|
+
// Just count features overlapping region per sample
|
|
41520
|
+
scores[sampleKey] = (scores[sampleKey] || 0) + 1;
|
|
41521
|
+
}
|
|
41522
|
+
} else {
|
|
41386
41523
|
|
|
41387
|
-
const sortSeg = () => {
|
|
41388
|
-
// Compute weighted average score for each sample
|
|
41389
|
-
const bpLength = end - start + 1;
|
|
41390
|
-
for (let segment of featureList) {
|
|
41391
|
-
if (segment.end < start) continue
|
|
41392
|
-
if (segment.start > end) break
|
|
41393
41524
|
const min = Math.max(start, segment.start);
|
|
41394
41525
|
const max = Math.min(end, segment.end);
|
|
41395
41526
|
const f = (max - min) / bpLength;
|
|
41396
|
-
|
|
41397
|
-
|
|
41398
|
-
scores[sampleKey] = s + f * segment.value;
|
|
41399
|
-
}
|
|
41400
|
-
|
|
41401
|
-
// Now sort sample names by score
|
|
41402
|
-
this.sampleKeys.sort(function (a, b) {
|
|
41403
|
-
let s1 = scores[a];
|
|
41404
|
-
let s2 = scores[b];
|
|
41405
|
-
if (!s1) s1 = d2 * Number.MAX_VALUE;
|
|
41406
|
-
if (!s2) s2 = d2 * Number.MAX_VALUE;
|
|
41407
|
-
if (s1 === s2) return 0
|
|
41408
|
-
else if (s1 > s2) return d2
|
|
41409
|
-
else return d2 * -1
|
|
41410
|
-
});
|
|
41411
|
-
};
|
|
41412
|
-
|
|
41413
|
-
const sortMut = () => {
|
|
41414
|
-
// Compute weighted average score for each sample
|
|
41415
|
-
for (let segment of featureList) {
|
|
41416
|
-
if (segment.end < start) continue
|
|
41417
|
-
if (segment.start > end) break
|
|
41418
|
-
const sampleKey = segment.sampleKey || segment.sample;
|
|
41419
|
-
if (!scores.hasOwnProperty(sampleKey) || segment.value.localeCompare(scores[sampleKey]) > 0) {
|
|
41420
|
-
scores[sampleKey] = segment.value;
|
|
41421
|
-
}
|
|
41422
|
-
}
|
|
41423
|
-
// Now sort sample names by score
|
|
41424
|
-
this.sampleKeys.sort(function (a, b) {
|
|
41425
|
-
let sa = scores[a] || "";
|
|
41426
|
-
let sb = scores[b] || "";
|
|
41427
|
-
return d2 * (sa.localeCompare(sb))
|
|
41428
|
-
});
|
|
41429
|
-
};
|
|
41430
|
-
|
|
41431
|
-
if ("mut" === this.type) {
|
|
41432
|
-
sortMut();
|
|
41433
|
-
} else {
|
|
41434
|
-
sortSeg();
|
|
41527
|
+
scores[sampleKey] = (scores[sampleKey] || 0) + f * segment.value;
|
|
41528
|
+
}
|
|
41435
41529
|
}
|
|
41436
41530
|
|
|
41437
|
-
|
|
41531
|
+
return scores
|
|
41438
41532
|
|
|
41439
41533
|
}
|
|
41440
41534
|
|
|
41535
|
+
|
|
41441
41536
|
sortByAttribute(attribute, sortDirection) {
|
|
41442
41537
|
|
|
41443
41538
|
this.sampleKeys = this.browser.sampleInfo.getSortedSampleKeysByAttribute(this.sampleKeys, attribute, sortDirection);
|
|
@@ -41506,7 +41601,7 @@ class SegTrack extends TrackBase {
|
|
|
41506
41601
|
const dirLabel = direction === "DESC" ? "descending" : "ascending";
|
|
41507
41602
|
const sortLabel = this.type === 'seg' || this.type === 'shoebox' ?
|
|
41508
41603
|
`Sort by value (${dirLabel})` :
|
|
41509
|
-
`Sort by
|
|
41604
|
+
`Sort by count (${dirLabel})`;
|
|
41510
41605
|
return {
|
|
41511
41606
|
label: sortLabel,
|
|
41512
41607
|
click: () => {
|
|
@@ -41518,7 +41613,6 @@ class SegTrack extends TrackBase {
|
|
|
41518
41613
|
end: Math.floor(genomicLocation + bpWidth)
|
|
41519
41614
|
};
|
|
41520
41615
|
sortHandler(sort);
|
|
41521
|
-
this.config.sort = sort;
|
|
41522
41616
|
}
|
|
41523
41617
|
}
|
|
41524
41618
|
})
|
|
@@ -41688,13 +41782,13 @@ class PairedAlignment {
|
|
|
41688
41782
|
}
|
|
41689
41783
|
}
|
|
41690
41784
|
|
|
41691
|
-
popupData(genomicLocation) {
|
|
41785
|
+
popupData(genomicLocation, hiddenTags, showTags) {
|
|
41692
41786
|
|
|
41693
|
-
let nameValues = this.firstAlignment.popupData(genomicLocation);
|
|
41787
|
+
let nameValues = this.firstAlignment.popupData(genomicLocation, hiddenTags, showTags);
|
|
41694
41788
|
|
|
41695
41789
|
if (this.secondAlignment) {
|
|
41696
41790
|
nameValues.push("-------------------------------");
|
|
41697
|
-
nameValues = nameValues.concat(this.secondAlignment.popupData(genomicLocation));
|
|
41791
|
+
nameValues = nameValues.concat(this.secondAlignment.popupData(genomicLocation, hiddenTags, showTags));
|
|
41698
41792
|
}
|
|
41699
41793
|
return nameValues
|
|
41700
41794
|
}
|
|
@@ -43408,7 +43502,7 @@ class BamAlignment {
|
|
|
43408
43502
|
isNegativeStrand() {
|
|
43409
43503
|
return (this.flags & READ_STRAND_FLAG$2) !== 0
|
|
43410
43504
|
}
|
|
43411
|
-
|
|
43505
|
+
|
|
43412
43506
|
isMateNegativeStrand() {
|
|
43413
43507
|
return (this.flags & MATE_STRAND_FLAG$2) !== 0
|
|
43414
43508
|
}
|
|
@@ -43456,7 +43550,7 @@ class BamAlignment {
|
|
|
43456
43550
|
return (genomicLocation >= s && genomicLocation <= (s + l))
|
|
43457
43551
|
}
|
|
43458
43552
|
|
|
43459
|
-
popupData(genomicLocation) {
|
|
43553
|
+
popupData(genomicLocation, hiddenTags, showTags) {
|
|
43460
43554
|
|
|
43461
43555
|
// if the user clicks on a base next to an insertion, show just the
|
|
43462
43556
|
// inserted bases in a popup (like in desktop IGV).
|
|
@@ -43539,15 +43633,20 @@ class BamAlignment {
|
|
|
43539
43633
|
}
|
|
43540
43634
|
}
|
|
43541
43635
|
|
|
43542
|
-
const hiddenTags = new Set(['SA', 'MD']);
|
|
43543
43636
|
nameValues.push('<hr/>');
|
|
43544
43637
|
for (let key in tagDict) {
|
|
43545
|
-
if (
|
|
43638
|
+
if (showTags?.has(key)) {
|
|
43639
|
+
nameValues.push({name: key, value: tagDict[key]});
|
|
43640
|
+
} else if (showTags) {
|
|
43641
|
+
hiddenTags.add(key);
|
|
43642
|
+
} else if (!hiddenTags.has(key)) {
|
|
43546
43643
|
nameValues.push({name: key, value: tagDict[key]});
|
|
43547
43644
|
}
|
|
43548
43645
|
}
|
|
43549
43646
|
|
|
43550
|
-
|
|
43647
|
+
if (hiddenTags && hiddenTags.size > 0) {
|
|
43648
|
+
nameValues.push({name: 'Hidden Tags', value: Array.from(hiddenTags).join(", ")});
|
|
43649
|
+
}
|
|
43551
43650
|
|
|
43552
43651
|
nameValues.push('<hr/>');
|
|
43553
43652
|
nameValues.push({name: 'Genomic Location: ', value: numberFormatter$1(1 + genomicLocation)});
|
|
@@ -43555,18 +43654,18 @@ class BamAlignment {
|
|
|
43555
43654
|
nameValues.push({name: 'Base Quality:', value: this.readBaseQualityAt(genomicLocation)});
|
|
43556
43655
|
|
|
43557
43656
|
const bmSets = this.getBaseModificationSets();
|
|
43558
|
-
if(bmSets) {
|
|
43657
|
+
if (bmSets) {
|
|
43559
43658
|
const i = this.positionToReadIndex(genomicLocation);
|
|
43560
|
-
if(undefined !== i) {
|
|
43659
|
+
if (undefined !== i) {
|
|
43561
43660
|
let found = false;
|
|
43562
43661
|
for (let bmSet of bmSets) {
|
|
43563
43662
|
if (bmSet.containsPosition(i)) {
|
|
43564
|
-
if(!found) {
|
|
43663
|
+
if (!found) {
|
|
43565
43664
|
nameValues.push('<hr/>');
|
|
43566
43665
|
nameValues.push('<b>Base modifications:</b>');
|
|
43567
43666
|
found = true;
|
|
43568
43667
|
}
|
|
43569
|
-
const lh = Math.round((100/255) * byteToUnsignedInt(bmSet.likelihoods.get(i)));
|
|
43668
|
+
const lh = Math.round((100 / 255) * byteToUnsignedInt(bmSet.likelihoods.get(i)));
|
|
43570
43669
|
nameValues.push(`${bmSet.fullName()} @ likelihood = ${lh}%`);
|
|
43571
43670
|
}
|
|
43572
43671
|
}
|
|
@@ -43656,7 +43755,7 @@ class BamAlignment {
|
|
|
43656
43755
|
const mm = this.tagDict["MM"] || this.tagDict["Mm"];
|
|
43657
43756
|
const ml = this.tagDict["ML"] || this.tagDict["Ml"];
|
|
43658
43757
|
|
|
43659
|
-
if (isString$
|
|
43758
|
+
if (isString$3(mm) && (!ml || Array.isArray(ml))) { // minimal validation, 10X uses these reserved tags for something completely different
|
|
43660
43759
|
if (mm.length === 0) {
|
|
43661
43760
|
this.baseModificationSets = EMPTY_SET;
|
|
43662
43761
|
} else {
|
|
@@ -43669,7 +43768,7 @@ class BamAlignment {
|
|
|
43669
43768
|
return this.baseModificationSets
|
|
43670
43769
|
}
|
|
43671
43770
|
|
|
43672
|
-
|
|
43771
|
+
getGroupValue(groupBy, tag, expectedPairOrientation) {
|
|
43673
43772
|
|
|
43674
43773
|
const al = this;
|
|
43675
43774
|
switch (groupBy) {
|
|
@@ -43706,7 +43805,7 @@ class BamAlignment {
|
|
|
43706
43805
|
}
|
|
43707
43806
|
}
|
|
43708
43807
|
|
|
43709
|
-
positionToReadIndex(
|
|
43808
|
+
positionToReadIndex(position) {
|
|
43710
43809
|
const block = blockAtGenomicLocation(this.blocks, position);
|
|
43711
43810
|
if (block) {
|
|
43712
43811
|
return (position - block.start) + block.seqOffset
|
|
@@ -45685,7 +45784,7 @@ function inferFileFormatFromName(fn) {
|
|
|
45685
45784
|
|
|
45686
45785
|
function inferIndexPath(url, extension) {
|
|
45687
45786
|
|
|
45688
|
-
if (isString$
|
|
45787
|
+
if (isString$3(url)) {
|
|
45689
45788
|
if (url.includes("?")) {
|
|
45690
45789
|
const idx = url.indexOf("?");
|
|
45691
45790
|
return url.substring(0, idx) + "." + extension + url.substring(idx)
|
|
@@ -45844,7 +45943,7 @@ class BamSource {
|
|
|
45844
45943
|
this.bamReader = new CramReader(config, genome, browser);
|
|
45845
45944
|
} else {
|
|
45846
45945
|
if (!this.config.indexURL && config.indexed !== false) {
|
|
45847
|
-
if (isString$
|
|
45946
|
+
if (isString$3(this.config.url)) {
|
|
45848
45947
|
const indexPath = inferIndexPath(this.config.url, "bai");
|
|
45849
45948
|
if (indexPath) {
|
|
45850
45949
|
console.warn(`Warning: no indexURL specified for ${this.config.url}. Guessing ${indexPath}`);
|
|
@@ -48156,7 +48255,7 @@ class AlignmentTrack extends TrackBase {
|
|
|
48156
48255
|
highlightColor: undefined,
|
|
48157
48256
|
minTLEN: undefined,
|
|
48158
48257
|
maxTLEN: undefined,
|
|
48159
|
-
tagColorPallete: "Set1"
|
|
48258
|
+
tagColorPallete: "Set1",
|
|
48160
48259
|
}
|
|
48161
48260
|
|
|
48162
48261
|
_colorTables = new Map()
|
|
@@ -48171,6 +48270,18 @@ class AlignmentTrack extends TrackBase {
|
|
|
48171
48270
|
this.colorTable = new ColorTable(config.tagColorTable);
|
|
48172
48271
|
}
|
|
48173
48272
|
|
|
48273
|
+
// Only one of showTags / hideTags should be specified. If both are specified showTags takes precedence.
|
|
48274
|
+
if (config.showTags && config.hideTags) {
|
|
48275
|
+
console.warn("Both showTags and hideTags specified. showTags will be used.");
|
|
48276
|
+
}
|
|
48277
|
+
if (config.showTags) {
|
|
48278
|
+
this.showTags = new Set(config.showTags);
|
|
48279
|
+
this.hiddenTags = new Set();
|
|
48280
|
+
} else {
|
|
48281
|
+
this.hiddenTags = new Set(config.hideTags || ["SA", "MD"]);
|
|
48282
|
+
}
|
|
48283
|
+
|
|
48284
|
+
|
|
48174
48285
|
// Backward compatibility overrides
|
|
48175
48286
|
if (config.largeFragmentLengthColor) this.largeTLENColor = config.largeFragmentLengthColor;
|
|
48176
48287
|
if (config.pairOrienation) this.expectedPairOrientation = config.pairOrientation;
|
|
@@ -48731,7 +48842,7 @@ class AlignmentTrack extends TrackBase {
|
|
|
48731
48842
|
|
|
48732
48843
|
popupData(clickState) {
|
|
48733
48844
|
const clickedObject = this.getClickedObject(clickState);
|
|
48734
|
-
return clickedObject
|
|
48845
|
+
return clickedObject?.popupData(clickState.genomicLocation, this.hiddenTags, this.showTags)
|
|
48735
48846
|
};
|
|
48736
48847
|
|
|
48737
48848
|
/**
|
|
@@ -48762,7 +48873,10 @@ class AlignmentTrack extends TrackBase {
|
|
|
48762
48873
|
colorByMenuItems.push({key: 'tlen', label: 'insert size (TLEN)'});
|
|
48763
48874
|
colorByMenuItems.push({key: 'unexpectedPair', label: 'pair orientation & insert size (TLEN)'});
|
|
48764
48875
|
}
|
|
48765
|
-
|
|
48876
|
+
if(this.colorBy && this.colorBy.startsWith("tag:")) {
|
|
48877
|
+
colorByMenuItems.push({key: this.colorBy, label: this.colorBy});
|
|
48878
|
+
}
|
|
48879
|
+
colorByMenuItems.push({key: 'tag', label: 'tag...'});
|
|
48766
48880
|
for (let item of colorByMenuItems) {
|
|
48767
48881
|
const selected = (this.colorBy === undefined && item.key === 'none') || this.colorBy === item.key;
|
|
48768
48882
|
menuItems.push(this.colorByCB(item, selected));
|
|
@@ -49278,7 +49392,7 @@ class AlignmentTrack extends TrackBase {
|
|
|
49278
49392
|
|
|
49279
49393
|
if (seqstring.length < maxSequenceSize$1) {
|
|
49280
49394
|
list.push({
|
|
49281
|
-
label: 'BLAT
|
|
49395
|
+
label: 'BLAT visible sequence',
|
|
49282
49396
|
click: () => {
|
|
49283
49397
|
const sequence = clickedAlignment.isNegativeStrand() ? reverseComplementSequence(seqstring) : seqstring;
|
|
49284
49398
|
const name = `${clickedAlignment.readName} - blat`;
|
|
@@ -49488,14 +49602,19 @@ class AlignmentTrack extends TrackBase {
|
|
|
49488
49602
|
case "tag":
|
|
49489
49603
|
const tagValue = alignment.tags()[tag];
|
|
49490
49604
|
if (tagValue !== undefined) {
|
|
49491
|
-
|
|
49605
|
+
|
|
49606
|
+
// If the tag value can be interpreted as a color, use it
|
|
49607
|
+
if(typeof tagValue.startsWith === 'function') {
|
|
49492
49608
|
color = IGVColor.createColorStringSafe(tagValue);
|
|
49493
49609
|
}
|
|
49494
|
-
if (!this.colorTable) {
|
|
49495
|
-
this.colorTable = new PaletteColorTable(this.tagColorPallete);
|
|
49496
|
-
}
|
|
49497
|
-
color = this.colorTable.getColor(tagValue);
|
|
49498
49610
|
|
|
49611
|
+
// Tag value is not a color, use a color table
|
|
49612
|
+
if (!color) {
|
|
49613
|
+
if (!this.colorTable) {
|
|
49614
|
+
this.colorTable = new PaletteColorTable(this.tagColorPallete);
|
|
49615
|
+
}
|
|
49616
|
+
color = this.colorTable.getColor(tagValue);
|
|
49617
|
+
}
|
|
49499
49618
|
}
|
|
49500
49619
|
break
|
|
49501
49620
|
}
|
|
@@ -65712,7 +65831,7 @@ function autoscale(chr, featureArrays) {
|
|
|
65712
65831
|
* THE SOFTWARE.
|
|
65713
65832
|
*/
|
|
65714
65833
|
|
|
65715
|
-
const isString = isString$
|
|
65834
|
+
const isString$1 = isString$3;
|
|
65716
65835
|
|
|
65717
65836
|
const DEFAULT_VISIBILITY_WINDOW = 1000000;
|
|
65718
65837
|
const TOP_MARGIN = 10;
|
|
@@ -65835,7 +65954,7 @@ class VariantTrack extends TrackBase {
|
|
|
65835
65954
|
}
|
|
65836
65955
|
if (undefined === this.visibilityWindow && this.config.indexed !== false) {
|
|
65837
65956
|
const fn = isFile(this.config.url) ? this.config.url.name : this.config.url;
|
|
65838
|
-
if (isString(fn) && fn.toLowerCase().includes("gnomad")) {
|
|
65957
|
+
if (isString$1(fn) && fn.toLowerCase().includes("gnomad")) {
|
|
65839
65958
|
this.visibilityWindow = 1000; // these are known to be very dense
|
|
65840
65959
|
} else if (typeof this.featureSource.defaultVisibilityWindow === 'function') {
|
|
65841
65960
|
this.visibilityWindow = await this.featureSource.defaultVisibilityWindow();
|
|
@@ -69719,6 +69838,9 @@ const trackFunctions =
|
|
|
69719
69838
|
['image', (config, browser) => new ImageTrack(config, browser)]
|
|
69720
69839
|
]);
|
|
69721
69840
|
|
|
69841
|
+
function knownTrackTypes () {
|
|
69842
|
+
return new Set(trackFunctions.keys())
|
|
69843
|
+
}
|
|
69722
69844
|
|
|
69723
69845
|
/**
|
|
69724
69846
|
* Return a track of the given type, passing configuration and a point to the IGV "Browser" object to its constructor function*
|
|
@@ -70211,7 +70333,7 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
|
|
|
70211
70333
|
})
|
|
70212
70334
|
}
|
|
70213
70335
|
|
|
70214
|
-
const _version = "3.
|
|
70336
|
+
const _version = "3.4.1";
|
|
70215
70337
|
function version() {
|
|
70216
70338
|
return _version
|
|
70217
70339
|
}
|
|
@@ -70262,7 +70384,7 @@ class ChromosomeSelectWidget {
|
|
|
70262
70384
|
if (this.select.value.trim().toLowerCase() === "all" || this.select.value === "*") {
|
|
70263
70385
|
if (browser.genome.wholeGenomeView) {
|
|
70264
70386
|
const wgChr = browser.genome.getChromosome("all");
|
|
70265
|
-
|
|
70387
|
+
browser.updateLoci([{chr: "all", start: 0, end: wgChr.bpLength}]);
|
|
70266
70388
|
}
|
|
70267
70389
|
} else {
|
|
70268
70390
|
const chromosome = await browser.genome.loadChromosome(this.select.value);
|
|
@@ -72067,6 +72189,26 @@ class ROIMenu {
|
|
|
72067
72189
|
}
|
|
72068
72190
|
|
|
72069
72191
|
|
|
72192
|
+
const found = this.browser.findTracks(track => typeof track.sortByValue === 'function');
|
|
72193
|
+
if (found.length > 0) {
|
|
72194
|
+
const { chr, start, end } = feature;
|
|
72195
|
+
items.push(
|
|
72196
|
+
'<hr/>',
|
|
72197
|
+
{
|
|
72198
|
+
label: 'Sort by value (ascending)',
|
|
72199
|
+
click: () => Promise.all(found.map(track => track.sortByValue({ option: 'VALUE', direction: 'ASC', chr, start, end })))
|
|
72200
|
+
});
|
|
72201
|
+
|
|
72202
|
+
items.push(
|
|
72203
|
+
'<hr/>',
|
|
72204
|
+
{
|
|
72205
|
+
label: 'Sort by value (descending)',
|
|
72206
|
+
click: () => Promise.all(found.map(track => track.sortByValue({ option: 'VALUE', direction: 'DESC', chr, start, end })))
|
|
72207
|
+
});
|
|
72208
|
+
|
|
72209
|
+
}
|
|
72210
|
+
|
|
72211
|
+
|
|
72070
72212
|
if (roiSet.isUserDefined) {
|
|
72071
72213
|
items.push(
|
|
72072
72214
|
'<hr/>',
|
|
@@ -72075,7 +72217,7 @@ class ROIMenu {
|
|
|
72075
72217
|
click: async () => {
|
|
72076
72218
|
roiSet.removeFeature(feature);
|
|
72077
72219
|
const userDefinedFeatures = await roiSet.getAllFeatures();
|
|
72078
|
-
|
|
72220
|
+
|
|
72079
72221
|
// Delete user defined ROI Set if it is empty
|
|
72080
72222
|
if (Object.keys(userDefinedFeatures).length === 0) {
|
|
72081
72223
|
roiManager.deleteUserDefinedROISet();
|
|
@@ -73248,6 +73390,189 @@ class CytobandFile {
|
|
|
73248
73390
|
|
|
73249
73391
|
}
|
|
73250
73392
|
|
|
73393
|
+
/**
|
|
73394
|
+
* Update deprecated fasta & index urls in the reference object.
|
|
73395
|
+
*/
|
|
73396
|
+
|
|
73397
|
+
const isString = (x) => {
|
|
73398
|
+
return (x && typeof x === "string") || x instanceof String
|
|
73399
|
+
};
|
|
73400
|
+
|
|
73401
|
+
/**
|
|
73402
|
+
* Replaces deprecated s3 fasta URLs with twoBitURLs. The purpose here is to rescue references from saved sessions
|
|
73403
|
+
* that contain pointers to deprectated IGV s3 buckets. These buckets will eventually be deleted
|
|
73404
|
+
*
|
|
73405
|
+
* @param reference
|
|
73406
|
+
*/
|
|
73407
|
+
function updateReference(reference) {
|
|
73408
|
+
|
|
73409
|
+
if (!(requiresUpdate(reference) && updates[reference.id])) {
|
|
73410
|
+
return
|
|
73411
|
+
}
|
|
73412
|
+
|
|
73413
|
+
const updatedReference = updates[reference.id];
|
|
73414
|
+
if (updatedReference) {
|
|
73415
|
+
delete reference.fastaURL;
|
|
73416
|
+
if (reference.indexURL) delete reference.indexURL;
|
|
73417
|
+
reference.twoBitURL = updatedReference.twoBitURL;
|
|
73418
|
+
if (updatedReference.twoBitBptURL) reference.twoBitBptURL = updatedReference.twoBitBptURL;
|
|
73419
|
+
if (updatedReference.chromSizesURL) reference.chromSizesURL = updatedReference.chromSizesURL;
|
|
73420
|
+
}
|
|
73421
|
+
}
|
|
73422
|
+
|
|
73423
|
+
function requiresUpdate(reference) {
|
|
73424
|
+
return isString(reference.fastaURL) &&
|
|
73425
|
+
(reference.fastaURL.startsWith("https://igv.org") ||
|
|
73426
|
+
["igv.org.genomes", "igv.broadinstitute.org", "igv.genepattern.org", "igvdata.broadinstitute.org",
|
|
73427
|
+
"igv-genepattern-org"].some(bucket => reference.fastaURL.includes(bucket)))
|
|
73428
|
+
}
|
|
73429
|
+
|
|
73430
|
+
const updates = {
|
|
73431
|
+
"hs1": {
|
|
73432
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hs1/bigZips/hs1.2bit",
|
|
73433
|
+
"twoBitBptURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hs1/bigZips/hs1.2bit.bpt",
|
|
73434
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hs1/bigZips/hs1.chrom.sizes.txt"
|
|
73435
|
+
},
|
|
73436
|
+
"hg38": {
|
|
73437
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.2bit",
|
|
73438
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.chrom.sizes"
|
|
73439
|
+
},
|
|
73440
|
+
"hg19": {
|
|
73441
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg19/bigZips/hg19.2bit",
|
|
73442
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg19/bigZips/hg19.chrom.sizes"
|
|
73443
|
+
},
|
|
73444
|
+
"hg18": {
|
|
73445
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg18/bigZips/hg18.2bit",
|
|
73446
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/hg18/bigZips/hg18.chrom.sizes"
|
|
73447
|
+
},
|
|
73448
|
+
"mm39": {
|
|
73449
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm39/bigZips/mm39.2bit",
|
|
73450
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm39/bigZips/mm39.chrom.sizes"
|
|
73451
|
+
},
|
|
73452
|
+
"mm10": {
|
|
73453
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm10/bigZips/mm10.2bit",
|
|
73454
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm10/bigZips/mm10.chrom.sizes"
|
|
73455
|
+
},
|
|
73456
|
+
"mm9": {
|
|
73457
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm9/bigZips/mm9.2bit",
|
|
73458
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/mm9/bigZips/mm9.chrom.sizes"
|
|
73459
|
+
},
|
|
73460
|
+
"rn7": {
|
|
73461
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/015/227/675/GCF_015227675.2/GCF_015227675.2.2bit",
|
|
73462
|
+
"twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/015/227/675/GCF_015227675.2/GCF_015227675.2.2bit.bpt",
|
|
73463
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/015/227/675/GCF_015227675.2/GCF_015227675.2.chrom.sizes.txt"
|
|
73464
|
+
},
|
|
73465
|
+
"rn6": {
|
|
73466
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/895/GCF_000001895.5/GCF_000001895.5.2bit",
|
|
73467
|
+
"twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/895/GCF_000001895.5/GCF_000001895.5.2bit.bpt",
|
|
73468
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/895/GCF_000001895.5/GCF_000001895.5.chrom.sizes.txt"
|
|
73469
|
+
},
|
|
73470
|
+
"gorGor6": {
|
|
73471
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/gorGor6/bigZips/gorGor6.2bit",
|
|
73472
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/gorGor6/bigZips/gorGor6.chrom.sizes"
|
|
73473
|
+
},
|
|
73474
|
+
"gorGor4": {
|
|
73475
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/gorGor4/bigZips/gorGor4.2bit",
|
|
73476
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/gorGor4/bigZips/gorGor4.chrom.sizes"
|
|
73477
|
+
},
|
|
73478
|
+
"panTro6": {
|
|
73479
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro6/bigZips/panTro6.2bit",
|
|
73480
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro6/bigZips/panTro6.chrom.sizes"
|
|
73481
|
+
},
|
|
73482
|
+
"panTro5": {
|
|
73483
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro5/bigZips/panTro5.2bit",
|
|
73484
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro5/bigZips/panTro5.chrom.sizes"
|
|
73485
|
+
},
|
|
73486
|
+
"panTro4": {
|
|
73487
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro4/bigZips/panTro4.2bit",
|
|
73488
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panTro4/bigZips/panTro4.chrom.sizes"
|
|
73489
|
+
},
|
|
73490
|
+
"macFas5": {
|
|
73491
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/macFas5/bigZips/macFas5.2bit",
|
|
73492
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/macFas5/bigZips/macFas5.chrom.sizes"
|
|
73493
|
+
},
|
|
73494
|
+
"panPan2": {
|
|
73495
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panPan2/bigZips/panPan2.2bit",
|
|
73496
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/panPan2/bigZips/panPan2.chrom.sizes"
|
|
73497
|
+
},
|
|
73498
|
+
"canFam6": {
|
|
73499
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam6/bigZips/canFam6.2bit",
|
|
73500
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam6/bigZips/canFam6.chrom.sizes"
|
|
73501
|
+
},
|
|
73502
|
+
"canFam5": {
|
|
73503
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam5/bigZips/canFam5.2bit",
|
|
73504
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam5/bigZips/canFam5.chrom.sizes"
|
|
73505
|
+
},
|
|
73506
|
+
"canFam4": {
|
|
73507
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam4/bigZips/canFam4.2bit",
|
|
73508
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam4/bigZips/canFam4.chrom.sizes"
|
|
73509
|
+
},
|
|
73510
|
+
"canFam3": {
|
|
73511
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam3/bigZips/canFam3.2bit",
|
|
73512
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/canFam3/bigZips/canFam3.chrom.sizes"
|
|
73513
|
+
},
|
|
73514
|
+
"bosTau9": {
|
|
73515
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/bosTau9/bigZips/bosTau9.2bit",
|
|
73516
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/bosTau9/bigZips/bosTau9.chrom.sizes"
|
|
73517
|
+
},
|
|
73518
|
+
"bosTau8": {
|
|
73519
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/bosTau8/bigZips/bosTau8.2bit",
|
|
73520
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/bosTau8/bigZips/bosTau8.chrom.sizes"
|
|
73521
|
+
},
|
|
73522
|
+
"susScr11": {
|
|
73523
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/susScr11/bigZips/susScr11.2bit",
|
|
73524
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/susScr11/bigZips/susScr11.chrom.sizes"
|
|
73525
|
+
},
|
|
73526
|
+
"galGal6": {
|
|
73527
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/galGal6/bigZips/galGal6.2bit",
|
|
73528
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/galGal6/bigZips/galGal6.chrom.sizes"
|
|
73529
|
+
},
|
|
73530
|
+
"danRer11": {
|
|
73531
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/danRer11/bigZips/danRer11.2bit",
|
|
73532
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/danRer11/bigZips/danRer11.chrom.sizes"
|
|
73533
|
+
},
|
|
73534
|
+
"danRer10": {
|
|
73535
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/danRer10/bigZips/danRer10.2bit",
|
|
73536
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/danRer10/bigZips/danRer10.chrom.sizes"
|
|
73537
|
+
},
|
|
73538
|
+
"ce11": {
|
|
73539
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/ce11/bigZips/ce11.2bit",
|
|
73540
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/ce11/bigZips/ce11.chrom.sizes"
|
|
73541
|
+
},
|
|
73542
|
+
"dm6": {
|
|
73543
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/dm6/bigZips/dm6.2bit",
|
|
73544
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/dm6/bigZips/dm6.chrom.sizes"
|
|
73545
|
+
},
|
|
73546
|
+
"dm3": {
|
|
73547
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/goldenPath/dm3/bigZips/dm3.2bit",
|
|
73548
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/goldenPath/dm3/bigZips/dm3.chrom.sizes"
|
|
73549
|
+
},
|
|
73550
|
+
"sacCer3": {
|
|
73551
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/146/045/GCF_000146045.2/GCF_000146045.2.2bit",
|
|
73552
|
+
"twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/146/045/GCF_000146045.2/GCF_000146045.2.2bit.bpt",
|
|
73553
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/146/045/GCF_000146045.2/GCF_000146045.2.chrom.sizes.txt"
|
|
73554
|
+
},
|
|
73555
|
+
"GCF_000002945.1": {
|
|
73556
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/002/945/GCF_000002945.1/GCF_000002945.1.2bit",
|
|
73557
|
+
"twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/002/945/GCF_000002945.1/GCF_000002945.1.2bit.bpt",
|
|
73558
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/002/945/GCF_000002945.1/GCF_000002945.1.chrom.sizes.txt"
|
|
73559
|
+
},
|
|
73560
|
+
"GCF_009858895.2": {
|
|
73561
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/009/858/895/GCF_009858895.2/GCF_009858895.2.2bit",
|
|
73562
|
+
"twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/009/858/895/GCF_009858895.2/GCF_009858895.2.2bit.bpt",
|
|
73563
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/009/858/895/GCF_009858895.2/GCF_009858895.2.chrom.sizes.txt"
|
|
73564
|
+
},
|
|
73565
|
+
"tair10": {
|
|
73566
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/735/GCF_000001735.3/GCF_000001735.3.2bit",
|
|
73567
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCF/000/001/735/GCF_000001735.3/GCF_000001735.3.chrom.sizes.txt"
|
|
73568
|
+
},
|
|
73569
|
+
"GCA_000022165.1": {
|
|
73570
|
+
"twoBitURL": "https://hgdownload.soe.ucsc.edu/hubs/GCA/000/022/165/GCA_000022165.1/GCA_000022165.1.2bit",
|
|
73571
|
+
"twoBitBptURL": "https://hgdownload.soe.ucsc.edu/hubs/GCA/000/022/165/GCA_000022165.1/GCA_000022165.1.2bit.bpt",
|
|
73572
|
+
"chromSizesURL": "https://hgdownload.soe.ucsc.edu/hubs/GCA/000/022/165/GCA_000022165.1/GCA_000022165.1.chrom.sizes.txt"
|
|
73573
|
+
}
|
|
73574
|
+
};
|
|
73575
|
+
|
|
73251
73576
|
const ucsdIDMap = new Map([
|
|
73252
73577
|
["1kg_ref", "hg18"],
|
|
73253
73578
|
["1kg_v37", "hg19"],
|
|
@@ -73269,6 +73594,7 @@ class Genome {
|
|
|
73269
73594
|
|
|
73270
73595
|
static async createGenome(options, browser) {
|
|
73271
73596
|
|
|
73597
|
+
updateReference(options);
|
|
73272
73598
|
const genome = new Genome(options, browser);
|
|
73273
73599
|
await genome.init();
|
|
73274
73600
|
return genome
|
|
@@ -73292,7 +73618,6 @@ class Genome {
|
|
|
73292
73618
|
// Load sequence
|
|
73293
73619
|
this.sequence = await loadSequence(config, this.browser);
|
|
73294
73620
|
|
|
73295
|
-
|
|
73296
73621
|
// Load cytobands. This is optional but required to support the ideogram. Only needed for whole genome view
|
|
73297
73622
|
if(false !== config.showIdeogram && false !== config.wholeGenomeView) {
|
|
73298
73623
|
if (config.cytobandURL) {
|
|
@@ -73336,10 +73661,14 @@ class Genome {
|
|
|
73336
73661
|
} else {
|
|
73337
73662
|
this.#wgChromosomeNames = config.chromosomeOrder.split(',').map(nm => nm.trim());
|
|
73338
73663
|
}
|
|
73664
|
+
// Trim to remove non-existent chromosomes
|
|
73665
|
+
await this.chromAlias.preload(this.#wgChromosomeNames);
|
|
73666
|
+
this.#wgChromosomeNames =
|
|
73667
|
+
this.#wgChromosomeNames.map(c => this.getChromosomeName(c)).filter(c => this.chromosomes.has(c));
|
|
73339
73668
|
} else {
|
|
73340
73669
|
this.#wgChromosomeNames = trimSmallChromosomes(this.chromosomes);
|
|
73670
|
+
await this.chromAlias.preload(this.#wgChromosomeNames);
|
|
73341
73671
|
}
|
|
73342
|
-
await this.chromAlias.preload(this.#wgChromosomeNames);
|
|
73343
73672
|
}
|
|
73344
73673
|
|
|
73345
73674
|
// Optionally create the psuedo chromosome "all" to support whole genome view
|
|
@@ -73612,7 +73941,7 @@ function trimSmallChromosomes(chromosomes) {
|
|
|
73612
73941
|
function generateGenomeID(config) {
|
|
73613
73942
|
if (config.id !== undefined) {
|
|
73614
73943
|
return config.id
|
|
73615
|
-
} else if (config.fastaURL && isString$
|
|
73944
|
+
} else if (config.fastaURL && isString$3(config.fastaURL) && !config.fastaURL.startsWith("data:")) {
|
|
73616
73945
|
return config.fastaURL
|
|
73617
73946
|
} else if (config.fastaURL && config.fastaURL.name) {
|
|
73618
73947
|
return config.fastaURL.name
|
|
@@ -74345,7 +74674,7 @@ class Browser {
|
|
|
74345
74674
|
const urlOrFile = options.url || options.file;
|
|
74346
74675
|
|
|
74347
74676
|
let config;
|
|
74348
|
-
if (options.url && isString$
|
|
74677
|
+
if (options.url && isString$3(options.url) && (options.url.startsWith("blob:") || options.url.startsWith("data:"))) {
|
|
74349
74678
|
const json = Browser.uncompressSession(options.url);
|
|
74350
74679
|
config = JSON.parse(json);
|
|
74351
74680
|
} else {
|
|
@@ -74424,7 +74753,7 @@ class Browser {
|
|
|
74424
74753
|
return
|
|
74425
74754
|
}
|
|
74426
74755
|
|
|
74427
|
-
const genomeConfig = isString$
|
|
74756
|
+
const genomeConfig = isString$3(genomeOrReference) ?
|
|
74428
74757
|
await GenomeUtils.expandReference(this.alert, genomeOrReference) :
|
|
74429
74758
|
genomeOrReference;
|
|
74430
74759
|
|
|
@@ -74546,7 +74875,7 @@ class Browser {
|
|
|
74546
74875
|
}
|
|
74547
74876
|
|
|
74548
74877
|
/**
|
|
74549
|
-
* Load a reference genome object. This includes the
|
|
74878
|
+
* Load a reference genome object. This includes the sequence, and optional cytoband, but no tracks. This method
|
|
74550
74879
|
* is used by loadGenome and loadSession.
|
|
74551
74880
|
*
|
|
74552
74881
|
* @param genomeConfig
|
|
@@ -74579,7 +74908,10 @@ class Browser {
|
|
|
74579
74908
|
|
|
74580
74909
|
const locusFound = await this.search(locus, true);
|
|
74581
74910
|
if (!locusFound) {
|
|
74582
|
-
|
|
74911
|
+
console.error(`Cannot set initial locus ${locus}`);
|
|
74912
|
+
if(locus !== genome.initialLocus) {
|
|
74913
|
+
await this.search(genome.initialLocus);
|
|
74914
|
+
}
|
|
74583
74915
|
}
|
|
74584
74916
|
|
|
74585
74917
|
if (genomeChange) {
|
|
@@ -74611,7 +74943,7 @@ class Browser {
|
|
|
74611
74943
|
|
|
74612
74944
|
// Translate the generic "url" field, used by clients such as igv-webapp
|
|
74613
74945
|
if (idOrConfig.url) {
|
|
74614
|
-
if (isString$
|
|
74946
|
+
if (isString$3(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt")) {
|
|
74615
74947
|
idOrConfig.hubURL = idOrConfig.url;
|
|
74616
74948
|
delete idOrConfig.url;
|
|
74617
74949
|
} else if ("gbk" === getFileExtension(idOrConfig.url)) {
|
|
@@ -74621,11 +74953,11 @@ class Browser {
|
|
|
74621
74953
|
}
|
|
74622
74954
|
|
|
74623
74955
|
let genomeConfig;
|
|
74624
|
-
const isHubGenome = idOrConfig.hubURL || (idOrConfig.url && isString$
|
|
74956
|
+
const isHubGenome = idOrConfig.hubURL || (idOrConfig.url && isString$3(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt"));
|
|
74625
74957
|
if (isHubGenome) {
|
|
74626
74958
|
const hub = await loadHub(idOrConfig.hubURL || idOrConfig.url);
|
|
74627
74959
|
genomeConfig = hub.getGenomeConfig();
|
|
74628
|
-
} else if (isString$
|
|
74960
|
+
} else if (isString$3(idOrConfig) || !(idOrConfig.url || idOrConfig.fastaURL || idOrConfig.twoBitURL || idOrConfig.gbkURL)) {
|
|
74629
74961
|
// Either an ID, a json string, or an object missing required properties.
|
|
74630
74962
|
genomeConfig = await GenomeUtils.expandReference(this.alert, idOrConfig);
|
|
74631
74963
|
} else {
|
|
@@ -74792,7 +75124,7 @@ class Browser {
|
|
|
74792
75124
|
async #loadTrackHelper(config) {
|
|
74793
75125
|
|
|
74794
75126
|
// config might be json
|
|
74795
|
-
if (isString$
|
|
75127
|
+
if (isString$3(config)) {
|
|
74796
75128
|
config = JSON.parse(config);
|
|
74797
75129
|
}
|
|
74798
75130
|
|
|
@@ -74922,7 +75254,7 @@ class Browser {
|
|
|
74922
75254
|
|
|
74923
75255
|
// Resolve function and promise urls
|
|
74924
75256
|
let url = await resolveURL(config.url || config.fastaURL);
|
|
74925
|
-
if (isString$
|
|
75257
|
+
if (isString$3(url)) {
|
|
74926
75258
|
url = url.trim();
|
|
74927
75259
|
}
|
|
74928
75260
|
|
|
@@ -74970,7 +75302,7 @@ class Browser {
|
|
|
74970
75302
|
const featureSource = FeatureSource(config, this.genome);
|
|
74971
75303
|
config._featureSource = featureSource; // This is a temp variable, bit of a hack
|
|
74972
75304
|
const trackType = await featureSource.trackType();
|
|
74973
|
-
if (trackType) {
|
|
75305
|
+
if (trackType && knownTrackTypes().has(trackType)) {
|
|
74974
75306
|
type = trackType;
|
|
74975
75307
|
} else {
|
|
74976
75308
|
type = "annotation";
|