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.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
- }
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);
8518
8468
 
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
- }
8469
+ const qIdx = gsUrl.indexOf('?');
8470
+ let paramString = (qIdx > 0) ? gsUrl.substring(qIdx) : "";
8528
8471
 
8529
- if (bucket && object) {
8530
- return {
8531
- bucket, object
8532
- }
8533
- } else {
8534
- throw Error(`Unrecognized Google Storage URI: ${url}`)
8535
- }
8472
+ if (!paramString.includes("alt=media")) {
8473
+ paramString = paramString ? `${paramString}&alt=media` : "?alt=media";
8474
+ }
8536
8475
 
8537
- }
8538
-
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.file";
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
  /**
@@ -8981,9 +8823,6 @@
8981
8823
  return buffer
8982
8824
  }
8983
8825
  } else {
8984
- if (url.startsWith("https://drive.google.com")) {
8985
- url = getDriveDownloadURL(url);
8986
- }
8987
8826
  if (isGoogleDriveURL(url) || url.startsWith("https://www.dropbox.com")) {
8988
8827
  return this.googleThrottle.add(async () => {
8989
8828
  return this._loadURL(url, options)
@@ -9007,9 +8846,15 @@
9007
8846
 
9008
8847
  options = options || {};
9009
8848
 
9010
- let oauthToken = options.oauthToken || this.getOauthToken(url);
9011
- if (oauthToken) {
9012
- 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
+ }
9013
8858
  }
9014
8859
 
9015
8860
  return new Promise(function (resolve, reject) {
@@ -9370,8 +9215,10 @@
9370
9215
  }
9371
9216
 
9372
9217
  function addTeamDrive(url) {
9373
- if (url.includes("supportsTeamDrive")) {
9218
+ if (url.includes("supportsAllDrives")) {
9374
9219
  return url
9220
+ } else if (url.includes("supportsTeamDrives")) {
9221
+ return url.replaceAll("supportsTeamDrives", "supportsAllDrives")
9375
9222
  } else {
9376
9223
  const paramSeparator = url.includes("?") ? "&" : "?";
9377
9224
  url = url + paramSeparator + "supportsTeamDrive=true";
@@ -9386,8 +9233,6 @@
9386
9233
 
9387
9234
  if (url.startsWith("https://www.dropbox.com")) {
9388
9235
  return url.replace("//www.dropbox.com", "//dl.dropboxusercontent.com")
9389
- } else if (url.startsWith("https://drive.google.com")) {
9390
- return getDriveDownloadURL(url)
9391
9236
  } else if (url.includes("//www.broadinstitute.org/igvdata")) {
9392
9237
  return url.replace("//www.broadinstitute.org/igvdata", "//data.broadinstitute.org/igvdata")
9393
9238
  } else if (url.includes("//igvdata.broadinstitute.org")) {
@@ -9583,17 +9428,17 @@
9583
9428
 
9584
9429
  var searchInterval = new Interval$1(start, end, 0);
9585
9430
 
9586
- if (this.root === NIL$1) return [];
9431
+ if (this.root === NIL$1) return []
9587
9432
 
9588
9433
  var intervals = searchAll$1.call(this, searchInterval, this.root, []);
9589
9434
 
9590
9435
  if (intervals.length > 1) {
9591
9436
  intervals.sort(function (i1, i2) {
9592
- return i1.low - i2.low;
9437
+ return i1.low - i2.low
9593
9438
  });
9594
9439
  }
9595
9440
 
9596
- return intervals;
9441
+ return intervals
9597
9442
  }
9598
9443
 
9599
9444
  /**
@@ -9678,7 +9523,7 @@
9678
9523
  searchAll$1.call(this, interval, node.right, results);
9679
9524
  }
9680
9525
 
9681
- return results;
9526
+ return results
9682
9527
  }
9683
9528
 
9684
9529
  function leftRotate$1(x) {
@@ -9757,35 +9602,35 @@
9757
9602
 
9758
9603
  equals(other) {
9759
9604
  if (!other) {
9760
- return false;
9605
+ return false
9761
9606
  }
9762
9607
  if (this === other) {
9763
- return true;
9608
+ return true
9764
9609
  }
9765
9610
  return (this.low === other.low &&
9766
- this.high === other.high);
9611
+ this.high === other.high)
9767
9612
 
9768
9613
  }
9769
9614
 
9770
9615
  compareTo(other) {
9771
9616
  if (this.low < other.low)
9772
- return -1;
9617
+ return -1
9773
9618
  if (this.low > other.low)
9774
- return 1;
9619
+ return 1
9775
9620
 
9776
9621
  if (this.high < other.high)
9777
- return -1;
9622
+ return -1
9778
9623
  if (this.high > other.high)
9779
- return 1;
9624
+ return 1
9780
9625
 
9781
- return 0;
9626
+ return 0
9782
9627
  }
9783
9628
 
9784
9629
  /**
9785
9630
  * Returns true if this interval overlaps the other.
9786
9631
  */
