igv 2.13.6 → 2.13.8

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
@@ -7204,8 +7204,8 @@
7204
7204
  function encode(objectName) {
7205
7205
  let result = '';
7206
7206
  objectName.split('').forEach(function (letter) {
7207
- if (encodings$1.has(letter)) {
7208
- result += encodings$1.get(letter);
7207
+ if (encodings$2.has(letter)) {
7208
+ result += encodings$2.get(letter);
7209
7209
  } else {
7210
7210
  result += letter;
7211
7211
  }
@@ -7214,27 +7214,27 @@
7214
7214
  }
7215
7215
 
7216
7216
  // %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
7217
- const encodings$1 = new Map();
7218
- encodings$1.set("!", "%21");
7219
- encodings$1.set("#", "%23");
7220
- encodings$1.set("$", "%24");
7221
- encodings$1.set("%", "%25");
7222
- encodings$1.set("&", "%26");
7223
- encodings$1.set("'", "%27");
7224
- encodings$1.set("(", "%28");
7225
- encodings$1.set(")", "%29");
7226
- encodings$1.set("*", "%2A");
7227
- encodings$1.set("+", "%2B");
7228
- encodings$1.set(",", "%2C");
7229
- encodings$1.set("/", "%2F");
7230
- encodings$1.set(":", "%3A");
7231
- encodings$1.set(";", "%3B");
7232
- encodings$1.set("=", "%3D");
7233
- encodings$1.set("?", "%3F");
7234
- encodings$1.set("@", "%40");
7235
- encodings$1.set("[", "%5B");
7236
- encodings$1.set("]", "%5D");
7237
- encodings$1.set(" ", "%20");
7217
+ const encodings$2 = new Map();
7218
+ encodings$2.set("!", "%21");
7219
+ encodings$2.set("#", "%23");
7220
+ encodings$2.set("$", "%24");
7221
+ encodings$2.set("%", "%25");
7222
+ encodings$2.set("&", "%26");
7223
+ encodings$2.set("'", "%27");
7224
+ encodings$2.set("(", "%28");
7225
+ encodings$2.set(")", "%29");
7226
+ encodings$2.set("*", "%2A");
7227
+ encodings$2.set("+", "%2B");
7228
+ encodings$2.set(",", "%2C");
7229
+ encodings$2.set("/", "%2F");
7230
+ encodings$2.set(":", "%3A");
7231
+ encodings$2.set(";", "%3B");
7232
+ encodings$2.set("=", "%3D");
7233
+ encodings$2.set("?", "%3F");
7234
+ encodings$2.set("@", "%40");
7235
+ encodings$2.set("[", "%5B");
7236
+ encodings$2.set("]", "%5D");
7237
+ encodings$2.set(" ", "%20");
7238
7238
 
7239
7239
  // Convenience functions for the gapi oAuth library.
7240
7240
  const FIVE_MINUTES = 5 * 60 * 1000;
@@ -13966,7 +13966,7 @@
13966
13966
  const {
13967
13967
  Inflate,
13968
13968
  inflate: inflate$3,
13969
- inflateRaw,
13969
+ inflateRaw: inflateRaw$2,
13970
13970
  ungzip: ungzip$2
13971
13971
  } = inflate_1$1;
13972
13972
  var Deflate_1 = Deflate;
@@ -13975,7 +13975,7 @@
13975
13975
  var gzip_1 = gzip;
13976
13976
  var Inflate_1 = Inflate;
13977
13977
  var inflate_1 = inflate$3;
13978
- var inflateRaw_1 = inflateRaw;
13978
+ var inflateRaw_1 = inflateRaw$2;
13979
13979
  var ungzip_1 = ungzip$2;
13980
13980
  var constants_1 = constants$2;
13981
13981
  var pako = {
@@ -14008,7 +14008,7 @@
14008
14008
 
14009
14009
  pako.deflateRaw;
14010
14010
  pako.deflate;
14011
- pako.inflateRaw;
14011
+ const inflateRaw = pako.inflateRaw;
14012
14012
  const inflate = pako.inflate;
14013
14013
  pako.gzip;
14014
14014
  const FEXTRA = 4; // gzip spec F.EXTRA flag
@@ -14080,7 +14080,7 @@
14080
14080
  return out;
14081
14081
  }
14082
14082
  }
14083
- function bgzBlockSize(data) {
14083
+ function bgzBlockSize$1(data) {
14084
14084
  const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
14085
14085
  const bsize = (ba[17] << 8 | ba[16]) + 1;
14086
14086
  return bsize;
@@ -17840,27 +17840,27 @@
17840
17840
  }
17841
17841
 
17842
17842
  // %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
17843
- const encodings = new Map();
17844
- encodings.set("!", "%21");
17845
- encodings.set("#", "%23");
17846
- encodings.set("$", "%24");
17847
- encodings.set("%", "%25");
17848
- encodings.set("&", "%26");
17849
- encodings.set("'", "%27");
17850
- encodings.set("(", "%28");
17851
- encodings.set(")", "%29");
17852
- encodings.set("*", "%2A");
17853
- encodings.set("+", "%2B");
17854
- encodings.set(",", "%2C");
17855
- encodings.set("/", "%2F");
17856
- encodings.set(":", "%3A");
17857
- encodings.set(";", "%3B");
17858
- encodings.set("=", "%3D");
17859
- encodings.set("?", "%3F");
17860
- encodings.set("@", "%40");
17861
- encodings.set("[", "%5B");
17862
- encodings.set("]", "%5D");
17863
- encodings.set(" ", "%20");
17843
+ const encodings$1 = new Map();
17844
+ encodings$1.set("!", "%21");
17845
+ encodings$1.set("#", "%23");
17846
+ encodings$1.set("$", "%24");
17847
+ encodings$1.set("%", "%25");
17848
+ encodings$1.set("&", "%26");
17849
+ encodings$1.set("'", "%27");
17850
+ encodings$1.set("(", "%28");
17851
+ encodings$1.set(")", "%29");
17852
+ encodings$1.set("*", "%2A");
17853
+ encodings$1.set("+", "%2B");
17854
+ encodings$1.set(",", "%2C");
17855
+ encodings$1.set("/", "%2F");
17856
+ encodings$1.set(":", "%3A");
17857
+ encodings$1.set(";", "%3B");
17858
+ encodings$1.set("=", "%3D");
17859
+ encodings$1.set("?", "%3F");
17860
+ encodings$1.set("@", "%40");
17861
+ encodings$1.set("[", "%5B");
17862
+ encodings$1.set("]", "%5D");
17863
+ encodings$1.set(" ", "%20");
17864
17864
  if (typeof process === 'object' && typeof window === 'undefined') {
17865
17865
  global.atob = function (str) {
17866
17866
  return Buffer.from(str, 'base64').toString('binary');
@@ -18865,7 +18865,7 @@
18865
18865
 
18866
18866
  const doAutoscale = function (features) {
18867
18867
  var min, max;
18868
- if (features.length > 0) {
18868
+ if (features && features.length > 0) {
18869
18869
  min = Number.MAX_VALUE;
18870
18870
  max = -Number.MAX_VALUE;
18871
18871
  features.forEach(function (f) {
@@ -21939,7 +21939,7 @@
21939
21939
  }
21940
21940
  };
21941
21941
 
21942
- const _version = "2.13.6";
21942
+ const _version = "2.13.8";
21943
21943
  function version$1() {
21944
21944
  return _version;
21945
21945
  }
@@ -23469,7 +23469,7 @@
23469
23469
 
23470
23470
  const delim = 'gff3' === format ? '=' : ' ';
23471
23471
  return new GFFFeature({
23472
- source: tokens[1],
23472
+ source: decodeGFFAttribute(tokens[1]),
23473
23473
  type: tokens[2],
23474
23474
  chr: tokens[0],
23475
23475
  start: parseInt(tokens[3]) - 1,
@@ -23570,7 +23570,7 @@
23570
23570
  const idx = kv.indexOf(keyValueDelim);
23571
23571
  if (idx > 0 && idx < kv.length - 1) {
23572
23572
  const key = kv.substring(0, idx);
23573
- let value = stripQuotes(decodeURIComponent(kv.substring(idx + 1).trim()));
23573
+ let value = stripQuotes(decodeGFFAttribute(kv.substring(idx + 1).trim()));
23574
23574
  attributes.push([key, value]);
23575
23575
  }
23576
23576
  }
@@ -23583,6 +23583,42 @@
23583
23583
  return value;
23584
23584
  }
23585
23585
 
23586
+ // GFF3 attributes have specific percent encoding rules, the list below are required, all others are forbidden
23587
+ /*
23588
+ tab (%09)
23589
+ newline (%0A)
23590
+ carriage return (%0D)
23591
+ % percent (%25)
23592
+ control characters (%00 through %1F, %7F)
23593
+ In addition, the following characters have reserved meanings in column 9 and must be escaped when used in other contexts:
23594
+ ; semicolon (%3B)
23595
+ = equals (%3D)
23596
+ & ampersand (%26)
23597
+ , comma (%2C)
23598
+ */
23599
+
23600
+ const encodings = new Map([["%09", "\t"], ["%0A", "\n"], ["%0D", "\r"], ["%25", "%"], ["%3B", ";"], ["%3D", "="], ["%26", "&"], ["%2C", ","]]);
23601
+ function decodeGFFAttribute(str) {
23602
+ if (!str.includes("%")) {
23603
+ return str;
23604
+ }
23605
+ let decoded = "";
23606
+ for (let i = 0; i < str.length; i++) {
23607
+ if (str.charCodeAt(i) === 37 && i < str.length - 2) {
23608
+ const key = str.substring(i, i + 3);
23609
+ if (encodings.has(key)) {
23610
+ decoded += encodings.get(key);
23611
+ } else {
23612
+ decoded += key;
23613
+ }
23614
+ i += 2;
23615
+ } else {
23616
+ decoded += str.charAt(i);
23617
+ }
23618
+ }
23619
+ return decoded;
23620
+ }
23621
+
23586
23622
  /**
23587
23623
  * Wrapper class to record a decoding error.
23588
23624
  */
@@ -26426,12 +26462,81 @@
26426
26462
  isGreaterThan(vp) {
26427
26463
  return this.block > vp.block || this.block === vp.block && this.offset > vp.offset;
26428
26464
  }
26465
+ isEqualTo(vp) {
26466
+ return this.block === vp.block && this.offset === vp.offset;
26467
+ }
26429
26468
  print() {
26430
26469
  return "" + this.block + ":" + this.offset;
26431
26470
  }
26432
26471
  }
26433
26472
 
26434
- // Represents a BAM index.
26473
+ function optimizeChunks(chunks, lowest) {
26474
+ if (chunks.length === 0) return chunks;
26475
+ chunks.sort(function (c0, c1) {
26476
+ const dif = c0.minv.block - c1.minv.block;
26477
+ if (dif !== 0) {
26478
+ return dif;
26479
+ } else {
26480
+ return c0.minv.offset - c1.minv.offset;
26481
+ }
26482
+ });
26483
+ if (chunks.length <= 1) {
26484
+ return chunks;
26485
+ }
26486
+
26487
+ // console.log("Before trimming " + chunks.length)
26488
+ // for (let c of chunks) {
26489
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
26490
+ // }
26491
+
26492
+ if (lowest) {
26493
+ chunks = chunks.filter(c => c.maxv.isGreaterThan(lowest));
26494
+ }
26495
+
26496
+ // console.log("Before merging " + chunks.length)
26497
+ // for (let c of chunks) {
26498
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
26499
+ // }
26500
+
26501
+ const mergedChunks = [];
26502
+ let lastChunk;
26503
+ for (let chunk of chunks) {
26504
+ if (!lastChunk) {
26505
+ mergedChunks.push(chunk);
26506
+ lastChunk = chunk;
26507
+ } else {
26508
+ if (canMerge(lastChunk, chunk)) {
26509
+ if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
26510
+ lastChunk.maxv = chunk.maxv;
26511
+ }
26512
+ } else {
26513
+ mergedChunks.push(chunk);
26514
+ lastChunk = chunk;
26515
+ }
26516
+ }
26517
+ }
26518
+
26519
+ // console.log("After merging " + mergedChunks.length)
26520
+ // for (let c of mergedChunks) {
26521
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
26522
+ // }
26523
+
26524
+ return mergedChunks;
26525
+ }
26526
+
26527
+ /**
26528
+ * Merge 2 blocks if the file position gap between them is < 16 kb, and the total size is < ~5 mb
26529
+ * @param chunk1
26530
+ * @param chunk2
26531
+ * @returns {boolean|boolean}
26532
+ */
26533
+ function canMerge(chunk1, chunk2) {
26534
+ const gap = chunk2.minv.block - chunk1.maxv.block;
26535
+ const sizeEstimate = chunk1.maxv.block - chunk1.minv.block;
26536
+ return gap < 65000 && sizeEstimate < 5000000;
26537
+ }
26538
+
26539
+ // Represents a CSI Bam or Tabix index
26435
26540
  const CSI1_MAGIC$1 = 21582659; // CSI\1
26436
26541
  const CSI2_MAGIC$1 = 38359875; // CSI\2
26437
26542
 
@@ -26543,7 +26648,7 @@
26543
26648
  * @param max genomic end position
26544
26649
  * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}}
26545
26650
  */
26546
- blocksForRange(refId, min, max) {
26651
+ chunksForRange(refId, min, max) {
26547
26652
  const ba = this.indices[refId];
26548
26653
  if (!ba) {
26549
26654
  return [];
@@ -26569,7 +26674,7 @@
26569
26674
  }
26570
26675
  }
26571
26676
  const lowestOffset = ba.loffset[overlappingBins[0]];
26572
- return optimizeChunks$1(chunks, lowestOffset);
26677
+ return optimizeChunks(chunks, lowestOffset);
26573
26678
  }
26574
26679
  }
26575
26680
 
@@ -26601,48 +26706,10 @@
26601
26706
  return ((1 << (this.depth + 1) * 3) - 1) / 7;
26602
26707
  }
26603
26708
  }
26604
- function optimizeChunks$1(chunks, lowest) {
26605
- const mergedChunks = [];
26606
- let lastChunk = null;
26607
- if (chunks.length === 0) return chunks;
26608
- chunks.sort(function (c0, c1) {
26609
- const dif = c0.minv.block - c1.minv.block;
26610
- if (dif !== 0) {
26611
- return dif;
26612
- } else {
26613
- return c0.minv.offset - c1.minv.offset;
26614
- }
26615
- });
26616
- chunks.forEach(function (chunk) {
26617
- if (!lowest || chunk.maxv.isGreaterThan(lowest)) {
26618
- if (lastChunk === null) {
26619
- mergedChunks.push(chunk);
26620
- lastChunk = chunk;
26621
- } else {
26622
- if (canMerge$1(lastChunk, chunk)) {
26623
- if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
26624
- lastChunk.maxv = chunk.maxv;
26625
- }
26626
- } else {
26627
- mergedChunks.push(chunk);
26628
- lastChunk = chunk;
26629
- }
26630
- }
26631
- }
26632
- });
26633
- return mergedChunks;
26634
- }
26635
- function canMerge$1(chunk1, chunk2) {
26636
- return chunk2.minv.block - chunk1.maxv.block < 65000 && chunk2.maxv.block - chunk1.minv.block < 5000000;
26637
- // lastChunk.minv.block === lastChunk.maxv.block &&
26638
- // lastChunk.maxv.block === chunk.minv.block &&
26639
- // chunk.minv.block === chunk.maxv.block
26640
- }
26641
26709
 
