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.js CHANGED
@@ -392,12 +392,12 @@
392
392
  decsep = '.';
393
393
 
394
394
  return dec[0].split('').reverse().reduce(function (prev, now, i) {
395
- return i % 3 === 0 ? prev + sep + now : prev + now;
396
- }).split('').reverse().join('') + (dec[1] ? decsep + dec[1] : '');
395
+ return i % 3 === 0 ? prev + sep + now : prev + now
396
+ }).split('').reverse().join('') + (dec[1] ? decsep + dec[1] : '')
397
397
  }
398
398
 
399
399
  const splitLines$3 = function (string) {
400
- return string.split(/\n|\r\n|\r/g);
400
+ return string.split(/\n|\r\n|\r/g)
401
401
  };
402
402
 
403
403
 
@@ -425,24 +425,24 @@
425
425
  }
426
426
  }
427
427
  }
428
- return tokens;
428
+ return tokens
429
429
  }
430
430
 
431
431
  function stripQuotes$2(str) {
432
- if(str === undefined) {
433
- return str;
432
+ if (str === undefined) {
433
+ return str
434
434
  }
435
- if(str.startsWith("'") || str.startsWith('"')) {
435
+ if (str.startsWith("'") || str.startsWith('"')) {
436
436
  str = str.substring(1);
437
437
  }
438
438
  if (str.endsWith("'") || str.endsWith('"')) {
439
439
  str = str.substring(0, str.length - 1);
440
440
  }
441
- return str;
441
+ return str
442
442
  }
443
443
 
444
444
  function capitalize(str) {
445
- return str.length > 0 ? str.charAt(0).toUpperCase() + str.slice(1) : str;
445
+ return str.length > 0 ? str.charAt(0).toUpperCase() + str.slice(1) : str
446
446
  }
447
447
 
448
448
 
@@ -466,7 +466,7 @@
466
466
  range.end = range.start + 1;
467
467
  }
468
468
 
469
- return range;
469
+ return range
470
470
  }
471
471
 
472
472
  /**
@@ -475,7 +475,7 @@
475
475
  * @param urlOrFile
476
476
  */
477
477
 
