igv 3.5.2 → 3.5.4
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 +414 -444
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +414 -444
- package/dist/igv.min.js +9 -9
- package/dist/igv.min.js.map +1 -1
- package/package.json +2 -2
package/dist/igv.esm.js
CHANGED
|
@@ -386,12 +386,12 @@ function numberFormatter$1(rawNumber) {
|
|
|
386
386
|
decsep = '.';
|
|
387
387
|
|
|
388
388
|
return dec[0].split('').reverse().reduce(function (prev, now, i) {
|
|
389
|
-
return i % 3 === 0 ? prev + sep + now : prev + now
|
|
390
|
-
}).split('').reverse().join('') + (dec[1] ? decsep + dec[1] : '')
|
|
389
|
+
return i % 3 === 0 ? prev + sep + now : prev + now
|
|
390
|
+
}).split('').reverse().join('') + (dec[1] ? decsep + dec[1] : '')
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
const splitLines$3 = function (string) {
|
|
394
|
-
return string.split(/\n|\r\n|\r/g)
|
|
394
|
+
return string.split(/\n|\r\n|\r/g)
|
|
395
395
|
};
|
|
396
396
|
|
|
397
397
|
|
|
@@ -419,24 +419,24 @@ function splitStringRespectingQuotes(string, delim) {
|
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
|
-
return tokens
|
|
422
|
+
return tokens
|
|
423
423
|
}
|
|
424
424
|
|
|
425
425
|
function stripQuotes$2(str) {
|
|
426
|
-
if(str === undefined) {
|
|
427
|
-
return str
|
|
426
|
+
if (str === undefined) {
|
|
427
|
+
return str
|
|
428
428
|
}
|
|
429
|
-
if(str.startsWith("'") || str.startsWith('"')) {
|
|
429
|
+
if (str.startsWith("'") || str.startsWith('"')) {
|
|
430
430
|
str = str.substring(1);
|
|
431
431
|
}
|
|
432
432
|
if (str.endsWith("'") || str.endsWith('"')) {
|
|
433
433
|
str = str.substring(0, str.length - 1);
|
|
434
434
|
}
|
|
435
|
-
return str
|
|
435
|
+
return str
|
|
436
436
|
}
|
|
437
437
|
|
|
438
438
|
function capitalize(str) {
|
|
439
|
-
return str.length > 0 ? str.charAt(0).toUpperCase() + str.slice(1) : str
|
|
439
|
+
return str.length > 0 ? str.charAt(0).toUpperCase() + str.slice(1) : str
|
|
440
440
|
}
|
|
441
441
|
|
|
442
442
|
|
|
@@ -460,7 +460,7 @@ function parseLocusString$1(string) {
|
|
|
460
460
|
range.end = range.start + 1;
|
|
461
461
|
}
|
|
462
462
|
|
|
463
|
-
return range
|
|
463
|
+
return range
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
/**
|
|
@@ -469,7 +469,7 @@ function parseLocusString$1(string) {
|
|
|
469
469
|
* @param urlOrFile
|
|
470
470
|
*/
|
|
471
471
|
|
|
472
|
-
function getFilename$
|
|
472
|
+
function getFilename$1(urlOrFile) {
|
|
473
473
|
|
|
474
474
|
if (urlOrFile.name !== undefined) {
|
|
475
475
|
return urlOrFile.name
|
|
@@ -496,8 +496,8 @@ function getFilename$2(urlOrFile) {
|
|
|
496
496
|
* @param object
|
|
497
497
|
*/
|
|
498
498
|
function isFile(object) {
|
|
499
|
-
if(!object) {
|
|
500
|
-
return false
|
|
499
|
+
if (!object) {
|
|
500
|
+
return false
|
|
501
501
|
}
|
|
502
502
|
return typeof object !== 'function' &&
|
|
503
503
|
(object instanceof File ||
|
|
@@ -517,7 +517,7 @@ function download(filename, data) {
|
|
|
517
517
|
|
|
518
518
|
if (typeof process === 'object' && typeof window === 'undefined') {
|
|
519
519
|
global.atob = function (str) {
|
|
520
|
-
return Buffer.from(str, 'base64').toString('binary')
|
|
520
|
+
return Buffer.from(str, 'base64').toString('binary')
|
|
521
521
|
};
|
|
522
522
|
}
|
|
523
523
|
|
|
@@ -536,7 +536,7 @@ function parseUri(str) {
|
|
|
536
536
|
if ($1) uri[o.q.name][$1] = $2;
|
|
537
537
|
});
|
|
538
538
|
|
|
539
|
-
return uri
|
|
539
|
+
return uri
|
|
540
540
|
}
|
|
541
541
|
|
|
542
542
|
const options = {
|
|
@@ -559,7 +559,7 @@ const options = {
|
|
|
559
559
|
* @returns {Promise<*>}
|
|
560
560
|
*/
|
|
561
561
|
async function resolveURL(url) {
|
|
562
|
-
return (typeof url === 'function')
|
|
562
|
+
return (typeof url === 'function') ? url() : url
|
|
563
563
|
}
|
|
564
564
|
|
|
565
565
|
/*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */
|
|
@@ -7421,7 +7421,7 @@ const FEXTRA$1 = 4; // gzip spec F.EXTRA flag
|
|
|
7421
7421
|
|
|
7422
7422
|
function isgzipped(data) {
|
|
7423
7423
|
const b = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
|
|
7424
|
-
return b[0] ===31 && b[1] === 139
|
|
7424
|
+
return b[0] === 31 && b[1] === 139
|
|
7425
7425
|
}
|
|
7426
7426
|
|
|
7427
7427
|
/**
|
|
@@ -7431,9 +7431,9 @@ function ungzip_blocks(data) {
|
|
|
7431
7431
|
const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
|
|
7432
7432
|
const b = ba[3] & FEXTRA$1;
|
|
7433
7433
|
if (b !== 0 && ba[12] === 66 && ba[13] === 67) {
|
|
7434
|
-
return unbgzf(ba.buffer)
|
|
7434
|
+
return unbgzf(ba.buffer)
|
|
7435
7435
|
} else {
|
|
7436
|
-
return ungzip_1$1(ba)
|
|
7436
|
+
return ungzip_1$1(ba)
|
|
7437
7437
|
}
|
|
7438
7438
|
}
|
|
7439
7439
|
|
|
@@ -7459,7 +7459,7 @@ function unbgzf(data, lim) {
|
|
|
7459
7459
|
const start = 12 + xlen + ptr; // Start of CDATA
|
|
7460
7460
|
const bytesLeft = data.byteLength - start;
|
|
7461
7461
|
const cDataSize = bsize - xlen - 19;
|
|
7462
|
-
if (bytesLeft < cDataSize || cDataSize <= 0) break
|
|
7462
|
+
if (bytesLeft < cDataSize || cDataSize <= 0) break
|
|
7463
7463
|
|
|
7464
7464
|
const a = new Uint8Array(data, start, cDataSize);
|
|
7465
7465
|
const unc = inflateRaw_1(a);
|
|
@@ -7472,13 +7472,13 @@ function unbgzf(data, lim) {
|
|
|
7472
7472
|
oBlockList.push(unc);
|
|
7473
7473
|
} catch (e) {
|
|
7474
7474
|
console.error(e);
|
|
7475
|
-
break
|
|
7475
|
+
break
|
|
7476
7476
|
}
|
|
7477
7477
|
}
|
|
7478
7478
|
|
|
7479
7479
|
// Concatenate decompressed blocks
|
|
7480
7480
|
if (oBlockList.length === 1) {
|
|
7481
|
-
return oBlockList[0]
|
|
7481
|
+
return oBlockList[0]
|
|
7482
7482
|
} else {
|
|
7483
7483
|
const out = new Uint8Array(totalSize);
|
|
7484
7484
|
let cursor = 0;
|
|
@@ -7487,14 +7487,14 @@ function unbgzf(data, lim) {
|
|
|
7487
7487
|
arrayCopy(b, 0, out, cursor, b.length);
|
|
7488
7488
|
cursor += b.length;
|
|
7489
7489
|
}
|
|
7490
|
-
return out
|
|
7490
|
+
return out
|
|
7491
7491
|
}
|
|
7492
7492
|
}
|
|
7493
7493
|
|
|
7494
7494
|
function bgzBlockSize$1(data) {
|
|
7495
7495
|
const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
|
|
7496
7496
|
const bsize = (ba[17] << 8 | ba[16]) + 1;
|
|
7497
|
-
return bsize
|
|
7497
|
+
return bsize
|
|
7498
7498
|
}
|
|
7499
7499
|
|
|
7500
7500
|
// From Thomas Down's zlib implementation
|
|
@@ -7504,12 +7504,12 @@ const hasSubarray = (typeof testArray.subarray === 'function');
|
|
|
7504
7504
|
|
|
7505
7505
|
function arrayCopy(src, srcOffset, dest, destOffset, count) {
|
|
7506
7506
|
if (count === 0) {
|
|
7507
|
-
return
|
|
7507
|
+
return
|
|
7508
7508
|
}
|
|
7509
7509
|
if (!src) {
|
|
7510
|
-
throw "Undef src"
|
|
7510
|
+
throw "Undef src"
|
|
7511
7511
|
} else if (!dest) {
|
|
7512
|
-
throw "Undef dest"
|
|
7512
|
+
throw "Undef dest"
|
|
7513
7513
|
}
|
|
7514
7514
|
if (srcOffset === 0 && count === src.length) {
|
|
7515
7515
|
arrayCopy_fast(src, dest, destOffset);
|
|
@@ -7546,7 +7546,7 @@ function compressString(str) {
|
|
|
7546
7546
|
const compressedBytes = new deflateRaw_1(bytes); // UInt8Arry
|
|
7547
7547
|
const compressedString = String.fromCharCode.apply(null, compressedBytes); // Convert to string
|
|
7548
7548
|
let enc = btoa(compressedString);
|
|
7549
|
-
return enc.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '-')
|
|
7549
|
+
return enc.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '-') // URL safe
|
|
7550
7550
|
}
|
|
7551
7551
|
|
|
7552
7552
|
/**
|
|
@@ -7571,7 +7571,7 @@ function uncompressString(enc) {
|
|
|
7571
7571
|
for (let b of bytes) {
|
|
7572
7572
|
str += String.fromCharCode(b);
|
|
7573
7573
|
}
|
|
7574
|
-
return str
|
|
7574
|
+
return str
|
|
7575
7575
|
}
|
|
7576
7576
|
|
|
7577
7577
|
|
|
@@ -7601,7 +7601,7 @@ function decodeDataURI$1(dataURI, gzip) {
|
|
|
7601
7601
|
}
|
|
7602
7602
|
return plain
|
|
7603
7603
|
} else {
|
|
7604
|
-
return decodeURIComponent(dataString)
|
|
7604
|
+
return decodeURIComponent(dataString) // URL encoded string -- not currently used or tested
|
|
7605
7605
|
}
|
|
7606
7606
|
}
|
|
7607
7607
|
|
|
@@ -7633,7 +7633,7 @@ function decodeDataURI$1(dataURI, gzip) {
|
|
|
7633
7633
|
const IGVMath = {
|
|
7634
7634
|
|
|
7635
7635
|
lerp: (v0, v1, t) => {
|
|
7636
|
-
return (1 - t) * v0 + t * v1
|
|
7636
|
+
return (1 - t) * v0 + t * v1
|
|
7637
7637
|
},
|
|
7638
7638
|
|
|
7639
7639
|
mean: function (array) {
|
|
@@ -7646,7 +7646,7 @@ const IGVMath = {
|
|
|
7646
7646
|
n++;
|
|
7647
7647
|
}
|
|
7648
7648
|
}
|
|
7649
|
-
return n > 0 ? t / n : 0
|
|
7649
|
+
return n > 0 ? t / n : 0
|
|
7650
7650
|
},
|
|
7651
7651
|
|
|
7652
7652
|
meanAndStdev: function (array) {
|
|
@@ -7663,7 +7663,7 @@ const IGVMath = {
|
|
|
7663
7663
|
n++;
|
|
7664
7664
|
}
|
|
7665
7665
|
}
|
|
7666
|
-
return n > 0 ? {mean: t / n, stdev: Math.sqrt(t2 - t * t / n)} : {mean: 0, stdev: 0}
|
|
7666
|
+
return n > 0 ? {mean: t / n, stdev: Math.sqrt(t2 - t * t / n)} : {mean: 0, stdev: 0}
|
|
7667
7667
|
},
|
|
7668
7668
|
|
|
7669
7669
|
median: function (numbers) {
|
|
@@ -7681,33 +7681,33 @@ const IGVMath = {
|
|
|
7681
7681
|
median = numbers[(numsLen - 1) / 2];
|
|
7682
7682
|
}
|
|
7683
7683
|
|
|
7684
|
-
return median
|
|
7684
|
+
return median
|
|
7685
7685
|
},
|
|
7686
7686
|
|
|
7687
7687
|
// Fast percentile function for "p" near edges. This needs profiled for p in middle (e.g. median)
|
|
7688
7688
|
percentile: function (array, p) {
|
|
7689
7689
|
|
|
7690
|
-
if (array.length === 0) return undefined
|
|
7690
|
+
if (array.length === 0) return undefined
|
|
7691
7691
|
|
|
7692
7692
|
var k = Math.floor(array.length * ((100 - p) / 100));
|
|
7693
7693
|
if (k === 0) {
|
|
7694
7694
|
array.sort(function (a, b) {
|
|
7695
7695
|
return b - a
|
|
7696
7696
|
});
|
|
7697
|
-
return array[k]
|
|
7697
|
+
return array[k]
|
|
7698
7698
|
} else {
|
|
7699
|
-
return selectElement(array, k)
|
|
7699
|
+
return selectElement(array, k)
|
|
7700
7700
|
}
|
|
7701
7701
|
|
|
7702
7702
|
},
|
|
7703
7703
|
|
|
7704
7704
|
|
|
7705
7705
|
clamp: function (value, min, max) {
|
|
7706
|
-
return Math.min(Math.max(value, min), max)
|
|
7706
|
+
return Math.min(Math.max(value, min), max)
|
|
7707
7707
|
},
|
|
7708
7708
|
|
|
7709
7709
|
log2: function (x) {
|
|
7710
|
-
return Math.log(x) / Math.LN2
|
|
7710
|
+
return Math.log(x) / Math.LN2
|
|
7711
7711
|
}
|
|
7712
7712
|
|
|
7713
7713
|
};
|
|
@@ -7733,7 +7733,7 @@ function selectElement(array, k) {
|
|
|
7733
7733
|
}
|
|
7734
7734
|
}
|
|
7735
7735
|
|
|
7736
|
-
return heap.content[0]
|
|
7736
|
+
return heap.content[0]
|
|
7737
7737
|
}
|
|
7738
7738
|
|
|
7739
7739
|
|
|
@@ -7760,7 +7760,7 @@ BinaryHeap.prototype = {
|
|
|
7760
7760
|
this.content[0] = end;
|
|
7761
7761
|
this.sinkDown(0);
|
|
7762
7762
|
}
|
|
7763
|
-
return result
|
|
7763
|
+
return result
|
|
7764
7764
|
},
|
|
7765
7765
|
|
|
7766
7766
|
remove: function (node) {
|
|
@@ -7768,24 +7768,24 @@ BinaryHeap.prototype = {
|
|
|
7768
7768
|
// To remove a value, we must search through the array to find
|
|
7769
7769
|
// it.
|
|
7770
7770
|
for (var i = 0; i < length; i++) {
|
|
7771
|
-
if (this.content[i] !== node) continue
|
|
7771
|
+
if (this.content[i] !== node) continue
|
|
7772
7772
|
// When it is found, the process seen in 'pop' is repeated
|
|
7773
7773
|
// to fill up the hole.
|
|
7774
7774
|
var end = this.content.pop();
|
|
7775
7775
|
// If the element we popped was the one we needed to remove,
|
|
7776
7776
|
// we're done.
|
|
7777
|
-
if (i === length - 1) break
|
|
7777
|
+
if (i === length - 1) break
|
|
7778
7778
|
// Otherwise, we replace the removed element with the popped
|
|
7779
7779
|
// one, and allow it to float up or sink down as appropriate.
|
|
7780
7780
|
this.content[i] = end;
|
|
7781
7781
|
this.bubbleUp(i);
|
|
7782
7782
|
this.sinkDown(i);
|
|
7783
|
-
break
|
|
7783
|
+
break
|
|
7784
7784
|
}
|
|
7785
7785
|
},
|
|
7786
7786
|
|
|
7787
7787
|
size: function () {
|
|
7788
|
-
return this.content.length
|
|
7788
|
+
return this.content.length
|
|
7789
7789
|
},
|
|
7790
7790
|
|
|
7791
7791
|
bubbleUp: function (n) {
|
|
@@ -7799,7 +7799,7 @@ BinaryHeap.prototype = {
|
|
|
7799
7799
|
// If the parent has a lesser score, things are in order and we
|
|
7800
7800
|
// are done.
|
|
7801
7801
|
if (score >= parent)
|
|
7802
|
-
break
|
|
7802
|
+
break
|
|
7803
7803
|
|
|
7804
7804
|
// Otherwise, swap the parent with the current element and
|
|
7805
7805
|
// continue.
|
|
@@ -7839,7 +7839,7 @@ BinaryHeap.prototype = {
|
|
|
7839
7839
|
}
|
|
7840
7840
|
|
|
7841
7841
|
// No need to swap further, we are done.
|
|
7842
|
-
if (swap == null) break
|
|
7842
|
+
if (swap == null) break
|
|
7843
7843
|
|
|
7844
7844
|
// Otherwise, swap and continue.
|
|
7845
7845
|
this.content[n] = this.content[swap];
|
|
@@ -8433,145 +8433,78 @@ function regExpEscape(s) {
|
|
|
8433
8433
|
}
|
|
8434
8434
|
|
|
8435
8435
|
function isGoogleURL(url) {
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
}
|
|
8440
|
-
|
|
8441
|
-
function isGoogleStorageURL(url) {
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
}
|
|
8447
|
-
|
|
8448
|
-
function isGoogleDriveURL(url) {
|
|
8449
|
-
|
|
8450
|
-
}
|
|
8451
|
-
|
|
8452
|
-
/**
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
function translateGoogleCloudURL(gsUrl) {
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
const qIdx = gsUrl.indexOf('?');
|
|
8464
|
-
const paramString = (qIdx > 0) ? gsUrl.substring(qIdx) + "&alt=media" : "?alt=media";
|
|
8465
|
-
|
|
8466
|
-
return `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}${paramString}`
|
|
8467
|
-
}
|
|
8468
|
-
|
|
8469
|
-
/**
|
|
8470
|
-
* Parse a google bucket and object name from a google storage URL. Known forms include
|
|
8471
|
-
*
|
|
8472
|
-
* gs://BUCKET_NAME/OBJECT_NAME
|
|
8473
|
-
* https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME
|
|
8474
|
-
* https://storage.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME
|
|
8475
|
-
* https://www.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME"
|
|
8476
|
-
* https://storage.googleapis.com/download/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME
|
|
8477
|
-
*
|
|
8478
|
-
* @param url
|
|
8479
|
-
*/
|
|
8480
|
-
function parseBucketName(url) {
|
|
8481
|
-
|
|
8482
|
-
let bucket;
|
|
8483
|
-
let object;
|
|
8484
|
-
|
|
8485
|
-
if (url.startsWith("gs://")) {
|
|
8486
|
-
const i = url.indexOf('/', 5);
|
|
8487
|
-
if (i >= 0) {
|
|
8488
|
-
bucket = url.substring(5, i);
|
|
8489
|
-
const qIdx = url.indexOf('?');
|
|
8490
|
-
object = (qIdx < 0) ? url.substring(i + 1) : url.substring(i + 1, qIdx);
|
|
8491
|
-
}
|
|
8492
|
-
|
|
8493
|
-
} else if (url.startsWith("https://storage.googleapis.com") || url.startsWith("https://storage.cloud.google.com")) {
|
|
8494
|
-
const bucketIdx = url.indexOf("/v1/b/", 8);
|
|
8495
|
-
if (bucketIdx > 0) {
|
|
8496
|
-
const objIdx = url.indexOf("/o/", bucketIdx);
|
|
8497
|
-
if (objIdx > 0) {
|
|
8498
|
-
const queryIdx = url.indexOf("?", objIdx);
|
|
8499
|
-
bucket = url.substring(bucketIdx + 6, objIdx);
|
|
8500
|
-
object = queryIdx > 0 ? url.substring(objIdx + 3, queryIdx) : url.substring(objIdx + 3);
|
|
8501
|
-
}
|
|
8502
|
-
|
|
8503
|
-
} else {
|
|
8504
|
-
const idx1 = url.indexOf("/", 8);
|
|
8505
|
-
const idx2 = url.indexOf("/", idx1+1);
|
|
8506
|
-
const idx3 = url.indexOf("?", idx2);
|
|
8507
|
-
if (idx2 > 0) {
|
|
8508
|
-
bucket = url.substring(idx1+1, idx2);
|
|
8509
|
-
object = idx3 < 0 ? url.substring(idx2+1) : url.substring(idx2+1, idx3);
|
|
8510
|
-
}
|
|
8511
|
-
}
|
|
8512
|
-
|
|
8513
|
-
} else if (url.startsWith("https://www.googleapis.com/storage/v1/b")) {
|
|
8514
|
-
const bucketIdx = url.indexOf("/v1/b/", 8);
|
|
8515
|
-
const objIdx = url.indexOf("/o/", bucketIdx);
|
|
8516
|
-
if (objIdx > 0) {
|
|
8517
|
-
const queryIdx = url.indexOf("?", objIdx);
|
|
8518
|
-
bucket = url.substring(bucketIdx + 6, objIdx);
|
|
8519
|
-
object = queryIdx > 0 ? url.substring(objIdx + 3, queryIdx) : url.substring(objIdx + 3);
|
|
8520
|
-
}
|
|
8521
|
-
}
|
|
8436
|
+
return (url.includes("googleapis") && !url.includes("urlshortener")) ||
|
|
8437
|
+
isGoogleStorageURL(url) ||
|
|
8438
|
+
isGoogleDriveURL(url);
|
|
8439
|
+
}
|
|
8440
|
+
|
|
8441
|
+
function isGoogleStorageURL(url) {
|
|
8442
|
+
return url.startsWith("gs://") ||
|
|
8443
|
+
url.startsWith("https://www.googleapis.com/storage") ||
|
|
8444
|
+
url.startsWith("https://storage.cloud.google.com") ||
|
|
8445
|
+
url.startsWith("https://storage.googleapis.com");
|
|
8446
|
+
}
|
|
8447
|
+
|
|
8448
|
+
function isGoogleDriveURL(url) {
|
|
8449
|
+
return url.startsWith("https://www.googleapis.com/drive/v3/files");
|
|
8450
|
+
}
|
|
8451
|
+
|
|
8452
|
+
/**
|
|
8453
|
+
* Translate gs:// urls to https
|
|
8454
|
+
* See https://cloud.google.com/storage/docs/json_api/v1
|
|
8455
|
+
* @param gsUrl
|
|
8456
|
+
* @returns {string|*}
|
|
8457
|
+
*/
|
|
8458
|
+
function translateGoogleCloudURL(gsUrl) {
|
|
8459
|
+
try {
|
|
8460
|
+
let {bucket, object} = parseBucketName(gsUrl);
|
|
8461
|
+
object = encode(object);
|
|
8522
8462
|
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
bucket, object
|
|
8526
|
-
}
|
|
8527
|
-
} else {
|
|
8528
|
-
throw Error(`Unrecognized Google Storage URI: ${url}`)
|
|
8529
|
-
}
|
|
8463
|
+
const qIdx = gsUrl.indexOf('?');
|
|
8464
|
+
let paramString = (qIdx > 0) ? gsUrl.substring(qIdx) : "";
|
|
8530
8465
|
|
|
8531
|
-
|
|
8466
|
+
if (!paramString.includes("alt=media")) {
|
|
8467
|
+
paramString = paramString ? `${paramString}&alt=media` : "?alt=media";
|
|
8468
|
+
}
|
|
8532
8469
|
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
*/
|
|
8470
|
+
return `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}${paramString}`;
|
|
8471
|
+
} catch (error) {
|
|
8472
|
+
throw new Error(`Failed to translate Google Cloud URL: ${error.message}`);
|
|
8473
|
+
}
|
|
8474
|
+
}
|
|
8539
8475
|
|
|
8540
|
-
|
|
8476
|
+
/**
|
|
8477
|
+
* Parse a google bucket and object name from a google storage URL.
|
|
8478
|
+
* @param url
|
|
8479
|
+
*/
|
|
8480
|
+
function parseBucketName(url) {
|
|
8481
|
+
const regex = /gs:\/\/([a-zA-Z0-9._-]+)\/([^?]+)|https?:\/\/(?:storage\.googleapis\.com|storage\.cloud\.google\.com|www\.googleapis\.com)\/(?:storage\/v1\/b\/)?([a-zA-Z0-9._-]+)\/(?:o\/)?([^?]+)/;
|
|
8482
|
+
const match = url.match(regex);
|
|
8483
|
+
|
|
8484
|
+
if (match) {
|
|
8485
|
+
const bucket = match[1] || match[3];
|
|
8486
|
+
const object = match[2] || match[4];
|
|
8487
|
+
if (bucket && object) {
|
|
8488
|
+
return { bucket, object };
|
|
8489
|
+
}
|
|
8490
|
+
}
|
|
8491
|
+
throw new Error(`Unrecognized Google Storage URI: ${url}`);
|
|
8492
|
+
}
|
|
8541
8493
|
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
});
|
|
8550
|
-
return result;
|
|
8551
|
-
}
|
|
8494
|
+
/**
|
|
8495
|
+
* Percent a GCS object name. See https://cloud.google.com/storage/docs/request-endpoints
|
|
8496
|
+
* @param objectName
|
|
8497
|
+
*/
|
|
8498
|
+
function encode(objectName) {
|
|
8499
|
+
return objectName.split('').map(letter => encodings$1.get(letter) || letter).join('');
|
|
8500
|
+
}
|
|
8552
8501
|
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
encodings$1.set("&", "%26");
|
|
8560
|
-
encodings$1.set("'", "%27");
|
|
8561
|
-
encodings$1.set("(", "%28");
|
|
8562
|
-
encodings$1.set(")", "%29");
|
|
8563
|
-
encodings$1.set("*", "%2A");
|
|
8564
|
-
encodings$1.set("+", "%2B");
|
|
8565
|
-
encodings$1.set(",", "%2C");
|
|
8566
|
-
encodings$1.set("/", "%2F");
|
|
8567
|
-
encodings$1.set(":", "%3A");
|
|
8568
|
-
encodings$1.set(";", "%3B");
|
|
8569
|
-
encodings$1.set("=", "%3D");
|
|
8570
|
-
encodings$1.set("?", "%3F");
|
|
8571
|
-
encodings$1.set("@", "%40");
|
|
8572
|
-
encodings$1.set("[", "%5B");
|
|
8573
|
-
encodings$1.set("]", "%5D");
|
|
8574
|
-
encodings$1.set(" ", "%20");
|
|
8502
|
+
const encodings$1 = new Map([
|
|
8503
|
+
["!", "%21"], ["#", "%23"], ["$", "%24"], ["%", "%25"], ["&", "%26"],
|
|
8504
|
+
["'", "%27"], ["(", "%28"], [")", "%29"], ["*", "%2A"], ["+", "%2B"],
|
|
8505
|
+
[",", "%2C"], ["/", "%2F"], [":", "%3A"], [";", "%3B"], ["=", "%3D"],
|
|
8506
|
+
["?", "%3F"], ["@", "%40"], ["[", "%5B"], ["]", "%5D"], [" ", "%20"]
|
|
8507
|
+
]);
|
|
8575
8508
|
|
|
8576
8509
|
// Convenience functions for the gapi oAuth library.
|
|
8577
8510
|
|
|
@@ -8602,7 +8535,8 @@ async function init(config) {
|
|
|
8602
8535
|
// Attach an object to keep igv state
|
|
8603
8536
|
google.igv = {
|
|
8604
8537
|
tokenClient: tokenClient,
|
|
8605
|
-
apiKey: config.apiKey
|
|
8538
|
+
apiKey: config.apiKey,
|
|
8539
|
+
appId: config.appId
|
|
8606
8540
|
};
|
|
8607
8541
|
}
|
|
8608
8542
|
|
|
@@ -8622,8 +8556,6 @@ function getCurrentAccessToken() {
|
|
|
8622
8556
|
undefined
|
|
8623
8557
|
}
|
|
8624
8558
|
|
|
8625
|
-
|
|
8626
|
-
let promise;
|
|
8627
8559
|
/**
|
|
8628
8560
|
* Return a promise for an access token for the given scope. If the user hasn't authorized the scope request it
|
|
8629
8561
|
*
|
|
@@ -8642,27 +8574,21 @@ async function getAccessToken(scope) {
|
|
|
8642
8574
|
return google.igv.tokenResponse.access_token
|
|
8643
8575
|
} else {
|
|
8644
8576
|
const tokenClient = google.igv.tokenClient;
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
} catch (err) {
|
|
8661
|
-
console.log(err);
|
|
8662
|
-
}
|
|
8663
|
-
});
|
|
8664
|
-
}
|
|
8665
|
-
return promise
|
|
8577
|
+
return new Promise((resolve, reject) => {
|
|
8578
|
+
tokenClient.callback = (tokenResponse) => {
|
|
8579
|
+
if (tokenResponse.error !== undefined) {
|
|
8580
|
+
return reject(tokenResponse)
|
|
8581
|
+
}
|
|
8582
|
+
google.igv.tokenResponse = tokenResponse;
|
|
8583
|
+
google.igv.tokenExpiresAt = Date.now() + tokenResponse.expires_in * 1000;
|
|
8584
|
+
resolve(tokenResponse.access_token);
|
|
8585
|
+
};
|
|
8586
|
+
try {
|
|
8587
|
+
tokenClient.requestAccessToken({scope});
|
|
8588
|
+
} catch (err) {
|
|
8589
|
+
reject(err);
|
|
8590
|
+
}
|
|
8591
|
+
})
|
|
8666
8592
|
}
|
|
8667
8593
|
}
|
|
8668
8594
|
|
|
@@ -8681,90 +8607,6 @@ function getScopeForURL(url) {
|
|
|
8681
8607
|
}
|
|
8682
8608
|
}
|
|
8683
8609
|
|
|
8684
|
-
function getApiKey() {
|
|
8685
|
-
return google.igv.apiKey
|
|
8686
|
-
}
|
|
8687
|
-
|
|
8688
|
-
/**
|
|
8689
|
-
* Return information about a specific google drive URL
|
|
8690
|
-
*
|
|
8691
|
-
* @param googleDriveURL
|
|
8692
|
-
* @returns {Promise<any>}
|
|
8693
|
-
*/
|
|
8694
|
-
async function getDriveFileInfo(googleDriveURL) {
|
|
8695
|
-
|
|
8696
|
-
const id = getGoogleDriveFileID(googleDriveURL);
|
|
8697
|
-
let endPoint = "https://www.googleapis.com/drive/v3/files/" + id + "?supportsTeamDrives=true";
|
|
8698
|
-
const apiKey = getApiKey();
|
|
8699
|
-
if (apiKey) {
|
|
8700
|
-
endPoint += "&key=" + apiKey;
|
|
8701
|
-
}
|
|
8702
|
-
const response = await fetch(endPoint);
|
|
8703
|
-
let json = await response.json();
|
|
8704
|
-
if (json.error && json.error.code === 404) {
|
|
8705
|
-
let scope = "https://www.googleapis.com/auth/drive.readonly";
|
|
8706
|
-
const access_token = await getAccessToken(scope);
|
|
8707
|
-
if (access_token) {
|
|
8708
|
-
const response = await fetch(endPoint, {
|
|
8709
|
-
headers: {
|
|
8710
|
-
'Authorization': `Bearer ${access_token}`
|
|
8711
|
-
}
|
|
8712
|
-
});
|
|
8713
|
-
json = await response.json();
|
|
8714
|
-
if (json.error) {
|
|
8715
|
-
throw Error(json.error);
|
|
8716
|
-
}
|
|
8717
|
-
} else {
|
|
8718
|
-
throw Error(json.error);
|
|
8719
|
-
}
|
|
8720
|
-
}
|
|
8721
|
-
return json;
|
|
8722
|
-
}
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
function getDriveDownloadURL(link) {
|
|
8726
|
-
// Return a google drive download url for the sharable link
|
|
8727
|
-
//https://drive.google.com/open?id=0B-lleX9c2pZFbDJ4VVRxakJzVGM
|
|
8728
|
-
//https://drive.google.com/file/d/1_FC4kCeO8E3V4dJ1yIW7A0sn1yURKIX-/view?usp=sharing
|
|
8729
|
-
var id = getGoogleDriveFileID(link);
|
|
8730
|
-
return id ? "https://www.googleapis.com/drive/v3/files/" + id + "?alt=media&supportsTeamDrives=true" : link;
|
|
8731
|
-
}
|
|
8732
|
-
|
|
8733
|
-
function getGoogleDriveFileID(link) {
|
|
8734
|
-
|
|
8735
|
-
//https://drive.google.com/file/d/1_FC4kCeO8E3V4dJ1yIW7A0sn1yURKIX-/view?usp=sharing
|
|
8736
|
-
//https://www.googleapis.com/drive/v3/files/1w-tvo6p1SH4p1OaQSVxpkV_EJgGIstWF?alt=media&supportsTeamDrives=true"
|
|
8737
|
-
|
|
8738
|
-
if (link.includes("/open?id=")) {
|
|
8739
|
-
const i1 = link.indexOf("/open?id=") + 9;
|
|
8740
|
-
const i2 = link.indexOf("&");
|
|
8741
|
-
if (i1 > 0 && i2 > i1) {
|
|
8742
|
-
return link.substring(i1, i2)
|
|
8743
|
-
} else if (i1 > 0) {
|
|
8744
|
-
return link.substring(i1);
|
|
8745
|
-
}
|
|
8746
|
-
|
|
8747
|
-
} else if (link.includes("/file/d/")) {
|
|
8748
|
-
const i1 = link.indexOf("/file/d/") + 8;
|
|
8749
|
-
const i2 = link.lastIndexOf("/");
|
|
8750
|
-
return link.substring(i1, i2);
|
|
8751
|
-
|
|
8752
|
-
} else if (link.startsWith("https://www.googleapis.com/drive")) {
|
|
8753
|
-
let i1 = link.indexOf("/files/");
|
|
8754
|
-
const i2 = link.indexOf("?");
|
|
8755
|
-
if (i1 > 0) {
|
|
8756
|
-
i1 += 7;
|
|
8757
|
-
return i2 > 0 ?
|
|
8758
|
-
link.substring(i1, i2) :
|
|
8759
|
-
link.substring(i1)
|
|
8760
|
-
}
|
|
8761
|
-
}
|
|
8762
|
-
|
|
8763
|
-
throw Error("Unknown Google Drive url format: " + link);
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
}
|
|
8767
|
-
|
|
8768
8610
|
// The MIT License (MIT)
|
|
8769
8611
|
|
|
8770
8612
|
/**
|
|
@@ -8796,7 +8638,7 @@ class Throttle {
|
|
|
8796
8638
|
asyncFunction: asyncFunction,
|
|
8797
8639
|
});
|
|
8798
8640
|
self.dequeue();
|
|
8799
|
-
})
|
|
8641
|
+
})
|
|
8800
8642
|
}
|
|
8801
8643
|
|
|
8802
8644
|
/**
|
|
@@ -8809,10 +8651,10 @@ class Throttle {
|
|
|
8809
8651
|
*/
|
|
8810
8652
|
addAll(promises, options) {
|
|
8811
8653
|
var addedPromises = promises.map(function (promise) {
|
|
8812
|
-
return this.add(promise, options)
|
|
8654
|
+
return this.add(promise, options)
|
|
8813
8655
|
}.bind(this));
|
|
8814
8656
|
|
|
8815
|
-
return Promise.all(addedPromises)
|
|
8657
|
+
return Promise.all(addedPromises)
|
|
8816
8658
|
};
|
|
8817
8659
|
|
|
8818
8660
|
/**
|
|
@@ -8884,6 +8726,10 @@ class Throttle {
|
|
|
8884
8726
|
|
|
8885
8727
|
class IGVXhr {
|
|
8886
8728
|
|
|
8729
|
+
UCSC_HOST = "hgdownload.soe.ucsc.edu"
|
|
8730
|
+
UCSC_BACKUP_HOST = "genome-browser.s3.us-east-1.amazonaws.com"
|
|
8731
|
+
|
|
8732
|
+
|
|
8887
8733
|
constructor() {
|
|
8888
8734
|
this.apiKey = undefined;
|
|
8889
8735
|
this.googleThrottle = new Throttle({
|
|
@@ -8891,6 +8737,7 @@ class IGVXhr {
|
|
|
8891
8737
|
});
|
|
8892
8738
|
this.RANGE_WARNING_GIVEN = false;
|
|
8893
8739
|
this.oauth = new Oauth();
|
|
8740
|
+
this.corsProxy = undefined;
|
|
8894
8741
|
}
|
|
8895
8742
|
|
|
8896
8743
|
setApiKey(key) {
|
|
@@ -8915,7 +8762,7 @@ class IGVXhr {
|
|
|
8915
8762
|
* @param options
|
|
8916
8763
|
* @returns {Promise<Uint8Array>}
|
|
8917
8764
|
*/
|
|
8918
|
-
|
|
8765
|
+
async loadByteArray(url, options) {
|
|
8919
8766
|
const arraybuffer = await this.loadArrayBuffer(url, options);
|
|
8920
8767
|
let plain;
|
|
8921
8768
|
if (isgzipped(arraybuffer)) {
|
|
@@ -8960,7 +8807,7 @@ class IGVXhr {
|
|
|
8960
8807
|
|
|
8961
8808
|
if (isFile(url)) {
|
|
8962
8809
|
return this._loadFileSlice(url, options)
|
|
8963
|
-
} else if (
|
|
8810
|
+
} else if (isString$3(url)) {
|
|
8964
8811
|
if (url.startsWith("data:")) {
|
|
8965
8812
|
const buffer = decodeDataURI$1(url).buffer;
|
|
8966
8813
|
if (options.range) {
|
|
@@ -8970,9 +8817,6 @@ class IGVXhr {
|
|
|
8970
8817
|
return buffer
|
|
8971
8818
|
}
|
|
8972
8819
|
} else {
|
|
8973
|
-
if (url.startsWith("https://drive.google.com")) {
|
|
8974
|
-
url = getDriveDownloadURL(url);
|
|
8975
|
-
}
|
|
8976
8820
|
if (isGoogleDriveURL(url) || url.startsWith("https://www.dropbox.com")) {
|
|
8977
8821
|
return this.googleThrottle.add(async () => {
|
|
8978
8822
|
return this._loadURL(url, options)
|
|
@@ -8990,14 +8834,21 @@ class IGVXhr {
|
|
|
8990
8834
|
|
|
8991
8835
|
const self = this;
|
|
8992
8836
|
const _url = url; // The unmodified URL, needed in case of an oAuth retry
|
|
8837
|
+
const {host} = parseUri(_url);
|
|
8993
8838
|
|
|
8994
8839
|
url = mapUrl$1(url);
|
|
8995
8840
|
|
|
8996
8841
|
options = options || {};
|
|
8997
8842
|
|
|
8998
|
-
let oauthToken
|
|
8999
|
-
if (
|
|
9000
|
-
|
|
8843
|
+
let oauthToken;
|
|
8844
|
+
if (isGoogleDriveURL(url)) {
|
|
8845
|
+
// Google drive urls always require oAuth
|
|
8846
|
+
oauthToken = await getAccessToken("https://www.googleapis.com/auth/drive.file");
|
|
8847
|
+
} else {
|
|
8848
|
+
oauthToken = options.oauthToken || this.getOauthToken(url);
|
|
8849
|
+
if (oauthToken) {
|
|
8850
|
+
oauthToken = await (typeof oauthToken === 'function' ? oauthToken() : oauthToken);
|
|
8851
|
+
}
|
|
9001
8852
|
}
|
|
9002
8853
|
|
|
9003
8854
|
return new Promise(function (resolve, reject) {
|
|
@@ -9070,8 +8921,10 @@ class IGVXhr {
|
|
|
9070
8921
|
|
|
9071
8922
|
xhr.onload = async function (event) {
|
|
9072
8923
|
|
|
8924
|
+
const isFileProtocol = url.toLowerCase().startsWith("file:");
|
|
8925
|
+
|
|
9073
8926
|
// when the url points to a local file, the status is 0
|
|
9074
|
-
if (xhr.status
|
|
8927
|
+
if ((xhr.status >= 200 && xhr.status <= 300) || (isFileProtocol && xhr.status === 0)) {
|
|
9075
8928
|
if ("HEAD" === options.method) {
|
|
9076
8929
|
// Support fetching specific headers. Attempting to fetch all headers can be problematic with CORS
|
|
9077
8930
|
const headers = options.requestedHeaders || ['content-length'];
|
|
@@ -9087,7 +8940,7 @@ class IGVXhr {
|
|
|
9087
8940
|
// For small files a range starting at 0 can return the whole file => 200
|
|
9088
8941
|
// Provide just the slice we asked for, throw out the rest quietly
|
|
9089
8942
|
// If file is large warn user
|
|
9090
|
-
if (xhr.response.length >
|
|
8943
|
+
if (xhr.response.length > 1000000 && !self.RANGE_WARNING_GIVEN) {
|
|
9091
8944
|
alert(`Warning: Range header ignored for URL: ${url}. This can have severe performance impacts.`);
|
|
9092
8945
|
}
|
|
9093
8946
|
resolve(xhr.response.slice(range.start, range.start + range.size));
|
|
@@ -9104,20 +8957,29 @@ class IGVXhr {
|
|
|
9104
8957
|
tryGoogleAuth();
|
|
9105
8958
|
|
|
9106
8959
|
} else {
|
|
8960
|
+
const error = new Error(`Error accessing resource: ${url} status: ${xhr.status}`);
|
|
9107
8961
|
if (xhr.status === 403) {
|
|
9108
8962
|
handleError("Access forbidden: " + url);
|
|
8963
|
+
} else if (host === self.UCSC_HOST) {
|
|
8964
|
+
tryUcscBackup(self.UCSC_HOST, self.UCSC_BACKUP_HOST, error);
|
|
8965
|
+
} else if (xhr.status === 0 && self.corsProxy && !options.corsProxyRetried) {
|
|
8966
|
+
tryCorsProxy(error);
|
|
9109
8967
|
} else {
|
|
9110
|
-
handleError(
|
|
8968
|
+
handleError(error);
|
|
9111
8969
|
}
|
|
9112
8970
|
}
|
|
9113
8971
|
};
|
|
9114
8972
|
|
|
9115
|
-
|
|
9116
8973
|
xhr.onerror = function (event) {
|
|
8974
|
+
const error = new Error(`Error accessing resource: ${url} status: ${xhr.status}`);
|
|
9117
8975
|
if (isGoogleURL(url) && !options.retries) {
|
|
9118
8976
|
tryGoogleAuth();
|
|
8977
|
+
} else if (host === self.UCSC_HOST) {
|
|
8978
|
+
tryUcscBackup(self.UCSC_HOST, self.UCSC_BACKUP_HOST, error);
|
|
8979
|
+
} else if (self.corsProxy && !options.corsProxyRetried) {
|
|
8980
|
+
tryCorsProxy(error);
|
|
9119
8981
|
} else {
|
|
9120
|
-
handleError(
|
|
8982
|
+
handleError(error);
|
|
9121
8983
|
}
|
|
9122
8984
|
};
|
|
9123
8985
|
|
|
@@ -9149,6 +9011,27 @@ class IGVXhr {
|
|
|
9149
9011
|
}
|
|
9150
9012
|
}
|
|
9151
9013
|
|
|
9014
|
+
async function tryCorsProxy(error) {
|
|
9015
|
+
options.corsProxyRetried = true;
|
|
9016
|
+
const proxyUrl = self.corsProxy + (_url.includes("?") ? "&" : "?") + "url=" + encodeURIComponent(_url);
|
|
9017
|
+
try {
|
|
9018
|
+
const result = await self._loadURL(proxyUrl, options);
|
|
9019
|
+
resolve(result);
|
|
9020
|
+
} catch (e) {
|
|
9021
|
+
handleError(error);
|
|
9022
|
+
}
|
|
9023
|
+
}
|
|
9024
|
+
|
|
9025
|
+
async function tryUcscBackup(UCSC_HOST, UCSC_BACKUP_HOST, error) {
|
|
9026
|
+
const backupUrl = _url.replace(UCSC_HOST, UCSC_BACKUP_HOST);
|
|
9027
|
+
try {
|
|
9028
|
+
const result = await self._loadURL(backupUrl, options);
|
|
9029
|
+
resolve(result);
|
|
9030
|
+
} catch (e) {
|
|
9031
|
+
handleError(error);
|
|
9032
|
+
}
|
|
9033
|
+
}
|
|
9034
|
+
|
|
9152
9035
|
async function tryGoogleAuth() {
|
|
9153
9036
|
try {
|
|
9154
9037
|
const accessToken = await fetchGoogleAccessToken(_url);
|
|
@@ -9167,6 +9050,8 @@ class IGVXhr {
|
|
|
9167
9050
|
}
|
|
9168
9051
|
}
|
|
9169
9052
|
}
|
|
9053
|
+
|
|
9054
|
+
|
|
9170
9055
|
})
|
|
9171
9056
|
|
|
9172
9057
|
}
|
|
@@ -9239,20 +9124,29 @@ class IGVXhr {
|
|
|
9239
9124
|
}
|
|
9240
9125
|
|
|
9241
9126
|
/**
|
|
9242
|
-
*
|
|
9243
|
-
*
|
|
9244
|
-
*
|
|
9127
|
+
* Return the content length of the file at the given URL. This is not guaranteed to succeed, some servers
|
|
9128
|
+
* do not support or allow the content-length header.
|
|
9129
|
+
*
|
|
9245
9130
|
* @param url
|
|
9246
9131
|
* @param options
|
|
9247
9132
|
* @returns {Promise<unknown>}
|
|
9248
9133
|
*/
|
|
9249
9134
|
async getContentLength(url, options) {
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9135
|
+
if (isFile(url)) {
|
|
9136
|
+
return url.size
|
|
9137
|
+
} else {
|
|
9138
|
+
try {
|
|
9139
|
+
options = options || {};
|
|
9140
|
+
options.method = 'HEAD';
|
|
9141
|
+
options.requestedHeaders = ['content-length'];
|
|
9142
|
+
const headerMap = await this._loadURL(url, options);
|
|
9143
|
+
const contentLengthString = headerMap['content-length'];
|
|
9144
|
+
return contentLengthString ? Number.parseInt(contentLengthString) : 0
|
|
9145
|
+
} catch (e) {
|
|
9146
|
+
console.error(e);
|
|
9147
|
+
return -1
|
|
9148
|
+
}
|
|
9149
|
+
}
|
|
9256
9150
|
}
|
|
9257
9151
|
|
|
9258
9152
|
}
|
|
@@ -9315,8 +9209,10 @@ function addApiKey(url) {
|
|
|
9315
9209
|
}
|
|
9316
9210
|
|
|
9317
9211
|
function addTeamDrive(url) {
|
|
9318
|
-
if (url.includes("
|
|
9212
|
+
if (url.includes("supportsAllDrives")) {
|
|
9319
9213
|
return url
|
|
9214
|
+
} else if (url.includes("supportsTeamDrives")) {
|
|
9215
|
+
return url.replaceAll("supportsTeamDrives", "supportsAllDrives")
|
|
9320
9216
|
} else {
|
|
9321
9217
|
const paramSeparator = url.includes("?") ? "&" : "?";
|
|
9322
9218
|
url = url + paramSeparator + "supportsTeamDrive=true";
|
|
@@ -9331,8 +9227,6 @@ function mapUrl$1(url) {
|
|
|
9331
9227
|
|
|
9332
9228
|
if (url.startsWith("https://www.dropbox.com")) {
|
|
9333
9229
|
return url.replace("//www.dropbox.com", "//dl.dropboxusercontent.com")
|
|
9334
|
-
} else if (url.startsWith("https://drive.google.com")) {
|
|
9335
|
-
return getDriveDownloadURL(url)
|
|
9336
9230
|
} else if (url.includes("//www.broadinstitute.org/igvdata")) {
|
|
9337
9231
|
return url.replace("//www.broadinstitute.org/igvdata", "//data.broadinstitute.org/igvdata")
|
|
9338
9232
|
} else if (url.includes("//igvdata.broadinstitute.org")) {
|
|
@@ -9528,17 +9422,17 @@ class IntervalTree$1 {
|
|
|
9528
9422
|
|
|
9529
9423
|
var searchInterval = new Interval$1(start, end, 0);
|
|
9530
9424
|
|
|
9531
|
-
if (this.root === NIL$1) return []
|
|
9425
|
+
if (this.root === NIL$1) return []
|
|
9532
9426
|
|
|
9533
9427
|
var intervals = searchAll$1.call(this, searchInterval, this.root, []);
|
|
9534
9428
|
|
|
9535
9429
|
if (intervals.length > 1) {
|
|
9536
9430
|
intervals.sort(function (i1, i2) {
|
|
9537
|
-
return i1.low - i2.low
|
|
9431
|
+
return i1.low - i2.low
|
|
9538
9432
|
});
|
|
9539
9433
|
}
|
|
9540
9434
|
|
|
9541
|
-
return intervals
|
|
9435
|
+
return intervals
|
|
9542
9436
|
}
|
|
9543
9437
|
|
|
9544
9438
|
/**
|
|
@@ -9623,7 +9517,7 @@ function searchAll$1(interval, node, results) {
|
|
|
9623
9517
|
searchAll$1.call(this, interval, node.right, results);
|
|
9624
9518
|
}
|
|
9625
9519
|
|
|
9626
|
-
return results
|
|
9520
|
+
return results
|
|
9627
9521
|
}
|
|
9628
9522
|
|
|
9629
9523
|
function leftRotate$1(x) {
|
|
@@ -9702,35 +9596,35 @@ class Interval$1 {
|
|
|
9702
9596
|
|
|
9703
9597
|
equals(other) {
|
|
9704
9598
|
if (!other) {
|
|
9705
|
-
return false
|
|
9599
|
+
return false
|
|
9706
9600
|
}
|
|
9707
9601
|
if (this === other) {
|
|
9708
|
-
return true
|
|
9602
|
+
return true
|
|
9709
9603
|
}
|
|
9710
9604
|
return (this.low === other.low &&
|
|
9711
|
-
this.high === other.high)
|
|
9605
|
+
this.high === other.high)
|
|
9712
9606
|
|
|
9713
9607
|
}
|
|
9714
9608
|
|
|
9715
9609
|
compareTo(other) {
|
|
9716
9610
|
if (this.low < other.low)
|
|
9717
|
-
return -1
|
|
9611
|
+
return -1
|
|
9718
9612
|
if (this.low > other.low)
|
|
9719
|
-
return 1
|
|
9613
|
+
return 1
|
|
9720
9614
|
|
|
9721
9615
|
if (this.high < other.high)
|
|
9722
|
-
return -1
|
|
9616
|
+
return -1
|
|
9723
9617
|
if (this.high > other.high)
|
|
9724
|
-
return 1
|
|
9618
|
+
return 1
|
|
9725
9619
|
|
|
9726
|
-
return 0
|
|
9620
|
+
return 0
|
|
9727
9621
|
}
|
|
9728
9622
|
|
|
9729
9623
|
/**
|
|
9730
9624
|
* Returns true if this interval overlaps the other.
|
|
9731
9625
|
*/
|
|
9732
9626
|
overlaps(other) {
|
|
9733
|
-
|
|
9627
|
+
return (this.low <= other.high && other.low <= this.high)
|
|
9734
9628
|
}
|
|
9735
9629
|
}
|
|
9736
9630
|
|
|
@@ -9775,19 +9669,19 @@ const FeatureUtils = {
|
|
|
9775
9669
|
var start;
|
|
9776
9670
|
var end;
|
|
9777
9671
|
|
|
9778
|
-
if (!features) return
|
|
9672
|
+
if (!features) return
|
|
9779
9673
|
|
|
9780
9674
|
maxRows = maxRows || 10000;
|
|
9781
9675
|
|
|
9782
9676
|
if (!sorted) {
|
|
9783
9677
|
features.sort(function (a, b) {
|
|
9784
|
-
return a.start - b.start
|
|
9678
|
+
return a.start - b.start
|
|
9785
9679
|
});
|
|
9786
9680
|
}
|
|
9787
9681
|
|
|
9788
9682
|
|
|
9789
9683
|
if (features.length === 0) {
|
|
9790
|
-
return []
|
|
9684
|
+
return []
|
|
9791
9685
|
|
|
9792
9686
|
} else {
|
|
9793
9687
|
|
|
@@ -9840,7 +9734,7 @@ const FeatureUtils = {
|
|
|
9840
9734
|
} // while (bucket)
|
|
9841
9735
|
|
|
9842
9736
|
if (!bucket) {
|
|
9843
|
-
break
|
|
9737
|
+
break
|
|
9844
9738
|
}
|
|
9845
9739
|
feature = bucket.pop();
|
|
9846
9740
|
if (0 === bucket.length) {
|
|
@@ -9857,7 +9751,7 @@ const FeatureUtils = {
|
|
|
9857
9751
|
row++;
|
|
9858
9752
|
nextStart = bucketStart;
|
|
9859
9753
|
|
|
9860
|
-
if (allocatedCount === lastAllocatedCount) break
|
|
9754
|
+
if (allocatedCount === lastAllocatedCount) break // Protect from infinite loops
|
|
9861
9755
|
|
|
9862
9756
|
lastAllocatedCount = allocatedCount;
|
|
9863
9757
|
|
|
@@ -9879,13 +9773,13 @@ const FeatureUtils = {
|
|
|
9879
9773
|
findOverlapping: function (featureList, start, end) {
|
|
9880
9774
|
|
|
9881
9775
|
if (!featureList || featureList.length === 0) {
|
|
9882
|
-
return []
|
|
9776
|
+
return []
|
|
9883
9777
|
} else {
|
|
9884
9778
|
const tree = buildIntervalTree$1(featureList);
|
|
9885
9779
|
const intervals = tree.findOverlapping(start, end);
|
|
9886
9780
|
|
|
9887
9781
|
if (intervals.length === 0) {
|
|
9888
|
-
return []
|
|
9782
|
+
return []
|
|
9889
9783
|
} else {
|
|
9890
9784
|
// Trim the list of features in the intervals to those
|
|
9891
9785
|
// overlapping the requested range.
|
|
@@ -9898,7 +9792,7 @@ const FeatureUtils = {
|
|
|
9898
9792
|
const len = intervalFeatures.length;
|
|
9899
9793
|
for (let i = 0; i < len; i++) {
|
|
9900
9794
|
const feature = intervalFeatures[i];
|
|
9901
|
-
if (feature.start > end) break
|
|
9795
|
+
if (feature.start > end) break
|
|
9902
9796
|
else if (feature.end > start) {
|
|
9903
9797
|
featureList.push(feature);
|
|
9904
9798
|
}
|
|
@@ -9906,10 +9800,10 @@ const FeatureUtils = {
|
|
|
9906
9800
|
});
|
|
9907
9801
|
|
|
9908
9802
|
featureList.sort(function (a, b) {
|
|
9909
|
-
return a.start - b.start
|
|
9803
|
+
return a.start - b.start
|
|
9910
9804
|
});
|
|
9911
9805
|
|
|
9912
|
-
return featureList
|
|
9806
|
+
return featureList
|
|
9913
9807
|
}
|
|
9914
9808
|
}
|
|
9915
9809
|
|
|
@@ -9930,7 +9824,7 @@ function buildIntervalTree$1(featureList) {
|
|
|
9930
9824
|
const chunkSize = Math.max(10, Math.round(len / 100));
|
|
9931
9825
|
|
|
9932
9826
|
featureList.sort(function (f1, f2) {
|
|
9933
|
-
return (f1.start === f2.start ? 0 : (f1.start > f2.start ? 1 : -1))
|
|
9827
|
+
return (f1.start === f2.start ? 0 : (f1.start > f2.start ? 1 : -1))
|
|
9934
9828
|
});
|
|
9935
9829
|
|
|
9936
9830
|
for (let i = 0; i < len; i += chunkSize) {
|
|
@@ -9944,7 +9838,7 @@ function buildIntervalTree$1(featureList) {
|
|
|
9944
9838
|
tree.insert(iStart, iEnd, subArray);
|
|
9945
9839
|
}
|
|
9946
9840
|
|
|
9947
|
-
return tree
|
|
9841
|
+
return tree
|
|
9948
9842
|
}
|
|
9949
9843
|
|
|
9950
9844
|
function hexToRGB(hex) {
|
|
@@ -10395,7 +10289,7 @@ const doAutoscale = function (features) {
|
|
|
10395
10289
|
min = Number.MAX_VALUE;
|
|
10396
10290
|
max = -Number.MAX_VALUE;
|
|
10397
10291
|
|
|
10398
|
-
for(let f of features) {
|
|
10292
|
+
for (let f of features) {
|
|
10399
10293
|
if (!Number.isNaN(f.value)) {
|
|
10400
10294
|
min = Math.min(min, f.value);
|
|
10401
10295
|
max = Math.max(max, f.value);
|
|
@@ -10469,19 +10363,6 @@ const isNumber = function (num) {
|
|
|
10469
10363
|
return false
|
|
10470
10364
|
};
|
|
10471
10365
|
|
|
10472
|
-
async function getFilename$1(url) {
|
|
10473
|
-
if (isString$3(url) && url.startsWith("https://drive.google.com")) {
|
|
10474
|
-
// This will fail if Google API key is not defined
|
|
10475
|
-
if (getApiKey() === undefined) {
|
|
10476
|
-
throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
|
|
10477
|
-
}
|
|
10478
|
-
const json = await getDriveFileInfo(url);
|
|
10479
|
-
return json.originalFileName || json.name
|
|
10480
|
-
} else {
|
|
10481
|
-
return getFilename$2(url)
|
|
10482
|
-
}
|
|
10483
|
-
}
|
|
10484
|
-
|
|
10485
10366
|
function prettyBasePairNumber(raw) {
|
|
10486
10367
|
|
|
10487
10368
|
var denom,
|
|
@@ -10544,12 +10425,12 @@ function getElementVerticalDimension(element) {
|
|
|
10544
10425
|
const marginTop = parseInt(style.marginTop);
|
|
10545
10426
|
const marginBottom = parseInt(style.marginBottom);
|
|
10546
10427
|
|
|
10547
|
-
const {
|
|
10428
|
+
const {top, bottom, height} = element.getBoundingClientRect();
|
|
10548
10429
|
return {
|
|
10549
10430
|
top: Math.floor(top) - marginTop,
|
|
10550
10431
|
bottom: Math.floor(bottom) + marginBottom,
|
|
10551
10432
|
height: Math.floor(height) + marginTop + marginBottom
|
|
10552
|
-
}
|
|
10433
|
+
}
|
|
10553
10434
|
}
|
|
10554
10435
|
|
|
10555
10436
|
class Popover {
|
|
@@ -10739,7 +10620,7 @@ function createMenuElements$1(itemList, popover) {
|
|
|
10739
10620
|
return list;
|
|
10740
10621
|
}
|
|
10741
10622
|
|
|
10742
|
-
/*! @license DOMPurify 3.
|
|
10623
|
+
/*! @license DOMPurify 3.3.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.0/LICENSE */
|
|
10743
10624
|
|
|
10744
10625
|
const {
|
|
10745
10626
|
entries,
|
|
@@ -10925,7 +10806,7 @@ function lookupGetter(object, prop) {
|
|
|
10925
10806
|
}
|
|
10926
10807
|
|
|
10927
10808
|
const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
|
|
10928
|
-
const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', '
|
|
10809
|
+
const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
|
|
10929
10810
|
const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
|
|
10930
10811
|
// List of SVG elements that are disallowed by default.
|
|
10931
10812
|
// We still need to know them so that we can do namespace
|
|
@@ -10939,7 +10820,7 @@ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongd
|
|
|
10939
10820
|
const text = freeze(['#text']);
|
|
10940
10821
|
|
|
10941
10822
|
const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
|
|
10942
|
-
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
|
|
10823
|
+
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
|
|
10943
10824
|
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
|
|
10944
10825
|
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
10945
10826
|
|
|
@@ -11046,7 +10927,7 @@ const _createHooksMap = function _createHooksMap() {
|
|
|
11046
10927
|
function createDOMPurify() {
|
|
11047
10928
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
11048
10929
|
const DOMPurify = root => createDOMPurify(root);
|
|
11049
|
-
DOMPurify.version = '3.
|
|
10930
|
+
DOMPurify.version = '3.3.0';
|
|
11050
10931
|
DOMPurify.removed = [];
|
|
11051
10932
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
11052
10933
|
// Not running in a browser, provide a factory function
|
|
@@ -11157,6 +11038,21 @@ function createDOMPurify() {
|
|
|
11157
11038
|
let FORBID_TAGS = null;
|
|
11158
11039
|
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
11159
11040
|
let FORBID_ATTR = null;
|
|
11041
|
+
/* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */
|
|
11042
|
+
const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {
|
|
11043
|
+
tagCheck: {
|
|
11044
|
+
writable: true,
|
|
11045
|
+
configurable: false,
|
|
11046
|
+
enumerable: true,
|
|
11047
|
+
value: null
|
|
11048
|
+
},
|
|
11049
|
+
attributeCheck: {
|
|
11050
|
+
writable: true,
|
|
11051
|
+
configurable: false,
|
|
11052
|
+
enumerable: true,
|
|
11053
|
+
value: null
|
|
11054
|
+
}
|
|
11055
|
+
}));
|
|
11160
11056
|
/* Decide if ARIA attributes are okay */
|
|
11161
11057
|
let ALLOW_ARIA_ATTR = true;
|
|
11162
11058
|
/* Decide if custom data attributes are okay */
|
|
@@ -11349,16 +11245,24 @@ function createDOMPurify() {
|
|
|
11349
11245
|
}
|
|
11350
11246
|
/* Merge configuration parameters */
|
|
11351
11247
|
if (cfg.ADD_TAGS) {
|
|
11352
|
-
if (
|
|
11353
|
-
|
|
11248
|
+
if (typeof cfg.ADD_TAGS === 'function') {
|
|
11249
|
+
EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
|
|
11250
|
+
} else {
|
|
11251
|
+
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
11252
|
+
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
11253
|
+
}
|
|
11254
|
+
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
11354
11255
|
}
|
|
11355
|
-
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
11356
11256
|
}
|
|
11357
11257
|
if (cfg.ADD_ATTR) {
|
|
11358
|
-
if (
|
|
11359
|
-
|
|
11258
|
+
if (typeof cfg.ADD_ATTR === 'function') {
|
|
11259
|
+
EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
|
|
11260
|
+
} else {
|
|
11261
|
+
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
11262
|
+
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
11263
|
+
}
|
|
11264
|
+
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
11360
11265
|
}
|
|
11361
|
-
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
11362
11266
|
}
|
|
11363
11267
|
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
11364
11268
|
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
@@ -11666,7 +11570,7 @@ function createDOMPurify() {
|
|
|
11666
11570
|
return true;
|
|
11667
11571
|
}
|
|
11668
11572
|
/* Remove element if anything forbids its presence */
|
|
11669
|
-
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
11573
|
+
if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
|
|
11670
11574
|
/* Check if we have a custom element to handle */
|
|
11671
11575
|
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
11672
11576
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
|
|
@@ -11738,7 +11642,7 @@ function createDOMPurify() {
|
|
|
11738
11642
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
11739
11643
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
11740
11644
|
We don't need to check the value; it's always URI safe. */
|
|
11741
|
-
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
|
|
11645
|
+
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
|
|
11742
11646
|
if (
|
|
11743
11647
|
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
11744
11648
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
@@ -17145,7 +17049,7 @@ class TrackBase {
|
|
|
17145
17049
|
} else if (isFile(config.url)) {
|
|
17146
17050
|
this.name = config.url.name;
|
|
17147
17051
|
} else if (isString$3(config.url) && !config.url.startsWith("data:")) {
|
|
17148
|
-
this.name = getFilename$
|
|
17052
|
+
this.name = getFilename$1(config.url);
|
|
17149
17053
|
}
|
|
17150
17054
|
|
|
17151
17055
|
this.url = config.url;
|
|
@@ -20664,6 +20568,7 @@ class FeatureFileReader {
|
|
|
20664
20568
|
|
|
20665
20569
|
switch (config.format) {
|
|
20666
20570
|
case "vcf":
|
|
20571
|
+
case "vcftabix":
|
|
20667
20572
|
return new VcfParser(config)
|
|
20668
20573
|
case "seg" :
|
|
20669
20574
|
return new SegParser("seg")
|
|
@@ -21104,7 +21009,7 @@ class HtsgetReader {
|
|
|
21104
21009
|
config.format = format.toLowerCase();
|
|
21105
21010
|
config.sourceType = "htsget";
|
|
21106
21011
|
if (!config.name) {
|
|
21107
|
-
config.name =
|
|
21012
|
+
config.name = getFilename$1(config.url);
|
|
21108
21013
|
}
|
|
21109
21014
|
}
|
|
21110
21015
|
} catch (e) {
|
|
@@ -21275,6 +21180,7 @@ class HtsgetVariantReader extends HtsgetReader {
|
|
|
21275
21180
|
}
|
|
21276
21181
|
}
|
|
21277
21182
|
|
|
21183
|
+
// Base class for feature sources. Subclasses must implement getFeatures().
|
|
21278
21184
|
class BaseFeatureSource {
|
|
21279
21185
|
|
|
21280
21186
|
constructor(genome) {
|
|
@@ -21336,59 +21242,10 @@ class BaseFeatureSource {
|
|
|
21336
21242
|
}
|
|
21337
21243
|
}
|
|
21338
21244
|
|
|
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
|
-
}
|
|
21245
|
+
// Subclasses must implement
|
|
21246
|
+
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
|
|
21247
|
+
throw new Error("getFeatures not implemented")
|
|
21390
21248
|
}
|
|
21391
|
-
|
|
21392
21249
|
}
|
|
21393
21250
|
|
|
21394
21251
|
const GZIP_FLAG = 0x1;
|
|
@@ -26527,6 +26384,88 @@ class GenbankFeatureSource extends BaseFeatureSource {
|
|
|
26527
26384
|
}
|
|
26528
26385
|
}
|
|
26529
26386
|
|
|
26387
|
+
/**
|
|
26388
|
+
* A feature source for a "list" file. A list file is a text file with two columns, chromosome and URL. It was
|
|
26389
|
+
* created for the 1KG genotype files, which are split by chromosome, and at the moment that is the only use case.
|
|
26390
|
+
*/
|
|
26391
|
+
|
|
26392
|
+
class ListFeatureSource extends BaseFeatureSource {
|
|
26393
|
+
|
|
26394
|
+
constructor(config, genome) {
|
|
26395
|
+
super(genome);
|
|
26396
|
+
this.config = config;
|
|
26397
|
+
this.featureSourceMap = null;
|
|
26398
|
+
this.header = null;
|
|
26399
|
+
}
|
|
26400
|
+
|
|
26401
|
+
async getHeader() {
|
|
26402
|
+
|
|
26403
|
+
if (!this.header) {
|
|
26404
|
+
|
|
26405
|
+
if (!this.featureSourceMap) {
|
|
26406
|
+
await this.init();
|
|
26407
|
+
}
|
|
26408
|
+
// Return the header from the first feature source. It is assumed that all sources have a common header.
|
|
26409
|
+
const firstFS = this.featureSourceMap.values().next().value;
|
|
26410
|
+
if (firstFS && firstFS.getHeader) {
|
|
26411
|
+
this.header = firstFS.getHeader();
|
|
26412
|
+
} else {
|
|
26413
|
+
this.header = Promise.resolve(undefined);
|
|
26414
|
+
}
|
|
26415
|
+
}
|
|
26416
|
+
|
|
26417
|
+
return this.header
|
|
26418
|
+
|
|
26419
|
+
}
|
|
26420
|
+
|
|
26421
|
+
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
|
|
26422
|
+
|
|
26423
|
+
if (!this.featureSourceMap) {
|
|
26424
|
+
await this.init();
|
|
26425
|
+
}
|
|
26426
|
+
const fs = this.featureSourceMap.get(chr);
|
|
26427
|
+
if (fs) {
|
|
26428
|
+
return fs.getFeatures({chr, start, end, bpPerPixel, visibilityWindow})
|
|
26429
|
+
} else {
|
|
26430
|
+
return []
|
|
26431
|
+
}
|
|
26432
|
+
}
|
|
26433
|
+
|
|
26434
|
+
async init() {
|
|
26435
|
+
this.featureSourceMap = new Map();
|
|
26436
|
+
|
|
26437
|
+
const options = buildOptions(this.config);
|
|
26438
|
+
const data = await igvxhr.loadByteArray(this.config.url, options);
|
|
26439
|
+
const dataWrapper = getDataWrapper(data);
|
|
26440
|
+
|
|
26441
|
+
let line;
|
|
26442
|
+
while ((line = dataWrapper.nextLine()) !== undefined) {
|
|
26443
|
+
const trimmed = line.trim();
|
|
26444
|
+
if (!trimmed.startsWith('#')) {
|
|
26445
|
+
const tokens = trimmed.split(/\s+/);
|
|
26446
|
+
if (tokens.length > 1) {
|
|
26447
|
+
const chr = tokens[0];
|
|
26448
|
+
const path = tokens[1];
|
|
26449
|
+
const sourceConfig = Object.assign({}, this.config);
|
|
26450
|
+
sourceConfig.url = path;
|
|
26451
|
+
if (path.endsWith(".vcf.gz")) {
|
|
26452
|
+
sourceConfig.format = "vcf";
|
|
26453
|
+
sourceConfig.indexURL = path + ".tbi";
|
|
26454
|
+
}
|
|
26455
|
+
this.featureSourceMap.set(chr, FeatureSource(sourceConfig, this.genome));
|
|
26456
|
+
}
|
|
26457
|
+
}
|
|
26458
|
+
}
|
|
26459
|
+
}
|
|
26460
|
+
|
|
26461
|
+
supportWholeGenome() {
|
|
26462
|
+
return false
|
|
26463
|
+
}
|
|
26464
|
+
}
|
|
26465
|
+
|
|
26466
|
+
// chrY https://1000genomes.s3.amazonaws.com/release/20130502/ALL.chrY.phase3_integrated_v1b.20130502.genotypes.vcf.gz
|
|
26467
|
+
// chrX https://1000genomes.s3.amazonaws.com/release/20130502/ALL.chrX.phase3_shapeit2_mvncall_integrated_v1b.20130502.genotypes.vcf.gz
|
|
26468
|
+
|
|
26530
26469
|
const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
|
|
26531
26470
|
|
|
26532
26471
|
function FeatureSource(config, genome) {
|
|
@@ -26541,6 +26480,9 @@ function FeatureSource(config, genome) {
|
|
|
26541
26480
|
return new TDFSource(config, genome)
|
|
26542
26481
|
} else if ("gbk" === format) {
|
|
26543
26482
|
return new GenbankFeatureSource(config, genome)
|
|
26483
|
+
} else if ("vcf.list" === format) {
|
|
26484
|
+
// This is a text file with two columns: <chr> <url to vcf>
|
|
26485
|
+
return new ListFeatureSource(config, genome)
|
|
26544
26486
|
} else {
|
|
26545
26487
|
return new TextFeatureSource(config, genome)
|
|
26546
26488
|
}
|
|
@@ -30403,6 +30345,19 @@ const supportedTypes = new Set([
|
|
|
30403
30345
|
const filterTracks = new Set(["cytoBandIdeo", "assembly", "gap", "gapOverlap", "allGaps",
|
|
30404
30346
|
"cpgIslandExtUnmasked", "windowMasker"]);
|
|
30405
30347
|
|
|
30348
|
+
const vizModeMap = new Map([
|
|
30349
|
+
["pack", "EXPANDED"],
|
|
30350
|
+
["full", "EXPANDED"],
|
|
30351
|
+
["squish", "SQUISHED"],
|
|
30352
|
+
["dense", "COLLAPSED"]
|
|
30353
|
+
]);
|
|
30354
|
+
const typeFormatMap = new Map([
|
|
30355
|
+
["vcftabix", "vcf"],
|
|
30356
|
+
["vcfphasedtrio", "vcf"],
|
|
30357
|
+
["bigdbsnp", "bigbed"],
|
|
30358
|
+
["genepred", "refgene"]
|
|
30359
|
+
]);
|
|
30360
|
+
|
|
30406
30361
|
class TrackDbHub {
|
|
30407
30362
|
|
|
30408
30363
|
constructor(trackStanzas, groupStanzas) {
|
|
@@ -30548,14 +30503,13 @@ class TrackDbHub {
|
|
|
30548
30503
|
*/
|
|
30549
30504
|
#getTrackConfig(t) {
|
|
30550
30505
|
|
|
30551
|
-
const format = t.format;
|
|
30506
|
+
const format = typeFormatMap.get(t.format) || t.format;
|
|
30552
30507
|
|
|
30553
30508
|
const config = {
|
|
30554
30509
|
"id": t.getProperty("track"),
|
|
30555
30510
|
"name": t.getProperty("shortLabel"),
|
|
30556
30511
|
"format": format,
|
|
30557
|
-
"url": t.getProperty("bigDataUrl")
|
|
30558
|
-
"displayMode": t.displayMode,
|
|
30512
|
+
"url": t.getProperty("bigDataUrl")
|
|
30559
30513
|
};
|
|
30560
30514
|
|
|
30561
30515
|
if ("vcfTabix" === format) {
|
|
@@ -30605,9 +30559,14 @@ class TrackDbHub {
|
|
|
30605
30559
|
|
|
30606
30560
|
}
|
|
30607
30561
|
if (t.hasProperty("itemRgb")) ;
|
|
30608
|
-
if
|
|
30609
|
-
|
|
30610
|
-
|
|
30562
|
+
if(t.hasProperty("visibility")) {
|
|
30563
|
+
if ("hide" === t.getProperty("visibility")) {
|
|
30564
|
+
// TODO -- this not supported yet
|
|
30565
|
+
config.visible = false;
|
|
30566
|
+
}
|
|
30567
|
+
else {
|
|
30568
|
+
config.displayMode = vizModeMap.get(t.getProperty("visibility")) || "COLLAPSED";
|
|
30569
|
+
}
|
|
30611
30570
|
}
|
|
30612
30571
|
if (t.hasProperty("url")) {
|
|
30613
30572
|
config.infoURL = t.getProperty("url");
|
|
@@ -31585,7 +31544,7 @@ class TrackViewport extends Viewport {
|
|
|
31585
31544
|
|
|
31586
31545
|
this.overlayElement.style.top = `-${contentTop}px`;
|
|
31587
31546
|
|
|
31588
|
-
if(!this.checkZoomIn()) return
|
|
31547
|
+
if (!this.checkZoomIn()) return
|
|
31589
31548
|
|
|
31590
31549
|
if (!this.canvas) {
|
|
31591
31550
|
this.repaint();
|
|
@@ -32224,7 +32183,7 @@ class TrackViewport extends Viewport {
|
|
|
32224
32183
|
|
|
32225
32184
|
popupTimerID = setTimeout(() => {
|
|
32226
32185
|
|
|
32227
|
-
const content = this.
|
|
32186
|
+
const content = this.handleTrackClick(event);
|
|
32228
32187
|
if (content) {
|
|
32229
32188
|
|
|
32230
32189
|
if (false === event.shiftKey) {
|
|
@@ -32389,7 +32348,7 @@ class TrackViewport extends Viewport {
|
|
|
32389
32348
|
|
|
32390
32349
|
}
|
|
32391
32350
|
|
|
32392
|
-
|
|
32351
|
+
handleTrackClick(event) {
|
|
32393
32352
|
|
|
32394
32353
|
const clickState = this.createClickState(event);
|
|
32395
32354
|
|
|
@@ -37788,6 +37747,8 @@ function inferTrackType(format) {
|
|
|
37788
37747
|
case "tdf":
|
|
37789
37748
|
return "wig"
|
|
37790
37749
|
case "vcf":
|
|
37750
|
+
case "vcftabix":
|
|
37751
|
+
case "vcf.list":
|
|
37791
37752
|
return "variant"
|
|
37792
37753
|
case "seg":
|
|
37793
37754
|
return "seg"
|
|
@@ -37842,7 +37803,7 @@ function translateDeprecatedTypes(config) {
|
|
|
37842
37803
|
} else if ("bam" === config.type) {
|
|
37843
37804
|
config.type = "alignment";
|
|
37844
37805
|
config.format = "bam";
|
|
37845
|
-
} else if ("vcf" === config.type) {
|
|
37806
|
+
} else if ("vcf" === config.type || "vcftabix" === config.type) {
|
|
37846
37807
|
config.type = "variant";
|
|
37847
37808
|
config.format = "vcf";
|
|
37848
37809
|
} else if ("t2d" === config.type) {
|
|
@@ -38122,7 +38083,7 @@ class SegTrack extends TrackBase {
|
|
|
38122
38083
|
const groupIndeces = NULL_GROUP !== this.groupBy ?
|
|
38123
38084
|
this.sampleKeys.map(sample => this.getGroupIndex(sample)) : undefined;
|
|
38124
38085
|
return {
|
|
38125
|
-
names: this.sampleKeys,
|
|
38086
|
+
names: this.sampleKeys || [],
|
|
38126
38087
|
height: this.sampleHeight,
|
|
38127
38088
|
yOffset: 0,
|
|
38128
38089
|
groups: this.groups,
|
|
@@ -42535,7 +42496,7 @@ async function inferFileFormat(config) {
|
|
|
42535
42496
|
let format;
|
|
42536
42497
|
|
|
42537
42498
|
// First try determining format from file extension
|
|
42538
|
-
const filename = config.filename ||
|
|
42499
|
+
const filename = config.filename || getFilename$1(config.url);
|
|
42539
42500
|
if(filename) {
|
|
42540
42501
|
format = await inferFileFormatFromName(filename);
|
|
42541
42502
|
}
|
|
@@ -62819,7 +62780,8 @@ class VariantTrack extends TrackBase {
|
|
|
62819
62780
|
}
|
|
62820
62781
|
|
|
62821
62782
|
get supportsWholeGenome() {
|
|
62822
|
-
|
|
62783
|
+
const sourceSupportsWG = typeof this.featureSource.supportsWholeGenome === 'function' && this.featureSource.supportsWholeGenome();
|
|
62784
|
+
return sourceSupportsWG || this.config.supportsWholeGenome === true
|
|
62823
62785
|
}
|
|
62824
62786
|
|
|
62825
62787
|
get color() {
|
|
@@ -62892,7 +62854,7 @@ class VariantTrack extends TrackBase {
|
|
|
62892
62854
|
const yOffset = TOP_MARGIN + nVariantRows * (variantHeight + vGap);
|
|
62893
62855
|
|
|
62894
62856
|
return {
|
|
62895
|
-
names: this.sampleKeys,
|
|
62857
|
+
names: this.sampleKeys || [],
|
|
62896
62858
|
yOffset,
|
|
62897
62859
|
height,
|
|
62898
62860
|
// groups: this.groups,
|
|
@@ -67023,7 +66985,7 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
|
|
|
67023
66985
|
})
|
|
67024
66986
|
}
|
|
67025
66987
|
|
|
67026
|
-
const _version = "3.5.
|
|
66988
|
+
const _version = "3.5.4";
|
|
67027
66989
|
function version() {
|
|
67028
66990
|
return _version
|
|
67029
66991
|
}
|
|
@@ -68927,7 +68889,7 @@ class ROIManager {
|
|
|
68927
68889
|
|
|
68928
68890
|
for (let config of configs) {
|
|
68929
68891
|
if (!config.name && config.url) {
|
|
68930
|
-
config.name =
|
|
68892
|
+
config.name = getFilename$1(config.url);
|
|
68931
68893
|
}
|
|
68932
68894
|
if (config.url && !config.format) {
|
|
68933
68895
|
config.format = await inferFileFormat(config);
|
|
@@ -73714,7 +73676,8 @@ class EventEmitter {
|
|
|
73714
73676
|
return handler.apply(scope, args)
|
|
73715
73677
|
});
|
|
73716
73678
|
|
|
73717
|
-
return
|
|
73679
|
+
// The only event that uses the return value is "trackclick", which implicitly assumes a single handler
|
|
73680
|
+
return results[0]
|
|
73718
73681
|
}
|
|
73719
73682
|
}
|
|
73720
73683
|
|
|
@@ -74085,7 +74048,7 @@ class Browser {
|
|
|
74085
74048
|
} else {
|
|
74086
74049
|
let filename = options.filename;
|
|
74087
74050
|
if (!filename) {
|
|
74088
|
-
filename = (options.url ?
|
|
74051
|
+
filename = (options.url ? getFilename$1(options.url) : options.file.name);
|
|
74089
74052
|
}
|
|
74090
74053
|
|
|
74091
74054
|
if (filename.endsWith(".xml")) {
|
|
@@ -74099,10 +74062,8 @@ class Browser {
|
|
|
74099
74062
|
config = {
|
|
74100
74063
|
reference: genomeConfig
|
|
74101
74064
|
};
|
|
74102
|
-
} else
|
|
74065
|
+
} else {
|
|
74103
74066
|
config = await igvxhr.loadJson(urlOrFile);
|
|
74104
|
-
} else {
|
|
74105
|
-
throw Error("Unrecognized session file format:" + filename)
|
|
74106
74067
|
}
|
|
74107
74068
|
}
|
|
74108
74069
|
setDefaults(config, defaults);
|
|
@@ -74570,6 +74531,10 @@ class Browser {
|
|
|
74570
74531
|
config = JSON.parse(config);
|
|
74571
74532
|
}
|
|
74572
74533
|
|
|
74534
|
+
if(config.format && config.format.toLowerCase() === 'sampleinfo') {
|
|
74535
|
+
return this.loadSampleInfo(config)
|
|
74536
|
+
}
|
|
74537
|
+
|
|
74573
74538
|
let track;
|
|
74574
74539
|
try {
|
|
74575
74540
|
track = await this.createTrack(config);
|
|
@@ -76346,6 +76311,10 @@ function setOauthToken(accessToken, host) {
|
|
|
76346
76311
|
return igvxhr.setOauthToken(accessToken, host)
|
|
76347
76312
|
}
|
|
76348
76313
|
|
|
76314
|
+
function setCORSProxy(proxyURL) {
|
|
76315
|
+
igvxhr.corsProxy = proxyURL;
|
|
76316
|
+
}
|
|
76317
|
+
|
|
76349
76318
|
// Backward compatibility
|
|
76350
76319
|
const oauth = igvxhr.oauth;
|
|
76351
76320
|
|
|
@@ -76362,6 +76331,7 @@ var index = {
|
|
|
76362
76331
|
visibilityChange,
|
|
76363
76332
|
setGoogleOauthToken,
|
|
76364
76333
|
setOauthToken,
|
|
76334
|
+
setCORSProxy,
|
|
76365
76335
|
oauth,
|
|
76366
76336
|
version,
|
|
76367
76337
|
setApiKey,
|