26642
- // Represents a BAM index.
26710
+ // Represents a BAM or Tabix index.
26643
26711
  const BAI_MAGIC$1 = 21578050;
26644
26712
  const TABIX_MAGIC$1 = 21578324;
26645
- const MB = 1000000;
26646
26713
  async function parseBamIndex(arrayBuffer, genome) {
26647
26714
  const index = new BamIndex();
26648
26715
  await index.parse(arrayBuffer, false, genome);
@@ -26744,22 +26811,27 @@
26744
26811
  }
26745
26812
 
26746
26813
  /**
26747
- * Fetch blocks for a particular genomic range. This method is public so it can be unit-tested.
26814
+ * Fetch chunks for a particular genomic range. This method is public so it can be unit-tested.
26748
26815
  *
26749
26816
  * @param refId the sequence dictionary index of the chromosome
26750
26817
  * @param min genomic start position
26751
26818
  * @param max genomic end position
26752
- * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}}
26819
+ * @param return an array of objects representing chunks (file spans) {minv: {block, offset}, {maxv: {block, offset}}
26753
26820
  */
26754
- blocksForRange(refId, min, max) {
26821
+ chunksForRange(refId, min, max) {
26755
26822
  const bam = this;
26756
26823
  const ba = bam.indices[refId];
26757
26824
  if (!ba) {
26758
26825
  return [];
26759
26826
  } else {
26760
26827
  const overlappingBins = reg2bins(min, max); // List of bin #s that overlap min, max
26761
- const chunks = [];
26762
26828
 
26829
+ //console.log("bin ranges")
26830
+ //for(let b of overlappingBins) {
26831
+ // console.log(`${b[0]} - ${b[1]}`)
26832
+ //}
26833
+
26834
+ const chunks = [];
26763
26835
  // Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
26764
26836
  for (let binRange of overlappingBins) {
26765
26837
  for (let bin = binRange[0]; bin <= binRange[1]; bin++) {
@@ -26770,8 +26842,7 @@
26770
26842
  const ce = c[1];
26771
26843
  chunks.push({
26772
26844
  minv: cs,
26773
- maxv: ce,
26774
- bin: bin
26845
+ maxv: ce
26775
26846
  });
26776
26847
  }
26777
26848
  }
@@ -26780,65 +26851,20 @@
26780
26851
 
26781
26852
  // Use the linear index to find minimum file position of chunks that could contain alignments in the region
26782
26853
  const nintv = ba.linearIndex.length;
26783
- let lowest = null;
26784
- const minLin = Math.min(min >> 14, nintv - 1);
26854
+ let lowest;
26855
+ const minLin = Math.min(min >> 14, nintv - 1); // i.e. min / 16384
26785
26856
  const maxLin = Math.min(max >> 14, nintv - 1);
26786
- for (let i = minLin; i < maxLin; i++) {
26857
+ for (let i = minLin; i <= maxLin; i++) {
26787
26858
  const vp = ba.linearIndex[i];
26788
26859
  if (vp) {
26789
- // todo -- I think, but am not sure, that the values in the linear index have to be in increasing order. So the first non-null should be minimum
26790
- if (!lowest || vp.isLessThan(lowest)) {
26791
- lowest = vp;
26792
- }
26860
+ lowest = vp; // lowest file offset that contains alignments overlapping (min, max)
26861
+ break;
26793
26862
  }
26794
26863
  }
26795
26864
  return optimizeChunks(chunks, lowest);
26796
26865
  }
26797
26866
  }
26798
26867
  }
26799
- function optimizeChunks(chunks, lowest) {
26800
- const mergedChunks = [];
26801
- let lastChunk = null;
26802
- if (chunks.length === 0) return chunks;
26803
- chunks.sort(function (c0, c1) {
26804
- const dif = c0.minv.block - c1.minv.block;
26805
- if (dif !== 0) {
26806
- return dif;
26807
- } else {
26808
- return c0.minv.offset - c1.minv.offset;
26809
- }
26810
- });
26811
- chunks.forEach(function (chunk) {
26812
- if (!lowest || chunk.maxv.isGreaterThan(lowest)) {
26813
- if (lastChunk === null) {
26814
- mergedChunks.push(chunk);
26815
- lastChunk = chunk;
26816
- } else {
26817
- if (canMerge(lastChunk, chunk)) {
26818
- if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
26819
- lastChunk.maxv = chunk.maxv;
26820
- }
26821
- } else {
26822
- mergedChunks.push(chunk);
26823
- lastChunk = chunk;
26824
- }
26825
- }
26826
- }
26827
- });
26828
- return mergedChunks;
26829
- }
26830
-
26831
- /**
26832
- * Merge 2 blocks if the gap between them is < 1kb and the total resulting size < 100mb
26833
- * @param chunk1
26834
- * @param chunk2
26835
- * @returns {boolean|boolean}
26836
- */
26837
- function canMerge(chunk1, chunk2) {
26838
- const gap = chunk2.minv.block - chunk1.maxv.block;
26839
- const total = chunk2.maxv.block - chunk1.minv.block;
26840
- return gap < 30000 && total < 10 * MB;
26841
- }
26842
26868
 