478
- function getFilename$2(urlOrFile) {
478
+ function getFilename$1(urlOrFile) {
479
479
 
480
480
  if (urlOrFile.name !== undefined) {
481
481
  return urlOrFile.name
@@ -502,8 +502,8 @@
502
502
  * @param object
503
503
  */
504
504
  function isFile(object) {
505
- if(!object) {
506
- return false;
505
+ if (!object) {
506
+ return false
507
507
  }
508
508
  return typeof object !== 'function' &&
509
509
  (object instanceof File ||
@@ -523,7 +523,7 @@
523
523
 
524
524
  if (typeof process === 'object' && typeof window === 'undefined') {
525
525
  global.atob = function (str) {
526
- return Buffer.from(str, 'base64').toString('binary');
526
+ return Buffer.from(str, 'base64').toString('binary')
527
527
  };
528
528
  }
529
529
 
@@ -542,7 +542,7 @@
542
542
  if ($1) uri[o.q.name][$1] = $2;
543
543
  });
544
544
 
545
- return uri;
545
+ return uri
546
546
  }
547
547
 
548
548
  const options = {
@@ -565,7 +565,7 @@
565
565
  * @returns {Promise<*>}
566
566
  */
567
567
  async function resolveURL(url) {
568
- return (typeof url === 'function') ? url() : url;
568
+ return (typeof url === 'function') ? url() : url
569
569
  }
570
570
 
571
571
  /*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */
@@ -7427,7 +7427,7 @@
7427
7427
 
7428
7428
  function isgzipped(data) {
7429
7429
  const b = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
7430
- return b[0] ===31 && b[1] === 139;
7430
+ return b[0] === 31 && b[1] === 139
7431
7431
  }
7432
7432
 
7433
7433
  /**
@@ -7437,9 +7437,9 @@
7437
7437
  const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
7438
7438
  const b = ba[3] & FEXTRA$1;
7439
7439
  if (b !== 0 && ba[12] === 66 && ba[13] === 67) {
7440
- return unbgzf(ba.buffer);
7440
+ return unbgzf(ba.buffer)
7441
7441
  } else {
7442
- return ungzip_1$1(ba);
7442
+ return ungzip_1$1(ba)
7443
7443
  }
7444
7444
  }
7445
7445
 
@@ -7465,7 +7465,7 @@
7465
7465
  const start = 12 + xlen + ptr; // Start of CDATA
7466
7466
  const bytesLeft = data.byteLength - start;
7467
7467
  const cDataSize = bsize - xlen - 19;
7468
- if (bytesLeft < cDataSize || cDataSize <= 0) break;
7468
+ if (bytesLeft < cDataSize || cDataSize <= 0) break
7469
7469
 
7470
7470
  const a = new Uint8Array(data, start, cDataSize);
7471
7471
  const unc = inflateRaw_1(a);
@@ -7478,13 +7478,13 @@
7478
7478
  oBlockList.push(unc);
7479
7479
  } catch (e) {
7480
7480
  console.error(e);
7481
- break;
7481
+ break
7482
7482
  }
7483
7483
  }
7484
7484
 
7485
7485
  // Concatenate decompressed blocks
7486
7486
  if (oBlockList.length === 1) {
7487
- return oBlockList[0];
7487
+ return oBlockList[0]
7488
7488
  } else {
7489
7489
  const out = new Uint8Array(totalSize);
7490
7490
  let cursor = 0;
@@ -7493,14 +7493,14 @@
7493
7493
  arrayCopy(b, 0, out, cursor, b.length);
7494
7494
  cursor += b.length;
7495
7495
  }
7496
- return out;
7496
+ return out
7497
7497
  }
7498
7498
  }
7499
7499
 
7500
7500
  function bgzBlockSize$1(data) {
7501
7501
  const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
7502
7502
  const bsize = (ba[17] << 8 | ba[16]) + 1;
7503
- return bsize;
7503
+ return bsize
7504
7504
  }
7505
7505
 
7506
7506
  // From Thomas Down's zlib implementation
@@ -7510,12 +7510,12 @@
7510
7510
 
7511
7511
  function arrayCopy(src, srcOffset, dest, destOffset, count) {
7512
7512
  if (count === 0) {
7513
- return;
7513
+ return
7514
7514
  }
7515
7515
  if (!src) {
7516
- throw "Undef src";
7516
+ throw "Undef src"
7517
7517
  } else if (!dest) {
7518
- throw "Undef dest";
7518
+ throw "Undef dest"
7519
7519
  }
7520
7520
  if (srcOffset === 0 && count === src.length) {
7521
7521
  arrayCopy_fast(src, dest, destOffset);
@@ -7552,7 +7552,7 @@
7552
7552
  const compressedBytes = new deflateRaw_1(bytes); // UInt8Arry
7553
7553
  const compressedString = String.fromCharCode.apply(null, compressedBytes); // Convert to string
7554
7554
  let enc = btoa(compressedString);
7555
- return enc.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '-'); // URL safe
7555
+ return enc.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '-') // URL safe
7556
7556
  }
7557
7557
 
7558
7558
  /**
@@ -7577,7 +7577,7 @@
7577
7577
  for (let b of bytes) {
7578
7578
  str += String.fromCharCode(b);
7579
7579
  }
7580
- return str;
7580
+ return str
7581
7581
  }
7582
7582
 
7583
7583
 
@@ -7607,7 +7607,7 @@
7607
7607
  }
7608
7608
  return plain
7609
7609
  } else {
7610
- return decodeURIComponent(dataString); // URL encoded string -- not currently used or tested
7610
+ return decodeURIComponent(dataString) // URL encoded string -- not currently used or tested
7611
7611
  }
7612
7612
  }
7613
7613
 
@@ -7639,7 +7639,7 @@
7639
7639
  const IGVMath = {
7640
7640
 
7641
7641
  lerp: (v0, v1, t) => {
7642
- return (1 - t) * v0 + t * v1;
7642
+ return (1 - t) * v0 + t * v1
7643
7643
  },
7644
7644
 
7645
7645
  mean: function (array) {
@@ -7652,7 +7652,7 @@
7652
7652
  n++;
7653
7653
  }
7654
7654
  }
7655
- return n > 0 ? t / n : 0;
7655
+ return n > 0 ? t / n : 0
7656
7656
  },
7657
7657
 
7658
7658
  meanAndStdev: function (array) {
@@ -7669,7 +7669,7 @@
7669
7669
  n++;
7670
7670
  }
7671
7671
  }
7672
- return n > 0 ? {mean: t / n, stdev: Math.sqrt(t2 - t * t / n)} : {mean: 0, stdev: 0};
7672
+ return n > 0 ? {mean: t / n, stdev: Math.sqrt(t2 - t * t / n)} : {mean: 0, stdev: 0}
7673
7673
  },
7674
7674
 
7675
7675
  median: function (numbers) {
@@ -7687,33 +7687,33 @@
7687
7687
  median = numbers[(numsLen - 1) / 2];
7688
7688
  }
7689
7689
 
7690
- return median;
7690
+ return median
7691
7691
  },
7692
7692
 
7693
7693
  // Fast percentile function for "p" near edges. This needs profiled for p in middle (e.g. median)
7694
7694
  percentile: function (array, p) {
7695
7695
 
7696
- if (array.length === 0) return undefined;
7696
+ if (array.length === 0) return undefined
7697
7697
 
7698
7698
  var k = Math.floor(array.length * ((100 - p) / 100));
7699
7699
  if (k === 0) {
7700
7700
  array.sort(function (a, b) {
7701
7701
  return b - a
7702
7702
  });
7703
- return array[k];
7703
+ return array[k]
7704
7704
  } else {
7705
- return selectElement(array, k);
7705
+ return selectElement(array, k)
7706
7706
  }
7707
7707
 
7708
7708
  },
7709
7709
 
7710
7710
 
7711
7711
  clamp: function (value, min, max) {
7712
- return Math.min(Math.max(value, min), max);
7712
+ return Math.min(Math.max(value, min), max)
7713
7713
  },
7714
7714
 
7715
7715
  log2: function (x) {
7716
- return Math.log(x) / Math.LN2;
7716
+ return Math.log(x) / Math.LN2
7717
7717
  }
7718
7718
 
7719
7719
  };
@@ -7739,7 +7739,7 @@
7739
7739
  }
7740
7740
  }
7741
7741
 
7742
- return heap.content[0];
7742
+ return heap.content[0]
7743
7743
  }
7744
7744
 
7745
7745
 
@@ -7766,7 +7766,7 @@
7766
7766
  this.content[0] = end;
7767
7767
  this.sinkDown(0);
7768
7768
  }
7769
- return result;
7769
+ return result
7770
7770
  },
7771
7771
 
7772
7772
  remove: function (node) {
@@ -7774,24 +7774,24 @@
7774
7774
  // To remove a value, we must search through the array to find
7775
7775
  // it.
7776
7776
  for (var i = 0; i < length; i++) {
7777
- if (this.content[i] !== node) continue;
7777
+ if (this.content[i] !== node) continue
7778
7778
  // When it is found, the process seen in 'pop' is repeated
7779
7779
  // to fill up the hole.
7780
7780
  var end = this.content.pop();
7781
7781
  // If the element we popped was the one we needed to remove,
7782
7782
  // we're done.
7783
- if (i === length - 1) break;
7783
+ if (i === length - 1) break
7784
7784
  // Otherwise, we replace the removed element with the popped
7785
7785
  // one, and allow it to float up or sink down as appropriate.
7786
7786
  this.content[i] = end;
7787
7787
  this.bubbleUp(i);
7788
7788
  this.sinkDown(i);
7789
- break;
7789
+ break
7790
7790
  }
7791
7791
  },
7792
7792
 
7793
7793
  size: function () {
7794
- return this.content.length;
7794
+ return this.content.length
7795
7795
  },
7796
7796
 
7797
7797
  bubbleUp: function (n) {
@@ -7805,7 +7805,7 @@
7805
7805
  // If the parent has a lesser score, things are in order and we
7806
7806
  // are done.
7807
7807
  if (score >= parent)
7808
- break;
7808
+ break
7809
7809
 
7810
7810
  // Otherwise, swap the parent with the current element and
7811
7811
  // continue.
@@ -7845,7 +7845,7 @@
7845
7845
  }
7846
7846
 
7847
7847
  // No need to swap further, we are done.
7848
- if (swap == null) break;
7848
+ if (swap == null) break
7849
7849
 
7850
7850
  // Otherwise, swap and continue.
7851
7851
  this.content[n] = this.content[swap];
@@ -8439,145 +8439,78 @@
8439
8439
  }
8440
8440
 
8441
8441
  function isGoogleURL(url) {
8442
- return (url.includes("googleapis") && !url.includes("urlshortener")) ||
8443
- isGoogleStorageURL(url) ||
8444
- isGoogleDriveURL(url)
8445
- }
8446
-
8447
- function isGoogleStorageURL(url) {
8448
- return url.startsWith("gs://") ||
8449
- url.startsWith("https://www.googleapis.com/storage") ||
8450
- url.startsWith("https://storage.cloud.google.com") ||
8451
- url.startsWith("https://storage.googleapis.com");
8452
- }
8453
-
8454
- function isGoogleDriveURL(url) {
8455
- return url.indexOf("drive.google.com") >= 0 || url.indexOf("www.googleapis.com/drive") > 0
8456
- }
8457
-
8458
- /**
8459
- * Translate gs:// urls to https
8460
- * See https://cloud.google.com/storage/docs/json_api/v1
8461
- * @param gsUrl
8462
- * @returns {string|*}
8463
- */
8464
- function translateGoogleCloudURL(gsUrl) {
8465
-
8466
- let {bucket, object} = parseBucketName(gsUrl);
8467
- object = encode(object);
8468
-
8469
- const qIdx = gsUrl.indexOf('?');
8470
- const paramString = (qIdx > 0) ? gsUrl.substring(qIdx) + "&alt=media" : "?alt=media";
8471
-
8472
- return `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}${paramString}`
8473
- }
8474
-
8475
- /**
8476
- * Parse a google bucket and object name from a google storage URL. Known forms include
8477
- *
8478
- * gs://BUCKET_NAME/OBJECT_NAME
8479
- * https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME
8480
- * https://storage.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME
8481
- * https://www.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME"
8482
- * https://storage.googleapis.com/download/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME
8483
- *
8484
- * @param url
8485
- */
8486
- function parseBucketName(url) {
8487
-
8488
- let bucket;
8489
- let object;
8490
-
8491
- if (url.startsWith("gs://")) {
8492
- const i = url.indexOf('/', 5);
8493
- if (i >= 0) {
8494
- bucket = url.substring(5, i);
8495
- const qIdx = url.indexOf('?');
8496
- object = (qIdx < 0) ? url.substring(i + 1) : url.substring(i + 1, qIdx);
8497
- }
8498
-
8499
- } else if (url.startsWith("https://storage.googleapis.com") || url.startsWith("https://storage.cloud.google.com")) {
8500
- const bucketIdx = url.indexOf("/v1/b/", 8);
8501
- if (bucketIdx > 0) {
8502
- const objIdx = url.indexOf("/o/", bucketIdx);
8503
- if (objIdx > 0) {
8504
- const queryIdx = url.indexOf("?", objIdx);
8505
- bucket = url.substring(bucketIdx + 6, objIdx);
8506
- object = queryIdx > 0 ? url.substring(objIdx + 3, queryIdx) : url.substring(objIdx + 3);
8507
- }
8508
-
8509
- } else {
8510
- const idx1 = url.indexOf("/", 8);
8511
- const idx2 = url.indexOf("/", idx1+1);
8512
- const idx3 = url.indexOf("?", idx2);
8513
- if (idx2 > 0) {
8514
- bucket = url.substring(idx1+1, idx2);
8515
- object = idx3 < 0 ? url.substring(idx2+1) : url.substring(idx2+1, idx3);
8516
- }
8517
- }
8518
-
8519
- } else if (url.startsWith("https://www.googleapis.com/storage/v1/b")) {
8520
- const bucketIdx = url.indexOf("/v1/b/", 8);
8521
- const objIdx = url.indexOf("/o/", bucketIdx);
8522
- if (objIdx > 0) {
8523
- const queryIdx = url.indexOf("?", objIdx);
8524
- bucket = url.substring(bucketIdx + 6, objIdx);
8525
- object = queryIdx > 0 ? url.substring(objIdx + 3, queryIdx) : url.substring(objIdx + 3);
8526
- }
8527
- }
8442
+ return (url.includes("googleapis") && !url.includes("urlshortener")) ||
8443
+ isGoogleStorageURL(url) ||
8444
+ isGoogleDriveURL(url);
8445
+ }
8446
+
8447
+ function isGoogleStorageURL(url) {
8448
+ return url.startsWith("gs://") ||
8449
+ url.startsWith("https://www.googleapis.com/storage") ||
8450
+ url.startsWith("https://storage.cloud.google.com") ||
8451
+ url.startsWith("https://storage.googleapis.com");
8452
+ }
8453
+
8454
+ function isGoogleDriveURL(url) {
8455
+ return url.startsWith("https://www.googleapis.com/drive/v3/files");
8456
+ }
8457
+
8458
+ /**
8459
+ * Translate gs:// urls to https
8460
+ * See https://cloud.google.com/storage/docs/json_api/v1
8461
+ * @param gsUrl
8462
+ * @returns {string|*}
8463
+ */
8464
+ function translateGoogleCloudURL(gsUrl) {
8465
+ try {
8466
+ let {bucket, object} = parseBucketName(gsUrl);
8467
+ object = encode(object);
8528
8468
 
8529
- if (bucket && object) {
8530
- return {
8531
- bucket, object
8532
- }
8533
- } else {
8534
- throw Error(`Unrecognized Google Storage URI: ${url}`)
8535
- }
8469
+ const qIdx = gsUrl.indexOf('?');
8470
+ let paramString = (qIdx > 0) ? gsUrl.substring(qIdx) : "";
8536
8471
 
8537
- }
8472
+ if (!paramString.includes("alt=media")) {
8473
+ paramString = paramString ? `${paramString}&alt=media` : "?alt=media";
8474
+ }
8538
8475
 
8539
- /**
8540
- * Percent a GCS object name. See https://cloud.google.com/storage/docs/request-endpoints
8541
- * Specific characters to encode:
8542
- * !, #, $, &, ', (, ), *, +, ,, /, :, ;, =, ?, @, [, ], and space characters.
8543
- * @param obj
8544
- */
8476
+ return `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}${paramString}`;
8477
+ } catch (error) {
8478
+ throw new Error(`Failed to translate Google Cloud URL: ${error.message}`);
8479
+ }
8480
+ }
8545
8481
 
8546
- function encode(objectName) {
8482
+ /**
8483
+ * Parse a google bucket and object name from a google storage URL.
8484
+ * @param url
8485
+ */
8486
+ function parseBucketName(url) {
8487
+ 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\/)?([^?]+)/;
8488
+ const match = url.match(regex);
8489
+
8490
+ if (match) {
8491
+ const bucket = match[1] || match[3];
8492
+ const object = match[2] || match[4];
8493
+ if (bucket && object) {
8494
+ return { bucket, object };
8495
+ }
8496
+ }
8497
+ throw new Error(`Unrecognized Google Storage URI: ${url}`);
8498
+ }
8547
8499
 
8548
- let result = '';
8549
- objectName.split('').forEach(function(letter) {
8550
- if(encodings$1.has(letter)) {
8551
- result += encodings$1.get(letter);
8552
- } else {
8553
- result += letter;
8554
- }
8555
- });
8556
- return result;
8557
- }
8500
+ /**
8501
+ * Percent a GCS object name. See https://cloud.google.com/storage/docs/request-endpoints
8502
+ * @param objectName
8503
+ */
8504
+ function encode(objectName) {
8505
+ return objectName.split('').map(letter => encodings$1.get(letter) || letter).join('');
8506
+ }
8558
8507
 
8559
- // %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
8560
- const encodings$1 = new Map();
8561
- encodings$1.set("!", "%21");
8562
- encodings$1.set("#", "%23");
8563
- encodings$1.set("$", "%24");
8564
- encodings$1.set("%", "%25");
8565
- encodings$1.set("&", "%26");
8566
- encodings$1.set("'", "%27");
8567
- encodings$1.set("(", "%28");
8568
- encodings$1.set(")", "%29");
8569
- encodings$1.set("*", "%2A");
8570
- encodings$1.set("+", "%2B");
8571
- encodings$1.set(",", "%2C");
8572
- encodings$1.set("/", "%2F");
8573
- encodings$1.set(":", "%3A");
8574
- encodings$1.set(";", "%3B");
8575
- encodings$1.set("=", "%3D");
8576
- encodings$1.set("?", "%3F");
8577
- encodings$1.set("@", "%40");
8578
- encodings$1.set("[", "%5B");
8579
- encodings$1.set("]", "%5D");
8580
- encodings$1.set(" ", "%20");
8508
+ const encodings$1 = new Map([
8509
+ ["!", "%21"], ["#", "%23"], ["$", "%24"], ["%", "%25"], ["&", "%26"],
8510
+ ["'", "%27"], ["(", "%28"], [")", "%29"], ["*", "%2A"], ["+", "%2B"],
8511
+ [",", "%2C"], ["/", "%2F"], [":", "%3A"], [";", "%3B"], ["=", "%3D"],
8512
+ ["?", "%3F"], ["@", "%40"], ["[", "%5B"], ["]", "%5D"], [" ", "%20"]
8513
+ ]);
8581
8514
 
8582
8515
  // Convenience functions for the gapi oAuth library.
8583
8516
 
@@ -8608,7 +8541,8 @@
8608
8541
  // Attach an object to keep igv state
8609
8542
  google.igv = {
8610
8543
  tokenClient: tokenClient,
8611
- apiKey: config.apiKey
8544
+ apiKey: config.apiKey,
8545
+ appId: config.appId
8612
8546
  };
8613
8547
  }
8614
8548
 
@@ -8628,8 +8562,6 @@
8628
8562
  undefined
8629
8563
  }