9787
9632
  overlaps(other) {
9788
- return (this.low <= other.high && other.low <= this.high);
9633
+ return (this.low <= other.high && other.low <= this.high)
9789
9634
  }
9790
9635
  }
9791
9636
 
@@ -9830,19 +9675,19 @@
9830
9675
  var start;
9831
9676
  var end;
9832
9677
 
9833
- if (!features) return;
9678
+ if (!features) return
9834
9679
 
9835
9680
  maxRows = maxRows || 10000;
9836
9681
 
9837
9682
  if (!sorted) {
9838
9683
  features.sort(function (a, b) {
9839
- return a.start - b.start;
9684
+ return a.start - b.start
9840
9685
  });
9841
9686
  }
9842
9687
 
9843
9688
 
9844
9689
  if (features.length === 0) {
9845
- return [];
9690
+ return []
9846
9691
 
9847
9692
  } else {
9848
9693
 
@@ -9895,7 +9740,7 @@
9895
9740
  } // while (bucket)
9896
9741
 
9897
9742
  if (!bucket) {
9898
- break;
9743
+ break
9899
9744
  }
9900
9745
  feature = bucket.pop();
9901
9746
  if (0 === bucket.length) {
@@ -9912,7 +9757,7 @@
9912
9757
  row++;
9913
9758
  nextStart = bucketStart;
9914
9759
 
9915
- if (allocatedCount === lastAllocatedCount) break; // Protect from infinite loops
9760
+ if (allocatedCount === lastAllocatedCount) break // Protect from infinite loops
9916
9761
 
9917
9762
  lastAllocatedCount = allocatedCount;
9918
9763
 
@@ -9934,13 +9779,13 @@
9934
9779
  findOverlapping: function (featureList, start, end) {
9935
9780
 
9936
9781
  if (!featureList || featureList.length === 0) {
9937
- return [];
9782
+ return []
9938
9783
  } else {
9939
9784
  const tree = buildIntervalTree$1(featureList);
9940
9785
  const intervals = tree.findOverlapping(start, end);
9941
9786
 
9942
9787
  if (intervals.length === 0) {
9943
- return [];
9788
+ return []
9944
9789
  } else {
9945
9790
  // Trim the list of features in the intervals to those
9946
9791
  // overlapping the requested range.
@@ -9953,7 +9798,7 @@
9953
9798
  const len = intervalFeatures.length;
9954
9799
  for (let i = 0; i < len; i++) {
9955
9800
  const feature = intervalFeatures[i];
9956
- if (feature.start > end) break;
9801
+ if (feature.start > end) break
9957
9802
  else if (feature.end > start) {
9958
9803
  featureList.push(feature);
9959
9804
  }
@@ -9961,10 +9806,10 @@
9961
9806
  });
9962
9807
 
9963
9808
  featureList.sort(function (a, b) {
9964
- return a.start - b.start;
9809
+ return a.start - b.start
9965
9810
  });
9966
9811
 
9967
- return featureList;
9812
+ return featureList
9968
9813
  }
9969
9814
  }
9970
9815
 
@@ -9985,7 +9830,7 @@
9985
9830
  const chunkSize = Math.max(10, Math.round(len / 100));
9986
9831
 
9987
9832
  featureList.sort(function (f1, f2) {
9988
- 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))
9989
9834
  });
9990
9835
 
9991
9836
  for (let i = 0; i < len; i += chunkSize) {
@@ -9999,7 +9844,7 @@
9999
9844
  tree.insert(iStart, iEnd, subArray);
10000
9845
  }
10001
9846
 
10002
- return tree;
9847
+ return tree
10003
9848
  }
10004
9849
 