26843
26869
  /**
26844
26870
  * Calculate the list of bins that overlap with region [beg, end]
@@ -26970,7 +26996,7 @@
26970
26996
  * @param min genomic start position
26971
26997
  * @param max genomic end position
26972
26998
  */
26973
- blocksForRange(queryChr, min, max) {
26999
+ chunksForRange(queryChr, min, max) {
26974
27000
  const chrIdx = this.chrIndex[queryChr];
26975
27001
  if (chrIdx) {
26976
27002
  const blocks = chrIdx.blocks;
@@ -27130,7 +27156,12 @@
27130
27156
  }
27131
27157
  }
27132
27158
 
27133
- class BGZipLineReader {
27159
+ /**
27160
+ * Class to iterate line-by-line over a BGZipped text file. This class is useful for iterating from the start of
27161
+ * the file. Not useful for indexed queries.
27162
+ */
27163
+
27164
+ class BGZLineReader {
27134
27165
  constructor(config) {
27135
27166
  this.config = config;
27136
27167
  this.filePtr = 0;
@@ -27170,7 +27201,7 @@
27170
27201
  }
27171
27202
  });
27172
27203
  const abuffer = await igvxhr.loadArrayBuffer(this.config.url, bsizeOptions);
27173
- const bufferSize = bgzBlockSize(abuffer);
27204
+ const bufferSize = bgzBlockSize$1(abuffer);
27174
27205
  //console.log(`next block ${this.filePtr} ${bufferSize}`);
27175
27206
 
27176
27207
  if (bufferSize === 0) {
@@ -27195,6 +27226,244 @@
27195
27226
  }
27196
27227
  }