8630
8564
 
8631
-
8632
- let promise;
8633
8565
  /**
8634
8566
  * Return a promise for an access token for the given scope. If the user hasn't authorized the scope request it
8635
8567
  *
@@ -8648,27 +8580,21 @@
8648
8580
  return google.igv.tokenResponse.access_token
8649
8581
  } else {
8650
8582
  const tokenClient = google.igv.tokenClient;
8651
- if(!promise) {
8652
- promise = new Promise((resolve, reject) => {
8653
- try {
8654
- // Settle this promise in the response callback for requestAccessToken()
8655
- tokenClient.callback = (tokenResponse) => {
8656
- if (tokenResponse.error !== undefined) {
8657
- reject(tokenResponse);
8658
- }
8659
- google.igv.tokenResponse = tokenResponse;
8660
- google.igv.tokenExpiresAt = Date.now() + tokenResponse.expires_in * 1000;
8661
- console.log("Access token expires at " + new Date(google.igv.tokenExpiresAt));
8662
- resolve(tokenResponse.access_token);
8663
- };
8664
- console.log("Requesting access token");
8665
- tokenClient.requestAccessToken({scope});
8666
- } catch (err) {
8667
- console.log(err);
8668
- }
8669
- });
8670
- }
8671
- return promise
8583
+ return new Promise((resolve, reject) => {
8584
+ tokenClient.callback = (tokenResponse) => {
8585
+ if (tokenResponse.error !== undefined) {
8586
+ return reject(tokenResponse)
8587
+ }
8588
+ google.igv.tokenResponse = tokenResponse;
8589
+ google.igv.tokenExpiresAt = Date.now() + tokenResponse.expires_in * 1000;
8590
+ resolve(tokenResponse.access_token);
8591
+ };
8592
+ try {
8593
+ tokenClient.requestAccessToken({scope});
8594
+ } catch (err) {
8595
+ reject(err);
8596
+ }
8597
+ })
8672
8598
  }
8673
8599
  }
8674
8600
 
@@ -8687,90 +8613,6 @@
8687
8613
  }
8688
8614
  }
8689
8615
 
8690
- function getApiKey() {
8691
- return google.igv.apiKey
8692
- }
8693
-
8694
- /**
8695
- * Return information about a specific google drive URL
8696
- *
8697
- * @param googleDriveURL
8698
- * @returns {Promise<any>}
8699
- */
8700
- async function getDriveFileInfo(googleDriveURL) {
8701
-
8702
- const id = getGoogleDriveFileID(googleDriveURL);
8703
- let endPoint = "https://www.googleapis.com/drive/v3/files/" + id + "?supportsTeamDrives=true";
8704
- const apiKey = getApiKey();
8705
- if (apiKey) {
8706
- endPoint += "&key=" + apiKey;
8707
- }
8708
- const response = await fetch(endPoint);
8709
- let json = await response.json();
8710
- if (json.error && json.error.code === 404) {
8711
- let scope = "https://www.googleapis.com/auth/drive.readonly";
8712
- const access_token = await getAccessToken(scope);
8713
- if (access_token) {
8714
- const response = await fetch(endPoint, {
8715
- headers: {
8716
- 'Authorization': `Bearer ${access_token}`
8717
- }
8718
- });
8719
- json = await response.json();
8720
- if (json.error) {
8721
- throw Error(json.error);
8722
- }
8723
- } else {
8724
- throw Error(json.error);
8725
- }
8726
- }
8727
- return json;
8728
- }
8729
-
8730
-
8731
- function getDriveDownloadURL(link) {
8732
- // Return a google drive download url for the sharable link
8733
- //https://drive.google.com/open?id=0B-lleX9c2pZFbDJ4VVRxakJzVGM
8734
- //https://drive.google.com/file/d/1_FC4kCeO8E3V4dJ1yIW7A0sn1yURKIX-/view?usp=sharing
8735
- var id = getGoogleDriveFileID(link);
8736
- return id ? "https://www.googleapis.com/drive/v3/files/" + id + "?alt=media&supportsTeamDrives=true" : link;
8737
- }
8738
-
8739
- function getGoogleDriveFileID(link) {
8740
-
8741
- //https://drive.google.com/file/d/1_FC4kCeO8E3V4dJ1yIW7A0sn1yURKIX-/view?usp=sharing
8742
- //https://www.googleapis.com/drive/v3/files/1w-tvo6p1SH4p1OaQSVxpkV_EJgGIstWF?alt=media&supportsTeamDrives=true"
8743
-
8744
- if (link.includes("/open?id=")) {
8745
- const i1 = link.indexOf("/open?id=") + 9;
8746
- const i2 = link.indexOf("&");
8747
- if (i1 > 0 && i2 > i1) {
8748
- return link.substring(i1, i2)
8749
- } else if (i1 > 0) {
8750
- return link.substring(i1);
8751
- }
8752
-
8753
- } else if (link.includes("/file/d/")) {
8754
- const i1 = link.indexOf("/file/d/") + 8;
8755
- const i2 = link.lastIndexOf("/");
8756
- return link.substring(i1, i2);
8757
-
8758
- } else if (link.startsWith("https://www.googleapis.com/drive")) {
8759
- let i1 = link.indexOf("/files/");
8760
- const i2 = link.indexOf("?");
8761
- if (i1 > 0) {
8762
- i1 += 7;
8763
- return i2 > 0 ?
8764
- link.substring(i1, i2) :
8765
- link.substring(i1)
8766
- }
8767
- }
8768
-
8769
- throw Error("Unknown Google Drive url format: " + link);
8770
-
8771
-
8772
- }
8773
-
8774
8616
  // The MIT License (MIT)
