igv 3.5.3 → 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
- }
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);
8512
8462
 
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
- }
8463
+ const qIdx = gsUrl.indexOf('?');
8464
+ let paramString = (qIdx > 0) ? gsUrl.substring(qIdx) : "";
8522
8465
 
8523
- if (bucket && object) {
8524
- return {
8525
- bucket, object
8526
- }
8527
- } else {
8528
- throw Error(`Unrecognized Google Storage URI: ${url}`)
8529
- }
8466
+ if (!paramString.includes("alt=media")) {
8467
+ paramString = paramString ? `${paramString}&alt=media` : "?alt=media";
8468
+ }
8530
8469
 
8531
- }
8532
-
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.file";
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
  /**
@@ -8975,9 +8817,6 @@ class IGVXhr {
8975
8817
  return buffer
8976
8818
  }
8977
8819
  } else {
8978
- if (url.startsWith("https://drive.google.com")) {
8979
- url = getDriveDownloadURL(url);
8980
- }
8981
8820
  if (isGoogleDriveURL(url) || url.startsWith("https://www.dropbox.com")) {
8982
8821
  return this.googleThrottle.add(async () => {
8983
8822
  return this._loadURL(url, options)
@@ -9001,9 +8840,15 @@ class IGVXhr {
9001
8840
 
9002
8841
  options = options || {};
9003
8842
 
9004
- let oauthToken = options.oauthToken || this.getOauthToken(url);
9005
- if (oauthToken) {
9006
- 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
+ }
9007
8852
  }
9008
8853
 
9009
8854
  return new Promise(function (resolve, reject) {
@@ -9364,8 +9209,10 @@ function addApiKey(url) {
9364
9209
  }
9365
9210
 
9366
9211
  function addTeamDrive(url) {
9367
- if (url.includes("supportsTeamDrive")) {
9212
+ if (url.includes("supportsAllDrives")) {
9368
9213
  return url
9214
+ } else if (url.includes("supportsTeamDrives")) {
9215
+ return url.replaceAll("supportsTeamDrives", "supportsAllDrives")
9369
9216
  } else {
9370
9217
  const paramSeparator = url.includes("?") ? "&" : "?";
9371
9218
  url = url + paramSeparator + "supportsTeamDrive=true";
@@ -9380,8 +9227,6 @@ function mapUrl$1(url) {
9380
9227
 
9381
9228
  if (url.startsWith("https://www.dropbox.com")) {
9382
9229
  return url.replace("//www.dropbox.com", "//dl.dropboxusercontent.com")
9383
- } else if (url.startsWith("https://drive.google.com")) {
9384
- return getDriveDownloadURL(url)
9385
9230
  } else if (url.includes("//www.broadinstitute.org/igvdata")) {
9386
9231
  return url.replace("//www.broadinstitute.org/igvdata", "//data.broadinstitute.org/igvdata")
9387
9232
  } else if (url.includes("//igvdata.broadinstitute.org")) {
@@ -9577,17 +9422,17 @@ class IntervalTree$1 {
9577
9422
 
9578
9423
  var searchInterval = new Interval$1(start, end, 0);
9579
9424
 
9580
- if (this.root === NIL$1) return [];
9425
+ if (this.root === NIL$1) return []
9581
9426
 
9582
9427
  var intervals = searchAll$1.call(this, searchInterval, this.root, []);
9583
9428
 
9584
9429
  if (intervals.length > 1) {
9585
9430
  intervals.sort(function (i1, i2) {
9586
- return i1.low - i2.low;
9431
+ return i1.low - i2.low
9587
9432
  });
9588
9433
  }
9589
9434
 
9590
- return intervals;
9435
+ return intervals
9591
9436
  }
9592
9437
 
9593
9438
  /**
@@ -9672,7 +9517,7 @@ function searchAll$1(interval, node, results) {
9672
9517
  searchAll$1.call(this, interval, node.right, results);
9673
9518
  }
9674
9519
 
9675
- return results;
9520
+ return results
9676
9521
  }
9677
9522
 
9678
9523
  function leftRotate$1(x) {
@@ -9751,35 +9596,35 @@ class Interval$1 {
9751
9596
 
9752
9597
  equals(other) {
9753
9598
  if (!other) {
9754
- return false;
9599
+ return false
9755
9600
  }
9756
9601
  if (this === other) {
9757
- return true;
9602
+ return true
9758
9603
  }
9759
9604
  return (this.low === other.low &&
9760
- this.high === other.high);
9605
+ this.high === other.high)
9761
9606
 
9762
9607
  }
9763
9608
 
9764
9609
  compareTo(other) {
9765
9610
  if (this.low < other.low)
9766
- return -1;
9611
+ return -1
9767
9612
  if (this.low > other.low)
9768
- return 1;
9613
+ return 1
9769
9614
 
9770
9615
  if (this.high < other.high)
9771
- return -1;
9616
+ return -1
9772
9617
  if (this.high > other.high)
9773
- return 1;
9618
+ return 1
9774
9619
 
9775
- return 0;
9620
+ return 0
9776
9621
  }
9777
9622
 
9778
9623
  /**
9779
9624
  * Returns true if this interval overlaps the other.
9780
9625
  */