27197
27228
 
27229
+ function concatenateArrayBuffers(arrayBuffers) {
27230
+ if (arrayBuffers.length === 1) {
27231
+ return arrayBuffers[0];
27232
+ }
27233
+ let len = 0;
27234
+ for (const b of arrayBuffers) {
27235
+ len += b.byteLength;
27236
+ }
27237
+ const c = new Uint8Array(len);
27238
+ let offset = 0;
27239
+ for (const b of arrayBuffers) {
27240
+ c.set(new Uint8Array(b), offset);
27241
+ offset += b.byteLength;
27242
+ }
27243
+ return c.buffer;
27244
+ }
27245
+
27246
+ /**
27247
+ * Return the block size for the data buffer.
27248
+ * @param data
27249
+ * @returns {number}
27250
+ */
27251
+ const bgzBlockSize = data => {
27252
+ const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
27253
+ const bsize = (ba[17] << 8 | ba[16]) + 1;
27254
+ return bsize;
27255
+ };
27256
+ class BGZBlockLoader {
27257
+ constructor(config) {
27258
+ this.config = config;
27259
+ this.cacheBlocks = false != config.cacheBlocks; // Default to true
27260
+ this.cache = undefined;
27261
+ }
27262
+
27263
+ /**
27264
+ * Return inflated data from startBlock through endBlock as an UInt8Array
27265
+ *
27266
+ * @param startBlock
27267
+ * @param endBlock
27268
+ * @returns {Promise<Uint8Array>}
27269
+ */
27270
+ async getData(startBlock, endBlock) {
27271
+ const blocks = await this.getInflatedBlocks(startBlock, endBlock);
27272
+ if (blocks.length === 1) {
27273
+ return blocks[0];
27274
+ }
27275
+ let len = 0;
27276
+ for (const b of blocks) {
27277
+ len += b.byteLength;
27278
+ }
27279
+ const c = new Uint8Array(len);
27280
+ let offset = 0;
27281
+ for (const b of blocks) {
27282
+ c.set(b, offset);
27283
+ offset += b.byteLength;
27284
+ }
27285
+ return c;
27286
+ }
27287
+
27288
+ /**
27289
+ * Return the inflated data for the specified blocks as an array of Uint8Arrays. This method is public so
27290
+ * it can be unit tested. *
27291
+ * @param startBlock
27292
+ * @param endBlock
27293
+ * @returns {Promise<*[Uint8Array]>}
27294
+ */
27295
+ async getInflatedBlocks(startBlock, endBlock) {
27296
+ if (!this.cacheBlocks) {
27297
+ const buffer = await this.loadBLockData(startBlock, endBlock);
27298
+ return inflateBlocks(buffer);
27299
+ } else {
27300
+ const c = this.cache;
27301
+ if (c && c.startBlock <= startBlock && c.endBlock >= endBlock) {
27302
+ //console.log("Complete overlap")
27303
+ const startOffset = startBlock - c.startBlock;
27304
+ const endOffset = endBlock - c.startBlock;
27305
+ return inflateBlocks(c.buffer, startOffset, endOffset);
27306
+ // Don't update cache, still valid
27307
+ } else {
27308
+ let buffer;
27309
+ if (!c || c.startBlock > endBlock || c.endBlock < startBlock) {
27310
+ // no overlap with cache
27311
+ buffer = await this.loadBLockData(startBlock, endBlock);
27312
+ } else {
27313
+ //console.log("Some overlap")
27314
+ const arrayBuffers = [];
27315
+
27316
+ // Load blocks preceding cache start, if any
27317
+ if (startBlock < c.startBlock) {
27318
+ // load first blocks
27319
+ const startBuffer = await this.loadBLockData(startBlock, c.startBlock, {
27320
+ skipEnd: true
27321
+ });
27322
+ arrayBuffers.push(startBuffer);
27323
+ }
27324
+
27325
+ // Slice cached buffer as needed
27326
+ let cachedBuffer;
27327
+ if (startBlock <= c.startBlock && endBlock >= c.endBlock) {
27328
+ cachedBuffer = c.buffer;
27329
+ } else {
27330
+ const start = Math.max(0, startBlock - c.startBlock);
27331
+ let end;
27332
+ if (endBlock >= c.endBlock) {
27333
+ end = c.buffer.byteLength;
27334
+ } else {
27335
+ // We need to find the byte position of the end of "endBlock"
27336
+ const boundaries = findBlockBoundaries(c.buffer);
27337
+ for (let i = 0; i < boundaries.length - 1; i++) {
27338
+ if (c.startBlock + boundaries[i] === endBlock) {
27339
+ end = boundaries[i + 1];
27340
+ break;
27341
+ }
27342
+ }
27343
+ // Do something if end not found
27344
+ }
27345
+
27346
+ cachedBuffer = c.buffer.slice(start, end);
27347
+ }
27348
+ arrayBuffers.push(cachedBuffer);
27349
+
27350
+ // Load end blocks, if any
27351
+ if (endBlock > c.endBlock) {
27352
+ const endBuffer = await this.loadBLockData(c.endBlock, endBlock, {
27353
+ skipStart: true
27354
+ });
27355
+ arrayBuffers.push(endBuffer);
27356
+ }
27357
+ buffer = concatenateArrayBuffers(arrayBuffers);
27358
+ }
27359
+ this.cache = {
27360
+ startBlock,
27361
+ endBlock,
27362
+ buffer
27363
+ };
27364
+ return inflateBlocks(buffer);
27365
+ }
27366
+ }
27367
+ }
27368
+ async loadBLockData(startBlock, endBlock, options) {
27369
+ const config = this.config;
27370
+ const skipStart = options && options.skipStart;
27371
+ const skipEnd = options && options.skipEnd;
27372
+
27373
+ // Get size of last block if not skipped
27374
+ let lastBlockSize = 0;
27375
+ if (!skipEnd) {
27376
+ const bsizeOptions = buildOptions(config, {
27377
+ range: {
27378
+ start: endBlock,
27379
+ size: 26
27380
+ }
27381
+ });
27382
+ const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
27383
+ lastBlockSize = bgzBlockSize(abuffer);
27384
+ }
27385
+ if (skipStart) {
27386
+ const bsizeOptions = buildOptions(config, {
27387
+ range: {
27388
+ start: startBlock,
27389
+ size: 26
27390
+ }
27391
+ });
27392
+ const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
27393
+ startBlock += bgzBlockSize(abuffer);
27394
+ }
27395
+
27396
+ // Load data for all blocks
27397
+ const loadOptions = buildOptions(config, {
27398
+ range: {
27399
+ start: startBlock,
27400
+ size: endBlock + lastBlockSize - startBlock
27401
+ }
27402
+ });
27403
+
27404
+ //console.log(`${this.config.name} Loaded ${startBlock} - ${endBlock + lastBlockSize} (${(endBlock + lastBlockSize - startBlock) / 1000} kb)`)
27405
+
27406
+ return igvxhr.loadArrayBuffer(config.url, loadOptions);
27407
+ }
27408
+ }
27409
+ function findBlockBoundaries(arrayBuffer) {
27410
+ const byteLengh = arrayBuffer.byteLength;
27411
+ let offset = 0;
27412
+ const blockBoundaries = [0];
27413
+ while (offset < byteLengh) {
27414
+ //console.log("Cache block " + offset)
27415
+ const ba = new Uint8Array(arrayBuffer, offset);
27416
+ const bsize = (ba[17] << 8 | ba[16]) + 1;
27417
+ offset += bsize;
27418
+ if (offset < byteLengh) {
27419
+ blockBoundaries.push(offset);
27420
+ }
27421
+ }
27422
+ return blockBoundaries;
27423
+ }
27424
+
27425
+ /**
27426
+ * Inflate compressed blocks within the data buffer*
27427
+ * @param data
27428
+ * @param startBlock - optional file location for start block. Default == 0
27429
+ * @param endBlock - optional file location for last block to decompress.
27430
+ * @returns {*[]}
27431
+ */
27432
+ function inflateBlocks(data, startBlock, endBlock) {
27433
+ startBlock = startBlock || 0;
27434
+ const oBlockList = [];
27435
+ let ptr = startBlock;
27436
+ const lim = data.byteLength - 18;
27437
+ while (ptr < lim) {
27438
+ try {
27439
+ //console.log(113873 + ptr)
27440
+ const header = new Uint8Array(data, ptr, 18);
27441
+ const xlen = header[11] << 8 | header[10];
27442
+ const bsize = header[17] << 8 | header[16]; // Total block size, including header, minus 1
27443
+ const start = 12 + xlen + ptr; // Start of CDATA
27444
+ const bytesLeft = data.byteLength - start;
27445
+ const cDataSize = bsize - xlen - 18;
27446
+ if (bytesLeft < cDataSize || cDataSize <= 0) {
27447
+ // This is unexpected. Throw error?
27448
+ break;
27449
+ }
27450
+ const cdata = new Uint8Array(data, start, cDataSize);
27451
+ const unc = inflateRaw(cdata);
27452
+ oBlockList.push(unc);
27453
+ if (endBlock === ptr) {
27454
+ break;
27455
+ } else {
27456
+ // Advance to next block
27457
+ ptr += bsize + 1;
27458
+ }
27459
+ } catch (e) {
27460
+ console.error(e);
27461
+ break;
27462
+ }
27463
+ }
27464
+ return oBlockList;
27465
+ }
27466
+
27198
27467
  /*
27199
27468
  * The MIT License (MIT)
27200
27469
  *
@@ -27296,7 +27565,8 @@
27296
27565
  }
27297
27566
  let dataWrapper;
27298
27567
  if (index.tabix) {
27299
- dataWrapper = new BGZipLineReader(this.config);
27568
+ this._blockLoader = new BGZBlockLoader(this.config);
27569
+ dataWrapper = new BGZLineReader(this.config);
27300
27570
  } else {
27301
27571
  // Tribble
27302
27572
  const maxSize = Object.values(index.chrIndex).flatMap(chr => chr.blocks).map(block => block.max).reduce((previous, current) => Math.min(previous, current), Number.MAX_SAFE_INTEGER);
@@ -27372,46 +27642,25 @@
27372
27642
  return [];
27373
27643
  }
27374
27644
  const genome = this.genome;
27375
- const blocks = this.index.blocksForRange(refId, start, end);
27376
- if (!blocks || blocks.length === 0) {
27645
+ const chunks = this.index.chunksForRange(refId, start, end);
27646
+ if (!chunks || chunks.length === 0) {
27377
27647
  return [];
27378
27648
  } else {
27379
27649
  const allFeatures = [];
27380
- for (let block of blocks) {
27381
- const startPos = block.minv.block;
27382
- const startOffset = block.minv.offset;
27383
- const endOffset = block.maxv.offset;
27384
- let endPos;
27385
- if (tabix) {
27386
- let lastBlockSize = 0;
27387
- if (endOffset > 0) {
27388
- const bsizeOptions = buildOptions(config, {
27389
- range: {
27390
- start: block.maxv.block,
27391
- size: 26
27392
- }
27393
- });
27394
- const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
27395
- lastBlockSize = bgzBlockSize(abuffer);
27396
- }
27397
- endPos = block.maxv.block + lastBlockSize;
27398
- } else {
27399
- endPos = block.maxv.block;
27400
- }
27401
- const options = buildOptions(config, {
27402
- range: {
27403
- start: startPos,
27404
- size: endPos - startPos + 1
27405
- }
27406
- });
27650
+ for (let chunk of chunks) {
27407
27651
  let inflated;
27408
27652
  if (tabix) {
27409
- const data = await igvxhr.loadArrayBuffer(config.url, options);
27410
- inflated = unbgzf(data);
27653
+ inflated = await this._blockLoader.getData(chunk.minv.block, chunk.maxv.block);
27411
27654
  } else {
27655
+ const options = buildOptions(config, {
27656
+ range: {
27657
+ start: chunk.minv.block,
27658
+ size: chunk.maxv.block - chunk.minv.block + 1
27659
+ }
27660
+ });
27412
27661
  inflated = await igvxhr.loadString(config.url, options);
27413
27662
  }
27414
- const slicedData = startOffset ? inflated.slice(startOffset) : inflated;
27663
+ const slicedData = chunk.minv.offset ? inflated.slice(chunk.minv.offset) : inflated;
27415
27664
  const dataWrapper = getDataWrapper(slicedData);
27416
27665
  let slicedFeatures = await parser.parseFeatures(dataWrapper);
27417
27666
 
@@ -29837,6 +30086,7 @@
29837
30086
  var documentAll$2 = typeof document == 'object' && document.all;
29838
30087
 
29839
30088
  // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot
30089
+ // eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing
29840
30090
  var IS_HTMLDDA = typeof documentAll$2 == 'undefined' && documentAll$2 !== undefined;
29841
30091
  var documentAll_1 = {
29842
30092
  all: documentAll$2,
@@ -30091,10 +30341,10 @@
30091
30341
  (module.exports = function (key, value) {
30092
30342
  return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
30093
30343
  })('versions', []).push({
30094
- version: '3.26.1',
30344
+ version: '3.27.1',
30095
30345
  mode: 'global',
30096
30346
  copyright: '© 2014-2022 Denis Pushkarev (zloirock.ru)',
30097
- license: 'https://github.com/zloirock/core-js/blob/v3.26.1/LICENSE',
30347
+ license: 'https://github.com/zloirock/core-js/blob/v3.27.1/LICENSE',
30098
30348
  source: 'https://github.com/zloirock/core-js'
30099
30349
  });
30100
30350
  });
@@ -33332,6 +33582,7 @@
33332
33582
  this.bamPath = config.url;
33333
33583
  this.baiPath = config.indexURL;
33334
33584
  BamUtils.setReaderDefaults(this, config);
33585
+ this._blockLoader = new BGZBlockLoader(config);
33335
33586
  }
33336
33587
  async readAlignments(chr, bpStart, bpEnd) {
33337
33588
  const chrToIndex = await this.getChrIndex();
@@ -33342,37 +33593,14 @@
33342
33593
  return alignmentContainer;
33343
33594
  } else {
33344
33595
  const bamIndex = await this.getIndex();
33345
- const chunks = bamIndex.blocksForRange(chrId, bpStart, bpEnd);
33596
+ const chunks = bamIndex.chunksForRange(chrId, bpStart, bpEnd);
33346
33597
  if (!chunks || chunks.length === 0) {
33347
33598
  return alignmentContainer;
33348
33599
  }
33349
33600
  for (let c of chunks) {
33350
- let lastBlockSize;
33351
- if (c.maxv.offset === 0) {
33352
- lastBlockSize = 0; // Don't need to read the last block.
33353
- } else {
33354
- const bsizeOptions = buildOptions(this.config, {
33355
- range: {
33356
- start: c.maxv.block,
33357
- size: 26
33358
- }
33359
- });
33360
- const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions);
33361
- lastBlockSize = bgzBlockSize(abuffer);
33362
- }
33363
- const fetchMin = c.minv.block;
33364
- const fetchMax = c.maxv.block + lastBlockSize;
33365
- const range = {
33366
- start: fetchMin,
33367
- size: fetchMax - fetchMin + 1
33368
- };
33369
- const compressed = await igvxhr.loadArrayBuffer(this.bamPath, buildOptions(this.config, {
33370
- range: range
33371
- }));
33372
- var ba = unbgzf(compressed); //new Uint8Array(BGZip.unbgzf(compressed)); //, c.maxv.block - c.minv.block + 1));
33601
+ const ba = await this._blockLoader.getData(c.minv.block, c.maxv.block);
33373
33602
  const done = BamUtils.decodeBamRecords(ba, c.minv.offset, alignmentContainer, this.indexToChr, chrId, bpStart, bpEnd, this.filter);
33374
33603
  if (done) {
33375
- // console.log(`Loaded ${counter} chunks out of ${chunks.length}`);
33376
33604
  break;
33377
33605
  }
33378
33606
  }
@@ -33393,7 +33621,7 @@
33393
33621
  }
33394
33622
  });
33395
33623
  const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions);
33396
- const bsize = bgzBlockSize(abuffer);
33624
+ const bsize = bgzBlockSize$1(abuffer);
33397
33625
  len = index.firstBlockPosition + bsize; // Insure we get the complete compressed block containing the header
33398
33626
  } else {
33399
33627
  len = 64000;
@@ -33931,7 +34159,7 @@
33931
34159
  var clear = global$1.clearImmediate;
33932
34160
  var process$1 = global$1.process;
33933
34161
  var Dispatch = global$1.Dispatch;
33934
- var Function$1 = global$1.Function;
34162
+ var Function$2 = global$1.Function;
33935
34163
  var MessageChannel$1 = global$1.MessageChannel;
33936
34164
  var String$1 = global$1.String;
33937
34165
  var counter = 0;
@@ -33966,7 +34194,7 @@
33966
34194
  if (!set || !clear) {
33967
34195
  set = function setImmediate(handler) {
33968
34196
  validateArgumentsLength(arguments.length, 1);
33969
- var fn = isCallable(handler) ? handler : Function$1(handler);
34197
+ var fn = isCallable(handler) ? handler : Function$2(handler);
33970
34198
  var args = arraySlice(arguments, 1);
33971
34199
  queue[++counter] = function () {
33972
34200
  functionApply(fn, undefined, args);
@@ -34032,7 +34260,36 @@
34032
34260
  clearImmediate: clearImmediate
34033
34261
  });
34034
34262
 
34035
- var setImmediate = task.set;
34263
+ /* global Bun -- Deno case */
34264
+ var engineIsBun = typeof Bun == 'function' && Bun && typeof Bun.version == 'string';
34265
+
34266
+ var Function$1 = global$1.Function;
34267
+ // dirty IE9- and Bun 0.3.0- checks
34268
+ var WRAP = /MSIE .\./.test(engineUserAgent) || engineIsBun && function () {
34269
+ var version = global$1.Bun.version.split('.');
34270
+ return version.length < 3 || version[0] == 0 && (version[1] < 3 || version[1] == 3 && version[2] == 0);
34271
+ }();
34272
+
34273
+ // IE9- / Bun 0.3.0- setTimeout / setInterval / setImmediate additional parameters fix
34274
+ // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
34275
+ // https://github.com/oven-sh/bun/issues/1633
34276
+ var schedulersFix = function (scheduler, hasTimeArg) {
34277
+ var firstParamIndex = hasTimeArg ? 2 : 1;
34278
+ return WRAP ? function (handler, timeout /* , ...arguments */) {
34279
+ var boundArgs = validateArgumentsLength(arguments.length, 1) > firstParamIndex;
34280
+ var fn = isCallable(handler) ? handler : Function$1(handler);
34281
+ var params = boundArgs ? arraySlice(arguments, firstParamIndex) : [];
34282
+ var callback = boundArgs ? function () {
34283
+ functionApply(fn, this, params);
34284
+ } : fn;
34285
+ return hasTimeArg ? scheduler(callback, timeout) : scheduler(callback);
34286
+ } : scheduler;
34287
+ };
34288
+
34289
+ var setTask = task.set;
34290
+
34291
+ // https://github.com/oven-sh/bun/issues/1633
34292
+ var setImmediate = global$1.setImmediate ? schedulersFix(setTask, false) : setTask;
34036
34293
 
34037
34294
  // `setImmediate` method
34038
34295
  // http://w3c.github.io/setImmediate/#si-setImmediate
@@ -50375,7 +50632,9 @@
50375
50632
  hoverText(clickState) {
50376
50633
  if (true === this.showCoverage && clickState.y >= this.coverageTrack.top && clickState.y < this.coverageTrack.height) {
50377
50634
  const clickedObject = this.coverageTrack.getClickedObject(clickState);
50378
- return clickedObject.hoverText();
50635
+ if (clickedObject) {
50636
+ return clickedObject.hoverText();
50637
+ }
50379
50638
  }
50380
50639
  }
50381
50640
  menuItemList() {
@@ -62872,10 +63131,7 @@
62872
63131
  setOauthToken,
62873
63132
  oauth,
62874
63133
  version: version$1,
62875
- setApiKey,
62876
- doAutoscale,
62877
- TrackView,
62878
- GenomeUtils
63134
+ setApiKey
62879
63135
  };
62880
63136
 
62881
63137
  return index;