igv 3.0.4 → 3.0.6

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
@@ -22880,6 +22880,20 @@
22880
22880
  this.length = dataView.byteLength;
22881
22881
  }
22882
22882
 
22883
+ /**
22884
+ * Print the first "n" bytes to the console. Used for debugging.
22885
+ * @param n
22886
+ */
22887
+ dumpBytes (n = 100) {
22888
+ const pos = this.position;
22889
+ const bytes = [];
22890
+ for(let i=0; i<= n; i++) {
22891
+ bytes.push(this.getByte());
22892
+ }
22893
+ console.log(bytes.join(" "));
22894
+ this.setPosition(pos);
22895
+ }
22896
+
22883
22897
  setPosition(position) {
22884
22898
  this.position = position;
22885
22899
  }
@@ -31443,7 +31457,6 @@
31443
31457
  const idToName = [];
31444
31458
  let sumLengths = 0;
31445
31459
  const readTreeNode = (offset) => {
31446
-
31447
31460
  if (offset >= 0) binaryParser.position = offset;
31448
31461
  const type = binaryParser.getByte();
31449
31462
  binaryParser.getByte();
@@ -31480,7 +31493,7 @@
31480
31493
  };
31481
31494
 
31482
31495
  // Recursively walk tree to populate dictionary
31483
- readTreeNode(binaryParser);
31496
+ readTreeNode( -1);
31484
31497
 
31485
31498
  return new ChromTree(header, nameToId, idToName, sumLengths)
31486
31499
  }
@@ -32171,6 +32184,16 @@
32171
32184
  }
32172
32185
  }
32173
32186
 
32187
+ /**
32188
+ * The BB header consists of
32189
+ * (1) the common header
32190
+ * (2) the zoom headers
32191
+ * (3) autosql
32192
+ * (4) total summary block (version 2 and later)
32193
+ *
32194
+ * In addition, we read the chromomsome B+ tree
32195
+ * @returns {Promise<*>}
32196
+ */
32174
32197
  async loadHeader() {
32175
32198
 
32176
32199
  if (this.header) {
@@ -32188,7 +32211,7 @@
32188
32211
  // Assume low-to-high unless proven otherwise
32189
32212
  this.littleEndian = true;
32190
32213
 
32191
- let binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32214
+ const binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32192
32215
  let magic = binaryParser.getUInt();
32193
32216
  if (magic === BIGWIG_MAGIC_LTH$1) {
32194
32217
  this.type = "bigwig";
@@ -32223,30 +32246,33 @@
32223
32246
  extensionOffset: binaryParser.getLong()
32224
32247
  };
32225
32248
 
32249
+ // Read the next chunk containing zoom headers, autosql, and total summary if present. TotalSummary size = 40 bytes
32226
32250
  const startOffset = BBFILE_HEADER_SIZE;
32251
+ const size = header.totalSummaryOffset > 0 ?
32252
+ header.totalSummaryOffset - startOffset + 40 :
32253
+ Math.min(header.fullDataOffset, header.chromTreeOffset) - startOffset;
32227
32254
  let range = {
32228
32255
  start: startOffset,
32229
- size: (header.fullDataOffset - startOffset + 4)
32256
+ size: size
32230
32257
  };
32231
32258
  data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {range: range}));
32232
-
32233
- const nZooms = header.nZoomLevels;
32234
- binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32259
+ const extHeaderParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32235
32260
 
32236
32261
  // Load zoom headers, store in order of decreasing reduction level (increasing resolution)
32262
+ const nZooms = header.nZoomLevels;
32237
32263
  this.zoomLevelHeaders = [];
32238
32264
  this.firstZoomDataOffset = Number.MAX_SAFE_INTEGER;
32239
32265
  for (let i = 1; i <= nZooms; i++) {
32240
32266
  const zoomNumber = nZooms - i;
32241
- const zlh = new ZoomLevelHeader(zoomNumber, binaryParser);
32267
+ const zlh = new ZoomLevelHeader(zoomNumber, extHeaderParser);
32242
32268
  this.firstZoomDataOffset = Math.min(zlh.dataOffset, this.firstZoomDataOffset);
32243
32269
  this.zoomLevelHeaders[zoomNumber] = zlh;
32244
32270
  }
32245
32271
 
32246
32272
  // Autosql