10005
9850
  function hexToRGB(hex) {
@@ -10450,7 +10295,7 @@
10450
10295
  min = Number.MAX_VALUE;
10451
10296
  max = -Number.MAX_VALUE;
10452
10297
 
10453
- for(let f of features) {
10298
+ for (let f of features) {
10454
10299
  if (!Number.isNaN(f.value)) {
10455
10300
  min = Math.min(min, f.value);
10456
10301
  max = Math.max(max, f.value);
@@ -10524,19 +10369,6 @@
10524
10369
  return false
10525
10370
  };
10526
10371
 
10527
- async function getFilename$1(url) {
10528
- if (isString$3(url) && url.startsWith("https://drive.google.com")) {
10529
- // This will fail if Google API key is not defined
10530
- if (getApiKey() === undefined) {
10531
- throw Error("Google drive is referenced, but API key is not defined. An API key is required for Google Drive access")
10532
- }
10533
- const json = await getDriveFileInfo(url);
10534
- return json.originalFileName || json.name
10535
- } else {
10536
- return getFilename$2(url)
10537
- }
10538
- }
10539
-
10540
10372
  function prettyBasePairNumber(raw) {
10541
10373
 
10542
10374
  var denom,
@@ -10599,12 +10431,12 @@
10599
10431
  const marginTop = parseInt(style.marginTop);
10600
10432
  const marginBottom = parseInt(style.marginBottom);
10601
10433
 
10602
- const { top, bottom, height } = element.getBoundingClientRect();
10434
+ const {top, bottom, height} = element.getBoundingClientRect();
10603
10435
  return {
10604
10436
  top: Math.floor(top) - marginTop,
10605
10437
  bottom: Math.floor(bottom) + marginBottom,
10606
10438
  height: Math.floor(height) + marginTop + marginBottom
10607
- };
10439
+ }
10608
10440
  }
10609
10441
 
10610
10442
  class Popover {
@@ -10794,7 +10626,7 @@
10794
10626
  return list;
10795
10627
  }
10796
10628
 
10797
- /*! @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 */
10798
10630
 
10799
10631
  const {
10800
10632
  entries,
@@ -10980,7 +10812,7 @@
10980
10812
  }
10981
10813
 
10982
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']);
10983
- 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']);
10984
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']);
10985
10817
  // List of SVG elements that are disallowed by default.
10986
10818
  // We still need to know them so that we can do namespace
@@ -10994,7 +10826,7 @@
10994
10826
  const text = freeze(['#text']);
10995
10827
 
10996
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']);
10997
- 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']);
10998
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']);
10999
10831
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
11000
10832
 
@@ -11101,7 +10933,7 @@
11101
10933
  function createDOMPurify() {
11102
10934
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
11103
10935
  const DOMPurify = root => createDOMPurify(root);
11104
- DOMPurify.version = '3.2.7';
10936
+ DOMPurify.version = '3.3.0';
11105
10937
  DOMPurify.removed = [];
11106
10938
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
11107
10939
  // Not running in a browser, provide a factory function
@@ -11212,6 +11044,21 @@
11212
11044
  let FORBID_TAGS = null;
11213
11045
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
11214
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
+ }));
11215
11062
  /* Decide if ARIA attributes are okay */
11216
11063
  let ALLOW_ARIA_ATTR = true;
11217
11064
  /* Decide if custom data attributes are okay */
@@ -11404,16 +11251,24 @@
11404
11251
  }
11405
11252
  /* Merge configuration parameters */
11406
11253
  if (cfg.ADD_TAGS) {
11407
- if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
11408
- 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);
11409
11261
  }
11410
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
11411
11262
  }
11412
11263
  if (cfg.ADD_ATTR) {
11413
- if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
11414
- 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);
11415
11271
  }
11416
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
11417
11272
  }
11418
11273
  if (cfg.ADD_URI_SAFE_ATTR) {
11419
11274
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
@@ -11721,7 +11576,7 @@
11721
11576
  return true;
11722
11577
  }
11723
11578
  /* Remove element if anything forbids its presence */