9781
9626
  overlaps(other) {
9782
- return (this.low <= other.high && other.low <= this.high);
9627
+ return (this.low <= other.high && other.low <= this.high)
9783
9628
  }
9784
9629
  }
9785
9630
 
@@ -9824,19 +9669,19 @@ const FeatureUtils = {
9824
9669
  var start;
9825
9670
  var end;
9826
9671
 
9827
- if (!features) return;
9672
+ if (!features) return
9828
9673
 
9829
9674
  maxRows = maxRows || 10000;
9830
9675
 
9831
9676
  if (!sorted) {
9832
9677
  features.sort(function (a, b) {
9833
- return a.start - b.start;
9678
+ return a.start - b.start
9834
9679
  });
9835
9680
  }
9836
9681
 
9837
9682
 
9838
9683
  if (features.length === 0) {
9839
- return [];
9684
+ return []
9840
9685
 
9841
9686
  } else {
9842
9687
 
@@ -9889,7 +9734,7 @@ const FeatureUtils = {
9889
9734
  } // while (bucket)
9890
9735
 
9891
9736
  if (!bucket) {
9892
- break;
9737
+ break
9893
9738
  }
9894
9739
  feature = bucket.pop();
9895
9740
  if (0 === bucket.length) {
@@ -9906,7 +9751,7 @@ const FeatureUtils = {
9906
9751
  row++;
9907
9752
  nextStart = bucketStart;
9908
9753
 
9909
- if (allocatedCount === lastAllocatedCount) break; // Protect from infinite loops
9754
+ if (allocatedCount === lastAllocatedCount) break // Protect from infinite loops
9910
9755
 
9911
9756
  lastAllocatedCount = allocatedCount;
9912
9757
 
@@ -9928,13 +9773,13 @@ const FeatureUtils = {
9928
9773
  findOverlapping: function (featureList, start, end) {
9929
9774
 
9930
9775
  if (!featureList || featureList.length === 0) {
9931
- return [];
9776
+ return []
9932
9777
  } else {
9933
9778
  const tree = buildIntervalTree$1(featureList);
9934
9779
  const intervals = tree.findOverlapping(start, end);
9935
9780
 
9936
9781
  if (intervals.length === 0) {
9937
- return [];
9782
+ return []
9938
9783
  } else {
9939
9784
  // Trim the list of features in the intervals to those
9940
9785
  // overlapping the requested range.
@@ -9947,7 +9792,7 @@ const FeatureUtils = {
9947
9792
  const len = intervalFeatures.length;
9948
9793
  for (let i = 0; i < len; i++) {
9949
9794
  const feature = intervalFeatures[i];
9950
- if (feature.start > end) break;
9795
+ if (feature.start > end) break
9951
9796
  else if (feature.end > start) {
9952
9797
  featureList.push(feature);
9953
9798
  }
@@ -9955,10 +9800,10 @@ const FeatureUtils = {
9955
9800
  });
9956
9801
 
9957
9802
  featureList.sort(function (a, b) {
9958
- return a.start - b.start;
9803
+ return a.start - b.start
9959
9804
  });
9960
9805
 
9961
- return featureList;
9806
+ return featureList
9962
9807
  }
9963
9808
  }
9964
9809
 
@@ -9979,7 +9824,7 @@ function buildIntervalTree$1(featureList) {
9979
9824
  const chunkSize = Math.max(10, Math.round(len / 100));
9980
9825
 
9981
9826
  featureList.sort(function (f1, f2) {
9982
- 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))
9983
9828
  });
9984
9829
 
9985
9830
  for (let i = 0; i < len; i += chunkSize) {
@@ -9993,7 +9838,7 @@ function buildIntervalTree$1(featureList) {
9993
9838
  tree.insert(iStart, iEnd, subArray);
9994
9839
  }
9995
9840
 
9996
- return tree;
9841
+ return tree
9997
9842
  }
9998
9843
 
9999
9844
  function hexToRGB(hex) {
@@ -10444,7 +10289,7 @@ const doAutoscale = function (features) {
10444
10289
  min = Number.MAX_VALUE;
10445
10290
  max = -Number.MAX_VALUE;
10446
10291
 
10447
- for(let f of features) {
10292
+ for (let f of features) {
10448
10293
  if (!Number.isNaN(f.value)) {
10449
10294
  min = Math.min(min, f.value);
10450
10295
  max = Math.max(max, f.value);
@@ -10518,19 +10363,6 @@ const isNumber = function (num) {
10518
10363
  return false
10519
10364
  };
10520
10365
 
10521
- async function getFilename$1(url) {
10522
- if (isString$3(url) && url.startsWith("https://drive.google.com")) {
10523
- // This will fail if Google API key is not defined
10524
- if (getApiKey() === undefined) {
10525
- throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
10526
- }
10527
- const json = await getDriveFileInfo(url);
10528
- return json.originalFileName || json.name
10529
- } else {
10530
- return getFilename$2(url)
10531
- }
10532
- }
10533
-
10534
10366
  function prettyBasePairNumber(raw) {
10535
10367
 
10536
10368
  var denom,
@@ -10593,12 +10425,12 @@ function getElementVerticalDimension(element) {
10593
10425
  const marginTop = parseInt(style.marginTop);
10594
10426
  const marginBottom = parseInt(style.marginBottom);
10595
10427
 
10596
- const { top, bottom, height } = element.getBoundingClientRect();
10428
+ const {top, bottom, height} = element.getBoundingClientRect();
10597
10429
  return {
10598
10430
  top: Math.floor(top) - marginTop,
10599
10431
  bottom: Math.floor(bottom) + marginBottom,
10600
10432
  height: Math.floor(height) + marginTop + marginBottom
10601
- };
10433
+ }
10602
10434
  }
10603
10435
 
10604
10436
  class Popover {
@@ -10788,7 +10620,7 @@ function createMenuElements$1(itemList, popover) {
10788
10620
  return list;
10789
10621
  }
10790
10622
 
10791
- /*! @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 */
10792
10624
 
10793
10625
  const {
10794
10626
  entries,
@@ -10974,7 +10806,7 @@ function lookupGetter(object, prop) {
10974
10806
  }
10975
10807
 
10976
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']);
10977
- 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']);
10978
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']);
10979
10811
  // List of SVG elements that are disallowed by default.
10980
10812
  // We still need to know them so that we can do namespace
@@ -10988,7 +10820,7 @@ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongd
10988
10820
  const text = freeze(['#text']);
10989
10821
 
10990
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']);
10991
- 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']);
10992
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']);
10993
10825
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
10994
10826
 
@@ -11095,7 +10927,7 @@ const _createHooksMap = function _createHooksMap() {
11095
10927
  function createDOMPurify() {
11096
10928
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
11097
10929
  const DOMPurify = root => createDOMPurify(root);
11098
- DOMPurify.version = '3.2.7';
10930
+ DOMPurify.version = '3.3.0';
11099
10931
  DOMPurify.removed = [];
11100
10932
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
11101
10933
  // Not running in a browser, provide a factory function
@@ -11206,6 +11038,21 @@ function createDOMPurify() {
11206
11038
  let FORBID_TAGS = null;
11207
11039
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
11208
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
+ }));
11209
11056
  /* Decide if ARIA attributes are okay */
11210
11057
  let ALLOW_ARIA_ATTR = true;
11211
11058
  /* Decide if custom data attributes are okay */
@@ -11398,16 +11245,24 @@ function createDOMPurify() {
11398
11245
  }
11399
11246
  /* Merge configuration parameters */
11400
11247
  if (cfg.ADD_TAGS) {
11401
- if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
11402
- 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);
11403
11255
  }
11404
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
11405
11256
  }
11406
11257
  if (cfg.ADD_ATTR) {
11407
- if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
11408
- 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);
11409
11265
  }
11410
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
11411
11266
  }
11412
11267
  if (cfg.ADD_URI_SAFE_ATTR) {
11413
11268
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
@@ -11715,7 +11570,7 @@ function createDOMPurify() {
11715
11570
  return true;
11716
11571
  }
11717
11572
  /* Remove element if anything forbids its presence */
11718
- 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])) {
11719
11574
  /* Check if we have a custom element to handle */
11720
11575
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
11721
11576
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -11787,7 +11642,7 @@ function createDOMPurify() {
11787
11642
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
11788
11643
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
11789
11644
  We don't need to check the value; it's always URI safe. */
11790
- 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]) {
11791
11646
  if (
11792
11647
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
11793
11648
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -17194,7 +17049,7 @@ class TrackBase {
17194
17049
  } else if (isFile(config.url)) {
17195
17050
  this.name = config.url.name;
17196
17051
  } else if (isString$3(config.url) && !config.url.startsWith("data:")) {
17197
- this.name = getFilename$2(config.url);
17052
+ this.name = getFilename$1(config.url);
17198
17053
  }
17199
17054
 
17200
17055
  this.url = config.url;
@@ -21154,7 +21009,7 @@ class HtsgetReader {
21154
21009
  config.format = format.toLowerCase();
21155
21010
  config.sourceType = "htsget";
21156
21011
  if (!config.name) {
21157
- config.name = await getFilename$1(config.url);
21012
+ config.name = getFilename$1(config.url);
21158
21013
  }
21159
21014
  }
21160
21015
  } catch (e) {
@@ -31689,7 +31544,7 @@ class TrackViewport extends Viewport {
31689
31544
 
31690
31545
  this.overlayElement.style.top = `-${contentTop}px`;
31691
31546
 
31692
- if(!this.checkZoomIn()) return;
31547
+ if (!this.checkZoomIn()) return
31693
31548
 
31694
31549
  if (!this.canvas) {
31695
31550
  this.repaint();
@@ -32328,7 +32183,7 @@ class TrackViewport extends Viewport {
32328
32183
 
32329
32184
  popupTimerID = setTimeout(() => {
32330
32185
 
32331
- const content = this.getPopupContent(event);
32186
+ const content = this.handleTrackClick(event);
32332
32187
  if (content) {
32333
32188
 
32334
32189
  if (false === event.shiftKey) {
@@ -32493,7 +32348,7 @@ class TrackViewport extends Viewport {
32493
32348
 
32494
32349
  }
32495
32350
 
32496
- getPopupContent(event) {
32351
+ handleTrackClick(event) {
32497
32352
 
32498
32353
  const clickState = this.createClickState(event);
32499
32354
 
@@ -42641,7 +42496,7 @@ async function inferFileFormat(config) {
42641
42496
  let format;
42642
42497
 
42643
42498
  // First try determining format from file extension
42644
- const filename = config.filename || await getFilename$1(config.url);
42499
+ const filename = config.filename || getFilename$1(config.url);
42645
42500
  if(filename) {
42646
42501
  format = await inferFileFormatFromName(filename);
42647
42502
  }
@@ -67130,7 +66985,7 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
67130
66985
  })
67131
66986
  }
67132
66987
 
67133
- const _version = "3.5.3";
66988
+ const _version = "3.5.4";
67134
66989
  function version() {
67135
66990
  return _version
67136
66991
  }
@@ -69034,7 +68889,7 @@ class ROIManager {
69034
68889
 
69035
68890
  for (let config of configs) {
69036
68891
  if (!config.name && config.url) {
69037
- config.name = await getFilename$1(config.url);
68892
+ config.name = getFilename$1(config.url);
69038
68893
  }
69039
68894
  if (config.url && !config.format) {
69040
68895
  config.format = await inferFileFormat(config);
@@ -73821,7 +73676,8 @@ class EventEmitter {
73821
73676
  return handler.apply(scope, args)
73822
73677
  });
73823
73678
 
73824
- return results
73679
+ // The only event that uses the return value is "trackclick", which implicitly assumes a single handler
73680
+ return results[0]
73825
73681
  }
73826
73682
  }
73827
73683
 
@@ -74192,7 +74048,7 @@ class Browser {
74192
74048
  } else {
74193
74049
  let filename = options.filename;
74194
74050
  if (!filename) {
74195
- filename = (options.url ? await getFilename$1(options.url) : options.file.name);
74051
+ filename = (options.url ? getFilename$1(options.url) : options.file.name);
74196
74052
  }
74197
74053
 
74198
74054
  if (filename.endsWith(".xml")) {
@@ -74206,10 +74062,8 @@ class Browser {
74206
74062
  config = {
74207
74063
  reference: genomeConfig
74208
74064
  };
74209
- } else if (filename.endsWith(".json")) {
74065
+ } else {
74210
74066
  config = await igvxhr.loadJson(urlOrFile);
74211
- } else {
74212
- throw Error("Unrecognized session file format:" + filename)
74213
74067
  }
74214
74068
  }
74215
74069
  setDefaults(config, defaults);