32247
32273
  if (header.autoSqlOffset > 0) {
32248
- binaryParser.position = header.autoSqlOffset - startOffset;
32249
- const autoSqlString = binaryParser.getString();
32274
+ extHeaderParser.position = header.autoSqlOffset - startOffset;
32275
+ const autoSqlString = extHeaderParser.getString();
32250
32276
  if (autoSqlString) {
32251
32277
  this.autoSql = parseAutoSQL(autoSqlString);
32252
32278
  }
@@ -32254,37 +32280,75 @@
32254
32280
 
32255
32281
  // Total summary
32256
32282
  if (header.totalSummaryOffset > 0) {
32257
- binaryParser.position = header.totalSummaryOffset - startOffset;
32258
- this.totalSummary = new BWTotalSummary(binaryParser);
32259
- }
32260
-
32261
- // Chrom data index
32262
- if (header.chromTreeOffset > 0) {
32263
- binaryParser.position = header.chromTreeOffset - startOffset;
32264
- this.chromTree = await ChromTree.parseTree(binaryParser, startOffset, this.genome);
32265
- this.chrNames = new Set(this.chromTree.idToName);
32266
- } else {
32267
- // TODO -- this is an error, not expected
32268
- throw "BigWig chromosome tree offset <= 0"
32283
+ extHeaderParser.position = header.totalSummaryOffset - startOffset;
32284
+ this.totalSummary = new BWTotalSummary(extHeaderParser);
32269
32285
  }
32270
32286
 
32271
- //Finally total data count
32272
- binaryParser.position = header.fullDataOffset - startOffset;
32273
- header.dataCount = binaryParser.getInt();
32287
+ // Chrom data index. The start is known, size is not, but we can estimate it
32288
+ const bufferSize = Math.min(200000, Math.max(10000, header.fullDataOffset - header.chromTreeOffset));
32289
+ this.chromTree = await this.#readChromTree(header.chromTreeOffset, bufferSize);
32290
+ this.chrNames = new Set(this.chromTree.idToName);
32274
32291
 
32275
- this.featureDensity = header.dataCount / this.chromTree.sumLengths;
32292
+ // Estimate feature density from dataCount (bigbed only)
32293
+ if("bigbed" === this.type) {
32294
+ const dataCount = await this.#readDataCount(header.fullDataOffset);
32295
+ this.featureDensity = dataCount / this.chromTree.sumLengths;
32296
+ }
32276
32297
 
32277
32298
  this.header = header;
32278
32299
 
32279
-
32280
32300
  //extension
32281
32301
  if (header.extensionOffset > 0) {
32282
32302
  await this.loadExtendedHeader(header.extensionOffset);
32283
32303
  }
32284
- return this.header
32304
+ return this.header
32285
32305
  }
32286
32306
  }
32287
32307
 
32308
+ async #readDataCount(offset) {
32309
+ const data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
32310
+ range: {
32311
+ start: offset,
32312
+ size: 4
32313
+ }
32314
+ }));
32315
+ const binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32316
+ return binaryParser.getInt()
32317
+ }
32318
+
32319
+ /**
32320
+ * Used when the chromTreeOffset is > fullDataOffset, that is when the chrom tree is not in the initial chunk
32321
+ * read for parsing the header. We know the start position, but not the total size of the chrom tree
32322
+ *
32323
+ * @returns {Promise<void>}
32324
+ */
32325
+ async #readChromTree(chromTreeOffset, bufferSize) {
32326
+
32327
+ let size = bufferSize;
32328
+ const load = async () => {
32329
+ const data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
32330
+ range: {
32331
+ start: chromTreeOffset,
32332
+ size: size
32333
+ }
32334
+ }));
32335
+ const binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32336
+ return ChromTree.parseTree(binaryParser, chromTreeOffset, this.genome)
32337
+ };
32338
+
32339
+ let error;
32340
+ while (size < 1000000) {
32341
+ try {
32342
+ const chromTree = await load();
32343
+ return chromTree
32344
+ } catch (e) {
32345
+ error = e;
32346
+ size *= 2;
32347
+ }
32348
+ }
32349
+ throw (error)
32350
+ }
32351
+
32288
32352
  async loadExtendedHeader(offset) {
32289
32353
 
32290
32354
  let data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
@@ -33459,7 +33523,10 @@
33459
33523
  for (let feature of featureList) {
33460
33524
  for (let field of searchableFields) {
33461
33525
  let key;
33462
- if (typeof feature.getAttributeValue === 'function') {
33526
+ if(feature.hasOwnProperty(field)) {
33527
+ key = feature[field];
33528
+ }
33529
+ else if (typeof feature.getAttributeValue === 'function') {
33463
33530
  key = feature.getAttributeValue(field);
33464
33531
  }
33465
33532
  if (key) {
@@ -39611,6 +39678,10 @@
39611
39678
  if (this.featureMap[chr]) {
39612
39679
  const match = `${chr}-${start}-${end}`;
39613
39680
  this.featureMap[chr] = this.featureMap[chr].filter(feature => match !== `${feature.chr}-${feature.start}-${feature.end}`);
39681
+ // Check if featureMap for a specific chromosome is empty now and delete it if yes
39682
+ if (this.featureMap[chr].length === 0) {
39683
+ delete this.featureMap[chr];
39684
+ }
39614
39685
  }
39615
39686
  }
39616
39687
  }
@@ -43058,7 +43129,7 @@
43058
43129
  const viewportsToRepaint = visibleViewports.filter(vp => vp.needsRepaint()).filter(viewport => viewport.checkZoomIn());
43059
43130
 
43060
43131
  // Get viewports that require a data load
43061
- const viewportsToReload = visibleViewports.filter(viewport => viewport.needsReload());
43132
+ const viewportsToReload = visibleViewports.filter(viewport => viewport.checkZoomIn()).filter(viewport => viewport.needsReload());
43062
43133
 
43063
43134
  // Trigger viewport to load features needed to cover current genomic range
43064
43135
  // NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
@@ -70523,7 +70594,7 @@
70523
70594
  })
70524
70595
  }