8775
8617
 
8776
8618
  /**
@@ -8802,7 +8644,7 @@
8802
8644
  asyncFunction: asyncFunction,
8803
8645
  });
8804
8646
  self.dequeue();
8805
- });
8647
+ })
8806
8648
  }
8807
8649
 
8808
8650
  /**
@@ -8815,10 +8657,10 @@
8815
8657
  */
8816
8658
  addAll(promises, options) {
8817
8659
  var addedPromises = promises.map(function (promise) {
8818
- return this.add(promise, options);
8660
+ return this.add(promise, options)
8819
8661
  }.bind(this));
8820
8662
 
8821
- return Promise.all(addedPromises);
8663
+ return Promise.all(addedPromises)
8822
8664
  };
8823
8665
 
8824
8666
  /**
@@ -8890,6 +8732,10 @@
8890
8732
 
8891
8733
  class IGVXhr {
8892
8734
 
8735
+ UCSC_HOST = "hgdownload.soe.ucsc.edu"
8736
+ UCSC_BACKUP_HOST = "genome-browser.s3.us-east-1.amazonaws.com"
8737
+
8738
+
8893
8739
  constructor() {
8894
8740
  this.apiKey = undefined;
8895
8741
  this.googleThrottle = new Throttle({
@@ -8897,6 +8743,7 @@
8897
8743
  });
8898
8744
  this.RANGE_WARNING_GIVEN = false;
8899
8745
  this.oauth = new Oauth();
8746
+ this.corsProxy = undefined;
8900
8747
  }
8901
8748
 
8902
8749
  setApiKey(key) {
@@ -8921,7 +8768,7 @@
8921
8768
  * @param options
8922
8769
  * @returns {Promise<Uint8Array>}
8923
8770
  */
