igv 2.12.1 → 2.12.4

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
@@ -12,19 +12,19 @@ Below are examples and a quickstart guide. See the [Wiki](https://github.com/ig
12
12
 
13
13
  # Examples
14
14
 
15
- ***[Alignments](https://igv.org/web/release/2.12.1/examples/cram-vcf.html)***
15
+ ***[Alignments](https://igv.org/web/release/2.12.4/examples/cram-vcf.html)***
16
16
 
17
- ***[Interactions](https://igv.org/web/release/2.12.1/examples/interact.html)***
17
+ ***[Interactions](https://igv.org/web/release/2.12.4/examples/interact.html)***
18
18
 
19
- ***[Copy number](https://igv.org/web/release/2.12.1/examples/copyNumber.html)***
19
+ ***[Copy number](https://igv.org/web/release/2.12.4/examples/copyNumber.html)***
20
20
 
21
- ***[Multiple regions](https://igv.org/web/release/2.12.1/examples/multi-locus.html)***
21
+ ***[Multiple regions](https://igv.org/web/release/2.12.4/examples/multi-locus.html)***
22
22
 
23
- ***[Mutation Annotation Format (MAF)](https://igv.org/web/release/2.12.1/examples/maf-tcga.html)***
23
+ ***[Mutation Annotation Format (MAF)](https://igv.org/web/release/2.12.4/examples/maf-tcga.html)***
24
24
 
25
- ***[Variant color options](https://igv.org/web/release/2.12.1/examples/variant-colors.html)***
25
+ ***[Variant color options](https://igv.org/web/release/2.12.4/examples/variant-colors.html)***
26
26
 
27
- ***[More](https://igv.org/web/release/2.12.1/examples/)***
27
+ ***[More](https://igv.org/web/release/2.12.4/examples/)***
28
28
 
29
29
 
30
30
  # Quickstart
@@ -33,18 +33,18 @@ Below are examples and a quickstart guide. See the [Wiki](https://github.com/ig
33
33
  igv.js consists of a single javascript file with no external dependencies.
34
34
 
35
35
  Pre-built files for ES5 (igv.min.js) and ES6 (igv.esm.min.js)
36
- can be downloaded from [https://cdn.jsdelivr.net/npm/igv@2.12.1/dist/](https://cdn.jsdelivr.net/npm/igv@2.12.1/dist/).
36
+ can be downloaded from [https://cdn.jsdelivr.net/npm/igv@2.12.4/dist/](https://cdn.jsdelivr.net/npm/igv@2.12.4/dist/).
37
37
 
38
38
  To import igv as an ES6 module
39
39
 
40
40
  ```javascript
41
- import igv from "https://cdn.jsdelivr.net/npm/igv@2.12.1/dist/igv.esm.min.js"
41
+ import igv from "https://cdn.jsdelivr.net/npm/igv@2.12.4/dist/igv.esm.min.js"
42
42
  ```
43
43
 
44
44
  Or as a script include (defines the "igv" global)
45
45
 
46
46
  ```html
47
- <script src="https://cdn.jsdelivr.net/npm/igv@2.12.1/dist/igv.min.js"></script>
47
+ <script src="https://cdn.jsdelivr.net/npm/igv@2.12.4/dist/igv.min.js"></script>
48
48
  ```
49
49
 
50
50
  Alternatively you can install with npm
package/dist/igv.esm.js CHANGED
@@ -20251,6 +20251,12 @@ for (let i = 0; i < t1.length; i++) {
20251
20251
  complement[t1[i].toLowerCase()] = t2[i].toLowerCase();
20252
20252
  }
20253
20253
 
20254
+ const DEFAULT_HEIGHT = 25;
20255
+ const TRANSLATED_HEIGHT = 115;
20256
+ const SEQUENCE_HEIGHT = 15;
20257
+ const FRAME_HEIGHT = 25;
20258
+ const FRAME_BORDER = 5;
20259
+
20254
20260
  class SequenceTrack {
20255
20261
 
20256
20262
  constructor(config, browser) {
@@ -20262,13 +20268,13 @@ class SequenceTrack {
20262
20268
  this.name = "Sequence";
20263
20269
  this.id = "sequence";
20264
20270
  this.sequenceType = config.sequenceType || "dna"; // dna | rna | prot
20265
- this.height = 25;
20266
20271
  this.disableButtons = false;
20267
20272
  this.order = config.order || defaultSequenceTrackOrder;
20268
20273
  this.ignoreTrackMenu = false;
20269
20274
 
20270
- this.reversed = false;
20271
- this.frameTranslate = false;
20275
+ this.reversed = config.reversed === true;
20276
+ this.frameTranslate = config.frameTranslate === true;
20277
+ this.height = this.frameTranslate ? TRANSLATED_HEIGHT : DEFAULT_HEIGHT;
20272
20278
 
20273
20279
  }
20274
20280
 
@@ -20287,14 +20293,14 @@ class SequenceTrack {
20287
20293
  this.frameTranslate = !this.frameTranslate;
20288
20294
  if (this.frameTranslate) {
20289
20295
  for (let vp of this.trackView.viewports) {
20290
- vp.setContentHeight(115);
20296
+ vp.setContentHeight(TRANSLATED_HEIGHT);
20291
20297
  }
20292
- this.trackView.setTrackHeight(115);
20298
+ this.trackView.setTrackHeight(TRANSLATED_HEIGHT);
20293
20299
  } else {
20294
20300
  for (let vp of this.trackView.viewports) {
20295
- vp.setContentHeight(25);
20301
+ vp.setContentHeight(DEFAULT_HEIGHT);
20296
20302
  }
20297
- this.trackView.setTrackHeight(25);
20303
+ this.trackView.setTrackHeight(DEFAULT_HEIGHT);
20298
20304
  }
20299
20305
  this.trackView.repaintViews();
20300
20306
 
@@ -20377,7 +20383,8 @@ class SequenceTrack {
20377
20383
  }
20378
20384
 
20379
20385
  async getFeatures(chr, start, end, bpPerPixel) {
20380
-
20386
+ start = Math.floor(start);
20387
+ end = Math.floor(end);
20381
20388
  if (bpPerPixel && bpPerPixel > 1) {
20382
20389
  return null
20383
20390
  } else {
@@ -20395,89 +20402,79 @@ class SequenceTrack {
20395
20402
 
20396
20403
  if (options.features) {
20397
20404
 
20398
- const sequence = options.features.sequence;
20399
- const sequenceBpStart = options.features.bpStart;
20400
- const bpEnd = 1 + options.bpStart + (options.pixelWidth * options.bpPerPixel);
20401
-
20402
- let height = 15;
20403
- for (let bp = sequenceBpStart; bp <= bpEnd; bp++) {
20405
+ let sequence = options.features.sequence;
20404
20406
 
20405
- let seqOffsetBp = Math.floor(bp - sequenceBpStart);
20407
+ if (this.reversed) {
20408
+ sequence = sequence.split('').map(function (cv) {
20409
+ return complement[cv]
20410
+ }).join('');
20411
+ }
20406
20412
 
20407
- if (seqOffsetBp < sequence.length) {
20408
- let letter = sequence[seqOffsetBp];
20413
+ const sequenceBpStart = options.features.bpStart; // genomic position at start of sequence
20414
+ const bpEnd = 1 + options.bpStart + (options.pixelWidth * options.bpPerPixel);
20409
20415
 
20410
- if (this.reversed) {
20411
- letter = complement[letter] || "";
20412
- }
20416
+ for (let bp = Math.floor(options.bpStart); bp <= bpEnd; bp++) {
20413
20417
 
20414
- let offsetBP = bp - options.bpStart;
20415
- let aPixel = offsetBP / options.bpPerPixel;
20416
- let bPixel = (offsetBP + 1) / options.bpPerPixel;
20417
- let color = this.fillColor(letter);
20418
+ const seqIdx = Math.floor(bp - sequenceBpStart);
20418
20419
 
20419
- // IGVGraphics.fillRect(ctx, aPixel, 5, bPixel - aPixel, height - 5, { fillStyle: randomRGBConstantAlpha(150, 255, 0.75) });
20420
+ if (seqIdx >= 0 && seqIdx < sequence.length) {
20421
+ const baseLetter = sequence[seqIdx];
20422
+ const offsetBP = bp - options.bpStart;
20423
+ const aPixel = offsetBP / options.bpPerPixel;
20424
+ const pixelWidth = 1 / options.bpPerPixel;
20425
+ const color = this.fillColor(baseLetter);
20420
20426
 
20421
20427
  if (options.bpPerPixel > 1 / 10) {
20422
- IGVGraphics.fillRect(ctx, aPixel, 5, bPixel - aPixel, height - 5, {fillStyle: color});
20428
+ IGVGraphics.fillRect(ctx, aPixel, 5, pixelWidth, SEQUENCE_HEIGHT - 5, {fillStyle: color});
20423
20429
  } else {
20424
- let xPixel = 0.5 * (aPixel + bPixel - ctx.measureText(letter).width);
20425
- IGVGraphics.strokeText(ctx, letter, xPixel, height, {strokeStyle: color});
20430
+ let textPixel = aPixel + 0.5 * (pixelWidth - ctx.measureText(baseLetter).width);
20431
+ IGVGraphics.strokeText(ctx, baseLetter, textPixel, SEQUENCE_HEIGHT, {strokeStyle: color});
20426
20432
  }
20427
20433
  }
20428
20434
  }
20429
20435
 
20430
20436
  if (this.frameTranslate) {
20431
20437
 
20432
- let transSeq;
20433
- if (this.reversed) {
20434
- transSeq = sequence.split('').map(function (cv) {
20435
- return complement[cv]
20436
- });
20437
- transSeq = transSeq.join('');
20438
- } else {
20439
- transSeq = sequence;
20440
- }
20441
-
20442
- let y = height;
20443
- let translatedSequence = this.translateSequence(transSeq);
20444
- for (let arr of translatedSequence) {
20438
+ let y = SEQUENCE_HEIGHT + 2 * FRAME_BORDER;
20439
+ const translatedSequence = this.translateSequence(sequence);
20445
20440
 
20446
- let i = translatedSequence.indexOf(arr);
20447
- let fNum = i;
20448
- let h = 25;
20441
+ for (let fNum = 0; fNum < translatedSequence.length; fNum++) { // == 3, 1 for each frame
20449
20442
 
20450
- y = (i === 0) ? y + 10 : y + 30; //Little less room at first.
20443
+ const aaSequence = translatedSequence[fNum]; // AA sequence for this frame
20451
20444
 
20452
- for (let cv of arr) {
20445
+ for (let idx = 0; idx < aaSequence.length; idx++) {
20453
20446
 
20454
- let aaS;
20455
- let idx = arr.indexOf(cv);
20456
- let xSeed = (idx + fNum) + (2 * idx);
20457
20447
  let color = 0 === idx % 2 ? 'rgb(160,160,160)' : 'rgb(224,224,224)';
20448
+ const cv = aaSequence[idx];
20449
+
20450
+ const bpPos = sequenceBpStart + fNum + (idx * 3);
20451
+ const bpOffset = bpPos - options.bpStart;
20452
+ const p0 = Math.floor(bpOffset / options.bpPerPixel);
20453
+ const p1 = Math.floor((bpOffset + 3) / options.bpPerPixel);
20454
+ const pc = Math.round((p0 + p1) / 2);
20455
+
20456
+ if (p1 < 0) {
20457
+ continue // off left edge
20458
+ } else if (p0 > options.pixelWidth) {
20459
+ break // off right edge
20460
+ }
20458
20461
 
20459
- let p0 = Math.floor(xSeed / options.bpPerPixel);
20460
- let p1 = Math.floor((xSeed + 3) / options.bpPerPixel);
20461
- let pc = Math.round((p0 + p1) / 2);
20462
-
20462
+ let aaLabel = cv.aminoA;
20463
20463
  if (cv.aminoA.indexOf('STOP') > -1) {
20464
20464
  color = 'rgb(255, 0, 0)';
20465
- aaS = 'STOP'; //Color blind accessible
20466
- } else {
20467
- aaS = cv.aminoA;
20468
- }
20469
-
20470
- if (cv.aminoA === 'M') {
20465
+ aaLabel = 'STOP'; //Color blind accessible
20466
+ } else if (cv.aminoA === 'M') {
20471
20467
  color = 'rgb(0, 153, 0)';
20472
- aaS = 'START'; //Color blind accessible
20468
+ aaLabel = 'START'; //Color blind accessible
20473
20469
  }
20474
20470
 
20475
- IGVGraphics.fillRect(ctx, p0, y, p1 - p0, h, {fillStyle: color});
20471
+ IGVGraphics.fillRect(ctx, p0, y, p1 - p0, FRAME_HEIGHT, {fillStyle: color});
20476
20472
 
20477
20473
  if (options.bpPerPixel <= 1 / 10) {
20478
- IGVGraphics.strokeText(ctx, aaS, pc - (ctx.measureText(aaS).width / 2), y + 15);
20474
+ IGVGraphics.strokeText(ctx, aaLabel, pc - (ctx.measureText(aaLabel).width / 2), y + 15);
20479
20475
  }
20480
20476
  }
20477
+ y += (FRAME_HEIGHT + FRAME_BORDER);
20481
20478
  }
20482
20479
  }
20483
20480
  }
@@ -20488,6 +20485,7 @@ class SequenceTrack {
20488
20485
  }
20489
20486
 
20490
20487
  computePixelHeight(ignore) {
20488
+ this.height = this.frameTranslate ? TRANSLATED_HEIGHT : DEFAULT_HEIGHT;
20491
20489
  return this.height
20492
20490
  }
20493
20491
 
@@ -20500,8 +20498,26 @@ class SequenceTrack {
20500
20498
  } else {
20501
20499
  return 'rgb(0, 0, 150)'
20502
20500
  }
20501
+ }
20503
20502
 
20503
+ /**
20504
+ * Return the current state of the track. Used to create sessions and bookmarks.
20505
+ *
20506
+ * @returns {*|{}}
20507
+ */
20508
+ getState() {
20509
+ const config = {
20510
+ type: "sequence"
20511
+ };
20512
+ if (this.order !== defaultSequenceTrackOrder) {
20513
+ config.order = this.order;
20514
+ }
20515
+ if (this.reversed) {
20516
+ config.revealed = true;
20517
+ }
20518
+ return config
20504
20519
  }
20520
+
20505
20521
  }
20506
20522
 
20507
20523
  /*
@@ -22337,7 +22353,9 @@ class FastaSequence {
22337
22353
 
22338
22354
  async getSequence(chr, start, end) {
22339
22355
 
22340
- if (!(this.interval && this.interval.contains(chr, start, end))) {
22356
+ const hasCachedSquence = this.interval && this.interval.contains(chr, start, end);
22357
+
22358
+ if (!hasCachedSquence) {
22341
22359
 
22342
22360
  // Expand query, to minimum of 50kb
22343
22361
  let qstart = start;
@@ -22796,7 +22814,7 @@ const Cytoband = function (start, end, name, typestain) {
22796
22814
  }
22797
22815
  };
22798
22816
 
22799
- const _version = "2.12.1";
22817
+ const _version = "2.12.4";
22800
22818
  function version() {
22801
22819
  return _version
22802
22820
  }
@@ -23460,6 +23478,17 @@ class TrackViewport extends Viewport {
23460
23478
  }
23461
23479
  }
23462
23480
 
23481
+ repaintDimensions() {
23482
+ const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23483
+ const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
23484
+ const bpPerPixel = this.referenceFrame.bpPerPixel;
23485
+ const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23486
+ const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23487
+ return {
23488
+ startBP, endBP, pixelWidth
23489
+ }
23490
+ }
23491
+
23463
23492
  /**
23464
23493
  * Repaint the canvas using the cached features
23465
23494
  *
@@ -23474,11 +23503,11 @@ class TrackViewport extends Viewport {
23474
23503
  //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
23475
23504
 
23476
23505
  // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
23477
- const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23506
+ GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23478
23507
 
23479
23508
  // Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
23480
23509
  // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
23481
- const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
23510
+ const {startBP, endBP, pixelWidth} = this.repaintDimensions();
23482
23511
  const viewportHeight = this.$viewport.height();
23483
23512
  const contentHeight = this.getContentHeight();
23484
23513
  const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
@@ -23492,8 +23521,8 @@ class TrackViewport extends Viewport {
23492
23521
  const canvasTop = Math.max(0, -(this.$content.position().top) - viewportHeight);
23493
23522
 
23494
23523
  const bpPerPixel = this.referenceFrame.bpPerPixel;
23495
- const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23496
- const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23524
+ //const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel)
23525
+ //const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel)
23497
23526
  const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
23498
23527
 
23499
23528
  const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
@@ -23756,10 +23785,9 @@ class TrackViewport extends Viewport {
23756
23785
  if (!this.featureCache) return true
23757
23786
  const referenceFrame = this.referenceFrame;
23758
23787
  const chr = this.referenceFrame.chr;
23759
- const start = referenceFrame.start;
23760
- const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
23761
23788
  const bpPerPixel = referenceFrame.bpPerPixel;
23762
- return (!this.featureCache.containsRange(chr, start, end, bpPerPixel))
23789
+ const {startBP, endBP} = this.repaintDimensions();
23790
+ return (!this.featureCache.containsRange(chr, startBP, endBP, bpPerPixel))
23763
23791
  }
23764
23792
 
23765
23793
  createZoomInNotice($parent) {
@@ -24397,6 +24425,10 @@ class PairedAlignment {
24397
24425
  return this.firstAlignment.mate.strand // Assumption is mate is first-of-pair
24398
24426
  }
24399
24427
  }
24428
+
24429
+ hasTag(str) {
24430
+ return this.firstAlignment.hasTag(str) || (this.secondAlignment && this.secondAlignment.hasTag(str))
24431
+ }
24400
24432
  }
24401
24433
 
24402
24434
  /*
@@ -29176,7 +29208,7 @@ class TrackBase {
29176
29208
  }
29177
29209
 
29178
29210
  get supportsWholeGenome() {
29179
- return false
29211
+ return this.config.supportsWholeGenome === true
29180
29212
  }
29181
29213
 
29182
29214
  /**
@@ -31566,27 +31598,45 @@ const makePairedAlignmentChords = (alignments) => {
31566
31598
 
31567
31599
  const chords = [];
31568
31600
  for (let a of alignments) {
31569
- const mate = a.mate;
31570
- if (mate && mate.chr && mate.position) {
31571
- chords.push({
31572
- uniqueId: a.readName,
31573
- refName: shortChrName(a.chr),
31574
- start: a.start,
31575
- end: a.end,
31576
- mate: {
31577
- refName: shortChrName(mate.chr),
31578
- start: mate.position - 1,
31579
- end: mate.position,
31580
- }
31581
- });
31601
+
31602
+ if(a.paired) {
31603
+ if(a.firstAlignment && a.secondAlignment) {
31604
+ chords.push({
31605
+ uniqueId: a.readName,
31606
+ refName: shortChrName(a.firstAlignment.chr),
31607
+ start: a.firstAlignment.start,
31608
+ end: a.firstAlignment.end,
31609
+ mate: {
31610
+ refName: shortChrName(a.secondAlignment.chr),
31611
+ start: a.secondAlignment.start,
31612
+ end: a.secondAlignment.end,
31613
+ }
31614
+ });
31615
+ }
31616
+ }
31617
+ else {
31618
+ const mate = a.mate;
31619
+ if (mate && mate.chr && mate.position) {
31620
+ chords.push({
31621
+ uniqueId: a.readName,
31622
+ refName: shortChrName(a.chr),
31623
+ start: a.start,
31624
+ end: a.end,
31625
+ mate: {
31626
+ refName: shortChrName(mate.chr),
31627
+ start: mate.position - 1,
31628
+ end: mate.position,
31629
+ }
31630
+ });
31631
+ }
31582
31632
  }
31583
31633
  }
31584
31634
  return chords
31585
31635
  };
31586
31636
 
31587
31637
  const makeSupplementalAlignmentChords = (alignments) => {
31588
- const chords = [];
31589
- for (let a of alignments) {
31638
+
31639
+ const makeChords = (a) => {
31590
31640
  const sa = a.tags()['SA'];
31591
31641
  const supAl = createSupplementaryAlignments(sa);
31592
31642
  let n = 0;
@@ -31605,6 +31655,18 @@ const makeSupplementalAlignmentChords = (alignments) => {
31605
31655
  });
31606
31656
  }
31607
31657
  }
31658
+ };
31659
+
31660
+ const chords = [];
31661
+ for (let a of alignments) {
31662
+ if(a.paired) {
31663
+ makeChords(a.firstAlignment);
31664
+ if(a.secondAlignment) {
31665
+ makeChords(a.secondAlignment);
31666
+ }
31667
+ } else {
31668
+ makeChords(a);
31669
+ }
31608
31670
  }
31609
31671
  return chords
31610
31672
  };
@@ -32326,11 +32388,18 @@ class BAMTrack extends TrackBase {
32326
32388
  const refFrame = viewport.referenceFrame;
32327
32389
  for (let a of viewport.cachedFeatures.allAlignments()) {
32328
32390
  if (a.end >= refFrame.start
32329
- && a.start <= refFrame.end
32330
- && a.mate
32331
- && a.mate.chr
32332
- && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
32333
- inView.push(a);
32391
+ && a.start <= refFrame.end) {
32392
+ if (a.paired) {
32393
+ if (a.end - a.start > maxTemplateLength) {
32394
+ inView.push(a);
32395
+ }
32396
+ } else {
32397
+ if (a.mate
32398
+ && a.mate.chr
32399
+ && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
32400
+ inView.push(a);
32401
+ }
32402
+ }
32334
32403
  }
32335
32404
  }
32336
32405
  const chords = makePairedAlignmentChords(inView);
@@ -33909,7 +33978,7 @@ class SampleNameViewport {
33909
33978
  for (let {sampleNameViewport} of this.browser.trackViews) {
33910
33979
  sampleNameViewport.setWidth(this.browser.sampleNameViewportWidth);
33911
33980
  }
33912
- this.browser.resize();
33981
+ this.browser.layoutChange();
33913
33982
  }
33914
33983
  };
33915
33984
 
@@ -39395,7 +39464,7 @@ function pack(featureList, maxRows) {
39395
39464
  let r = 0;
39396
39465
  const len = Math.min(rows.length, maxRows);
39397
39466
  for (r = 0; r < len; r++) {
39398
- if (feature.start > rows[r]) {
39467
+ if (feature.start >= rows[r]) {
39399
39468
  feature.row = r;
39400
39469
  rows[r] = feature.end;
39401
39470
  break
@@ -40912,7 +40981,7 @@ class BWSource {
40912
40981
  }
40913
40982
 
40914
40983
  supportsWholeGenome() {
40915
- return this.reader.type === "bigwig" || this.defaultVisibilityWindow() <= 0
40984
+ return this.reader.type === "bigwig"
40916
40985
  }
40917
40986
 
40918
40987
  async trackType() {
@@ -42147,7 +42216,15 @@ class FeatureTrack extends TrackBase {
42147
42216
  }
42148
42217
 
42149
42218
  get supportsWholeGenome() {
42150
- return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false
42219
+ if (this.config.supportsWholeGenome !== undefined) {
42220
+ return this.config.supportsWholeGenome
42221
+ } else if (this.featureSource && typeof this.featureSource.supportsWholeGenome === 'function') {
42222
+ return this.featureSource.supportsWholeGenome()
42223
+ } else {
42224
+ if (this.visibilityWindow === undefined && (this.config.indexed === false || !this.config.indexURL)) {
42225
+ return true
42226
+ }
42227
+ }
42151
42228
  }
42152
42229
 
42153
42230
  async getFeatures(chr, start, end, bpPerPixel) {
@@ -42204,7 +42281,7 @@ class FeatureTrack extends TrackBase {
42204
42281
  options.rowLastX = [];
42205
42282
  options.rowLastLabelX = [];
42206
42283
  for (let feature of featureList) {
42207
- if(feature.start > bpStart && feature.end < bpEnd) {
42284
+ if (feature.start > bpStart && feature.end < bpEnd) {
42208
42285
  const row = this.displayMode === "COLLAPSED" ? 0 : feature.row || 0;
42209
42286
  if (rowFeatureCount[row] === undefined) {
42210
42287
  rowFeatureCount[row] = 1;
@@ -44618,9 +44695,9 @@ class VariantTrack extends TrackBase {
44618
44695
  IGVGraphics.fillRect(context, 0, pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
44619
44696
 
44620
44697
  const vGap = ("SQUISHED" === this.displayMode) ? this.squishedVGap : this.expandedVGap;
44621
- const rc = ("COLLAPSED" === this.displayMode) ? 1 : this.nVariantRows;
44698
+ const rowCount = ("COLLAPSED" === this.displayMode) ? 1 : this.nVariantRows;
44622
44699
  const variantHeight = ("SQUISHED" === this.displayMode) ? this.squishedVariantHeight : this.expandedVariantHeight;
44623
- this.variantBandHeight = TOP_MARGIN + rc * (variantHeight + vGap);
44700
+ this.variantBandHeight = TOP_MARGIN + rowCount * (variantHeight + vGap);
44624
44701
 
44625
44702
  const callSets = this.callSets;
44626
44703
  const nCalls = this.getCallsetsLength();
@@ -44753,15 +44830,17 @@ class VariantTrack extends TrackBase {
44753
44830
  if (yOffset <= this.variantBandHeight) {
44754
44831
  // Variant
44755
44832
  const variantHeight = ("SQUISHED" === this.displayMode) ? this.squishedVariantHeight : this.expandedVariantHeight;
44756
- const variantRow = (Math.floor)((yOffset - TOP_MARGIN) / (variantHeight + vGap));
44757
- featureList = featureList.filter(f => f.row === variantRow);
44833
+ const variantRow = Math.floor((yOffset - TOP_MARGIN) / (variantHeight + vGap));
44834
+ if("COLLAPSED" !== this.displayMode) {
44835
+ featureList = featureList.filter(f => f.row === variantRow);
44836
+ }
44758
44837
  } else if (this.callSets) {
44759
44838
  const callSets = this.callSets;
44760
44839
  const sampleY = yOffset - this.variantBandHeight;
44761
44840
  const sampleRow = Math.floor(sampleY / this.sampleHeight);
44762
44841
  if (sampleRow >= 0 && sampleRow < callSets.length) {
44763
44842
  const variantRow = Math.floor((sampleY - sampleRow * this.sampleHeight) / callHeight);
44764
- const variants = featureList.filter(f => f.row === variantRow);
44843
+ const variants = "COLLAPSED" === this.displayMode ? featureList : featureList.filter(f => f.row === variantRow);
44765
44844
  const cs = callSets[sampleRow];
44766
44845
  featureList = variants.map(v => {
44767
44846
  const call = v.calls[cs.id];
@@ -48642,7 +48721,7 @@ class SampleNameControl {
48642
48721
  }
48643
48722
  }
48644
48723
 
48645
- browser.resize();
48724
+ browser.layoutChange();
48646
48725
 
48647
48726
 
48648
48727
  });
@@ -50312,7 +50391,16 @@ class Browser {
50312
50391
 
50313
50392
  }
50314
50393
 
50394
+ /**
50395
+ * API function to signal that this browser visibility has changed, e.g. from hiding/showing in a tab interface.
50396
+ *
50397
+ * @returns {Promise<void>}
50398
+ */
50315
50399
  async visibilityChange() {
50400
+ this.layoutChange();
50401
+ }
50402
+
50403
+ async layoutChange() {
50316
50404
 
50317
50405
  const status = this.referenceFrameList.find(referenceFrame => referenceFrame.bpPerPixel < 0);
50318
50406