11724
- 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])) {
11725
11580
  /* Check if we have a custom element to handle */
11726
11581
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
11727
11582
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -11793,7 +11648,7 @@
11793
11648
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
11794
11649
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
11795
11650
  We don't need to check the value; it's always URI safe. */
11796
- 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]) {
11797
11652
  if (
11798
11653
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
11799
11654
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -17200,7 +17055,7 @@
17200
17055
  } else if (isFile(config.url)) {
17201
17056
  this.name = config.url.name;
17202
17057
  } else if (isString$3(config.url) && !config.url.startsWith("data:")) {
17203
- this.name = getFilename$2(config.url);
17058
+ this.name = getFilename$1(config.url);
17204
17059
  }
17205
17060
 
17206
17061
  this.url = config.url;
@@ -21160,7 +21015,7 @@
21160
21015
  config.format = format.toLowerCase();
21161
21016
  config.sourceType = "htsget";
21162
21017
  if (!config.name) {
21163
- config.name = await getFilename$1(config.url);
21018
+ config.name = getFilename$1(config.url);
21164
21019
  }
21165
21020
  }
21166
21021
  } catch (e) {
@@ -31695,7 +31550,7 @@
31695
31550
 
31696
31551
  this.overlayElement.style.top = `-${contentTop}px`;
31697
31552
 
31698
- if(!this.checkZoomIn()) return;
31553
+ if (!this.checkZoomIn()) return
31699
31554
 
31700
31555
  if (!this.canvas) {
31701
31556
  this.repaint();
@@ -32334,7 +32189,7 @@
32334
32189
 
32335
32190
  popupTimerID = setTimeout(() => {
32336
32191
 
32337
- const content = this.getPopupContent(event);
32192
+ const content = this.handleTrackClick(event);
32338
32193
  if (content) {
32339
32194
 
32340
32195
  if (false === event.shiftKey) {
@@ -32499,7 +32354,7 @@
32499
32354
 
32500
32355
  }
32501
32356
 
32502
- getPopupContent(event) {
32357
+ handleTrackClick(event) {
32503
32358
 
32504
32359
  const clickState = this.createClickState(event);
32505
32360
 
@@ -42647,7 +42502,7 @@
42647
42502
  let format;
42648
42503
 
42649
42504
  // First try determining format from file extension
42650
- const filename = config.filename || await getFilename$1(config.url);
42505
+ const filename = config.filename || getFilename$1(config.url);
42651
42506
  if(filename) {
42652
42507
  format = await inferFileFormatFromName(filename);
42653
42508
  }
@@ -67136,7 +66991,7 @@ ${indent}columns: ${matrix.columns}
67136
66991
  })
67137
66992
  }
67138
66993
 
67139
- const _version = "3.5.3";
66994
+ const _version = "3.5.4";
67140
66995
  function version() {
67141
66996
  return _version
67142
66997
  }
@@ -69040,7 +68895,7 @@ ${indent}columns: ${matrix.columns}
69040
68895
 
69041
68896
  for (let config of configs) {
69042
68897
  if (!config.name && config.url) {
69043
- config.name = await getFilename$1(config.url);
68898
+ config.name = getFilename$1(config.url);
69044
68899
  }
69045
68900
  if (config.url && !config.format) {
69046
68901
  config.format = await inferFileFormat(config);
@@ -73827,7 +73682,8 @@ ${indent}columns: ${matrix.columns}
73827
73682
  return handler.apply(scope, args)
73828
73683
  });
73829
73684
 
73830
- return results
73685
+ // The only event that uses the return value is "trackclick", which implicitly assumes a single handler
73686
+ return results[0]
73831
73687
  }
73832
73688
  }
73833
73689
 
@@ -74198,7 +74054,7 @@ ${indent}columns: ${matrix.columns}
74198
74054
  } else {
74199
74055
  let filename = options.filename;
74200
74056
  if (!filename) {
74201
- filename = (options.url ? await getFilename$1(options.url) : options.file.name);
74057
+ filename = (options.url ? getFilename$1(options.url) : options.file.name);
74202
74058
  }
74203
74059
 
74204
74060
  if (filename.endsWith(".xml")) {
@@ -74212,10 +74068,8 @@ ${indent}columns: ${matrix.columns}
74212
74068
  config = {
74213
74069
  reference: genomeConfig
74214
74070
  };
74215
- } else if (filename.endsWith(".json")) {
74071
+ } else {
74216
74072
  config = await igvxhr.loadJson(urlOrFile);
74217
- } else {
74218
- throw Error("Unrecognized session file format:" + filename)
74219
74073
  }
74220
74074
  }
74221
74075
  setDefaults(config, defaults);