8924
- async loadByteArray(url, options) {
8771
+ async loadByteArray(url, options) {
8925
8772
  const arraybuffer = await this.loadArrayBuffer(url, options);
8926
8773
  let plain;
8927
8774
  if (isgzipped(arraybuffer)) {
@@ -8966,7 +8813,7 @@
8966
8813
 
8967
8814
  if (isFile(url)) {
8968
8815
  return this._loadFileSlice(url, options)
8969
- } else if (typeof url.startsWith === 'function') { // Test for string
8816
+ } else if (isString$3(url)) {
8970
8817
  if (url.startsWith("data:")) {
8971
8818
  const buffer = decodeDataURI$1(url).buffer;
8972
8819
  if (options.range) {
@@ -8976,9 +8823,6 @@
8976
8823
  return buffer
8977
8824
  }
8978
8825
  } else {
8979
- if (url.startsWith("https://drive.google.com")) {
8980
- url = getDriveDownloadURL(url);
8981
- }
8982
8826
  if (isGoogleDriveURL(url) || url.startsWith("https://www.dropbox.com")) {
8983
8827
  return this.googleThrottle.add(async () => {
8984
8828
  return this._loadURL(url, options)
@@ -8996,14 +8840,21 @@
8996
8840
 
8997
8841
  const self = this;
8998
8842
  const _url = url; // The unmodified URL, needed in case of an oAuth retry
8843
+ const {host} = parseUri(_url);
8999
8844
 
9000
8845
  url = mapUrl$1(url);
9001
8846
 
9002
8847
  options = options || {};
9003
8848
 
9004
- let oauthToken = options.oauthToken || this.getOauthToken(url);
9005
- if (oauthToken) {
9006
- oauthToken = await (typeof oauthToken === 'function' ? oauthToken() : oauthToken);
8849
+ let oauthToken;
8850
+ if (isGoogleDriveURL(url)) {
8851
+ // Google drive urls always require oAuth
8852
+ oauthToken = await getAccessToken("https://www.googleapis.com/auth/drive.file");
8853
+ } else {
8854
+ oauthToken = options.oauthToken || this.getOauthToken(url);
8855
+ if (oauthToken) {
8856
+ oauthToken = await (typeof oauthToken === 'function' ? oauthToken() : oauthToken);
8857
+ }
9007
8858
  }
9008
8859
 
9009
8860
  return new Promise(function (resolve, reject) {
@@ -9076,8 +8927,10 @@
9076
8927
 
9077
8928
  xhr.onload = async function (event) {
9078
8929
 
8930
+ const isFileProtocol = url.toLowerCase().startsWith("file:");
8931
+
9079
8932
  // when the url points to a local file, the status is 0
9080
- if (xhr.status === 0 || (xhr.status >= 200 && xhr.status <= 300)) {
8933
+ if ((xhr.status >= 200 && xhr.status <= 300) || (isFileProtocol && xhr.status === 0)) {
9081
8934
  if ("HEAD" === options.method) {
9082
8935
  // Support fetching specific headers. Attempting to fetch all headers can be problematic with CORS
9083
8936
  const headers = options.requestedHeaders || ['content-length'];
@@ -9093,7 +8946,7 @@
9093
8946
  // For small files a range starting at 0 can return the whole file => 200
9094
8947
  // Provide just the slice we asked for, throw out the rest quietly
9095
8948
  // If file is large warn user
9096
- if (xhr.response.length > 100000 && !self.RANGE_WARNING_GIVEN) {
8949
+ if (xhr.response.length > 1000000 && !self.RANGE_WARNING_GIVEN) {
9097
8950
  alert(`Warning: Range header ignored for URL: ${url}. This can have severe performance impacts.`);
9098
8951
  }
9099
8952
  resolve(xhr.response.slice(range.start, range.start + range.size));
@@ -9110,20 +8963,29 @@
9110
8963
  tryGoogleAuth();
9111
8964
 
9112
8965
  } else {
8966
+ const error = new Error(`Error accessing resource: ${url} status: ${xhr.status}`);
9113
8967
  if (xhr.status === 403) {
9114
8968
  handleError("Access forbidden: " + url);
8969
+ } else if (host === self.UCSC_HOST) {
8970
+ tryUcscBackup(self.UCSC_HOST, self.UCSC_BACKUP_HOST, error);
8971
+ } else if (xhr.status === 0 && self.corsProxy && !options.corsProxyRetried) {
8972
+ tryCorsProxy(error);
9115
8973
  } else {
9116
- handleError(xhr.status);
8974
+ handleError(error);
9117
8975
  }
9118
8976
  }
9119
8977
  };
9120
8978
 
9121
-
9122
8979
  xhr.onerror = function (event) {
8980
+ const error = new Error(`Error accessing resource: ${url} status: ${xhr.status}`);
9123
8981
  if (isGoogleURL(url) && !options.retries) {
9124
8982
  tryGoogleAuth();
8983
+ } else if (host === self.UCSC_HOST) {
8984
+ tryUcscBackup(self.UCSC_HOST, self.UCSC_BACKUP_HOST, error);
8985
+ } else if (self.corsProxy && !options.corsProxyRetried) {
8986
+ tryCorsProxy(error);
9125
8987
  } else {
9126
- handleError("Error accessing resource: " + url + " Status: " + xhr.status);
8988
+ handleError(error);
9127
8989
  }
9128
8990
  };
9129
8991
 
@@ -9155,6 +9017,27 @@
9155
9017
  }
9156
9018
  }
9157
9019
 
9020
+ async function tryCorsProxy(error) {
9021
+ options.corsProxyRetried = true;
9022
+ const proxyUrl = self.corsProxy + (_url.includes("?") ? "&" : "?") + "url=" + encodeURIComponent(_url);
9023
+ try {
9024
+ const result = await self._loadURL(proxyUrl, options);
9025
+ resolve(result);
9026
+ } catch (e) {
9027
+ handleError(error);
9028
+ }
9029
+ }
9030
+
9031
+ async function tryUcscBackup(UCSC_HOST, UCSC_BACKUP_HOST, error) {
9032
+ const backupUrl = _url.replace(UCSC_HOST, UCSC_BACKUP_HOST);
9033
+ try {
9034
+ const result = await self._loadURL(backupUrl, options);
9035
+ resolve(result);
9036
+ } catch (e) {
9037
+ handleError(error);
9038
+ }
9039
+ }
9040
+
9158
9041
  async function tryGoogleAuth() {
9159
9042
  try {
9160
9043
  const accessToken = await fetchGoogleAccessToken(_url);
@@ -9173,6 +9056,8 @@
9173
9056
  }
9174
9057
  }
9175
9058
  }
9059
+
9060
+
9176
9061
  })
9177
9062
 
9178
9063
  }
@@ -9245,20 +9130,29 @@
9245
9130
  }
9246
9131
 
9247
9132
  /**
9248
- * This method should only be called when it is known the server supports HEAD requests. It is used to recover
9249
- * from 416 errors from out-of-spec WRT range request servers. Notably Globus.
9250
- * * *
9133
+ * Return the content length of the file at the given URL. This is not guaranteed to succeed, some servers
9134
+ * do not support or allow the content-length header.
9135
+ *
9251
9136
  * @param url
9252
9137
  * @param options
9253
9138
  * @returns {Promise<unknown>}
9254
9139
  */
9255
9140
  async getContentLength(url, options) {
9256
- options = options || {};
9257
- options.method = 'HEAD';
9258
- options.requestedHeaders = ['content-length'];
9259
- const headerMap = await this._loadURL(url, options);
9260
- const contentLengthString = headerMap['content-length'];
9261
- return contentLengthString ? Number.parseInt(contentLengthString) : 0
9141
+ if (isFile(url)) {
9142
+ return url.size
9143
+ } else {
9144
+ try {
9145
+ options = options || {};
9146
+ options.method = 'HEAD';
9147
+ options.requestedHeaders = ['content-length'];
9148
+ const headerMap = await this._loadURL(url, options);
9149
+ const contentLengthString = headerMap['content-length'];
9150
+ return contentLengthString ? Number.parseInt(contentLengthString) : 0
9151
+ } catch (e) {
9152
+ console.error(e);
9153
+ return -1
9154
+ }
9155
+ }
9262
9156
  }
9263
9157
 
9264
9158
  }
@@ -9321,8 +9215,10 @@
9321
9215
  }
9322
9216
 
9323
9217
  function addTeamDrive(url) {
9324
- if (url.includes("supportsTeamDrive")) {
9218
+ if (url.includes("supportsAllDrives")) {
9325
9219
  return url
9220
+ } else if (url.includes("supportsTeamDrives")) {
9221
+ return url.replaceAll("supportsTeamDrives", "supportsAllDrives")
9326
9222
  } else {
9327
9223
  const paramSeparator = url.includes("?") ? "&" : "?";
9328
9224
  url = url + paramSeparator + "supportsTeamDrive=true";
@@ -9337,8 +9233,6 @@
9337
9233
 
9338
9234
  if (url.startsWith("https://www.dropbox.com")) {
9339
9235
  return url.replace("//www.dropbox.com", "//dl.dropboxusercontent.com")
9340
- } else if (url.startsWith("https://drive.google.com")) {
9341
- return getDriveDownloadURL(url)
9342
9236
  } else if (url.includes("//www.broadinstitute.org/igvdata")) {
9343
9237
  return url.replace("//www.broadinstitute.org/igvdata", "//data.broadinstitute.org/igvdata")
9344
9238
  } else if (url.includes("//igvdata.broadinstitute.org")) {
@@ -9534,17 +9428,17 @@
9534
9428
 
9535
9429
  var searchInterval = new Interval$1(start, end, 0);
9536
9430
 
9537
- if (this.root === NIL$1) return [];
9431
+ if (this.root === NIL$1) return []
9538
9432
 
9539
9433
  var intervals = searchAll$1.call(this, searchInterval, this.root, []);
9540
9434
 
9541
9435
  if (intervals.length > 1) {
9542
9436
  intervals.sort(function (i1, i2) {
9543
- return i1.low - i2.low;
9437
+ return i1.low - i2.low
9544
9438
  });
9545
9439
  }
9546
9440
 
9547
- return intervals;
9441
+ return intervals
9548
9442
  }
9549
9443
 
9550
9444
  /**
@@ -9629,7 +9523,7 @@
9629
9523
  searchAll$1.call(this, interval, node.right, results);
9630
9524
  }
9631
9525
 
9632
- return results;
9526
+ return results
9633
9527
  }
9634
9528
 
9635
9529
  function leftRotate$1(x) {
@@ -9708,35 +9602,35 @@
9708
9602
 
9709
9603
  equals(other) {
9710
9604
  if (!other) {
9711
- return false;
9605
+ return false
9712
9606
  }
9713
9607
  if (this === other) {
9714
- return true;
9608
+ return true
9715
9609
  }
9716
9610
  return (this.low === other.low &&
9717
- this.high === other.high);
9611
+ this.high === other.high)
9718
9612
 
9719
9613
  }
9720
9614
 
9721
9615
  compareTo(other) {
9722
9616
  if (this.low < other.low)
9723
- return -1;
9617
+ return -1
9724
9618
  if (this.low > other.low)
9725
- return 1;
9619
+ return 1
9726
9620
 
9727
9621
  if (this.high < other.high)
9728
- return -1;
9622
+ return -1
9729
9623
  if (this.high > other.high)
9730
- return 1;
9624
+ return 1
9731
9625
 
9732
- return 0;
9626
+ return 0
9733
9627
  }
9734
9628
 
9735
9629
  /**
9736
9630
  * Returns true if this interval overlaps the other.
9737
9631
  */
9738
9632
  overlaps(other) {
9739
- return (this.low <= other.high && other.low <= this.high);
9633
+ return (this.low <= other.high && other.low <= this.high)
9740
9634
  }
9741
9635
  }
9742
9636
 
@@ -9781,19 +9675,19 @@
9781
9675
  var start;
9782
9676
  var end;
9783
9677
 
9784
- if (!features) return;
9678
+ if (!features) return
9785
9679
 
9786
9680
  maxRows = maxRows || 10000;
9787
9681
 
9788
9682
  if (!sorted) {
9789
9683
  features.sort(function (a, b) {
9790
- return a.start - b.start;
9684
+ return a.start - b.start
9791
9685
  });
9792
9686
  }
9793
9687
 
9794
9688
 
9795
9689
  if (features.length === 0) {
9796
- return [];
9690
+ return []
9797
9691
 
9798
9692
  } else {
9799
9693
 
@@ -9846,7 +9740,7 @@
9846
9740
  } // while (bucket)
9847
9741
 
9848
9742
  if (!bucket) {
9849
- break;
9743
+ break
9850
9744
  }
9851
9745
  feature = bucket.pop();
9852
9746
  if (0 === bucket.length) {
@@ -9863,7 +9757,7 @@
9863
9757
  row++;
9864
9758
  nextStart = bucketStart;
9865
9759
 
9866
- if (allocatedCount === lastAllocatedCount) break; // Protect from infinite loops
9760
+ if (allocatedCount === lastAllocatedCount) break // Protect from infinite loops
9867
9761
 
9868
9762
  lastAllocatedCount = allocatedCount;
9869
9763
 
@@ -9885,13 +9779,13 @@
9885
9779
  findOverlapping: function (featureList, start, end) {
9886
9780
 
9887
9781
  if (!featureList || featureList.length === 0) {
9888
- return [];
9782
+ return []
9889
9783
  } else {
9890
9784
  const tree = buildIntervalTree$1(featureList);
9891
9785
  const intervals = tree.findOverlapping(start, end);
9892
9786
 
9893
9787
  if (intervals.length === 0) {
9894
- return [];
9788
+ return []
9895
9789
  } else {
9896
9790
  // Trim the list of features in the intervals to those
9897
9791
  // overlapping the requested range.
@@ -9904,7 +9798,7 @@
9904
9798
  const len = intervalFeatures.length;
9905
9799
  for (let i = 0; i < len; i++) {
9906
9800
  const feature = intervalFeatures[i];
9907
- if (feature.start > end) break;
9801
+ if (feature.start > end) break
9908
9802
  else if (feature.end > start) {
9909
9803
  featureList.push(feature);
9910
9804
  }
@@ -9912,10 +9806,10 @@
9912
9806
  });
9913
9807
 
9914
9808
  featureList.sort(function (a, b) {
9915
- return a.start - b.start;
9809
+ return a.start - b.start
9916
9810
  });
9917
9811
 
9918
- return featureList;
9812
+ return featureList
9919
9813
  }
9920
9814
  }
9921
9815
 
@@ -9936,7 +9830,7 @@
9936
9830
  const chunkSize = Math.max(10, Math.round(len / 100));
9937
9831
 
9938
9832
  featureList.sort(function (f1, f2) {
9939
- return (f1.start === f2.start ? 0 : (f1.start > f2.start ? 1 : -1));
9833
+ return (f1.start === f2.start ? 0 : (f1.start > f2.start ? 1 : -1))
9940
9834
  });
9941
9835
 
9942
9836
  for (let i = 0; i < len; i += chunkSize) {
@@ -9950,7 +9844,7 @@
9950
9844
  tree.insert(iStart, iEnd, subArray);
9951
9845
  }
9952
9846
 
9953
- return tree;
9847
+ return tree
9954
9848
  }
9955
9849
 
9956
9850
  function hexToRGB(hex) {
@@ -10401,7 +10295,7 @@
10401
10295
  min = Number.MAX_VALUE;
10402
10296
  max = -Number.MAX_VALUE;
10403
10297
 
10404
- for(let f of features) {
10298
+ for (let f of features) {
10405
10299
  if (!Number.isNaN(f.value)) {
10406
10300
  min = Math.min(min, f.value);
10407
10301
  max = Math.max(max, f.value);
@@ -10475,19 +10369,6 @@
10475
10369
  return false
10476
10370
  };
10477
10371
 
10478
- async function getFilename$1(url) {
10479
- if (isString$3(url) && url.startsWith("https://drive.google.com")) {
10480
- // This will fail if Google API key is not defined
10481
- if (getApiKey() === undefined) {
10482
- throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
10483
- }
10484
- const json = await getDriveFileInfo(url);
10485
- return json.originalFileName || json.name
10486
- } else {
10487
- return getFilename$2(url)
10488
- }
10489
- }
10490
-
10491
10372
  function prettyBasePairNumber(raw) {
10492
10373
 
10493
10374
  var denom,
@@ -10550,12 +10431,12 @@
10550
10431
  const marginTop = parseInt(style.marginTop);
10551
10432
  const marginBottom = parseInt(style.marginBottom);
10552
10433
 
10553
- const { top, bottom, height } = element.getBoundingClientRect();
10434
+ const {top, bottom, height} = element.getBoundingClientRect();
10554
10435
  return {
10555
10436
  top: Math.floor(top) - marginTop,
10556
10437
  bottom: Math.floor(bottom) + marginBottom,
10557
10438
  height: Math.floor(height) + marginTop + marginBottom
10558
- };
10439
+ }
10559
10440
  }
10560
10441
 
10561
10442
  class Popover {
@@ -10745,7 +10626,7 @@
10745
10626
  return list;
10746
10627
  }
10747
10628
 
10748
- /*! @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 */
10629
+ /*! @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 */
10749
10630
 
10750
10631
  const {
10751
10632
  entries,
@@ -10931,7 +10812,7 @@
10931
10812
  }
10932
10813
 
10933
10814
  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']);
10934
- 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']);
10815
+ 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']);
10935
10816
  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']);
10936
10817
  // List of SVG elements that are disallowed by default.
10937
10818
  // We still need to know them so that we can do namespace
@@ -10945,7 +10826,7 @@
10945
10826
  const text = freeze(['#text']);
10946
10827
 
10947
10828
  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']);
10948
- 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']);
10829
+ 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']);
10949
10830
  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']);
10950
10831
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
10951
10832
 
@@ -11052,7 +10933,7 @@
11052
10933
  function createDOMPurify() {
11053
10934
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
11054
10935
  const DOMPurify = root => createDOMPurify(root);
11055
- DOMPurify.version = '3.2.7';
10936
+ DOMPurify.version = '3.3.0';
11056
10937
  DOMPurify.removed = [];
11057
10938
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
11058
10939
  // Not running in a browser, provide a factory function
@@ -11163,6 +11044,21 @@
11163
11044
  let FORBID_TAGS = null;
11164
11045
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
11165
11046
  let FORBID_ATTR = null;
11047
+ /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */
11048
+ const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {
11049
+ tagCheck: {
11050
+ writable: true,
11051
+ configurable: false,
11052
+ enumerable: true,
11053
+ value: null
11054
+ },
11055
+ attributeCheck: {
11056
+ writable: true,
11057
+ configurable: false,
11058
+ enumerable: true,
11059
+ value: null
11060
+ }
11061
+ }));
11166
11062
  /* Decide if ARIA attributes are okay */
11167
11063
  let ALLOW_ARIA_ATTR = true;
11168
11064
  /* Decide if custom data attributes are okay */
@@ -11355,16 +11251,24 @@
11355
11251
  }
11356
11252
  /* Merge configuration parameters */
11357
11253
  if (cfg.ADD_TAGS) {
11358
- if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
11359
- ALLOWED_TAGS = clone(ALLOWED_TAGS);
11254
+ if (typeof cfg.ADD_TAGS === 'function') {
11255
+ EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
11256
+ } else {
11257
+ if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
11258
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
11259
+ }
11260
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
11360
11261
  }
11361
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
11362
11262
  }
11363
11263
  if (cfg.ADD_ATTR) {
11364
- if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
11365
- ALLOWED_ATTR = clone(ALLOWED_ATTR);
11264
+ if (typeof cfg.ADD_ATTR === 'function') {
11265
+ EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
11266
+ } else {
11267
+ if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
11268
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
11269
+ }
11270
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
11366
11271
  }
11367
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
11368
11272
  }
11369
11273
  if (cfg.ADD_URI_SAFE_ATTR) {
11370
11274
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
@@ -11672,7 +11576,7 @@
11672
11576
  return true;
11673
11577
  }
11674
11578
  /* Remove element if anything forbids its presence */
11675
- if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
11579
+ if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
11676
11580
  /* Check if we have a custom element to handle */
11677
11581
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
11678
11582
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -11744,7 +11648,7 @@
11744
11648
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
11745
11649
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
11746
11650
  We don't need to check the value; it's always URI safe. */
11747
- 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]) {
11651
+ 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]) {
11748
11652
  if (
11749
11653
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
11750
11654
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -17151,7 +17055,7 @@
17151
17055
  } else if (isFile(config.url)) {
17152
17056
  this.name = config.url.name;
17153
17057
  } else if (isString$3(config.url) && !config.url.startsWith("data:")) {
17154
- this.name = getFilename$2(config.url);
17058
+ this.name = getFilename$1(config.url);
17155
17059
  }
17156
17060
 
17157
17061
  this.url = config.url;
@@ -20670,6 +20574,7 @@
20670
20574
 
20671
20575
  switch (config.format) {
20672
20576
  case "vcf":
20577
+ case "vcftabix":
20673
20578
  return new VcfParser(config)
20674
20579
  case "seg" :
20675
20580
  return new SegParser("seg")
@@ -21110,7 +21015,7 @@
21110
21015
  config.format = format.toLowerCase();
21111
21016
  config.sourceType = "htsget";
21112
21017
  if (!config.name) {
21113
- config.name = await getFilename$1(config.url);
21018
+ config.name = getFilename$1(config.url);
21114
21019
  }
21115
21020
  }
21116
21021
  } catch (e) {
@@ -21281,6 +21186,7 @@
21281
21186
  }
21282
21187
  }
21283
21188
 
21189
+ // Base class for feature sources. Subclasses must implement getFeatures().
21284
21190
  class BaseFeatureSource {
21285
21191
 
21286
21192
  constructor(genome) {
@@ -21342,59 +21248,10 @@
21342
21248
  }
21343
21249
  }
21344
21250
 
21345
- async previousFeature(chr, position, direction, visibilityWindow) {
21346
-
21347
- let chromosomeNames = this.genome.chromosomeNames || [chr];
21348
- let idx = chromosomeNames.indexOf(chr);
21349
- if (idx < 0) return // This shouldn't happen
21350
-
21351
- // Look ahead (or behind) in 10 kb intervals, but no further than visibilityWindow
21352
- const window = Math.min(10000, visibilityWindow || 10000);
21353
- let queryStart = direction ? position : Math.max(position - window, 0);
21354
- while (idx < chromosomeNames.length && idx >= 0) {
21355
- chr = chromosomeNames[idx];
21356
- const chromosome = this.genome.getChromosome(chr);
21357
- const chromosomeEnd = chromosome.bpLength;
21358
- while (queryStart < chromosomeEnd && queryStart >= 0) {
21359
- let queryEnd = Math.min(position, queryStart + window);
21360
- const featureList = await this.getFeatures({chr, start: queryStart, end: queryEnd, visibilityWindow});
21361
- if (featureList) {
21362
-
21363
- const compare = (o1, o2) => o1.start - o2.start + o1.end - o2.end;
21364
- const sortedList = Array.from(featureList);
21365
- sortedList.sort(compare);
21366
-
21367
- // Search for next or previous feature relative to centers. We use a linear search because the
21368
- // feature is likely to be near the first or end of the list
21369
- let idx = direction ? 0 : sortedList.length - 1;
21370
- while(idx >= 0 && idx < sortedList.length) {
21371
- const f = sortedList[idx];
21372
- const center = (f.start + f.end) / 2;
21373
- if(direction) {
21374
- if(center > position) return f
21375
- idx++;
21376
- } else {
21377
- if(center < position) return f
21378
- idx--;
21379
- }
21380
- }
21381
- }
21382
- queryStart = direction ? queryEnd : queryStart - window;
21383
- }
21384
- if (direction) {
21385
- idx++;
21386
- queryStart = 0;
21387
- position = 0;
21388
- } else {
21389
- idx--;
21390
- if (idx < 0) break
21391
- const prevChromosome = this.genome.getChromosome(chromosomeNames[idx]);
21392
- position = prevChromosome.bpLength;
21393
- queryStart = position - window;
21394
- }
21395
- }
21251
+ // Subclasses must implement
21252
+ async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
21253
+ throw new Error("getFeatures not implemented")
21396
21254
  }
21397
-
21398
21255
  }
