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/README.md CHANGED
@@ -18,19 +18,19 @@ Below are examples and a quickstart guide. See the [developer documentation](ht
18
18
 
19
19
  # Examples
20
20
 
21
- ***[Alignments](https://igv.org/web/release/3.0.3/examples/cram-vcf.html)***
21
+ ***[Alignments](https://igv.org/web/release/3.0.6/examples/cram-vcf.html)***
22
22
 
23
- ***[Interactions](https://igv.org/web/release/3.0.3/examples/interact.html)***
23
+ ***[Interactions](https://igv.org/web/release/3.0.6/examples/interact.html)***
24
24
 
25
- ***[Copy number](https://igv.org/web/release/3.0.3/examples/copyNumber.html)***
25
+ ***[Copy number](https://igv.org/web/release/3.0.6/examples/copyNumber.html)***
26
26
 
27
- ***[Multiple regions](https://igv.org/web/release/3.0.3/examples/multi-locus.html)***
27
+ ***[Multiple regions](https://igv.org/web/release/3.0.6/examples/multi-locus.html)***
28
28
 
29
- ***[Mutation Annotation Format (MAF)](https://igv.org/web/release/3.0.3/examples/maf-tcga.html)***
29
+ ***[Mutation Annotation Format (MAF)](https://igv.org/web/release/3.0.6/examples/maf-tcga.html)***
30
30
 
31
- ***[Variant color options](https://igv.org/web/release/3.0.3/examples/variant-colors.html)***
31
+ ***[Variant color options](https://igv.org/web/release/3.0.6/examples/variant-colors.html)***
32
32
 
33
- ***[More](https://igv.org/web/release/3.0.3/examples/)***
33
+ ***[More](https://igv.org/web/release/3.0.6/examples/)***
34
34
 
35
35
 
36
36
  # Quickstart
@@ -39,18 +39,18 @@ Below are examples and a quickstart guide. See the [developer documentation](ht
39
39
  igv.js consists of a single javascript file with no external dependencies.
40
40
 
41
41
  Pre-built files for script include, AMD, or CJS module systems (igv.min.js) and an ES6 module (igv.esm.min.js)
42
- can be downloaded from [https://cdn.jsdelivr.net/npm/igv@3.0.3/dist/](https://cdn.jsdelivr.net/npm/igv@3.0.3/dist/).
42
+ can be downloaded from [https://cdn.jsdelivr.net/npm/igv@3.0.6/dist/](https://cdn.jsdelivr.net/npm/igv@3.0.6/dist/).
43
43
 
44
44
  To import igv as an ES6 module
45
45
 
46
46
  ```javascript
47
- import igv from "https://cdn.jsdelivr.net/npm/igv@3.0.3/dist/igv.esm.min.js"
47
+ import igv from "https://cdn.jsdelivr.net/npm/igv@3.0.6/dist/igv.esm.min.js"
48
48
  ```
49
49
 
50
50
  Or as a script include (defines the "igv" global)
51
51
 
52
52
  ```html
53
- <script src="https://cdn.jsdelivr.net/npm/igv@3.0.3/dist/igv.min.js"></script>
53
+ <script src="https://cdn.jsdelivr.net/npm/igv@3.0.6/dist/igv.min.js"></script>
54
54
  ```
55
55
 
56
56
  Alternatively you can install with npm
@@ -91,7 +91,9 @@ a browser on a single alignment track opened at a specific locus:
91
91
  })
92
92
  ```
93
93
 
94
- For more details see the [Wiki](https://github.com/igvteam/igv.js/wiki) for full documentation of the API.
94
+ ## Documentation
95
+
96
+ Full documentation of the igv.js API is available at [https://igv.org/doc/igvjs/](https://igv.org/doc/igvjs/).
95
97
 
96
98
  ## Development
97
99
 
package/dist/igv.esm.js CHANGED
@@ -22874,6 +22874,20 @@ class BinaryParser$1 {
22874
22874
  this.length = dataView.byteLength;
22875
22875
  }
22876
22876
 
22877
+ /**
22878
+ * Print the first "n" bytes to the console. Used for debugging.
22879
+ * @param n
22880
+ */
22881
+ dumpBytes (n = 100) {
22882
+ const pos = this.position;
22883
+ const bytes = [];
22884
+ for(let i=0; i<= n; i++) {
22885
+ bytes.push(this.getByte());
22886
+ }
22887
+ console.log(bytes.join(" "));
22888
+ this.setPosition(pos);
22889
+ }
22890
+
22877
22891
  setPosition(position) {
22878
22892
  this.position = position;
22879
22893
  }
@@ -31437,7 +31451,6 @@ class ChromTree {
31437
31451
  const idToName = [];
31438
31452
  let sumLengths = 0;
31439
31453
  const readTreeNode = (offset) => {
31440
-
31441
31454
  if (offset >= 0) binaryParser.position = offset;
31442
31455
  const type = binaryParser.getByte();
31443
31456
  binaryParser.getByte();
@@ -31474,7 +31487,7 @@ class ChromTree {
31474
31487
  };
31475
31488
 
31476
31489
  // Recursively walk tree to populate dictionary
31477
- readTreeNode(binaryParser);
31490
+ readTreeNode( -1);
31478
31491
 
31479
31492
  return new ChromTree(header, nameToId, idToName, sumLengths)
31480
31493
  }
@@ -32165,6 +32178,16 @@ class BWReader {
32165
32178
  }
32166
32179
  }
32167
32180
 
32181
+ /**
32182
+ * The BB header consists of
32183
+ * (1) the common header
32184
+ * (2) the zoom headers
32185
+ * (3) autosql
32186
+ * (4) total summary block (version 2 and later)
32187
+ *
32188
+ * In addition, we read the chromomsome B+ tree
32189
+ * @returns {Promise<*>}
32190
+ */
32168
32191
  async loadHeader() {
32169
32192
 
32170
32193
  if (this.header) {
@@ -32182,7 +32205,7 @@ class BWReader {
32182
32205
  // Assume low-to-high unless proven otherwise
32183
32206
  this.littleEndian = true;
32184
32207
 
32185
- let binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32208
+ const binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32186
32209
  let magic = binaryParser.getUInt();
32187
32210
  if (magic === BIGWIG_MAGIC_LTH$1) {
32188
32211
  this.type = "bigwig";
@@ -32217,30 +32240,33 @@ class BWReader {
32217
32240
  extensionOffset: binaryParser.getLong()
32218
32241
  };
32219
32242
 
32243
+ // Read the next chunk containing zoom headers, autosql, and total summary if present. TotalSummary size = 40 bytes
32220
32244
  const startOffset = BBFILE_HEADER_SIZE;
32245
+ const size = header.totalSummaryOffset > 0 ?
32246
+ header.totalSummaryOffset - startOffset + 40 :
32247
+ Math.min(header.fullDataOffset, header.chromTreeOffset) - startOffset;
32221
32248
  let range = {
32222
32249
  start: startOffset,
32223
- size: (header.fullDataOffset - startOffset + 4)
32250
+ size: size
32224
32251
  };
32225
32252
  data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {range: range}));
32226
-
32227
- const nZooms = header.nZoomLevels;
32228
- binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32253
+ const extHeaderParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32229
32254
 
32230
32255
  // Load zoom headers, store in order of decreasing reduction level (increasing resolution)
32256
+ const nZooms = header.nZoomLevels;
32231
32257
  this.zoomLevelHeaders = [];
32232
32258
  this.firstZoomDataOffset = Number.MAX_SAFE_INTEGER;
32233
32259
  for (let i = 1; i <= nZooms; i++) {
32234
32260
  const zoomNumber = nZooms - i;
32235
- const zlh = new ZoomLevelHeader(zoomNumber, binaryParser);
32261
+ const zlh = new ZoomLevelHeader(zoomNumber, extHeaderParser);
32236
32262
  this.firstZoomDataOffset = Math.min(zlh.dataOffset, this.firstZoomDataOffset);
32237
32263
  this.zoomLevelHeaders[zoomNumber] = zlh;
32238
32264
  }
32239
32265
 
32240
32266
  // Autosql
32241
32267
  if (header.autoSqlOffset > 0) {
32242
- binaryParser.position = header.autoSqlOffset - startOffset;
32243
- const autoSqlString = binaryParser.getString();
32268
+ extHeaderParser.position = header.autoSqlOffset - startOffset;
32269
+ const autoSqlString = extHeaderParser.getString();
32244
32270
  if (autoSqlString) {
32245
32271
  this.autoSql = parseAutoSQL(autoSqlString);
32246
32272
  }
@@ -32248,37 +32274,75 @@ class BWReader {
32248
32274
 
32249
32275
  // Total summary
32250
32276
  if (header.totalSummaryOffset > 0) {
32251
- binaryParser.position = header.totalSummaryOffset - startOffset;
32252
- this.totalSummary = new BWTotalSummary(binaryParser);
32253
- }
32254
-
32255
- // Chrom data index
32256
- if (header.chromTreeOffset > 0) {
32257
- binaryParser.position = header.chromTreeOffset - startOffset;
32258
- this.chromTree = await ChromTree.parseTree(binaryParser, startOffset, this.genome);
32259
- this.chrNames = new Set(this.chromTree.idToName);
32260
- } else {
32261
- // TODO -- this is an error, not expected
32262
- throw "BigWig chromosome tree offset <= 0"
32277
+ extHeaderParser.position = header.totalSummaryOffset - startOffset;
32278
+ this.totalSummary = new BWTotalSummary(extHeaderParser);
32263
32279
  }
32264
32280
 
32265
- //Finally total data count
32266
- binaryParser.position = header.fullDataOffset - startOffset;
32267
- header.dataCount = binaryParser.getInt();
32281
+ // Chrom data index. The start is known, size is not, but we can estimate it
32282
+ const bufferSize = Math.min(200000, Math.max(10000, header.fullDataOffset - header.chromTreeOffset));
32283
+ this.chromTree = await this.#readChromTree(header.chromTreeOffset, bufferSize);
32284
+ this.chrNames = new Set(this.chromTree.idToName);
32268
32285
 
32269
- this.featureDensity = header.dataCount / this.chromTree.sumLengths;
32286
+ // Estimate feature density from dataCount (bigbed only)
32287
+ if("bigbed" === this.type) {
32288
+ const dataCount = await this.#readDataCount(header.fullDataOffset);
32289
+ this.featureDensity = dataCount / this.chromTree.sumLengths;
32290
+ }
32270
32291
 
32271
32292
  this.header = header;
32272
32293
 
32273
-
32274
32294
  //extension
32275
32295
  if (header.extensionOffset > 0) {
32276
32296
  await this.loadExtendedHeader(header.extensionOffset);
32277
32297
  }
32278
- return this.header
32298
+ return this.header
32279
32299
  }
32280
32300
  }
32281
32301
 
32302
+ async #readDataCount(offset) {
32303
+ const data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
32304
+ range: {
32305
+ start: offset,
32306
+ size: 4
32307
+ }
32308
+ }));
32309
+ const binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32310
+ return binaryParser.getInt()
32311
+ }
32312
+
32313
+ /**
32314
+ * Used when the chromTreeOffset is > fullDataOffset, that is when the chrom tree is not in the initial chunk
32315
+ * read for parsing the header. We know the start position, but not the total size of the chrom tree
32316
+ *
32317
+ * @returns {Promise<void>}
32318
+ */
32319
+ async #readChromTree(chromTreeOffset, bufferSize) {
32320
+
32321
+ let size = bufferSize;
32322
+ const load = async () => {
32323
+ const data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
32324
+ range: {
32325
+ start: chromTreeOffset,
32326
+ size: size
32327
+ }
32328
+ }));
32329
+ const binaryParser = new BinaryParser$1(new DataView(data), this.littleEndian);
32330
+ return ChromTree.parseTree(binaryParser, chromTreeOffset, this.genome)
32331
+ };
32332
+
32333
+ let error;
32334
+ while (size < 1000000) {
32335
+ try {
32336
+ const chromTree = await load();
32337
+ return chromTree
32338
+ } catch (e) {
32339
+ error = e;
32340
+ size *= 2;
32341
+ }
32342
+ }
32343
+ throw (error)
32344
+ }
32345
+
32282
32346
  async loadExtendedHeader(offset) {
32283
32347
 
32284
32348
  let data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
@@ -33453,7 +33517,10 @@ class TextFeatureSource extends BaseFeatureSource {
33453
33517
  for (let feature of featureList) {
33454
33518
  for (let field of searchableFields) {
33455
33519
  let key;
33456
- if (typeof feature.getAttributeValue === 'function') {
33520
+ if(feature.hasOwnProperty(field)) {
33521
+ key = feature[field];
33522
+ }
33523
+ else if (typeof feature.getAttributeValue === 'function') {
33457
33524
  key = feature.getAttributeValue(field);
33458
33525
  }
33459
33526
  if (key) {
@@ -39605,6 +39672,10 @@ class DynamicFeatureSource {
39605
39672
  if (this.featureMap[chr]) {
39606
39673
  const match = `${chr}-${start}-${end}`;
39607
39674
  this.featureMap[chr] = this.featureMap[chr].filter(feature => match !== `${feature.chr}-${feature.start}-${feature.end}`);
39675
+ // Check if featureMap for a specific chromosome is empty now and delete it if yes
39676
+ if (this.featureMap[chr].length === 0) {
39677
+ delete this.featureMap[chr];
39678
+ }
39608
39679
  }
39609
39680
  }
39610
39681
  }
@@ -43052,7 +43123,7 @@ class TrackView {
43052
43123
  const viewportsToRepaint = visibleViewports.filter(vp => vp.needsRepaint()).filter(viewport => viewport.checkZoomIn());
43053
43124
 
43054
43125
  // Get viewports that require a data load
43055
- const viewportsToReload = visibleViewports.filter(viewport => viewport.needsReload());
43126
+ const viewportsToReload = visibleViewports.filter(viewport => viewport.checkZoomIn()).filter(viewport => viewport.needsReload());
43056
43127
 
43057
43128
  // Trigger viewport to load features needed to cover current genomic range
43058
43129
  // NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
@@ -70517,7 +70588,7 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
70517
70588
  })
70518
70589
  }
70519
70590
 
70520
- const _version = "3.0.4";
70591
+ const _version = "3.0.6";
70521
70592
  function version() {
70522
70593
  return _version
70523
70594
  }
@@ -72067,9 +72138,16 @@ class ROIMenu {
72067
72138
  '<hr/>',
72068
72139
  {
72069
72140
  label: 'Delete',
72070
- click: () => {
72071
- this.browser.roiManager.deleteRegionWithKey(regionElement.dataset.region, this.browser.columnContainer);
72072
- this.browser.roiManager.repaintTable();
72141
+ click: async () => {
72142
+ roiSet.removeFeature(feature);
72143
+ const userDefinedFeatures = await roiSet.getAllFeatures();
72144
+
72145
+ // Delete user defined ROI Set if it is empty
72146
+ if (Object.keys(userDefinedFeatures).length === 0) {
72147
+ roiManager.deleteUserDefinedROISet();
72148
+ }
72149
+ roiManager.deleteRegionWithKey(regionElement.dataset.region, columnContainer);
72150
+ roiManager.repaintTable();
72073
72151
  }
72074
72152
  }
72075
72153
  );
@@ -72597,6 +72675,10 @@ class ROIManager {
72597
72675
  return this.roiSets.find(roiSet => true === roiSet.isUserDefined)
72598
72676
  }
72599
72677
 
72678
+ deleteUserDefinedROISet(){
72679
+ this.roiSets = this.roiSets.filter(roiSet => roiSet.isUserDefined !== true);
72680
+ }
72681
+
72600
72682
  initializeUserDefinedROISet() {
72601
72683
 
72602
72684
  const config =
@@ -72612,15 +72694,8 @@ class ROIManager {
72612
72694
  }
72613
72695
 
72614
72696
  async deleteRegionWithKey(regionKey, columnContainer) {
72615
-
72616
72697
  columnContainer.querySelectorAll(createSelector(regionKey)).forEach(node => node.remove());
72617
72698
 
72618
- const {feature, set} = await this.findRegionWithKey(regionKey);
72619
-
72620
- if (set) {
72621
- set.removeFeature(feature);
72622
- }
72623
-
72624
72699
  const records = await this.getTableRecords();
72625
72700
 
72626
72701
  if (0 === records.length) {
@@ -72630,23 +72705,6 @@ class ROIManager {
72630
72705
 
72631
72706
  }
72632
72707
 
72633
- async findRegionWithKey(regionKey) {
72634
-
72635
- const {chr, start, end} = parseRegionKey(regionKey);
72636
-
72637
- for (let set of this.roiSets) {
72638
- const features = await set.getFeatures(chr, start, end);
72639
-
72640
- for (let feature of features) {
72641
- if (feature.chr === chr && feature.start >= start && feature.end <= end) {
72642
- return {feature, set}
72643
- }
72644
- }
72645
- }
72646
-
72647
- return {feature: undefined, set: undefined}
72648
- }
72649
-
72650
72708
  toJSON() {
72651
72709
  return this.roiSets.map(roiSet => roiSet.toJSON())
72652
72710
  }
@@ -76265,7 +76323,7 @@ function mouseUpOrLeave(e) {
76265
76323
  async function keyUpHandler(event) {
76266
76324
 
76267
76325
  // Feature jumping disabled in multi-locus view
76268
- if (this.referenceFrameList.length > 1) return
76326
+ if (!this.referenceFrameList || this.referenceFrameList.length > 1) return
76269
76327
 
76270
76328
  if (event.code === 'KeyF' || event.code === 'KeyB') {
76271
76329