70525
70596
 
70526
- const _version = "3.0.4";
70597
+ const _version = "3.0.6";
70527
70598
  function version() {
70528
70599
  return _version
70529
70600
  }
@@ -72073,9 +72144,16 @@
72073
72144
  '<hr/>',
72074
72145
  {
72075
72146
  label: 'Delete',
72076
- click: () => {
72077
- this.browser.roiManager.deleteRegionWithKey(regionElement.dataset.region, this.browser.columnContainer);
72078
- this.browser.roiManager.repaintTable();
72147
+ click: async () => {
72148
+ roiSet.removeFeature(feature);
72149
+ const userDefinedFeatures = await roiSet.getAllFeatures();
72150
+
72151
+ // Delete user defined ROI Set if it is empty
72152
+ if (Object.keys(userDefinedFeatures).length === 0) {
72153
+ roiManager.deleteUserDefinedROISet();
72154
+ }
72155
+ roiManager.deleteRegionWithKey(regionElement.dataset.region, columnContainer);
72156
+ roiManager.repaintTable();
72079
72157
  }
72080
72158
  }
72081
72159
  );
@@ -72603,6 +72681,10 @@
72603
72681
  return this.roiSets.find(roiSet => true === roiSet.isUserDefined)
72604
72682
  }
72605
72683
 
72684
+ deleteUserDefinedROISet(){
72685
+ this.roiSets = this.roiSets.filter(roiSet => roiSet.isUserDefined !== true);
72686
+ }
72687
+
72606
72688
  initializeUserDefinedROISet() {
72607
72689
 
72608
72690
  const config =
@@ -72618,15 +72700,8 @@
72618
72700
  }
72619
72701
 
72620
72702
  async deleteRegionWithKey(regionKey, columnContainer) {
72621
-
72622
72703
  columnContainer.querySelectorAll(createSelector(regionKey)).forEach(node => node.remove());
72623
72704
 
72624
- const {feature, set} = await this.findRegionWithKey(regionKey);
72625
-
72626
- if (set) {
72627
- set.removeFeature(feature);
72628
- }
72629
-
72630
72705
  const records = await this.getTableRecords();
72631
72706
 
72632
72707
  if (0 === records.length) {
@@ -72636,23 +72711,6 @@
72636
72711
 
72637
72712
  }
72638
72713
 
72639
- async findRegionWithKey(regionKey) {
72640
-
72641
- const {chr, start, end} = parseRegionKey(regionKey);
72642
-
72643
- for (let set of this.roiSets) {
72644
- const features = await set.getFeatures(chr, start, end);
72645
-
72646
- for (let feature of features) {
72647
- if (feature.chr === chr && feature.start >= start && feature.end <= end) {
72648
- return {feature, set}
72649
- }
72650
- }
72651
- }
72652
-
72653
- return {feature: undefined, set: undefined}
72654
- }
72655
-
72656
72714
  toJSON() {
72657
72715
  return this.roiSets.map(roiSet => roiSet.toJSON())
72658
72716
  }
@@ -76271,7 +76329,7 @@
76271
76329
  async function keyUpHandler(event) {
76272
76330
 
76273
76331
  // Feature jumping disabled in multi-locus view
76274
- if (this.referenceFrameList.length > 1) return
76332
+ if (!this.referenceFrameList || this.referenceFrameList.length > 1) return
76275
76333
 
76276
76334
  if (event.code === 'KeyF' || event.code === 'KeyB') {
76277
76335