21399
21256
 
21400
21257
  const GZIP_FLAG = 0x1;
@@ -26533,6 +26390,88 @@
26533
26390
  }
26534
26391
  }
26535
26392
 
26393
+ /**
26394
+ * A feature source for a "list" file. A list file is a text file with two columns, chromosome and URL. It was
26395
+ * created for the 1KG genotype files, which are split by chromosome, and at the moment that is the only use case.
26396
+ */
26397
+
26398
+ class ListFeatureSource extends BaseFeatureSource {
26399
+
26400
+ constructor(config, genome) {
26401
+ super(genome);
26402
+ this.config = config;
26403
+ this.featureSourceMap = null;
26404
+ this.header = null;
26405
+ }
26406
+
26407
+ async getHeader() {
26408
+
26409
+ if (!this.header) {
26410
+
26411
+ if (!this.featureSourceMap) {
26412
+ await this.init();
26413
+ }
26414
+ // Return the header from the first feature source. It is assumed that all sources have a common header.
26415
+ const firstFS = this.featureSourceMap.values().next().value;
26416
+ if (firstFS && firstFS.getHeader) {
26417
+ this.header = firstFS.getHeader();
26418
+ } else {
26419
+ this.header = Promise.resolve(undefined);
26420
+ }
26421
+ }
26422
+
26423
+ return this.header
26424
+
26425
+ }
26426
+
26427
+ async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
26428
+
26429
+ if (!this.featureSourceMap) {
26430
+ await this.init();
26431
+ }
26432
+ const fs = this.featureSourceMap.get(chr);
26433
+ if (fs) {
26434
+ return fs.getFeatures({chr, start, end, bpPerPixel, visibilityWindow})
26435
+ } else {
26436
+ return []
26437
+ }
26438
+ }
26439
+
26440
+ async init() {
26441
+ this.featureSourceMap = new Map();
26442
+
26443
+ const options = buildOptions(this.config);
26444
+ const data = await igvxhr.loadByteArray(this.config.url, options);
26445
+ const dataWrapper = getDataWrapper(data);
26446
+
26447
+ let line;
26448
+ while ((line = dataWrapper.nextLine()) !== undefined) {
26449
+ const trimmed = line.trim();
26450
+ if (!trimmed.startsWith('#')) {
26451
+ const tokens = trimmed.split(/\s+/);
26452
+ if (tokens.length > 1) {
26453
+ const chr = tokens[0];
26454
+ const path = tokens[1];
26455
+ const sourceConfig = Object.assign({}, this.config);
26456
+ sourceConfig.url = path;
26457
+ if (path.endsWith(".vcf.gz")) {
26458
+ sourceConfig.format = "vcf";
26459
+ sourceConfig.indexURL = path + ".tbi";
26460
+ }
26461
+ this.featureSourceMap.set(chr, FeatureSource(sourceConfig, this.genome));
26462
+ }
26463
+ }
26464
+ }
26465
+ }
26466
+
26467
+ supportWholeGenome() {
26468
+ return false
26469
+ }
26470
+ }
26471
+
26472
+ // chrY https://1000genomes.s3.amazonaws.com/release/20130502/ALL.chrY.phase3_integrated_v1b.20130502.genotypes.vcf.gz
26473
+ // chrX https://1000genomes.s3.amazonaws.com/release/20130502/ALL.chrX.phase3_shapeit2_mvncall_integrated_v1b.20130502.genotypes.vcf.gz
26474
+
26536
26475
  const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
