igv 3.5.1 → 3.5.3
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.esm.js +249 -84
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +249 -84
- package/dist/igv.min.js +9 -9
- package/dist/igv.min.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,19 +18,19 @@ Below are examples and a quickstart guide. See the [developer documentation](ht
|
|
|
18
18
|
|
|
19
19
|
# Examples
|
|
20
20
|
|
|
21
|
-
***[Alignments](https://igv.org/web/release/3.5.
|
|
21
|
+
***[Alignments](https://igv.org/web/release/3.5.2/examples/cram-vcf.html)***
|
|
22
22
|
|
|
23
|
-
***[Interactions](https://igv.org/web/release/3.5.
|
|
23
|
+
***[Interactions](https://igv.org/web/release/3.5.2/examples/interact.html)***
|
|
24
24
|
|
|
25
|
-
***[Copy number](https://igv.org/web/release/3.5.
|
|
25
|
+
***[Copy number](https://igv.org/web/release/3.5.2/examples/copyNumber.html)***
|
|
26
26
|
|
|
27
|
-
***[Multiple regions](https://igv.org/web/release/3.5.
|
|
27
|
+
***[Multiple regions](https://igv.org/web/release/3.5.2/examples/multi-locus.html)***
|
|
28
28
|
|
|
29
|
-
***[Mutation Annotation Format (MAF)](https://igv.org/web/release/3.5.
|
|
29
|
+
***[Mutation Annotation Format (MAF)](https://igv.org/web/release/3.5.2/examples/maf-tcga.html)***
|
|
30
30
|
|
|
31
|
-
***[Variant color options](https://igv.org/web/release/3.5.
|
|
31
|
+
***[Variant color options](https://igv.org/web/release/3.5.2/examples/variant-colors.html)***
|
|
32
32
|
|
|
33
|
-
***[More](https://igv.org/web/release/3.5.
|
|
33
|
+
***[More](https://igv.org/web/release/3.5.2/examples/)***
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
# Quickstart
|
|
@@ -39,18 +39,18 @@ Below are examples and a quickstart guide. See the [developer documentation](ht
|
|
|
39
39
|
igv.js consists of a single javascript file with no external dependencies.
|
|
40
40
|
|
|
41
41
|
Pre-built files for script include, AMD, or CJS module systems (igv.min.js) and an ES6 module (igv.esm.min.js)
|
|
42
|
-
can be downloaded from [https://cdn.jsdelivr.net/npm/igv@3.5.
|
|
42
|
+
can be downloaded from [https://cdn.jsdelivr.net/npm/igv@3.5.2/dist/](https://cdn.jsdelivr.net/npm/igv@3.5.2/dist/).
|
|
43
43
|
|
|
44
44
|
To import igv as an ES6 module
|
|
45
45
|
|
|
46
46
|
```javascript
|
|
47
|
-
import igv from "https://cdn.jsdelivr.net/npm/igv@3.5.
|
|
47
|
+
import igv from "https://cdn.jsdelivr.net/npm/igv@3.5.2/dist/igv.esm.min.js"
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
Or as a script include (defines the "igv" global)
|
|
51
51
|
|
|
52
52
|
```html
|
|
53
|
-
<script src="https://cdn.jsdelivr.net/npm/igv@3.5.
|
|
53
|
+
<script src="https://cdn.jsdelivr.net/npm/igv@3.5.2/dist/igv.min.js"></script>
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
Alternatively you can install with npm
|
package/dist/igv.esm.js
CHANGED
|
@@ -8702,7 +8702,7 @@ async function getDriveFileInfo(googleDriveURL) {
|
|
|
8702
8702
|
const response = await fetch(endPoint);
|
|
8703
8703
|
let json = await response.json();
|
|
8704
8704
|
if (json.error && json.error.code === 404) {
|
|
8705
|
-
let scope = "https://www.googleapis.com/auth/drive.
|
|
8705
|
+
let scope = "https://www.googleapis.com/auth/drive.file";
|
|
8706
8706
|
const access_token = await getAccessToken(scope);
|
|
8707
8707
|
if (access_token) {
|
|
8708
8708
|
const response = await fetch(endPoint, {
|
|
@@ -8884,6 +8884,10 @@ class Throttle {
|
|
|
8884
8884
|
|
|
8885
8885
|
class IGVXhr {
|
|
8886
8886
|
|
|
8887
|
+
UCSC_HOST = "hgdownload.soe.ucsc.edu"
|
|
8888
|
+
UCSC_BACKUP_HOST = "genome-browser.s3.us-east-1.amazonaws.com"
|
|
8889
|
+
|
|
8890
|
+
|
|
8887
8891
|
constructor() {
|
|
8888
8892
|
this.apiKey = undefined;
|
|
8889
8893
|
this.googleThrottle = new Throttle({
|
|
@@ -8891,6 +8895,7 @@ class IGVXhr {
|
|
|
8891
8895
|
});
|
|
8892
8896
|
this.RANGE_WARNING_GIVEN = false;
|
|
8893
8897
|
this.oauth = new Oauth();
|
|
8898
|
+
this.corsProxy = undefined;
|
|
8894
8899
|
}
|
|
8895
8900
|
|
|
8896
8901
|
setApiKey(key) {
|
|
@@ -8915,7 +8920,7 @@ class IGVXhr {
|
|
|
8915
8920
|
* @param options
|
|
8916
8921
|
* @returns {Promise<Uint8Array>}
|
|
8917
8922
|
*/
|
|
8918
|
-
|
|
8923
|
+
async loadByteArray(url, options) {
|
|
8919
8924
|
const arraybuffer = await this.loadArrayBuffer(url, options);
|
|
8920
8925
|
let plain;
|
|
8921
8926
|
if (isgzipped(arraybuffer)) {
|
|
@@ -8960,7 +8965,7 @@ class IGVXhr {
|
|
|
8960
8965
|
|
|
8961
8966
|
if (isFile(url)) {
|
|
8962
8967
|
return this._loadFileSlice(url, options)
|
|
8963
|
-
} else if (
|
|
8968
|
+
} else if (isString$3(url)) {
|
|
8964
8969
|
if (url.startsWith("data:")) {
|
|
8965
8970
|
const buffer = decodeDataURI$1(url).buffer;
|
|
8966
8971
|
if (options.range) {
|
|
@@ -8990,6 +8995,7 @@ class IGVXhr {
|
|
|
8990
8995
|
|
|
8991
8996
|
const self = this;
|
|
8992
8997
|
const _url = url; // The unmodified URL, needed in case of an oAuth retry
|
|
8998
|
+
const {host} = parseUri(_url);
|
|
8993
8999
|
|
|
8994
9000
|
url = mapUrl$1(url);
|
|
8995
9001
|
|
|
@@ -9070,8 +9076,10 @@ class IGVXhr {
|
|
|
9070
9076
|
|
|
9071
9077
|
xhr.onload = async function (event) {
|
|
9072
9078
|
|
|
9079
|
+
const isFileProtocol = url.toLowerCase().startsWith("file:");
|
|
9080
|
+
|
|
9073
9081
|
// when the url points to a local file, the status is 0
|
|
9074
|
-
if (xhr.status
|
|
9082
|
+
if ((xhr.status >= 200 && xhr.status <= 300) || (isFileProtocol && xhr.status === 0)) {
|
|
9075
9083
|
if ("HEAD" === options.method) {
|
|
9076
9084
|
// Support fetching specific headers. Attempting to fetch all headers can be problematic with CORS
|
|
9077
9085
|
const headers = options.requestedHeaders || ['content-length'];
|
|
@@ -9087,7 +9095,7 @@ class IGVXhr {
|
|
|
9087
9095
|
// For small files a range starting at 0 can return the whole file => 200
|
|
9088
9096
|
// Provide just the slice we asked for, throw out the rest quietly
|
|
9089
9097
|
// If file is large warn user
|
|
9090
|
-
if (xhr.response.length >
|
|
9098
|
+
if (xhr.response.length > 1000000 && !self.RANGE_WARNING_GIVEN) {
|
|
9091
9099
|
alert(`Warning: Range header ignored for URL: ${url}. This can have severe performance impacts.`);
|
|
9092
9100
|
}
|
|
9093
9101
|
resolve(xhr.response.slice(range.start, range.start + range.size));
|
|
@@ -9104,20 +9112,29 @@ class IGVXhr {
|
|
|
9104
9112
|
tryGoogleAuth();
|
|
9105
9113
|
|
|
9106
9114
|
} else {
|
|
9115
|
+
const error = new Error(`Error accessing resource: ${url} status: ${xhr.status}`);
|
|
9107
9116
|
if (xhr.status === 403) {
|
|
9108
9117
|
handleError("Access forbidden: " + url);
|
|
9118
|
+
} else if (host === self.UCSC_HOST) {
|
|
9119
|
+
tryUcscBackup(self.UCSC_HOST, self.UCSC_BACKUP_HOST, error);
|
|
9120
|
+
} else if (xhr.status === 0 && self.corsProxy && !options.corsProxyRetried) {
|
|
9121
|
+
tryCorsProxy(error);
|
|
9109
9122
|
} else {
|
|
9110
|
-
handleError(
|
|
9123
|
+
handleError(error);
|
|
9111
9124
|
}
|
|
9112
9125
|
}
|
|
9113
9126
|
};
|
|
9114
9127
|
|
|
9115
|
-
|
|
9116
9128
|
xhr.onerror = function (event) {
|
|
9129
|
+
const error = new Error(`Error accessing resource: ${url} status: ${xhr.status}`);
|
|
9117
9130
|
if (isGoogleURL(url) && !options.retries) {
|
|
9118
9131
|
tryGoogleAuth();
|
|
9132
|
+
} else if (host === self.UCSC_HOST) {
|
|
9133
|
+
tryUcscBackup(self.UCSC_HOST, self.UCSC_BACKUP_HOST, error);
|
|
9134
|
+
} else if (self.corsProxy && !options.corsProxyRetried) {
|
|
9135
|
+
tryCorsProxy(error);
|
|
9119
9136
|
} else {
|
|
9120
|
-
handleError(
|
|
9137
|
+
handleError(error);
|
|
9121
9138
|
}
|
|
9122
9139
|
};
|
|
9123
9140
|
|
|
@@ -9149,6 +9166,27 @@ class IGVXhr {
|
|
|
9149
9166
|
}
|
|
9150
9167
|
}
|
|
9151
9168
|
|
|
9169
|
+
async function tryCorsProxy(error) {
|
|
9170
|
+
options.corsProxyRetried = true;
|
|
9171
|
+
const proxyUrl = self.corsProxy + (_url.includes("?") ? "&" : "?") + "url=" + encodeURIComponent(_url);
|
|
9172
|
+
try {
|
|
9173
|
+
const result = await self._loadURL(proxyUrl, options);
|
|
9174
|
+
resolve(result);
|
|
9175
|
+
} catch (e) {
|
|
9176
|
+
handleError(error);
|
|
9177
|
+
}
|
|
9178
|
+
}
|
|
9179
|
+
|
|
9180
|
+
async function tryUcscBackup(UCSC_HOST, UCSC_BACKUP_HOST, error) {
|
|
9181
|
+
const backupUrl = _url.replace(UCSC_HOST, UCSC_BACKUP_HOST);
|
|
9182
|
+
try {
|
|
9183
|
+
const result = await self._loadURL(backupUrl, options);
|
|
9184
|
+
resolve(result);
|
|
9185
|
+
} catch (e) {
|
|
9186
|
+
handleError(error);
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
|
|
9152
9190
|
async function tryGoogleAuth() {
|
|
9153
9191
|
try {
|
|
9154
9192
|
const accessToken = await fetchGoogleAccessToken(_url);
|
|
@@ -9167,6 +9205,8 @@ class IGVXhr {
|
|
|
9167
9205
|
}
|
|
9168
9206
|
}
|
|
9169
9207
|
}
|
|
9208
|
+
|
|
9209
|
+
|
|
9170
9210
|
})
|
|
9171
9211
|
|
|
9172
9212
|
}
|
|
@@ -9239,20 +9279,29 @@ class IGVXhr {
|
|
|
9239
9279
|
}
|
|
9240
9280
|
|
|
9241
9281
|
/**
|
|
9242
|
-
*
|
|
9243
|
-
*
|
|
9244
|
-
*
|
|
9282
|
+
* Return the content length of the file at the given URL. This is not guaranteed to succeed, some servers
|
|
9283
|
+
* do not support or allow the content-length header.
|
|
9284
|
+
*
|
|
9245
9285
|
* @param url
|
|
9246
9286
|
* @param options
|
|
9247
9287
|
* @returns {Promise<unknown>}
|
|
9248
9288
|
*/
|
|
9249
9289
|
async getContentLength(url, options) {
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9290
|
+
if (isFile(url)) {
|
|
9291
|
+
return url.size
|
|
9292
|
+
} else {
|
|
9293
|
+
try {
|
|
9294
|
+
options = options || {};
|
|
9295
|
+
options.method = 'HEAD';
|
|
9296
|
+
options.requestedHeaders = ['content-length'];
|
|
9297
|
+
const headerMap = await this._loadURL(url, options);
|
|
9298
|
+
const contentLengthString = headerMap['content-length'];
|
|
9299
|
+
return contentLengthString ? Number.parseInt(contentLengthString) : 0
|
|
9300
|
+
} catch (e) {
|
|
9301
|
+
console.error(e);
|
|
9302
|
+
return -1
|
|
9303
|
+
}
|
|
9304
|
+
}
|
|
9256
9305
|
}
|
|
9257
9306
|
|
|
9258
9307
|
}
|
|
@@ -20664,6 +20713,7 @@ class FeatureFileReader {
|
|
|
20664
20713
|
|
|
20665
20714
|
switch (config.format) {
|
|
20666
20715
|
case "vcf":
|
|
20716
|
+
case "vcftabix":
|
|
20667
20717
|
return new VcfParser(config)
|
|
20668
20718
|
case "seg" :
|
|
20669
20719
|
return new SegParser("seg")
|
|
@@ -21275,6 +21325,7 @@ class HtsgetVariantReader extends HtsgetReader {
|
|
|
21275
21325
|
}
|
|
21276
21326
|
}
|
|
21277
21327
|
|
|
21328
|
+
// Base class for feature sources. Subclasses must implement getFeatures().
|
|
21278
21329
|
class BaseFeatureSource {
|
|
21279
21330
|
|
|
21280
21331
|
constructor(genome) {
|
|
@@ -21336,59 +21387,10 @@ class BaseFeatureSource {
|
|
|
21336
21387
|
}
|
|
21337
21388
|
}
|
|
21338
21389
|
|
|
21339
|
-
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
let idx = chromosomeNames.indexOf(chr);
|
|
21343
|
-
if (idx < 0) return // This shouldn't happen
|
|
21344
|
-
|
|
21345
|
-
// Look ahead (or behind) in 10 kb intervals, but no further than visibilityWindow
|
|
21346
|
-
const window = Math.min(10000, visibilityWindow || 10000);
|
|
21347
|
-
let queryStart = direction ? position : Math.max(position - window, 0);
|
|
21348
|
-
while (idx < chromosomeNames.length && idx >= 0) {
|
|
21349
|
-
chr = chromosomeNames[idx];
|
|
21350
|
-
const chromosome = this.genome.getChromosome(chr);
|
|
21351
|
-
const chromosomeEnd = chromosome.bpLength;
|
|
21352
|
-
while (queryStart < chromosomeEnd && queryStart >= 0) {
|
|
21353
|
-
let queryEnd = Math.min(position, queryStart + window);
|
|
21354
|
-
const featureList = await this.getFeatures({chr, start: queryStart, end: queryEnd, visibilityWindow});
|
|
21355
|
-
if (featureList) {
|
|
21356
|
-
|
|
21357
|
-
const compare = (o1, o2) => o1.start - o2.start + o1.end - o2.end;
|
|
21358
|
-
const sortedList = Array.from(featureList);
|
|
21359
|
-
sortedList.sort(compare);
|
|
21360
|
-
|
|
21361
|
-
// Search for next or previous feature relative to centers. We use a linear search because the
|
|
21362
|
-
// feature is likely to be near the first or end of the list
|
|
21363
|
-
let idx = direction ? 0 : sortedList.length - 1;
|
|
21364
|
-
while(idx >= 0 && idx < sortedList.length) {
|
|
21365
|
-
const f = sortedList[idx];
|
|
21366
|
-
const center = (f.start + f.end) / 2;
|
|
21367
|
-
if(direction) {
|
|
21368
|
-
if(center > position) return f
|
|
21369
|
-
idx++;
|
|
21370
|
-
} else {
|
|
21371
|
-
if(center < position) return f
|
|
21372
|
-
idx--;
|
|
21373
|
-
}
|
|
21374
|
-
}
|
|
21375
|
-
}
|
|
21376
|
-
queryStart = direction ? queryEnd : queryStart - window;
|
|
21377
|
-
}
|
|
21378
|
-
if (direction) {
|
|
21379
|
-
idx++;
|
|
21380
|
-
queryStart = 0;
|
|
21381
|
-
position = 0;
|
|
21382
|
-
} else {
|
|
21383
|
-
idx--;
|
|
21384
|
-
if (idx < 0) break
|
|
21385
|
-
const prevChromosome = this.genome.getChromosome(chromosomeNames[idx]);
|
|
21386
|
-
position = prevChromosome.bpLength;
|
|
21387
|
-
queryStart = position - window;
|
|
21388
|
-
}
|
|
21389
|
-
}
|
|
21390
|
+
// Subclasses must implement
|
|
21391
|
+
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
|
|
21392
|
+
throw new Error("getFeatures not implemented")
|
|
21390
21393
|
}
|
|
21391
|
-
|
|
21392
21394
|
}
|
|
21393
21395
|
|
|
21394
21396
|
const GZIP_FLAG = 0x1;
|
|
@@ -26527,6 +26529,88 @@ class GenbankFeatureSource extends BaseFeatureSource {
|
|
|
26527
26529
|
}
|
|
26528
26530
|
}
|
|
26529
26531
|
|
|
26532
|
+
/**
|
|
26533
|
+
* A feature source for a "list" file. A list file is a text file with two columns, chromosome and URL. It was
|
|
26534
|
+
* created for the 1KG genotype files, which are split by chromosome, and at the moment that is the only use case.
|
|
26535
|
+
*/
|
|
26536
|
+
|
|
26537
|
+
class ListFeatureSource extends BaseFeatureSource {
|
|
26538
|
+
|
|
26539
|
+
constructor(config, genome) {
|
|
26540
|
+
super(genome);
|
|
26541
|
+
this.config = config;
|
|
26542
|
+
this.featureSourceMap = null;
|
|
26543
|
+
this.header = null;
|
|
26544
|
+
}
|
|
26545
|
+
|
|
26546
|
+
async getHeader() {
|
|
26547
|
+
|
|
26548
|
+
if (!this.header) {
|
|
26549
|
+
|
|
26550
|
+
if (!this.featureSourceMap) {
|
|
26551
|
+
await this.init();
|
|
26552
|
+
}
|
|
26553
|
+
// Return the header from the first feature source. It is assumed that all sources have a common header.
|
|
26554
|
+
const firstFS = this.featureSourceMap.values().next().value;
|
|
26555
|
+
if (firstFS && firstFS.getHeader) {
|
|
26556
|
+
this.header = firstFS.getHeader();
|
|
26557
|
+
} else {
|
|
26558
|
+
this.header = Promise.resolve(undefined);
|
|
26559
|
+
}
|
|
26560
|
+
}
|
|
26561
|
+
|
|
26562
|
+
return this.header
|
|
26563
|
+
|
|
26564
|
+
}
|
|
26565
|
+
|
|
26566
|
+
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
|
|
26567
|
+
|
|
26568
|
+
if (!this.featureSourceMap) {
|
|
26569
|
+
await this.init();
|
|
26570
|
+
}
|
|
26571
|
+
const fs = this.featureSourceMap.get(chr);
|
|
26572
|
+
if (fs) {
|
|
26573
|
+
return fs.getFeatures({chr, start, end, bpPerPixel, visibilityWindow})
|
|
26574
|
+
} else {
|
|
26575
|
+
return []
|
|
26576
|
+
}
|
|
26577
|
+
}
|
|
26578
|
+
|
|
26579
|
+
async init() {
|
|
26580
|
+
this.featureSourceMap = new Map();
|
|
26581
|
+
|
|
26582
|
+
const options = buildOptions(this.config);
|
|
26583
|
+
const data = await igvxhr.loadByteArray(this.config.url, options);
|
|
26584
|
+
const dataWrapper = getDataWrapper(data);
|
|
26585
|
+
|
|
26586
|
+
let line;
|
|
26587
|
+
while ((line = dataWrapper.nextLine()) !== undefined) {
|
|
26588
|
+
const trimmed = line.trim();
|
|
26589
|
+
if (!trimmed.startsWith('#')) {
|
|
26590
|
+
const tokens = trimmed.split(/\s+/);
|
|
26591
|
+
if (tokens.length > 1) {
|
|
26592
|
+
const chr = tokens[0];
|
|
26593
|
+
const path = tokens[1];
|
|
26594
|
+
const sourceConfig = Object.assign({}, this.config);
|
|
26595
|
+
sourceConfig.url = path;
|
|
26596
|
+
if (path.endsWith(".vcf.gz")) {
|
|
26597
|
+
sourceConfig.format = "vcf";
|
|
26598
|
+
sourceConfig.indexURL = path + ".tbi";
|
|
26599
|
+
}
|
|
26600
|
+
this.featureSourceMap.set(chr, FeatureSource(sourceConfig, this.genome));
|
|
26601
|
+
}
|
|
26602
|
+
}
|
|
26603
|
+
}
|
|
26604
|
+
}
|
|
26605
|
+
|
|
26606
|
+
supportWholeGenome() {
|
|
26607
|
+
return false
|
|
26608
|
+
}
|
|
26609
|
+
}
|
|
26610
|
+
|
|
26611
|
+
// chrY https://1000genomes.s3.amazonaws.com/release/20130502/ALL.chrY.phase3_integrated_v1b.20130502.genotypes.vcf.gz
|
|
26612
|
+
// chrX https://1000genomes.s3.amazonaws.com/release/20130502/ALL.chrX.phase3_shapeit2_mvncall_integrated_v1b.20130502.genotypes.vcf.gz
|
|
26613
|
+
|
|
26530
26614
|
const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
|
|
26531
26615
|
|
|
26532
26616
|
function FeatureSource(config, genome) {
|
|
@@ -26541,6 +26625,9 @@ function FeatureSource(config, genome) {
|
|
|
26541
26625
|
return new TDFSource(config, genome)
|
|
26542
26626
|
} else if ("gbk" === format) {
|
|
26543
26627
|
return new GenbankFeatureSource(config, genome)
|
|
26628
|
+
} else if ("vcf.list" === format) {
|
|
26629
|
+
// This is a text file with two columns: <chr> <url to vcf>
|
|
26630
|
+
return new ListFeatureSource(config, genome)
|
|
26544
26631
|
} else {
|
|
26545
26632
|
return new TextFeatureSource(config, genome)
|
|
26546
26633
|
}
|
|
@@ -27011,14 +27098,14 @@ function getAminoAcidLetterWithExonGap(strand, phase, phaseExtentStart, phaseExt
|
|
|
27011
27098
|
if (phase) {
|
|
27012
27099
|
stringA = sequenceInterval.getSequence(phaseExtentStart, phaseExtentEnd);
|
|
27013
27100
|
|
|
27014
|
-
if (
|
|
27101
|
+
if (!stringA) {
|
|
27015
27102
|
return undefined
|
|
27016
27103
|
}
|
|
27017
27104
|
|
|
27018
27105
|
[ss, ee] = [getEonStart(riteExon), getEonStart(riteExon) + (3 - phase)];
|
|
27019
27106
|
stringB = sequenceInterval.getSequence(ss, ee);
|
|
27020
27107
|
|
|
27021
|
-
if (
|
|
27108
|
+
if (!stringB) {
|
|
27022
27109
|
return undefined
|
|
27023
27110
|
}
|
|
27024
27111
|
|
|
@@ -27030,7 +27117,7 @@ function getAminoAcidLetterWithExonGap(strand, phase, phaseExtentStart, phaseExt
|
|
|
27030
27117
|
if (remainder) {
|
|
27031
27118
|
stringB = sequenceInterval.getSequence(remainder.start, remainder.end);
|
|
27032
27119
|
|
|
27033
|
-
if (
|
|
27120
|
+
if (!stringB) {
|
|
27034
27121
|
return undefined
|
|
27035
27122
|
}
|
|
27036
27123
|
|
|
@@ -27038,7 +27125,7 @@ function getAminoAcidLetterWithExonGap(strand, phase, phaseExtentStart, phaseExt
|
|
|
27038
27125
|
const leftEnd = getExonEnd(leftExon);
|
|
27039
27126
|
stringA = sequenceInterval.getSequence(leftEnd - leftPhase, leftEnd);
|
|
27040
27127
|
|
|
27041
|
-
if (
|
|
27128
|
+
if (!stringA) {
|
|
27042
27129
|
return undefined
|
|
27043
27130
|
}
|
|
27044
27131
|
|
|
@@ -30403,6 +30490,19 @@ const supportedTypes = new Set([
|
|
|
30403
30490
|
const filterTracks = new Set(["cytoBandIdeo", "assembly", "gap", "gapOverlap", "allGaps",
|
|
30404
30491
|
"cpgIslandExtUnmasked", "windowMasker"]);
|
|
30405
30492
|
|
|
30493
|
+
const vizModeMap = new Map([
|
|
30494
|
+
["pack", "EXPANDED"],
|
|
30495
|
+
["full", "EXPANDED"],
|
|
30496
|
+
["squish", "SQUISHED"],
|
|
30497
|
+
["dense", "COLLAPSED"]
|
|
30498
|
+
]);
|
|
30499
|
+
const typeFormatMap = new Map([
|
|
30500
|
+
["vcftabix", "vcf"],
|
|
30501
|
+
["vcfphasedtrio", "vcf"],
|
|
30502
|
+
["bigdbsnp", "bigbed"],
|
|
30503
|
+
["genepred", "refgene"]
|
|
30504
|
+
]);
|
|
30505
|
+
|
|
30406
30506
|
class TrackDbHub {
|
|
30407
30507
|
|
|
30408
30508
|
constructor(trackStanzas, groupStanzas) {
|
|
@@ -30548,14 +30648,13 @@ class TrackDbHub {
|
|
|
30548
30648
|
*/
|
|
30549
30649
|
#getTrackConfig(t) {
|
|
30550
30650
|
|
|
30551
|
-
const format = t.format;
|
|
30651
|
+
const format = typeFormatMap.get(t.format) || t.format;
|
|
30552
30652
|
|
|
30553
30653
|
const config = {
|
|
30554
30654
|
"id": t.getProperty("track"),
|
|
30555
30655
|
"name": t.getProperty("shortLabel"),
|
|
30556
30656
|
"format": format,
|
|
30557
|
-
"url": t.getProperty("bigDataUrl")
|
|
30558
|
-
"displayMode": t.displayMode,
|
|
30657
|
+
"url": t.getProperty("bigDataUrl")
|
|
30559
30658
|
};
|
|
30560
30659
|
|
|
30561
30660
|
if ("vcfTabix" === format) {
|
|
@@ -30605,9 +30704,14 @@ class TrackDbHub {
|
|
|
30605
30704
|
|
|
30606
30705
|
}
|
|
30607
30706
|
if (t.hasProperty("itemRgb")) ;
|
|
30608
|
-
if
|
|
30609
|
-
|
|
30610
|
-
|
|
30707
|
+
if(t.hasProperty("visibility")) {
|
|
30708
|
+
if ("hide" === t.getProperty("visibility")) {
|
|
30709
|
+
// TODO -- this not supported yet
|
|
30710
|
+
config.visible = false;
|
|
30711
|
+
}
|
|
30712
|
+
else {
|
|
30713
|
+
config.displayMode = vizModeMap.get(t.getProperty("visibility")) || "COLLAPSED";
|
|
30714
|
+
}
|
|
30611
30715
|
}
|
|
30612
30716
|
if (t.hasProperty("url")) {
|
|
30613
30717
|
config.infoURL = t.getProperty("url");
|
|
@@ -37788,6 +37892,8 @@ function inferTrackType(format) {
|
|
|
37788
37892
|
case "tdf":
|
|
37789
37893
|
return "wig"
|
|
37790
37894
|
case "vcf":
|
|
37895
|
+
case "vcftabix":
|
|
37896
|
+
case "vcf.list":
|
|
37791
37897
|
return "variant"
|
|
37792
37898
|
case "seg":
|
|
37793
37899
|
return "seg"
|
|
@@ -37842,7 +37948,7 @@ function translateDeprecatedTypes(config) {
|
|
|
37842
37948
|
} else if ("bam" === config.type) {
|
|
37843
37949
|
config.type = "alignment";
|
|
37844
37950
|
config.format = "bam";
|
|
37845
|
-
} else if ("vcf" === config.type) {
|
|
37951
|
+
} else if ("vcf" === config.type || "vcftabix" === config.type) {
|
|
37846
37952
|
config.type = "variant";
|
|
37847
37953
|
config.format = "vcf";
|
|
37848
37954
|
} else if ("t2d" === config.type) {
|
|
@@ -38122,7 +38228,7 @@ class SegTrack extends TrackBase {
|
|
|
38122
38228
|
const groupIndeces = NULL_GROUP !== this.groupBy ?
|
|
38123
38229
|
this.sampleKeys.map(sample => this.getGroupIndex(sample)) : undefined;
|
|
38124
38230
|
return {
|
|
38125
|
-
names: this.sampleKeys,
|
|
38231
|
+
names: this.sampleKeys || [],
|
|
38126
38232
|
height: this.sampleHeight,
|
|
38127
38233
|
yOffset: 0,
|
|
38128
38234
|
groups: this.groups,
|
|
@@ -42825,6 +42931,49 @@ class Locus {
|
|
|
42825
42931
|
const end = Number.parseInt(se[1].replace(/,/g, ""));
|
|
42826
42932
|
return new Locus({chr, start, end})
|
|
42827
42933
|
}
|
|
42934
|
+
|
|
42935
|
+
/**
|
|
42936
|
+
* Return true if the locus string represents a single base, e.g. "chr1:12345" or "chr1:12345-12345"
|
|
42937
|
+
* @param locus
|
|
42938
|
+
* @returns {boolean}
|
|
42939
|
+
*/
|
|
42940
|
+
static isSingleBaseLocusString(locus) {
|
|
42941
|
+
|
|
42942
|
+
if (!locus || typeof locus !== 'string') {
|
|
42943
|
+
return false
|
|
42944
|
+
}
|
|
42945
|
+
|
|
42946
|
+
const parts = locus.split(':');
|
|
42947
|
+
if (parts.length <= 1) {
|
|
42948
|
+
return false
|
|
42949
|
+
}
|
|
42950
|
+
|
|
42951
|
+
const range = parts[1].replace(/,/g, '');
|
|
42952
|
+
if (!range) {
|
|
42953
|
+
return false
|
|
42954
|
+
}
|
|
42955
|
+
|
|
42956
|
+
const rangeParts = range.split('-');
|
|
42957
|
+
const startString = rangeParts[0];
|
|
42958
|
+
const start = parseInt(startString, 10);
|
|
42959
|
+
|
|
42960
|
+
if (String(start) !== startString || !Number.isInteger(start)) {
|
|
42961
|
+
return false
|
|
42962
|
+
}
|
|
42963
|
+
|
|
42964
|
+
if (rangeParts.length === 1) {
|
|
42965
|
+
return true
|
|
42966
|
+
}
|
|
42967
|
+
|
|
42968
|
+
const endString = rangeParts[1];
|
|
42969
|
+
const end = parseInt(endString, 10);
|
|
42970
|
+
|
|
42971
|
+
if (String(end) !== endString || !Number.isInteger(end)) {
|
|
42972
|
+
return false
|
|
42973
|
+
}
|
|
42974
|
+
|
|
42975
|
+
return start === end
|
|
42976
|
+
}
|
|
42828
42977
|
}
|
|
42829
42978
|
|
|
42830
42979
|
/*!
|
|
@@ -62776,7 +62925,8 @@ class VariantTrack extends TrackBase {
|
|
|
62776
62925
|
}
|
|
62777
62926
|
|
|
62778
62927
|
get supportsWholeGenome() {
|
|
62779
|
-
|
|
62928
|
+
const sourceSupportsWG = typeof this.featureSource.supportsWholeGenome === 'function' && this.featureSource.supportsWholeGenome();
|
|
62929
|
+
return sourceSupportsWG || this.config.supportsWholeGenome === true
|
|
62780
62930
|
}
|
|
62781
62931
|
|
|
62782
62932
|
get color() {
|
|
@@ -62849,7 +62999,7 @@ class VariantTrack extends TrackBase {
|
|
|
62849
62999
|
const yOffset = TOP_MARGIN + nVariantRows * (variantHeight + vGap);
|
|
62850
63000
|
|
|
62851
63001
|
return {
|
|
62852
|
-
names: this.sampleKeys,
|
|
63002
|
+
names: this.sampleKeys || [],
|
|
62853
63003
|
yOffset,
|
|
62854
63004
|
height,
|
|
62855
63005
|
// groups: this.groups,
|
|
@@ -66980,7 +67130,7 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
|
|
|
66980
67130
|
})
|
|
66981
67131
|
}
|
|
66982
67132
|
|
|
66983
|
-
const _version = "3.5.
|
|
67133
|
+
const _version = "3.5.3";
|
|
66984
67134
|
function version() {
|
|
66985
67135
|
return _version
|
|
66986
67136
|
}
|
|
@@ -74235,6 +74385,12 @@ class Browser {
|
|
|
74235
74385
|
|
|
74236
74386
|
await this.loadTrackList(nonLocalTrackConfigurations);
|
|
74237
74387
|
|
|
74388
|
+
// If an initial locus is defined and represents a single basedo a "search" here. This will force micro
|
|
74389
|
+
// adjustments after width of track column(s) is known. This can be an issue when the center gide is shown
|
|
74390
|
+
// Without this adjustment the single base would be off center by a few pixels.
|
|
74391
|
+
if (session.locus && Locus.isSingleBaseLocusString(session.locus)) {
|
|
74392
|
+
await this.search(session.locus);
|
|
74393
|
+
}
|
|
74238
74394
|
}
|
|
74239
74395
|
|
|
74240
74396
|
cleanHouseForSession() {
|
|
@@ -74521,6 +74677,10 @@ class Browser {
|
|
|
74521
74677
|
config = JSON.parse(config);
|
|
74522
74678
|
}
|
|
74523
74679
|
|
|
74680
|
+
if(config.format && config.format.toLowerCase() === 'sampleinfo') {
|
|
74681
|
+
return this.loadSampleInfo(config)
|
|
74682
|
+
}
|
|
74683
|
+
|
|
74524
74684
|
let track;
|
|
74525
74685
|
try {
|
|
74526
74686
|
track = await this.createTrack(config);
|
|
@@ -76297,6 +76457,10 @@ function setOauthToken(accessToken, host) {
|
|
|
76297
76457
|
return igvxhr.setOauthToken(accessToken, host)
|
|
76298
76458
|
}
|
|
76299
76459
|
|
|
76460
|
+
function setCORSProxy(proxyURL) {
|
|
76461
|
+
igvxhr.corsProxy = proxyURL;
|
|
76462
|
+
}
|
|
76463
|
+
|
|
76300
76464
|
// Backward compatibility
|
|
76301
76465
|
const oauth = igvxhr.oauth;
|
|
76302
76466
|
|
|
@@ -76313,6 +76477,7 @@ var index = {
|
|
|
76313
76477
|
visibilityChange,
|
|
76314
76478
|
setGoogleOauthToken,
|
|
76315
76479
|
setOauthToken,
|
|
76480
|
+
setCORSProxy,
|
|
76316
76481
|
oauth,
|
|
76317
76482
|
version,
|
|
76318
76483
|
setApiKey,
|