igv 2.13.7 → 2.13.9

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
@@ -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;
@@ -18596,11 +18596,7 @@ RandomColorGenerator.prototype.get = function (saturation, value) {
18596
18596
 
18597
18597
  };
18598
18598
 
18599
- const randomColorGenerator = new RandomColorGenerator();
18600
-
18601
- function randomColor() {
18602
- return randomColorGenerator.get()
18603
- }
18599
+ new RandomColorGenerator();
18604
18600
 
18605
18601
  function randomRGB$1(min, max) {
18606
18602
 
@@ -20792,7 +20788,7 @@ function buildOptions(config, options) {
20792
20788
  const doAutoscale = function (features) {
20793
20789
  var min, max;
20794
20790
 
20795
- if (features.length > 0) {
20791
+ if (features && features.length > 0) {
20796
20792
  min = Number.MAX_VALUE;
20797
20793
  max = -Number.MAX_VALUE;
20798
20794
 
@@ -24215,7 +24211,7 @@ const Cytoband = function (start, end, name, typestain) {
24215
24211
  }
24216
24212
  };
24217
24213
 
24218
- const _version = "2.13.7";
24214
+ const _version = "2.13.9";
24219
24215
  function version() {
24220
24216
  return _version
24221
24217
  }
@@ -29233,12 +29229,88 @@ class VPointer {
29233
29229
  (this.block === vp.block && this.offset > vp.offset)
29234
29230
  }
29235
29231
 
29232
+ isEqualTo(vp) {
29233
+ return this.block === vp.block && this.offset === vp.offset
29234
+ }
29235
+
29236
29236
  print() {
29237
29237
  return "" + this.block + ":" + this.offset
29238
29238
  }
29239
29239
  }
29240
29240
 
29241
- // Represents a BAM index.
29241
+ function optimizeChunks(chunks, lowest) {
29242
+
29243
+ if (chunks.length === 0) return chunks
29244
+
29245
+ chunks.sort(function (c0, c1) {
29246
+
29247
+ const dif = c0.minv.block - c1.minv.block;
29248
+ if (dif !== 0) {
29249
+ return dif
29250
+ } else {
29251
+ return c0.minv.offset - c1.minv.offset
29252
+ }
29253
+ });
29254
+
29255
+ if(chunks.length <= 1) {
29256
+ return chunks
29257
+ }
29258
+
29259
+ // console.log("Before trimming " + chunks.length)
29260
+ // for (let c of chunks) {
29261
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
29262
+ // }
29263
+
29264
+ if (lowest) {
29265
+ chunks = chunks.filter(c => c.maxv.isGreaterThan(lowest));
29266
+ }
29267
+
29268
+ // console.log("Before merging " + chunks.length)
29269
+ // for (let c of chunks) {
29270
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
29271
+ // }
29272
+
29273
+ const mergedChunks = [];
29274
+ let lastChunk;
29275
+ for (let chunk of chunks) {
29276
+
29277
+ if (!lastChunk) {
29278
+ mergedChunks.push(chunk);
29279
+ lastChunk = chunk;
29280
+ } else {
29281
+ if (canMerge(lastChunk, chunk)) {
29282
+ if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
29283
+ lastChunk.maxv = chunk.maxv;
29284
+ }
29285
+ } else {
29286
+ mergedChunks.push(chunk);
29287
+ lastChunk = chunk;
29288
+ }
29289
+ }
29290
+ }
29291
+
29292
+ // console.log("After merging " + mergedChunks.length)
29293
+ // for (let c of mergedChunks) {
29294
+ // console.log(`${c.minv.block} ${c.minv.offset} - ${c.maxv.block} ${c.maxv.offset}`)
29295
+ // }
29296
+
29297
+ return mergedChunks
29298
+ }
29299
+
29300
+
29301
+ /**
29302
+ * Merge 2 blocks if the file position gap between them is < 16 kb, and the total size is < ~5 mb
29303
+ * @param chunk1
29304
+ * @param chunk2
29305
+ * @returns {boolean|boolean}
29306
+ */
29307
+ function canMerge(chunk1, chunk2) {
29308
+ const gap = chunk2.minv.block - chunk1.maxv.block;
29309
+ const sizeEstimate = chunk1.maxv.block - chunk1.minv.block;
29310
+ return gap < 65000 && sizeEstimate < 5000000
29311
+ }
29312
+
29313
+ // Represents a CSI Bam or Tabix index
29242
29314
 
29243
29315
  const CSI1_MAGIC$1 = 21582659; // CSI\1
29244
29316
  const CSI2_MAGIC$1 = 38359875; // CSI\2
@@ -29365,7 +29437,7 @@ class CSIIndex {
29365
29437
  * @param max genomic end position
29366
29438
  * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}}
29367
29439
  */
29368
- blocksForRange(refId, min, max) {
29440
+ chunksForRange(refId, min, max) {
29369
29441
 
29370
29442
  const ba = this.indices[refId];
29371
29443
  if (!ba) {
@@ -29391,7 +29463,7 @@ class CSIIndex {
29391
29463
 
29392
29464
  const lowestOffset = ba.loffset[overlappingBins[0]];
29393
29465
 
29394
- return optimizeChunks$1(chunks, lowestOffset)
29466
+ return optimizeChunks(chunks, lowestOffset)
29395
29467
  }
29396
29468
 
29397
29469
  }
@@ -29428,58 +29500,11 @@ class CSIIndex {
29428
29500
 
29429
29501
  }
29430
29502
 
29431
- function optimizeChunks$1(chunks, lowest) {
29432
-
29433
- const mergedChunks = [];
29434
- let lastChunk = null;
29435
-
29436
- if (chunks.length === 0) return chunks
29437
-
29438
- chunks.sort(function (c0, c1) {
29439
- const dif = c0.minv.block - c1.minv.block;
29440
- if (dif !== 0) {
29441
- return dif
29442
- } else {
29443
- return c0.minv.offset - c1.minv.offset
29444
- }
29445
- });
29446
-
29447
- chunks.forEach(function (chunk) {
29448
-
29449
- if (!lowest || chunk.maxv.isGreaterThan(lowest)) {
29450
- if (lastChunk === null) {
29451
- mergedChunks.push(chunk);
29452
- lastChunk = chunk;
29453
- } else {
29454
- if (canMerge$1(lastChunk, chunk)) {
29455
- if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
29456
- lastChunk.maxv = chunk.maxv;
29457
- }
29458
- } else {
29459
- mergedChunks.push(chunk);
29460
- lastChunk = chunk;
29461
- }
29462
- }
29463
- }
29464
- });
29465
-
29466
- return mergedChunks
29467
- }
29468
-
29469
- function canMerge$1(chunk1, chunk2) {
29470
- return (chunk2.minv.block - chunk1.maxv.block) < 65000 &&
29471
- (chunk2.maxv.block - chunk1.minv.block) < 5000000
29472
- // lastChunk.minv.block === lastChunk.maxv.block &&
29473
- // lastChunk.maxv.block === chunk.minv.block &&
29474
- // chunk.minv.block === chunk.maxv.block
29475
-
29476
- }
29477
-
29478
- // Represents a BAM index.
29503
+ // Represents a BAM or Tabix index.
29479
29504
 
29480
29505
  const BAI_MAGIC$1 = 21578050;
29481
29506
  const TABIX_MAGIC$1 = 21578324;
29482
- const MB = 1000000;
29507
+
29483
29508
 
29484
29509
  async function parseBamIndex(arrayBuffer, genome) {
29485
29510
  const index = new BamIndex();
@@ -29601,14 +29626,14 @@ class BamIndex {
29601
29626
  }
29602
29627
 
29603
29628
  /**
29604
- * Fetch blocks for a particular genomic range. This method is public so it can be unit-tested.
29629
+ * Fetch chunks for a particular genomic range. This method is public so it can be unit-tested.
29605
29630
  *
29606
29631
  * @param refId the sequence dictionary index of the chromosome
29607
29632
  * @param min genomic start position
29608
29633
  * @param max genomic end position
29609
- * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}}
29634
+ * @param return an array of objects representing chunks (file spans) {minv: {block, offset}, {maxv: {block, offset}}
29610
29635
  */
29611
- blocksForRange(refId, min, max) {
29636
+ chunksForRange(refId, min, max) {
29612
29637
 
29613
29638
  const bam = this;
29614
29639
  const ba = bam.indices[refId];
@@ -29617,8 +29642,13 @@ class BamIndex {
29617
29642
  return []
29618
29643
  } else {
29619
29644
  const overlappingBins = reg2bins(min, max); // List of bin #s that overlap min, max
29620
- const chunks = [];
29621
29645
 
29646
+ //console.log("bin ranges")
29647
+ //for(let b of overlappingBins) {
29648
+ // console.log(`${b[0]} - ${b[1]}`)
29649
+ //}
29650
+
29651
+ const chunks = [];
29622
29652
  // Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
29623
29653
  for (let binRange of overlappingBins) {
29624
29654
  for (let bin = binRange[0]; bin <= binRange[1]; bin++) {
@@ -29627,7 +29657,7 @@ class BamIndex {
29627
29657
  for (let c of binChunks) {
29628
29658
  const cs = c[0];
29629
29659
  const ce = c[1];
29630
- chunks.push({minv: cs, maxv: ce, bin: bin});
29660
+ chunks.push({minv: cs, maxv: ce});
29631
29661
  }
29632
29662
  }
29633
29663
  }
@@ -29635,16 +29665,15 @@ class BamIndex {
29635
29665
 
29636
29666
  // Use the linear index to find minimum file position of chunks that could contain alignments in the region
29637
29667
  const nintv = ba.linearIndex.length;
29638
- let lowest = null;
29639
- const minLin = Math.min(min >> 14, nintv - 1);
29668
+
29669
+ let lowest;
29670
+ const minLin = Math.min(min >> 14, nintv - 1); // i.e. min / 16384
29640
29671
  const maxLin = Math.min(max >> 14, nintv - 1);
29641
- for (let i = minLin; i < maxLin; i++) {
29672
+ for (let i = minLin; i <= maxLin; i++) {
29642
29673
  const vp = ba.linearIndex[i];
29643
29674
  if (vp) {
29644
- // 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
29645
- if (!lowest || vp.isLessThan(lowest)) {
29646
- lowest = vp;
29647
- }
29675
+ lowest = vp; // lowest file offset that contains alignments overlapping (min, max)
29676
+ break
29648
29677
  }
29649
29678
  }
29650
29679
 
@@ -29653,56 +29682,7 @@ class BamIndex {
29653
29682
  }
29654
29683
  }
29655
29684
 
29656
- function optimizeChunks(chunks, lowest) {
29657
-
29658
- const mergedChunks = [];
29659
- let lastChunk = null;
29660
-
29661
- if (chunks.length === 0) return chunks
29662
-
29663
- chunks.sort(function (c0, c1) {
29664
- const dif = c0.minv.block - c1.minv.block;
29665
- if (dif !== 0) {
29666
- return dif
29667
- } else {
29668
- return c0.minv.offset - c1.minv.offset
29669
- }
29670
- });
29671
-
29672
- chunks.forEach(function (chunk) {
29673
-
29674
- if (!lowest || chunk.maxv.isGreaterThan(lowest)) {
29675
- if (lastChunk === null) {
29676
- mergedChunks.push(chunk);
29677
- lastChunk = chunk;
29678
- } else {
29679
- if (canMerge(lastChunk, chunk)) {
29680
- if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
29681
- lastChunk.maxv = chunk.maxv;
29682
- }
29683
- } else {
29684
- mergedChunks.push(chunk);
29685
- lastChunk = chunk;
29686
- }
29687
- }
29688
- }
29689
- });
29690
-
29691
- return mergedChunks
29692
- }
29693
-
29694
29685
 
29695
- /**
29696
- * Merge 2 blocks if the gap between them is < 1kb and the total resulting size < 100mb
29697
- * @param chunk1
29698
- * @param chunk2
29699
- * @returns {boolean|boolean}
29700
- */
29701
- function canMerge(chunk1, chunk2) {
29702
- const gap = chunk2.minv.block - chunk1.maxv.block;
29703
- const total = chunk2.maxv.block - chunk1.minv.block;
29704
- return gap < 30000 && total < 10 * MB
29705
- }
29706
29686
 
29707
29687
  /**
29708
29688
  * Calculate the list of bins that overlap with region [beg, end]
@@ -29840,7 +29820,7 @@ class TribbleIndex {
29840
29820
  * @param min genomic start position
29841
29821
  * @param max genomic end position
29842
29822
  */
29843
- blocksForRange(queryChr, min, max) { //function (refId, min, max) {
29823
+ chunksForRange(queryChr, min, max) { //function (refId, min, max) {
29844
29824
  const chrIdx = this.chrIndex[queryChr];
29845
29825
 
29846
29826
  if (chrIdx) {
@@ -30007,7 +29987,12 @@ class ByteArrayDataWrapper {
30007
29987
 
30008
29988
  }
30009
29989
 
30010
- class BGZipLineReader {
29990
+ /**
29991
+ * Class to iterate line-by-line over a BGZipped text file. This class is useful for iterating from the start of
29992
+ * the file. Not useful for indexed queries.
29993
+ */
29994
+
29995
+ class BGZLineReader {
30011
29996
 
30012
29997
  constructor(config) {
30013
29998
  this.config = config;
@@ -30053,7 +30038,7 @@ class BGZipLineReader {
30053
30038
  }
30054
30039
  });
30055
30040
  const abuffer = await igvxhr.loadArrayBuffer(this.config.url, bsizeOptions);
30056
- const bufferSize = bgzBlockSize(abuffer);
30041
+ const bufferSize = bgzBlockSize$1(abuffer);
30057
30042
  //console.log(`next block ${this.filePtr} ${bufferSize}`);
30058
30043
 
30059
30044
  if (bufferSize === 0) {
@@ -30074,6 +30059,250 @@ class BGZipLineReader {
30074
30059
 
30075
30060
  }
30076
30061
 
30062
+ function concatenateArrayBuffers(arrayBuffers) {
30063
+
30064
+ if (arrayBuffers.length === 1) {
30065
+ return arrayBuffers[0]
30066
+ }
30067
+
30068
+ let len = 0;
30069
+ for (const b of arrayBuffers) {
30070
+ len += b.byteLength;
30071
+ }
30072
+ const c = new Uint8Array(len);
30073
+ let offset = 0;
30074
+ for (const b of arrayBuffers) {
30075
+ c.set(new Uint8Array(b), offset);
30076
+ offset += b.byteLength;
30077
+ }
30078
+ return c.buffer
30079
+ }
30080
+
30081
+ /**
30082
+ * Return the block size for the data buffer.
30083
+ * @param data
30084
+ * @returns {number}
30085
+ */
30086
+ const bgzBlockSize = (data) => {
30087
+ const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
30088
+ const bsize = (ba[17] << 8 | ba[16]) + 1;
30089
+ return bsize
30090
+ };
30091
+
30092
+ class BGZBlockLoader {
30093
+
30094
+ constructor(config) {
30095
+ this.config = config;
30096
+ this.cacheBlocks = false != config.cacheBlocks; // Default to true
30097
+ this.cache = undefined;
30098
+ }
30099
+
30100
+ /**
30101
+ * Return inflated data from startBlock through endBlock as an UInt8Array
30102
+ *
30103
+ * @param startBlock
30104
+ * @param endBlock
30105
+ * @returns {Promise<Uint8Array>}
30106
+ */
30107
+ async getData(startBlock, endBlock) {
30108
+
30109
+ const blocks = await this.getInflatedBlocks(startBlock, endBlock);
30110
+ if (blocks.length === 1) {
30111
+ return blocks[0]
30112
+ }
30113
+
30114
+ let len = 0;
30115
+ for (const b of blocks) {
30116
+ len += b.byteLength;
30117
+ }
30118
+ const c = new Uint8Array(len);
30119
+ let offset = 0;
30120
+ for (const b of blocks) {
30121
+ c.set(b, offset);
30122
+ offset += b.byteLength;
30123
+ }
30124
+ return c
30125
+ }
30126
+
30127
+ /**
30128
+ * Return the inflated data for the specified blocks as an array of Uint8Arrays. This method is public so
30129
+ * it can be unit tested. *
30130
+ * @param startBlock
30131
+ * @param endBlock
30132
+ * @returns {Promise<*[Uint8Array]>}
30133
+ */
30134
+ async getInflatedBlocks(startBlock, endBlock) {
30135
+
30136
+ if (!this.cacheBlocks) {
30137
+ const buffer = await this.loadBLockData(startBlock, endBlock);
30138
+ return inflateBlocks(buffer)
30139
+ } else {
30140
+
30141
+ const c = this.cache;
30142
+ if (c && (c.startBlock <= startBlock && c.endBlock >= endBlock)) {
30143
+ //console.log("Complete overlap")
30144
+ const startOffset = startBlock - c.startBlock;
30145
+ const endOffset = endBlock - c.startBlock;
30146
+ return inflateBlocks(c.buffer, startOffset, endOffset)
30147
+ // Don't update cache, still valid
30148
+ } else {
30149
+
30150
+ let buffer;
30151
+ if (!c || (c.startBlock > endBlock || c.endBlock < startBlock)) {
30152
+ // no overlap with cache
30153
+ buffer = await this.loadBLockData(startBlock, endBlock);
30154
+ } else {
30155
+
30156
+ //console.log("Some overlap")
30157
+ const arrayBuffers = [];
30158
+
30159
+ // Load blocks preceding cache start, if any
30160
+ if (startBlock < c.startBlock) {
30161
+ // load first blocks
30162
+ const startBuffer = await this.loadBLockData(startBlock, c.startBlock, {skipEnd: true});
30163
+ arrayBuffers.push(startBuffer);
30164
+ }
30165
+
30166
+ // Slice cached buffer as needed
30167
+ let cachedBuffer;
30168
+ if (startBlock <= c.startBlock && endBlock >= c.endBlock) {
30169
+ cachedBuffer = c.buffer;
30170
+ } else {
30171
+ const start = Math.max(0, startBlock - c.startBlock);
30172
+ let end;
30173
+ if (endBlock >= c.endBlock) {
30174
+ end = c.buffer.byteLength;
30175
+ } else {
30176
+ // We need to find the byte position of the end of "endBlock"
30177
+ const boundaries = findBlockBoundaries(c.buffer);
30178
+ for (let i = 0; i < boundaries.length - 1; i++) {
30179
+ if (c.startBlock + boundaries[i] === endBlock) {
30180
+ end = boundaries[i + 1];
30181
+ break
30182
+ }
30183
+ }
30184
+ // Do something if end not found
30185
+ }
30186
+ cachedBuffer = c.buffer.slice(start, end);
30187
+ }
30188
+ arrayBuffers.push(cachedBuffer);
30189
+
30190
+ // Load end blocks, if any
30191
+ if (endBlock > c.endBlock) {
30192
+ const endBuffer = await this.loadBLockData(c.endBlock, endBlock, {skipStart: true});
30193
+ arrayBuffers.push(endBuffer);
30194
+ }
30195
+
30196
+ buffer = concatenateArrayBuffers(arrayBuffers);
30197
+ }
30198
+
30199
+ this.cache = {startBlock, endBlock, buffer};
30200
+ return inflateBlocks(buffer)
30201
+ }
30202
+ }
30203
+ }
30204
+
30205
+ async loadBLockData(startBlock, endBlock, options) {
30206
+
30207
+ const config = this.config;
30208
+ const skipStart = options && options.skipStart;
30209
+ const skipEnd = options && options.skipEnd;
30210
+
30211
+ // Get size of last block if not skipped
30212
+ let lastBlockSize = 0;
30213
+ if (!skipEnd) {
30214
+ const bsizeOptions = buildOptions(config, {range: {start: endBlock, size: 26}});
30215
+ const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
30216
+ lastBlockSize = bgzBlockSize(abuffer);
30217
+ }
30218
+
30219
+ if (skipStart) {
30220
+ const bsizeOptions = buildOptions(config, {range: {start: startBlock, size: 26}});
30221
+ const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
30222
+ startBlock += bgzBlockSize(abuffer);
30223
+ }
30224
+
30225
+ // Load data for all blocks
30226
+ const loadOptions = buildOptions(config, {
30227
+ range: {
30228
+ start: startBlock,
30229
+ size: endBlock + lastBlockSize - startBlock
30230
+ }
30231
+ });
30232
+
30233
+ //console.log(`${this.config.name} Loaded ${startBlock} - ${endBlock + lastBlockSize} (${(endBlock + lastBlockSize - startBlock) / 1000} kb)`)
30234
+
30235
+ return igvxhr.loadArrayBuffer(config.url, loadOptions)
30236
+ }
30237
+ }
30238
+
30239
+ function findBlockBoundaries(arrayBuffer) {
30240
+
30241
+ const byteLengh = arrayBuffer.byteLength;
30242
+ let offset = 0;
30243
+ const blockBoundaries = [0];
30244
+ while (offset < byteLengh) {
30245
+ //console.log("Cache block " + offset)
30246
+ const ba = new Uint8Array(arrayBuffer, offset);
30247
+ const bsize = (ba[17] << 8 | ba[16]) + 1;
30248
+ offset += bsize;
30249
+ if (offset < byteLengh) {
30250
+ blockBoundaries.push(offset);
30251
+ }
30252
+ }
30253
+ return blockBoundaries
30254
+ }
30255
+
30256
+
30257
+ /**
30258
+ * Inflate compressed blocks within the data buffer*
30259
+ * @param data
30260
+ * @param startBlock - optional file location for start block. Default == 0
30261
+ * @param endBlock - optional file location for last block to decompress.
30262
+ * @returns {*[]}
30263
+ */
30264
+ function inflateBlocks(data, startBlock, endBlock) {
30265
+
30266
+ startBlock = startBlock || 0;
30267
+
30268
+ const oBlockList = [];
30269
+ let ptr = startBlock;
30270
+
30271
+ const lim = data.byteLength - 18;
30272
+ while (ptr < lim) {
30273
+ try {
30274
+ //console.log(113873 + ptr)
30275
+ const header = new Uint8Array(data, ptr, 18);
30276
+ const xlen = (header[11] << 8) | (header[10]);
30277
+ const bsize = ((header[17] << 8) | (header[16])); // Total block size, including header, minus 1
30278
+ const start = 12 + xlen + ptr; // Start of CDATA
30279
+ const bytesLeft = data.byteLength - start;
30280
+ const cDataSize = bsize - xlen - 18;
30281
+
30282
+ if (bytesLeft < cDataSize || cDataSize <= 0) {
30283
+ // This is unexpected. Throw error?
30284
+ break
30285
+ }
30286
+
30287
+ const cdata = new Uint8Array(data, start, cDataSize);
30288
+ const unc = inflateRaw(cdata);
30289
+ oBlockList.push(unc);
30290
+
30291
+ if (endBlock === ptr) {
30292
+ break
30293
+ } else {
30294
+ // Advance to next block
30295
+ ptr += bsize + 1;
30296
+ }
30297
+
30298
+ } catch (e) {
30299
+ console.error(e);
30300
+ break
30301
+ }
30302
+ }
30303
+ return oBlockList
30304
+ }
30305
+
30077
30306
  /*
30078
30307
  * The MIT License (MIT)
30079
30308
  *
@@ -30131,6 +30360,7 @@ class FeatureFileReader {
30131
30360
  if (this.config.format === "vcf" && !this.config.indexURL) {
30132
30361
  console.warn("Warning: index file not specified. The entire vcf file will be loaded.");
30133
30362
  }
30363
+
30134
30364
  }
30135
30365
 
30136
30366
  async defaultVisibilityWindow() {
@@ -30188,7 +30418,8 @@ class FeatureFileReader {
30188
30418
 
30189
30419
  let dataWrapper;
30190
30420
  if (index.tabix) {
30191
- dataWrapper = new BGZipLineReader(this.config);
30421
+ this._blockLoader = new BGZBlockLoader(this.config);
30422
+ dataWrapper = new BGZLineReader(this.config);
30192
30423
  } else {
30193
30424
  // Tribble
30194
30425
  const maxSize = Object.values(index.chrIndex)
@@ -30274,51 +30505,27 @@ class FeatureFileReader {
30274
30505
  }
30275
30506
 
30276
30507
  const genome = this.genome;
30277
- const blocks = this.index.blocksForRange(refId, start, end);
30278
- if (!blocks || blocks.length === 0) {
30508
+ const chunks = this.index.chunksForRange(refId, start, end);
30509
+ if (!chunks || chunks.length === 0) {
30279
30510
  return []
30280
30511
  } else {
30281
30512
  const allFeatures = [];
30282
- for (let block of blocks) {
30283
-
30284
- const startPos = block.minv.block;
30285
- const startOffset = block.minv.offset;
30286
- const endOffset = block.maxv.offset;
30287
- let endPos;
30288
-
30289
- if (tabix) {
30290
- let lastBlockSize = 0;
30291
- if (endOffset > 0) {
30292
- const bsizeOptions = buildOptions(config, {
30293
- range: {
30294
- start: block.maxv.block,
30295
- size: 26
30296
- }
30297
- });
30298
- const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
30299
- lastBlockSize = bgzBlockSize(abuffer);
30300
- }
30301
- endPos = block.maxv.block + lastBlockSize;
30302
- } else {
30303
- endPos = block.maxv.block;
30304
- }
30305
-
30306
- const options = buildOptions(config, {
30307
- range: {
30308
- start: startPos,
30309
- size: endPos - startPos + 1
30310
- }
30311
- });
30513
+ for (let chunk of chunks) {
30312
30514
 
30313
30515
  let inflated;
30314
30516
  if (tabix) {
30315
- const data = await igvxhr.loadArrayBuffer(config.url, options);
30316
- inflated = unbgzf(data);
30517
+ inflated = await this._blockLoader.getData(chunk.minv.block, chunk.maxv.block);
30317
30518
  } else {
30519
+ const options = buildOptions(config, {
30520
+ range: {
30521
+ start: chunk.minv.block,
30522
+ size: chunk.maxv.block - chunk.minv.block + 1
30523
+ }
30524
+ });
30318
30525
  inflated = await igvxhr.loadString(config.url, options);
30319
30526
  }
30320
30527
 
30321
- const slicedData = startOffset ? inflated.slice(startOffset) : inflated;
30528
+ const slicedData = chunk.minv.offset ? inflated.slice(chunk.minv.offset) : inflated;
30322
30529
  const dataWrapper = getDataWrapper(slicedData);
30323
30530
  let slicedFeatures = await parser.parseFeatures(dataWrapper);
30324
30531
 
@@ -36420,6 +36627,8 @@ class BamReader {
36420
36627
  this.bamPath = config.url;
36421
36628
  this.baiPath = config.indexURL;
36422
36629
  BamUtils.setReaderDefaults(this, config);
36630
+
36631
+ this._blockLoader = new BGZBlockLoader(config);
36423
36632
  }
36424
36633
 
36425
36634
  async readAlignments(chr, bpStart, bpEnd) {
@@ -36435,32 +36644,16 @@ class BamReader {
36435
36644
  } else {
36436
36645
 
36437
36646
  const bamIndex = await this.getIndex();
36438
- const chunks = bamIndex.blocksForRange(chrId, bpStart, bpEnd);
36647
+ const chunks = bamIndex.chunksForRange(chrId, bpStart, bpEnd);
36439
36648
 
36440
36649
  if (!chunks || chunks.length === 0) {
36441
36650
  return alignmentContainer
36442
36651
  }
36443
- for (let c of chunks) {
36444
-
36445
- let lastBlockSize;
36446
- if (c.maxv.offset === 0) {
36447
- lastBlockSize = 0; // Don't need to read the last block.
36448
- } else {
36449
- const bsizeOptions = buildOptions(this.config, {range: {start: c.maxv.block, size: 26}});
36450
- const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions);
36451
- lastBlockSize = bgzBlockSize(abuffer);
36452
- }
36453
- const fetchMin = c.minv.block;
36454
- const fetchMax = c.maxv.block + lastBlockSize;
36455
- const range = {start: fetchMin, size: fetchMax - fetchMin + 1};
36456
-
36457
- const compressed = await igvxhr.loadArrayBuffer(this.bamPath, buildOptions(this.config, {range: range}));
36458
36652
 
36459
- var ba = unbgzf(compressed); //new Uint8Array(BGZip.unbgzf(compressed)); //, c.maxv.block - c.minv.block + 1));
36653
+ for (let c of chunks) {
36654
+ const ba = await this._blockLoader.getData(c.minv.block, c.maxv.block);
36460
36655
  const done = BamUtils.decodeBamRecords(ba, c.minv.offset, alignmentContainer, this.indexToChr, chrId, bpStart, bpEnd, this.filter);
36461
-
36462
36656
  if (done) {
36463
- // console.log(`Loaded ${counter} chunks out of ${chunks.length}`);
36464
36657
  break
36465
36658
  }
36466
36659
  }
@@ -36477,7 +36670,7 @@ class BamReader {
36477
36670
  if (index.firstBlockPosition) {
36478
36671
  const bsizeOptions = buildOptions(this.config, {range: {start: index.firstBlockPosition, size: 26}});
36479
36672
  const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions);
36480
- const bsize = bgzBlockSize(abuffer);
36673
+ const bsize = bgzBlockSize$1(abuffer);
36481
36674
  len = index.firstBlockPosition + bsize; // Insure we get the complete compressed block containing the header
36482
36675
  } else {
36483
36676
  len = 64000;
@@ -40152,7 +40345,9 @@ class BAMTrack extends TrackBase {
40152
40345
  hoverText(clickState) {
40153
40346
  if (true === this.showCoverage && clickState.y >= this.coverageTrack.top && clickState.y < this.coverageTrack.height) {
40154
40347
  const clickedObject = this.coverageTrack.getClickedObject(clickState);
40155
- return clickedObject.hoverText()
40348
+ if(clickedObject) {
40349
+ return clickedObject.hoverText()
40350
+ }
40156
40351
  }
40157
40352
 
40158
40353
  }
@@ -42771,7 +42966,14 @@ class TrackView {
42771
42966
  const max = visibleViewport.featureCache.features.getMax(start, end);
42772
42967
  allFeatures = [{value: max}];
42773
42968
  } else {
42774
- allFeatures = FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end);
42969
+ const viewFeatures = FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end);
42970
+ if(!allFeatures) {
42971
+ allFeatures = viewFeatures;
42972
+ } else {
42973
+ for(let f of viewFeatures) {
42974
+ allFeatures.push(f);
42975
+ }
42976
+ }
42775
42977
  }
42776
42978
  }
42777
42979
  }
@@ -47105,21 +47307,16 @@ class EqtlTrack extends TrackBase {
47105
47307
  * Colors used for coding omosomes
47106
47308
  */
47107
47309
 
47108
- const Colors = {
47310
+ const GWASColors = {
47109
47311
  "X": "rgb(204, 153, 0)",
47110
47312
  "Y": "rgb(153, 204, 0)",
47111
47313
  "Un": "darkGray)",
47112
47314
  "1": "rgb(80, 80, 255)",
47113
- //"1": Color.red);
47114
- "I": "rgb(139, 155, 187)",
47115
47315
  "2": "rgb(206, 61, 50)",
47116
- "II": "rgb(206, 61, 50)",
47117
47316
  "2a": "rgb(210, 65, 55)",
47118
47317
  "2b": "rgb(215, 70, 60)",
47119
47318
  "3": "rgb(116, 155, 88)",
47120
- "III": "rgb(116, 155, 88)",
47121
47319
  "4": "rgb(240, 230, 133)",
47122
- "IV": "rgb(240, 230, 133)",
47123
47320
  "5": "rgb(70, 105, 131)",
47124
47321
  "6": "rgb(186, 99, 56)",
47125
47322
  "7": "rgb(93, 177, 221)",
@@ -47167,9 +47364,28 @@ const Colors = {
47167
47364
  };
47168
47365
 
47169
47366
  // aliasing
47170
- for (let key of Object.keys(Colors)) {
47367
+ for (let key of Object.keys(GWASColors)) {
47171
47368
  const altName = "chr" + key;
47172
- Colors[altName] = Colors[key];
47369
+ GWASColors[altName] = GWASColors[key];
47370
+ }
47371
+
47372
+ // romanizing
47373
+ for(let a = 1; a <= 48; a++) {
47374
+ if(a === 10) continue // Don't overide "X"
47375
+ const roman = romanize(a);
47376
+ GWASColors[roman] = GWASColors[a.toString()];
47377
+ }
47378
+
47379
+
47380
+ function romanize (num) {
47381
+ if (!+num) return false;
47382
+ var digits = String(+num).split('');
47383
+ var key = ['','C','CC','CCC','CD','D','DC','DCC','DCCC','CM',
47384
+ '','X','XX','XXX','XL','L','LX','LXX','LXXX','XC',
47385
+ '','I','II','III','IV','V','VI','VII','VIII','IX'];
47386
+ var roman = '', i = 3;
47387
+ while (i--) roman = (key[+digits.pop() + (i * 10)] || '') + roman;
47388
+ return Array(+digits.join('') + 1).join('M') + roman;
47173
47389
  }
47174
47390
 
47175
47391
  /*
@@ -47224,14 +47440,19 @@ class GWASTrack extends TrackBase {
47224
47440
  this.dotSize = config.dotSize || 3;
47225
47441
  this.popoverWindow = (config.popoverWindow === undefined ? DEFAULT_POPOVER_WINDOW : config.popoverWindow);
47226
47442
 
47227
- this.colorScales = config.color ?
47228
- new ConstantColorScale(config.color) :
47229
- {
47230
- "*": new BinnedColorScale(config.colorScale || {
47231
- thresholds: [5e-8, 5e-4, 0.5],
47232
- colors: ["rgb(255,50,50)", "rgb(251,100,100)", "rgb(251,170,170)", "rgb(227,238,249)"],
47233
- })
47234
- };
47443
+ // Color settings
47444
+ if (this.useChrColors) {
47445
+ this.colorScale = new ColorTable(config.colorTable || GWASColors);
47446
+ } else if (config.color) {
47447
+ this.colorScale = new ConstantColorScale(config.color);
47448
+ } else {
47449
+ this.colorScale =
47450
+ new BinnedColorScale(config.colorScale ||
47451
+ {
47452
+ thresholds: [5e-8, 5e-4, 0.5],
47453
+ colors: ["rgb(255,50,50)", "rgb(251,100,100)", "rgb(251,170,170)", "rgb(227,238,249)"],
47454
+ });
47455
+ }
47235
47456
 
47236
47457
  this.featureSource = FeatureSource(config, this.browser.genome);
47237
47458
  }
@@ -47240,7 +47461,7 @@ class GWASTrack extends TrackBase {
47240
47461
 
47241
47462
  if (typeof this.featureSource.getHeader === "function") {
47242
47463
  this.header = await this.featureSource.getHeader();
47243
- if(this.disposed) return; // This track was removed during async load
47464
+ if (this.disposed) return // This track was removed during async load
47244
47465
  }
47245
47466
 
47246
47467
  // Set properties from track line
@@ -47298,20 +47519,20 @@ class GWASTrack extends TrackBase {
47298
47519
  if (pos < bpStart) continue
47299
47520
  if (pos > bpEnd) break
47300
47521
 
47301
- const colorScale = this.getColorScale(variant._f ? variant._f.chr : variant.chr);
47302
-
47303
- let color;
47304
47522
  let val;
47305
47523
  if (this.posteriorProbability) {
47306
47524
  val = variant[this.valueProperty];
47307
- color = colorScale.getColor(val);
47308
47525
  } else {
47309
47526
  const pvalue = variant[this.valueProperty];
47310
47527
  if (!pvalue) continue
47311
47528
  val = -Math.log10(pvalue);
47312
- color = colorScale.getColor(val);
47313
47529
  }
47314
47530
 
47531
+ const colorKey = this.useChrColors ?
47532
+ variant._f ? variant._f.chr : variant.chr :
47533
+ val;
47534
+
47535
+ const color = this.colorScale.getColor(colorKey);
47315
47536
  const yScale = (this.dataRange.max - this.dataRange.min) / pixelHeight;
47316
47537
  const px = Math.round((pos - bpStart) / bpPerPixel);
47317
47538
  const py = Math.max(this.dotSize, pixelHeight - Math.round((val - this.dataRange.min) / yScale));
@@ -47326,21 +47547,6 @@ class GWASTrack extends TrackBase {
47326
47547
  }
47327
47548
  }
47328
47549
 
47329
- getColorScale(chr) {
47330
-
47331
- if (this.useChrColors) {
47332
- let cs = this.colorScales[chr];
47333
- if (!cs) {
47334
- const color = Colors[chr] || randomColor();
47335
- cs = new ConstantColorScale(color);
47336
- this.colorScales[chr] = cs;
47337
- }
47338
- return cs
47339
- } else {
47340
- return this.colorScales("*")
47341
- }
47342
- }
47343
-
47344
47550
  paintAxis(ctx, pixelWidth, pixelHeight) {
47345
47551
 
47346
47552
  IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
@@ -47377,7 +47583,7 @@ class GWASTrack extends TrackBase {
47377
47583
 
47378
47584
  popupData(clickState, features) {
47379
47585
 
47380
- if(features === undefined) features = clickState.viewport.cachedFeatures;
47586
+ if (features === undefined) features = clickState.viewport.cachedFeatures;
47381
47587
 
47382
47588
  let data = [];
47383
47589
  const track = clickState.viewport.trackView.track;
@@ -47438,13 +47644,10 @@ class GWASTrack extends TrackBase {
47438
47644
  } else {
47439
47645
  // No features -- pick something reasonable for PPAs and p-values
47440
47646
  if (this.posteriorProbability) {
47441
- this.dataRange.min = this.config.min || 0;
47442
- this.dataRange.max = this.config.max || 1;
47647
+ this.dataRange = {min: this.config.min || 0, max: this.config.max || 1};
47443
47648
  } else {
47444
- this.dataRange.max = this.config.max || 25;
47445
- this.dataRange.min = this.config.min || 0;
47649
+ this.dataRange = {min: this.config.max || 25, max: this.config.min || 0};
47446
47650
  }
47447
-
47448
47651
  }
47449
47652
 
47450
47653
  return this.dataRange