26537
26476
 
26538
26477
  function FeatureSource(config, genome) {
@@ -26547,6 +26486,9 @@
26547
26486
  return new TDFSource(config, genome)
26548
26487
  } else if ("gbk" === format) {
26549
26488
  return new GenbankFeatureSource(config, genome)
26489
+ } else if ("vcf.list" === format) {
26490
+ // This is a text file with two columns: <chr> <url to vcf>
26491
+ return new ListFeatureSource(config, genome)
26550
26492
  } else {
26551
26493
  return new TextFeatureSource(config, genome)
26552
26494
  }
@@ -30409,6 +30351,19 @@
30409
30351
  const filterTracks = new Set(["cytoBandIdeo", "assembly", "gap", "gapOverlap", "allGaps",
30410
30352
  "cpgIslandExtUnmasked", "windowMasker"]);
30411
30353
 
30354
+ const vizModeMap = new Map([
30355
+ ["pack", "EXPANDED"],
30356
+ ["full", "EXPANDED"],
30357
+ ["squish", "SQUISHED"],
30358
+ ["dense", "COLLAPSED"]
30359
+ ]);
30360
+ const typeFormatMap = new Map([
30361
+ ["vcftabix", "vcf"],
30362
+ ["vcfphasedtrio", "vcf"],
30363
+ ["bigdbsnp", "bigbed"],
30364
+ ["genepred", "refgene"]
30365
+ ]);
30366
+
30412
30367
  class TrackDbHub {
30413
30368
 
30414
30369
  constructor(trackStanzas, groupStanzas) {
@@ -30554,14 +30509,13 @@
30554
30509
  */
30555
30510
  #getTrackConfig(t) {
30556
30511
 
30557
- const format = t.format;
30512
+ const format = typeFormatMap.get(t.format) || t.format;
30558
30513
 
30559
30514
  const config = {
30560
30515
  "id": t.getProperty("track"),
30561
30516
  "name": t.getProperty("shortLabel"),
30562
30517
  "format": format,
30563
- "url": t.getProperty("bigDataUrl"),
30564
- "displayMode": t.displayMode,
30518
+ "url": t.getProperty("bigDataUrl")
30565
30519
  };
30566
30520
 
30567
30521
  if ("vcfTabix" === format) {
@@ -30611,9 +30565,14 @@
30611
30565
 
30612
30566
  }
30613
30567
  if (t.hasProperty("itemRgb")) ;
30614
- if ("hide" === t.getProperty("visibility")) {
30615
- // TODO -- this not supported yet
30616
- config.visible = false;
30568
+ if(t.hasProperty("visibility")) {
30569
+ if ("hide" === t.getProperty("visibility")) {
30570
+ // TODO -- this not supported yet
30571
+ config.visible = false;
30572
+ }
30573
+ else {
30574
+ config.displayMode = vizModeMap.get(t.getProperty("visibility")) || "COLLAPSED";
30575
+ }
30617
30576
  }
30618
30577
  if (t.hasProperty("url")) {
30619
30578
  config.infoURL = t.getProperty("url");
@@ -31591,7 +31550,7 @@
31591
31550
 
31592
31551
  this.overlayElement.style.top = `-${contentTop}px`;
31593
31552
 
31594
- if(!this.checkZoomIn()) return;
31553
+ if (!this.checkZoomIn()) return
31595
31554
 
31596
31555
  if (!this.canvas) {
31597
31556
  this.repaint();
@@ -32230,7 +32189,7 @@
32230
32189
 
32231
32190
  popupTimerID = setTimeout(() => {
32232
32191
 
32233
- const content = this.getPopupContent(event);
32192
+ const content = this.handleTrackClick(event);
32234
32193
  if (content) {
32235
32194
 
32236
32195
  if (false === event.shiftKey) {
@@ -32395,7 +32354,7 @@
32395
32354
 
32396
32355
  }
32397
32356
 
32398
- getPopupContent(event) {
32357
+ handleTrackClick(event) {
32399
32358
 
32400
32359
  const clickState = this.createClickState(event);
32401
32360
 
@@ -37794,6 +37753,8 @@
37794
37753
  case "tdf":
37795
37754
  return "wig"
37796
37755
  case "vcf":
37756
+ case "vcftabix":
37757
+ case "vcf.list":
37797
37758
  return "variant"
37798
37759
  case "seg":
37799
37760
  return "seg"
@@ -37848,7 +37809,7 @@
37848
37809
  } else if ("bam" === config.type) {
37849
37810
  config.type = "alignment";
37850
37811
  config.format = "bam";
37851
- } else if ("vcf" === config.type) {
37812
+ } else if ("vcf" === config.type || "vcftabix" === config.type) {
37852
37813
  config.type = "variant";
37853
37814
  config.format = "vcf";
37854
37815
  } else if ("t2d" === config.type) {
@@ -38128,7 +38089,7 @@
38128
38089
  const groupIndeces = NULL_GROUP !== this.groupBy ?
38129
38090
  this.sampleKeys.map(sample => this.getGroupIndex(sample)) : undefined;
38130
38091
  return {
38131
- names: this.sampleKeys,
38092
+ names: this.sampleKeys || [],
38132
38093
  height: this.sampleHeight,
38133
38094
  yOffset: 0,
38134
38095
  groups: this.groups,
@@ -42541,7 +42502,7 @@
42541
42502
  let format;
42542
42503
 
42543
42504
  // First try determining format from file extension
42544
- const filename = config.filename || await getFilename$1(config.url);
42505
+ const filename = config.filename || getFilename$1(config.url);
42545
42506
  if(filename) {
42546
42507
  format = await inferFileFormatFromName(filename);
42547
42508
  }
@@ -62825,7 +62786,8 @@ ${indent}columns: ${matrix.columns}
62825
62786
  }
62826
62787
 
62827
62788
  get supportsWholeGenome() {
62828
- return !this.config.indexURL || this.config.supportsWholeGenome === true
62789
+ const sourceSupportsWG = typeof this.featureSource.supportsWholeGenome === 'function' && this.featureSource.supportsWholeGenome();
62790
+ return sourceSupportsWG || this.config.supportsWholeGenome === true
62829
62791
  }
62830
62792
 
62831
62793
  get color() {
@@ -62898,7 +62860,7 @@ ${indent}columns: ${matrix.columns}
62898
62860
  const yOffset = TOP_MARGIN + nVariantRows * (variantHeight + vGap);
62899
62861
 
62900
62862
  return {
62901
- names: this.sampleKeys,
62863
+ names: this.sampleKeys || [],
62902
62864
  yOffset,
62903
62865
  height,
62904
62866
  // groups: this.groups,
@@ -67029,7 +66991,7 @@ ${indent}columns: ${matrix.columns}
67029
66991
  })
67030
66992
  }
67031
66993
 
67032
- const _version = "3.5.2";
66994
+ const _version = "3.5.4";
67033
66995
  function version() {
67034
66996
  return _version
67035
66997
  }
@@ -68933,7 +68895,7 @@ ${indent}columns: ${matrix.columns}
68933
68895
 
68934
68896
  for (let config of configs) {
68935
68897
  if (!config.name && config.url) {
68936
- config.name = await getFilename$1(config.url);
68898
+ config.name = getFilename$1(config.url);
68937
68899
  }
68938
68900
  if (config.url && !config.format) {
68939
68901
  config.format = await inferFileFormat(config);
@@ -73720,7 +73682,8 @@ ${indent}columns: ${matrix.columns}
73720
73682
  return handler.apply(scope, args)
73721
73683
  });
73722
73684
 
73723
- return results
73685
+ // The only event that uses the return value is "trackclick", which implicitly assumes a single handler
73686
+ return results[0]
73724
73687
  }
73725
73688
  }
73726
73689
 
@@ -74091,7 +74054,7 @@ ${indent}columns: ${matrix.columns}
74091
74054
  } else {
74092
74055
  let filename = options.filename;
74093
74056
  if (!filename) {
74094
- filename = (options.url ? await getFilename$1(options.url) : options.file.name);
74057
+ filename = (options.url ? getFilename$1(options.url) : options.file.name);
74095
74058
  }
74096
74059
 
74097
74060
  if (filename.endsWith(".xml")) {
@@ -74105,10 +74068,8 @@ ${indent}columns: ${matrix.columns}
74105
74068
  config = {
74106
74069
  reference: genomeConfig
74107
74070
  };
74108
- } else if (filename.endsWith(".json")) {
74071
+ } else {
74109
74072
  config = await igvxhr.loadJson(urlOrFile);
74110
- } else {
74111
- throw Error("Unrecognized session file format:" + filename)
74112
74073
  }
74113
74074
  }
74114
74075
  setDefaults(config, defaults);
@@ -74576,6 +74537,10 @@ ${indent}columns: ${matrix.columns}
74576
74537
  config = JSON.parse(config);
74577
74538
  }
74578
74539
 
74540
+ if(config.format && config.format.toLowerCase() === 'sampleinfo') {
74541
+ return this.loadSampleInfo(config)
74542
+ }
74543
+
74579
74544
  let track;
74580
74545
  try {
74581
74546
  track = await this.createTrack(config);
@@ -76352,6 +76317,10 @@ ${indent}columns: ${matrix.columns}
76352
76317
  return igvxhr.setOauthToken(accessToken, host)
76353
76318
  }
76354
76319
 
76320
+ function setCORSProxy(proxyURL) {
76321
+ igvxhr.corsProxy = proxyURL;
76322
+ }
76323
+
76355
76324
  // Backward compatibility
76356
76325
  const oauth = igvxhr.oauth;
76357
76326
 
@@ -76368,6 +76337,7 @@ ${indent}columns: ${matrix.columns}
76368
76337
  visibilityChange,
76369
76338
  setGoogleOauthToken,
76370
76339
  setOauthToken,
76340
+ setCORSProxy,
76371
76341
  oauth,
76372
76342
  version,
76373
76343
  setApiKey,