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.esm.js CHANGED
@@ -8482,8 +8482,8 @@ function encode(objectName) {
8482
8482
 
8483
8483
  let result = '';
8484
8484
  objectName.split('').forEach(function(letter) {
8485
- if(encodings$1.has(letter)) {
8486
- result += encodings$1.get(letter);
8485
+ if(encodings$2.has(letter)) {
8486
+ result += encodings$2.get(letter);
8487
8487
  } else {
8488
8488
  result += letter;
8489
8489
  }
@@ -8492,27 +8492,27 @@ function encode(objectName) {
8492
8492
  }
8493
8493
 
8494
8494
  // %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
8495
- const encodings$1 = new Map();
8496
- encodings$1.set("!", "%21");
8497
- encodings$1.set("#", "%23");
8498
- encodings$1.set("$", "%24");
8499
- encodings$1.set("%", "%25");
8500
- encodings$1.set("&", "%26");
8501
- encodings$1.set("'", "%27");
8502
- encodings$1.set("(", "%28");
8503
- encodings$1.set(")", "%29");
8504
- encodings$1.set("*", "%2A");
8505
- encodings$1.set("+", "%2B");
8506
- encodings$1.set(",", "%2C");
8507
- encodings$1.set("/", "%2F");
8508
- encodings$1.set(":", "%3A");
8509
- encodings$1.set(";", "%3B");
8510
- encodings$1.set("=", "%3D");
8511
- encodings$1.set("?", "%3F");
8512
- encodings$1.set("@", "%40");
8513
- encodings$1.set("[", "%5B");
8514
- encodings$1.set("]", "%5D");
8515
- encodings$1.set(" ", "%20");
8495
+ const encodings$2 = new Map();
8496
+ encodings$2.set("!", "%21");
8497
+ encodings$2.set("#", "%23");
8498
+ encodings$2.set("$", "%24");
8499
+ encodings$2.set("%", "%25");
8500
+ encodings$2.set("&", "%26");
8501
+ encodings$2.set("'", "%27");
8502
+ encodings$2.set("(", "%28");
8503
+ encodings$2.set(")", "%29");
8504
+ encodings$2.set("*", "%2A");
8505
+ encodings$2.set("+", "%2B");
8506
+ encodings$2.set(",", "%2C");
8507
+ encodings$2.set("/", "%2F");
8508
+ encodings$2.set(":", "%3A");
8509
+ encodings$2.set(";", "%3B");
8510
+ encodings$2.set("=", "%3D");
8511
+ encodings$2.set("?", "%3F");
8512
+ encodings$2.set("@", "%40");
8513
+ encodings$2.set("[", "%5B");
8514
+ encodings$2.set("]", "%5D");
8515
+ encodings$2.set(" ", "%20");
8516
8516
 
8517
8517
  // Convenience functions for the gapi oAuth library.
8518
8518
 
@@ -15473,7 +15473,7 @@ function download(filename, data) {
15473
15473
 
15474
15474
  const { Deflate, deflate, deflateRaw, gzip } = deflate_1$1;
15475
15475
 
15476
- const { Inflate, inflate: inflate$3, inflateRaw, ungzip: ungzip$2 } = inflate_1$1;
15476
+ const { Inflate, inflate: inflate$3, inflateRaw: inflateRaw$2, ungzip: ungzip$2 } = inflate_1$1;
15477
15477
 
15478
15478
 
15479
15479
 
@@ -15483,7 +15483,7 @@ function download(filename, data) {
15483
15483
  var gzip_1 = gzip;
15484
15484
  var Inflate_1 = Inflate;
15485
15485
  var inflate_1 = inflate$3;
15486
- var inflateRaw_1 = inflateRaw;
15486
+ var inflateRaw_1 = inflateRaw$2;
15487
15487
  var ungzip_1 = ungzip$2;
15488
15488
  var constants_1 = constants$2;
15489
15489
 
@@ -15517,7 +15517,7 @@ function download(filename, data) {
15517
15517
 
15518
15518
  pako.deflateRaw;
15519
15519
  pako.deflate;
15520
- pako.inflateRaw;
15520
+ const inflateRaw = pako.inflateRaw;
15521
15521
  const inflate = pako.inflate;
15522
15522
  pako.gzip;
15523
15523
 
@@ -15595,7 +15595,7 @@ function unbgzf(data, lim) {
15595
15595
  }
15596
15596
  }
15597
15597
 
15598
- function bgzBlockSize(data) {
15598
+ function bgzBlockSize$1(data) {
15599
15599
  const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
15600
15600
  const bsize = (ba[17] << 8 | ba[16]) + 1;
15601
15601
  return bsize;
@@ -19700,27 +19700,27 @@ function createMenuElements$1(itemList, popover) {
19700
19700
  }
19701
19701
 
19702
19702
  // %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
19703
- const encodings = new Map();
19704
- encodings.set("!", "%21");
19705
- encodings.set("#", "%23");
19706
- encodings.set("$", "%24");
19707
- encodings.set("%", "%25");
19708
- encodings.set("&", "%26");
19709
- encodings.set("'", "%27");
19710
- encodings.set("(", "%28");
19711
- encodings.set(")", "%29");
19712
- encodings.set("*", "%2A");
19713
- encodings.set("+", "%2B");
19714
- encodings.set(",", "%2C");
19715
- encodings.set("/", "%2F");
19716
- encodings.set(":", "%3A");
19717
- encodings.set(";", "%3B");
19718
- encodings.set("=", "%3D");
19719
- encodings.set("?", "%3F");
19720
- encodings.set("@", "%40");
19721
- encodings.set("[", "%5B");
19722
- encodings.set("]", "%5D");
19723
- encodings.set(" ", "%20");
19703
+ const encodings$1 = new Map();
19704
+ encodings$1.set("!", "%21");
19705
+ encodings$1.set("#", "%23");
19706
+ encodings$1.set("$", "%24");
19707
+ encodings$1.set("%", "%25");
19708
+ encodings$1.set("&", "%26");
19709
+ encodings$1.set("'", "%27");
19710
+ encodings$1.set("(", "%28");
19711
+ encodings$1.set(")", "%29");
19712
+ encodings$1.set("*", "%2A");
19713
+ encodings$1.set("+", "%2B");
19714
+ encodings$1.set(",", "%2C");
19715
+ encodings$1.set("/", "%2F");
19716
+ encodings$1.set(":", "%3A");
19717
+ encodings$1.set(";", "%3B");
19718
+ encodings$1.set("=", "%3D");
19719
+ encodings$1.set("?", "%3F");
19720
+ encodings$1.set("@", "%40");
19721
+ encodings$1.set("[", "%5B");
19722
+ encodings$1.set("]", "%5D");
19723
+ encodings$1.set(" ", "%20");
19724
19724
 
19725
19725
  if (typeof process === 'object' && typeof window === 'undefined') {
19726
19726
  global.atob = function (str) {
@@ -20792,7 +20792,7 @@ function buildOptions(config, options) {
20792
20792
  const doAutoscale = function (features) {
20793
20793
  var min, max;
20794
20794
 
20795
- if (features.length > 0) {
20795
+ if (features && features.length > 0) {
20796
20796
  min = Number.MAX_VALUE;
20797
20797
  max = -Number.MAX_VALUE;
20798
20798
 
@@ -24215,7 +24215,7 @@ const Cytoband = function (start, end, name, typestain) {
24215
24215
  }
24216
24216
  };
24217
24217
 
24218
- const _version = "2.13.6";
24218
+ const _version = "2.13.8";
24219
24219
  function version() {
24220
24220
  return _version
24221
24221
  }
@@ -25949,7 +25949,7 @@ function decode(tokens, header) {
25949
25949
 
25950
25950
  const delim = ('gff3' === format) ? '=' : ' ';
25951
25951
  return new GFFFeature({
25952
- source: tokens[1],
25952
+ source: decodeGFFAttribute(tokens[1]),
25953
25953
  type: tokens[2],
25954
25954
  chr: tokens[0],
25955
25955
  start: parseInt(tokens[3]) - 1,
@@ -26060,7 +26060,7 @@ function parseAttributeString(attributeString, keyValueDelim) {
26060
26060
  const idx = kv.indexOf(keyValueDelim);
26061
26061
  if (idx > 0 && idx < kv.length - 1) {
26062
26062
  const key = kv.substring(0, idx);
26063
- let value = stripQuotes(decodeURIComponent(kv.substring(idx + 1).trim()));
26063
+ let value = stripQuotes(decodeGFFAttribute(kv.substring(idx + 1).trim()));
26064
26064
  attributes.push([key, value]);
26065
26065
  }
26066
26066
  }
@@ -26074,6 +26074,55 @@ function stripQuotes(value) {
26074
26074
  return value
26075
26075
  }
26076
26076
 
26077
+ // GFF3 attributes have specific percent encoding rules, the list below are required, all others are forbidden
26078
+ /*
26079
+ tab (%09)
26080
+ newline (%0A)
26081
+ carriage return (%0D)
26082
+ % percent (%25)
26083
+ control characters (%00 through %1F, %7F)
26084
+ In addition, the following characters have reserved meanings in column 9 and must be escaped when used in other contexts:
26085
+ ; semicolon (%3B)
26086
+ = equals (%3D)
26087
+ & ampersand (%26)
26088
+ , comma (%2C)
26089
+ */
26090
+
26091
+ const encodings = new Map([
26092
+ ["%09", "\t"],
26093
+ ["%0A", "\n"],
26094
+ ["%0D", "\r"],
26095
+ ["%25", "%"],
26096
+ ["%3B", ";"],
26097
+ ["%3D", "="],
26098
+ ["%26", "&"],
26099
+ ["%2C", ","]
26100
+ ]);
26101
+
26102
+ function decodeGFFAttribute(str) {
26103
+
26104
+ if (!str.includes("%")) {
26105
+ return str
26106
+ }
26107
+ let decoded = "";
26108
+ for (let i = 0; i < str.length; i++) {
26109
+
26110
+ if (str.charCodeAt(i) === 37 && i < str.length - 2) {
26111
+ const key = str.substring(i, i + 3);
26112
+ if (encodings.has(key)) {
26113
+ decoded += encodings.get(key);
26114
+ } else {
26115
+ decoded += key;
26116
+ }
26117
+ i += 2;
26118
+ } else {
26119
+ decoded += str.charAt(i);
26120
+ }
26121
+ }
26122
+ return decoded
26123
+
26124
+ }
26125
+
26077
26126
  /**
26078
26127
  * Wrapper class to record a decoding error.
26079
26128
  */
@@ -29184,12 +29233,88 @@ class VPointer {
29184
29233
  (this.block === vp.block && this.offset > vp.offset)
29185
29234
  }
29186
29235
 
29236
+ isEqualTo(vp) {
29237
+ return this.block === vp.block && this.offset === vp.offset
29238
+ }
29239
+
29187
29240
  print() {
29188
29241
  return "" + this.block + ":" + this.offset
29189
29242
  }
29190
29243
  }
29191
29244
 
29192
- // Represents a BAM index.
29245
+ function optimizeChunks(chunks, lowest) {
29246
+
29247
+ if (chunks.length === 0) return chunks
29248
+
29249
+ chunks.sort(function (c0, c1) {
29250
+
29251
+ const dif = c0.minv.block - c1.minv.block;
29252
+ if (dif !== 0) {
29253
+ return dif
29254
+ } else {
29255
+ return c0.minv.offset - c1.minv.offset
29256
+ }
29257
+ });
29258
+
29259
+ if(chunks.length <= 1) {
29260
+ return chunks
29261
+ }
29262
+
29263
+ // console.log("Before trimming " + chunks.length)
29264
+ // for (let c of chunks) {
29265
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
29266
+ // }
29267
+
29268
+ if (lowest) {
29269
+ chunks = chunks.filter(c => c.maxv.isGreaterThan(lowest));
29270
+ }
29271
+
29272
+ // console.log("Before merging " + chunks.length)
29273
+ // for (let c of chunks) {
29274
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
29275
+ // }
29276
+
29277
+ const mergedChunks = [];
29278
+ let lastChunk;
29279
+ for (let chunk of chunks) {
29280
+
29281
+ if (!lastChunk) {
29282
+ mergedChunks.push(chunk);
29283
+ lastChunk = chunk;
29284
+ } else {
29285
+ if (canMerge(lastChunk, chunk)) {
29286
+ if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
29287
+ lastChunk.maxv = chunk.maxv;
29288
+ }
29289
+ } else {
29290
+ mergedChunks.push(chunk);
29291
+ lastChunk = chunk;
29292
+ }
29293
+ }
29294
+ }
29295
+
29296
+ // console.log("After merging " + mergedChunks.length)
29297
+ // for (let c of mergedChunks) {
29298
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
29299
+ // }
29300
+
29301
+ return mergedChunks
29302
+ }
29303
+
29304
+
29305
+ /**
29306
+ * Merge 2 blocks if the file position gap between them is < 16 kb, and the total size is < ~5 mb
29307
+ * @param chunk1
29308
+ * @param chunk2
29309
+ * @returns {boolean|boolean}
29310
+ */
29311
+ function canMerge(chunk1, chunk2) {
29312
+ const gap = chunk2.minv.block - chunk1.maxv.block;
29313
+ const sizeEstimate = chunk1.maxv.block - chunk1.minv.block;
29314
+ return gap < 65000 && sizeEstimate < 5000000
29315
+ }
29316
+
29317
+ // Represents a CSI Bam or Tabix index
29193
29318
 
29194
29319
  const CSI1_MAGIC$1 = 21582659; // CSI\1
29195
29320
  const CSI2_MAGIC$1 = 38359875; // CSI\2
@@ -29316,7 +29441,7 @@ class CSIIndex {
29316
29441
  * @param max genomic end position
29317
29442
  * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}}
29318
29443
  */
29319
- blocksForRange(refId, min, max) {
29444
+ chunksForRange(refId, min, max) {
29320
29445
 
29321
29446
  const ba = this.indices[refId];
29322
29447
  if (!ba) {
@@ -29342,7 +29467,7 @@ class CSIIndex {
29342
29467
 
29343
29468
  const lowestOffset = ba.loffset[overlappingBins[0]];
29344
29469
 
29345
- return optimizeChunks$1(chunks, lowestOffset)
29470
+ return optimizeChunks(chunks, lowestOffset)
29346
29471
  }
29347
29472
 
29348
29473
  }
@@ -29379,58 +29504,11 @@ class CSIIndex {
29379
29504
 
29380
29505
  }
29381
29506
 
29382
- function optimizeChunks$1(chunks, lowest) {
29383
-
29384
- const mergedChunks = [];
29385
- let lastChunk = null;
29386
-
29387
- if (chunks.length === 0) return chunks
29388
-
29389
- chunks.sort(function (c0, c1) {
29390
- const dif = c0.minv.block - c1.minv.block;
29391
- if (dif !== 0) {
29392
- return dif
29393
- } else {
29394
- return c0.minv.offset - c1.minv.offset
29395
- }
29396
- });
29397
-
29398
- chunks.forEach(function (chunk) {
29399
-
29400
- if (!lowest || chunk.maxv.isGreaterThan(lowest)) {
29401
- if (lastChunk === null) {
29402
- mergedChunks.push(chunk);
29403
- lastChunk = chunk;
29404
- } else {
29405
- if (canMerge$1(lastChunk, chunk)) {
29406
- if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
29407
- lastChunk.maxv = chunk.maxv;
29408
- }
29409
- } else {
29410
- mergedChunks.push(chunk);
29411
- lastChunk = chunk;
29412
- }
29413
- }
29414
- }
29415
- });
29416
-
29417
- return mergedChunks
29418
- }
29419
-
29420
- function canMerge$1(chunk1, chunk2) {
29421
- return (chunk2.minv.block - chunk1.maxv.block) < 65000 &&
29422
- (chunk2.maxv.block - chunk1.minv.block) < 5000000
29423
- // lastChunk.minv.block === lastChunk.maxv.block &&
29424
- // lastChunk.maxv.block === chunk.minv.block &&
29425
- // chunk.minv.block === chunk.maxv.block
29426
-
29427
- }
29428
-
29429
- // Represents a BAM index.
29507
+ // Represents a BAM or Tabix index.
29430
29508
 
29431
29509
  const BAI_MAGIC$1 = 21578050;
29432
29510
  const TABIX_MAGIC$1 = 21578324;
29433
- const MB = 1000000;
29511
+
29434
29512
 
29435
29513
  async function parseBamIndex(arrayBuffer, genome) {
29436
29514
  const index = new BamIndex();
@@ -29552,14 +29630,14 @@ class BamIndex {
29552
29630
  }
29553
29631
 
29554
29632
  /**
29555
- * Fetch blocks for a particular genomic range. This method is public so it can be unit-tested.
29633
+ * Fetch chunks for a particular genomic range. This method is public so it can be unit-tested.
29556
29634
  *
29557
29635
  * @param refId the sequence dictionary index of the chromosome
29558
29636
  * @param min genomic start position
29559
29637
  * @param max genomic end position
29560
- * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}}
29638
+ * @param return an array of objects representing chunks (file spans) {minv: {block, offset}, {maxv: {block, offset}}
29561
29639
  */
29562
- blocksForRange(refId, min, max) {
29640
+ chunksForRange(refId, min, max) {
29563
29641
 
29564
29642
  const bam = this;
29565
29643
  const ba = bam.indices[refId];
@@ -29568,8 +29646,13 @@ class BamIndex {
29568
29646
  return []
29569
29647
  } else {
29570
29648
  const overlappingBins = reg2bins(min, max); // List of bin #s that overlap min, max
29571
- const chunks = [];
29572
29649
 
29650
+ //console.log("bin ranges")
29651
+ //for(let b of overlappingBins) {
29652
+ // console.log(`${b[0]} - ${b[1]}`)
29653
+ //}
29654
+
29655
+ const chunks = [];
29573
29656
  // Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
29574
29657
  for (let binRange of overlappingBins) {
29575
29658
  for (let bin = binRange[0]; bin <= binRange[1]; bin++) {
@@ -29578,7 +29661,7 @@ class BamIndex {
29578
29661
  for (let c of binChunks) {
29579
29662
  const cs = c[0];
29580
29663
  const ce = c[1];
29581
- chunks.push({minv: cs, maxv: ce, bin: bin});
29664
+ chunks.push({minv: cs, maxv: ce});
29582
29665
  }
29583
29666
  }
29584
29667
  }
@@ -29586,16 +29669,15 @@ class BamIndex {
29586
29669
 
29587
29670
  // Use the linear index to find minimum file position of chunks that could contain alignments in the region
29588
29671
  const nintv = ba.linearIndex.length;
29589
- let lowest = null;
29590
- const minLin = Math.min(min >> 14, nintv - 1);
29672
+
29673
+ let lowest;
29674
+ const minLin = Math.min(min >> 14, nintv - 1); // i.e. min / 16384
29591
29675
  const maxLin = Math.min(max >> 14, nintv - 1);
29592
- for (let i = minLin; i < maxLin; i++) {
29676
+ for (let i = minLin; i <= maxLin; i++) {
29593
29677
  const vp = ba.linearIndex[i];
29594
29678
  if (vp) {
29595
- // 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
29596
- if (!lowest || vp.isLessThan(lowest)) {
29597
- lowest = vp;
29598
- }
29679
+ lowest = vp; // lowest file offset that contains alignments overlapping (min, max)
29680
+ break
29599
29681
  }
29600
29682
  }
29601
29683
 
@@ -29604,56 +29686,7 @@ class BamIndex {
29604
29686
  }
29605
29687
  }
29606
29688
 
29607
- function optimizeChunks(chunks, lowest) {
29608
-
29609
- const mergedChunks = [];
29610
- let lastChunk = null;
29611
-
29612
- if (chunks.length === 0) return chunks
29613
-
29614
- chunks.sort(function (c0, c1) {
29615
- const dif = c0.minv.block - c1.minv.block;
29616
- if (dif !== 0) {
29617
- return dif
29618
- } else {
29619
- return c0.minv.offset - c1.minv.offset
29620
- }
29621
- });
29622
-
29623
- chunks.forEach(function (chunk) {
29624
-
29625
- if (!lowest || chunk.maxv.isGreaterThan(lowest)) {
29626
- if (lastChunk === null) {
29627
- mergedChunks.push(chunk);
29628
- lastChunk = chunk;
29629
- } else {
29630
- if (canMerge(lastChunk, chunk)) {
29631
- if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
29632
- lastChunk.maxv = chunk.maxv;
29633
- }
29634
- } else {
29635
- mergedChunks.push(chunk);
29636
- lastChunk = chunk;
29637
- }
29638
- }
29639
- }
29640
- });
29641
-
29642
- return mergedChunks
29643
- }
29644
-
29645
29689
 
29646
- /**
29647
- * Merge 2 blocks if the gap between them is < 1kb and the total resulting size < 100mb
29648
- * @param chunk1
29649
- * @param chunk2
29650
- * @returns {boolean|boolean}
29651
- */
29652
- function canMerge(chunk1, chunk2) {
29653
- const gap = chunk2.minv.block - chunk1.maxv.block;
29654
- const total = chunk2.maxv.block - chunk1.minv.block;
29655
- return gap < 30000 && total < 10 * MB
29656
- }
29657
29690
 
29658
29691
  /**
29659
29692
  * Calculate the list of bins that overlap with region [beg, end]
@@ -29791,7 +29824,7 @@ class TribbleIndex {
29791
29824
  * @param min genomic start position
29792
29825
  * @param max genomic end position
29793
29826
  */
29794
- blocksForRange(queryChr, min, max) { //function (refId, min, max) {
29827
+ chunksForRange(queryChr, min, max) { //function (refId, min, max) {
29795
29828
  const chrIdx = this.chrIndex[queryChr];
29796
29829
 
29797
29830
  if (chrIdx) {
@@ -29958,7 +29991,12 @@ class ByteArrayDataWrapper {
29958
29991
 
29959
29992
  }
29960
29993
 
29961
- class BGZipLineReader {
29994
+ /**
29995
+ * Class to iterate line-by-line over a BGZipped text file. This class is useful for iterating from the start of
29996
+ * the file. Not useful for indexed queries.
29997
+ */
29998
+
29999
+ class BGZLineReader {
29962
30000
 
29963
30001
  constructor(config) {
29964
30002
  this.config = config;
@@ -30004,7 +30042,7 @@ class BGZipLineReader {
30004
30042
  }
30005
30043
  });
30006
30044
  const abuffer = await igvxhr.loadArrayBuffer(this.config.url, bsizeOptions);
30007
- const bufferSize = bgzBlockSize(abuffer);
30045
+ const bufferSize = bgzBlockSize$1(abuffer);
30008
30046
  //console.log(`next block ${this.filePtr} ${bufferSize}`);
30009
30047
 
30010
30048
  if (bufferSize === 0) {
@@ -30025,6 +30063,250 @@ class BGZipLineReader {
30025
30063
 
30026
30064
  }
30027
30065
 
30066
+ function concatenateArrayBuffers(arrayBuffers) {
30067
+
30068
+ if (arrayBuffers.length === 1) {
30069
+ return arrayBuffers[0]
30070
+ }
30071
+
30072
+ let len = 0;
30073
+ for (const b of arrayBuffers) {
30074
+ len += b.byteLength;
30075
+ }
30076
+ const c = new Uint8Array(len);
30077
+ let offset = 0;
30078
+ for (const b of arrayBuffers) {
30079
+ c.set(new Uint8Array(b), offset);
30080
+ offset += b.byteLength;
30081
+ }
30082
+ return c.buffer
30083
+ }
30084
+
30085
+ /**
30086
+ * Return the block size for the data buffer.
30087
+ * @param data
30088
+ * @returns {number}
30089
+ */
30090
+ const bgzBlockSize = (data) => {
30091
+ const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
30092
+ const bsize = (ba[17] << 8 | ba[16]) + 1;
30093
+ return bsize
30094
+ };
30095
+
30096
+ class BGZBlockLoader {
30097
+
30098
+ constructor(config) {
30099
+ this.config = config;
30100
+ this.cacheBlocks = false != config.cacheBlocks; // Default to true
30101
+ this.cache = undefined;
30102
+ }
30103
+
30104
+ /**
30105
+ * Return inflated data from startBlock through endBlock as an UInt8Array
30106
+ *
30107
+ * @param startBlock
30108
+ * @param endBlock
30109
+ * @returns {Promise<Uint8Array>}
30110
+ */
30111
+ async getData(startBlock, endBlock) {
30112
+
30113
+ const blocks = await this.getInflatedBlocks(startBlock, endBlock);
30114
+ if (blocks.length === 1) {
30115
+ return blocks[0]
30116
+ }
30117
+
30118
+ let len = 0;
30119
+ for (const b of blocks) {
30120
+ len += b.byteLength;
30121
+ }
30122
+ const c = new Uint8Array(len);
30123
+ let offset = 0;
30124
+ for (const b of blocks) {
30125
+ c.set(b, offset);
30126
+ offset += b.byteLength;
30127
+ }
30128
+ return c
30129
+ }
30130
+
30131
+ /**
30132
+ * Return the inflated data for the specified blocks as an array of Uint8Arrays. This method is public so
30133
+ * it can be unit tested. *
30134
+ * @param startBlock
30135
+ * @param endBlock
30136
+ * @returns {Promise<*[Uint8Array]>}
30137
+ */
30138
+ async getInflatedBlocks(startBlock, endBlock) {
30139
+
30140
+ if (!this.cacheBlocks) {
30141
+ const buffer = await this.loadBLockData(startBlock, endBlock);
30142
+ return inflateBlocks(buffer)
30143
+ } else {
30144
+
30145
+ const c = this.cache;
30146
+ if (c && (c.startBlock <= startBlock && c.endBlock >= endBlock)) {
30147
+ //console.log("Complete overlap")
30148
+ const startOffset = startBlock - c.startBlock;
30149
+ const endOffset = endBlock - c.startBlock;
30150
+ return inflateBlocks(c.buffer, startOffset, endOffset)
30151
+ // Don't update cache, still valid
30152
+ } else {
30153
+
30154
+ let buffer;
30155
+ if (!c || (c.startBlock > endBlock || c.endBlock < startBlock)) {
30156
+ // no overlap with cache
30157
+ buffer = await this.loadBLockData(startBlock, endBlock);
30158
+ } else {
30159
+
30160
+ //console.log("Some overlap")
30161
+ const arrayBuffers = [];
30162
+
30163
+ // Load blocks preceding cache start, if any
30164
+ if (startBlock < c.startBlock) {
30165
+ // load first blocks
30166
+ const startBuffer = await this.loadBLockData(startBlock, c.startBlock, {skipEnd: true});
30167
+ arrayBuffers.push(startBuffer);
30168
+ }
30169
+
30170
+ // Slice cached buffer as needed
30171
+ let cachedBuffer;
30172
+ if (startBlock <= c.startBlock && endBlock >= c.endBlock) {
30173
+ cachedBuffer = c.buffer;
30174
+ } else {
30175
+ const start = Math.max(0, startBlock - c.startBlock);
30176
+ let end;
30177
+ if (endBlock >= c.endBlock) {
30178
+ end = c.buffer.byteLength;
30179
+ } else {
30180
+ // We need to find the byte position of the end of "endBlock"
30181
+ const boundaries = findBlockBoundaries(c.buffer);
30182
+ for (let i = 0; i < boundaries.length - 1; i++) {
30183
+ if (c.startBlock + boundaries[i] === endBlock) {
30184
+ end = boundaries[i + 1];
30185
+ break
30186
+ }
30187
+ }
30188
+ // Do something if end not found
30189
+ }
30190
+ cachedBuffer = c.buffer.slice(start, end);
30191
+ }
30192
+ arrayBuffers.push(cachedBuffer);
30193
+
30194
+ // Load end blocks, if any
30195
+ if (endBlock > c.endBlock) {
30196
+ const endBuffer = await this.loadBLockData(c.endBlock, endBlock, {skipStart: true});
30197
+ arrayBuffers.push(endBuffer);
30198
+ }
30199
+
30200
+ buffer = concatenateArrayBuffers(arrayBuffers);
30201
+ }
30202
+
30203
+ this.cache = {startBlock, endBlock, buffer};
30204
+ return inflateBlocks(buffer)
30205
+ }
30206
+ }
30207
+ }
30208
+
30209
+ async loadBLockData(startBlock, endBlock, options) {
30210
+
30211
+ const config = this.config;
30212
+ const skipStart = options && options.skipStart;
30213
+ const skipEnd = options && options.skipEnd;
30214
+
30215
+ // Get size of last block if not skipped
30216
+ let lastBlockSize = 0;
30217
+ if (!skipEnd) {
30218
+ const bsizeOptions = buildOptions(config, {range: {start: endBlock, size: 26}});
30219
+ const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
30220
+ lastBlockSize = bgzBlockSize(abuffer);
30221
+ }
30222
+
30223
+ if (skipStart) {
30224
+ const bsizeOptions = buildOptions(config, {range: {start: startBlock, size: 26}});
30225
+ const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
30226
+ startBlock += bgzBlockSize(abuffer);
30227
+ }
30228
+
30229
+ // Load data for all blocks
30230
+ const loadOptions = buildOptions(config, {
30231
+ range: {
30232
+ start: startBlock,
30233
+ size: endBlock + lastBlockSize - startBlock
30234
+ }
30235
+ });
30236
+
30237
+ //console.log(`${this.config.name} Loaded ${startBlock} - ${endBlock + lastBlockSize} (${(endBlock + lastBlockSize - startBlock) / 1000} kb)`)
30238
+
30239
+ return igvxhr.loadArrayBuffer(config.url, loadOptions)
30240
+ }
30241
+ }
30242
+
30243
+ function findBlockBoundaries(arrayBuffer) {
30244
+
30245
+ const byteLengh = arrayBuffer.byteLength;
30246
+ let offset = 0;
30247
+ const blockBoundaries = [0];
30248
+ while (offset < byteLengh) {
30249
+ //console.log("Cache block " + offset)
30250
+ const ba = new Uint8Array(arrayBuffer, offset);
30251
+ const bsize = (ba[17] << 8 | ba[16]) + 1;
30252
+ offset += bsize;
30253
+ if (offset < byteLengh) {
30254
+ blockBoundaries.push(offset);
30255
+ }
30256
+ }
30257
+ return blockBoundaries
30258
+ }
30259
+
30260
+
30261
+ /**
30262
+ * Inflate compressed blocks within the data buffer*
30263
+ * @param data
30264
+ * @param startBlock - optional file location for start block. Default == 0
30265
+ * @param endBlock - optional file location for last block to decompress.
30266
+ * @returns {*[]}
30267
+ */
30268
+ function inflateBlocks(data, startBlock, endBlock) {
30269
+
30270
+ startBlock = startBlock || 0;
30271
+
30272
+ const oBlockList = [];
30273
+ let ptr = startBlock;
30274
+
30275
+ const lim = data.byteLength - 18;
30276
+ while (ptr < lim) {
30277
+ try {
30278
+ //console.log(113873 + ptr)
30279
+ const header = new Uint8Array(data, ptr, 18);
30280
+ const xlen = (header[11] << 8) | (header[10]);
30281
+ const bsize = ((header[17] << 8) | (header[16])); // Total block size, including header, minus 1
30282
+ const start = 12 + xlen + ptr; // Start of CDATA
30283
+ const bytesLeft = data.byteLength - start;
30284
+ const cDataSize = bsize - xlen - 18;
30285
+
30286
+ if (bytesLeft < cDataSize || cDataSize <= 0) {
30287
+ // This is unexpected. Throw error?
30288
+ break
30289
+ }
30290
+
30291
+ const cdata = new Uint8Array(data, start, cDataSize);
30292
+ const unc = inflateRaw(cdata);
30293
+ oBlockList.push(unc);
30294
+
30295
+ if (endBlock === ptr) {
30296
+ break
30297
+ } else {
30298
+ // Advance to next block
30299
+ ptr += bsize + 1;
30300
+ }
30301
+
30302
+ } catch (e) {
30303
+ console.error(e);
30304
+ break
30305
+ }
30306
+ }
30307
+ return oBlockList
30308
+ }
30309
+
30028
30310
  /*
30029
30311
  * The MIT License (MIT)
30030
30312
  *
@@ -30082,6 +30364,7 @@ class FeatureFileReader {
30082
30364
  if (this.config.format === "vcf" && !this.config.indexURL) {
30083
30365
  console.warn("Warning: index file not specified. The entire vcf file will be loaded.");
30084
30366
  }
30367
+
30085
30368
  }
30086
30369
 
30087
30370
  async defaultVisibilityWindow() {
@@ -30139,7 +30422,8 @@ class FeatureFileReader {
30139
30422
 
30140
30423
  let dataWrapper;
30141
30424
  if (index.tabix) {
30142
- dataWrapper = new BGZipLineReader(this.config);
30425
+ this._blockLoader = new BGZBlockLoader(this.config);
30426
+ dataWrapper = new BGZLineReader(this.config);
30143
30427
  } else {
30144
30428
  // Tribble
30145
30429
  const maxSize = Object.values(index.chrIndex)
@@ -30225,51 +30509,27 @@ class FeatureFileReader {
30225
30509
  }
30226
30510
 
30227
30511
  const genome = this.genome;
30228
- const blocks = this.index.blocksForRange(refId, start, end);
30229
- if (!blocks || blocks.length === 0) {
30512
+ const chunks = this.index.chunksForRange(refId, start, end);
30513
+ if (!chunks || chunks.length === 0) {
30230
30514
  return []
30231
30515
  } else {
30232
30516
  const allFeatures = [];
30233
- for (let block of blocks) {
30234
-
30235
- const startPos = block.minv.block;
30236
- const startOffset = block.minv.offset;
30237
- const endOffset = block.maxv.offset;
30238
- let endPos;
30239
-
30240
- if (tabix) {
30241
- let lastBlockSize = 0;
30242
- if (endOffset > 0) {
30243
- const bsizeOptions = buildOptions(config, {
30244
- range: {
30245
- start: block.maxv.block,
30246
- size: 26
30247
- }
30248
- });
30249
- const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
30250
- lastBlockSize = bgzBlockSize(abuffer);
30251
- }
30252
- endPos = block.maxv.block + lastBlockSize;
30253
- } else {
30254
- endPos = block.maxv.block;
30255
- }
30256
-
30257
- const options = buildOptions(config, {
30258
- range: {
30259
- start: startPos,
30260
- size: endPos - startPos + 1
30261
- }
30262
- });
30517
+ for (let chunk of chunks) {
30263
30518
 
30264
30519
  let inflated;
30265
30520
  if (tabix) {
30266
- const data = await igvxhr.loadArrayBuffer(config.url, options);
30267
- inflated = unbgzf(data);
30521
+ inflated = await this._blockLoader.getData(chunk.minv.block, chunk.maxv.block);
30268
30522
  } else {
30523
+ const options = buildOptions(config, {
30524
+ range: {
30525
+ start: chunk.minv.block,
30526
+ size: chunk.maxv.block - chunk.minv.block + 1
30527
+ }
30528
+ });
30269
30529
  inflated = await igvxhr.loadString(config.url, options);
30270
30530
  }
30271
30531
 
30272
- const slicedData = startOffset ? inflated.slice(startOffset) : inflated;
30532
+ const slicedData = chunk.minv.offset ? inflated.slice(chunk.minv.offset) : inflated;
30273
30533
  const dataWrapper = getDataWrapper(slicedData);
30274
30534
  let slicedFeatures = await parser.parseFeatures(dataWrapper);
30275
30535
 
@@ -36371,6 +36631,8 @@ class BamReader {
36371
36631
  this.bamPath = config.url;
36372
36632
  this.baiPath = config.indexURL;
36373
36633
  BamUtils.setReaderDefaults(this, config);
36634
+
36635
+ this._blockLoader = new BGZBlockLoader(config);
36374
36636
  }
36375
36637
 
36376
36638
  async readAlignments(chr, bpStart, bpEnd) {
@@ -36386,32 +36648,16 @@ class BamReader {
36386
36648
  } else {
36387
36649
 
36388
36650
  const bamIndex = await this.getIndex();
36389
- const chunks = bamIndex.blocksForRange(chrId, bpStart, bpEnd);
36651
+ const chunks = bamIndex.chunksForRange(chrId, bpStart, bpEnd);
36390
36652
 
36391
36653
  if (!chunks || chunks.length === 0) {
36392
36654
  return alignmentContainer
36393
36655
  }
36394
- for (let c of chunks) {
36395
-
36396
- let lastBlockSize;
36397
- if (c.maxv.offset === 0) {
36398
- lastBlockSize = 0; // Don't need to read the last block.
36399
- } else {
36400
- const bsizeOptions = buildOptions(this.config, {range: {start: c.maxv.block, size: 26}});
36401
- const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions);
36402
- lastBlockSize = bgzBlockSize(abuffer);
36403
- }
36404
- const fetchMin = c.minv.block;
36405
- const fetchMax = c.maxv.block + lastBlockSize;
36406
- const range = {start: fetchMin, size: fetchMax - fetchMin + 1};
36407
-
36408
- const compressed = await igvxhr.loadArrayBuffer(this.bamPath, buildOptions(this.config, {range: range}));
36409
36656
 
36410
- var ba = unbgzf(compressed); //new Uint8Array(BGZip.unbgzf(compressed)); //, c.maxv.block - c.minv.block + 1));
36657
+ for (let c of chunks) {
36658
+ const ba = await this._blockLoader.getData(c.minv.block, c.maxv.block);
36411
36659
  const done = BamUtils.decodeBamRecords(ba, c.minv.offset, alignmentContainer, this.indexToChr, chrId, bpStart, bpEnd, this.filter);
36412
-
36413
36660
  if (done) {
36414
- // console.log(`Loaded ${counter} chunks out of ${chunks.length}`);
36415
36661
  break
36416
36662
  }
36417
36663
  }
@@ -36428,7 +36674,7 @@ class BamReader {
36428
36674
  if (index.firstBlockPosition) {
36429
36675
  const bsizeOptions = buildOptions(this.config, {range: {start: index.firstBlockPosition, size: 26}});
36430
36676
  const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions);
36431
- const bsize = bgzBlockSize(abuffer);
36677
+ const bsize = bgzBlockSize$1(abuffer);
36432
36678
  len = index.firstBlockPosition + bsize; // Insure we get the complete compressed block containing the header
36433
36679
  } else {
36434
36680
  len = 64000;
@@ -40103,7 +40349,9 @@ class BAMTrack extends TrackBase {
40103
40349
  hoverText(clickState) {
40104
40350
  if (true === this.showCoverage && clickState.y >= this.coverageTrack.top && clickState.y < this.coverageTrack.height) {
40105
40351
  const clickedObject = this.coverageTrack.getClickedObject(clickState);
40106
- return clickedObject.hoverText()
40352
+ if(clickedObject) {
40353
+ return clickedObject.hoverText()
40354
+ }
40107
40355
  }
40108
40356
 
40109
40357
  }
@@ -54209,10 +54457,7 @@ var index = {
54209
54457
  setOauthToken,
54210
54458
  oauth,
54211
54459
  version,
54212
- setApiKey,
54213
- doAutoscale,
54214
- TrackView,
54215
- GenomeUtils
54460
+ setApiKey
54216
54461
  };
54217
54462
 
54218
54463
  export { index as default };