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/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$2(urlOrFile) {
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') ? url() : url;
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, '-'); // URL safe
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); // URL encoded string -- not currently used or tested
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
- 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.indexOf("drive.google.com") >= 0 || url.indexOf("www.googleapis.com/drive") > 0
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
-
8460
- let {bucket, object} = parseBucketName(gsUrl);
8461
- object = encode(object);
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
- if (bucket && object) {
8524
- return {
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
- * Percent a GCS object name. See https://cloud.google.com/storage/docs/request-endpoints
8535
- * Specific characters to encode:
8536
- * !, #, $, &, ', (, ), *, +, ,, /, :, ;, =, ?, @, [, ], and space characters.
8537
- * @param obj
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
- function encode(objectName) {
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
- let result = '';
8543
- objectName.split('').forEach(function(letter) {
8544
- if(encodings$1.has(letter)) {
8545
- result += encodings$1.get(letter);
8546
- } else {
8547
- result += letter;
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
- // %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
8554
- const encodings$1 = new Map();
8555
- encodings$1.set("!", "%21");
8556
- encodings$1.set("#", "%23");
8557
- encodings$1.set("$", "%24");
8558
- encodings$1.set("%", "%25");
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
- if(!promise) {
8646
- promise = new Promise((resolve, reject) => {
8647
- try {
8648
- // Settle this promise in the response callback for requestAccessToken()
8649
- tokenClient.callback = (tokenResponse) => {
8650
- if (tokenResponse.error !== undefined) {
8651
- reject(tokenResponse);
8652
- }
8653
- google.igv.tokenResponse = tokenResponse;
8654
- google.igv.tokenExpiresAt = Date.now() + tokenResponse.expires_in * 1000;
8655
- console.log("Access token expires at " + new Date(google.igv.tokenExpiresAt));
8656
- resolve(tokenResponse.access_token);
8657
- };
8658
- console.log("Requesting access token");
8659
- tokenClient.requestAccessToken({scope});
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
- async loadByteArray(url, options) {
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 (typeof url.startsWith === 'function') { // Test for string
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 = options.oauthToken || this.getOauthToken(url);
8999
- if (oauthToken) {
9000
- oauthToken = await (typeof oauthToken === 'function' ? oauthToken() : oauthToken);
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 === 0 || (xhr.status >= 200 && xhr.status <= 300)) {
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 > 100000 && !self.RANGE_WARNING_GIVEN) {
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(xhr.status);
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("Error accessing resource: " + url + " Status: " + xhr.status);
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
- * This method should only be called when it is known the server supports HEAD requests. It is used to recover
9243
- * from 416 errors from out-of-spec WRT range request servers. Notably Globus.
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
- options = options || {};
9251
- options.method = 'HEAD';
9252
- options.requestedHeaders = ['content-length'];
9253
- const headerMap = await this._loadURL(url, options);
9254
- const contentLengthString = headerMap['content-length'];
9255
- return contentLengthString ? Number.parseInt(contentLengthString) : 0
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("supportsTeamDrive")) {
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
- return (this.low <= other.high && other.low <= this.high);
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; // Protect from infinite loops
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 { top, bottom, height } = element.getBoundingClientRect();
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.2.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.7/LICENSE */
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', 'slot', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
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.2.7';
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 (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
11353
- ALLOWED_TAGS = clone(ALLOWED_TAGS);
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 (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
11359
- ALLOWED_ATTR = clone(ALLOWED_ATTR);
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$2(config.url);
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 = await getFilename$1(config.url);
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
- async previousFeature(chr, position, direction, visibilityWindow) {
21340
-
21341
- let chromosomeNames = this.genome.chromosomeNames || [chr];
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 ("hide" === t.getProperty("visibility")) {
30609
- // TODO -- this not supported yet
30610
- config.visible = false;
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.getPopupContent(event);
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
- getPopupContent(event) {
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 || await getFilename$1(config.url);
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
- return !this.config.indexURL || this.config.supportsWholeGenome === true
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.2";
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 = await getFilename$1(config.url);
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 results
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 ? await getFilename$1(options.url) : options.file.name);
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 if (filename.endsWith(".json")) {
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,