igv 2.12.0 → 2.12.3

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
@@ -19712,6 +19712,8 @@ const knownFileExtensions = new Set([
19712
19712
  "bb",
19713
19713
  "bigbed",
19714
19714
  "biginteract",
19715
+ "biggenepred",
19716
+ "bignarrowpeak",
19715
19717
  "bw",
19716
19718
  "bigwig",
19717
19719
  "bam",
@@ -19863,6 +19865,8 @@ function inferTrackType(config) {
19863
19865
  case "bed":
19864
19866
  case "bigbed":
19865
19867
  case "bb":
19868
+ case "biggenepred":
19869
+ case "bignarrowpeak":
19866
19870
  return "bedtype"
19867
19871
  default:
19868
19872
  return "annotation"
@@ -20247,6 +20251,12 @@ for (let i = 0; i < t1.length; i++) {
20247
20251
  complement[t1[i].toLowerCase()] = t2[i].toLowerCase();
20248
20252
  }
20249
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
+
20250
20260
  class SequenceTrack {
20251
20261
 
20252
20262
  constructor(config, browser) {
@@ -20258,13 +20268,13 @@ class SequenceTrack {
20258
20268
  this.name = "Sequence";
20259
20269
  this.id = "sequence";
20260
20270
  this.sequenceType = config.sequenceType || "dna"; // dna | rna | prot
20261
- this.height = 25;
20262
20271
  this.disableButtons = false;
20263
20272
  this.order = config.order || defaultSequenceTrackOrder;
20264
20273
  this.ignoreTrackMenu = false;
20265
20274
 
20266
- this.reversed = false;
20267
- this.frameTranslate = false;
20275
+ this.reversed = config.reversed === true;
20276
+ this.frameTranslate = config.frameTranslate === true;
20277
+ this.height = this.frameTranslate ? TRANSLATED_HEIGHT : DEFAULT_HEIGHT;
20268
20278
 
20269
20279
  }
20270
20280
 
@@ -20283,14 +20293,14 @@ class SequenceTrack {
20283
20293
  this.frameTranslate = !this.frameTranslate;
20284
20294
  if (this.frameTranslate) {
20285
20295
  for (let vp of this.trackView.viewports) {
20286
- vp.setContentHeight(115);
20296
+ vp.setContentHeight(TRANSLATED_HEIGHT);
20287
20297
  }
20288
- this.trackView.setTrackHeight(115);
20298
+ this.trackView.setTrackHeight(TRANSLATED_HEIGHT);
20289
20299
  } else {
20290
20300
  for (let vp of this.trackView.viewports) {
20291
- vp.setContentHeight(25);
20301
+ vp.setContentHeight(DEFAULT_HEIGHT);
20292
20302
  }
20293
- this.trackView.setTrackHeight(25);
20303
+ this.trackView.setTrackHeight(DEFAULT_HEIGHT);
20294
20304
  }
20295
20305
  this.trackView.repaintViews();
20296
20306
 
@@ -20373,7 +20383,8 @@ class SequenceTrack {
20373
20383
  }
20374
20384
 
20375
20385
  async getFeatures(chr, start, end, bpPerPixel) {
20376
-
20386
+ start = Math.floor(start);
20387
+ end = Math.floor(end);
20377
20388
  if (bpPerPixel && bpPerPixel > 1) {
20378
20389
  return null
20379
20390
  } else {
@@ -20391,89 +20402,79 @@ class SequenceTrack {
20391
20402
 
20392
20403
  if (options.features) {
20393
20404
 
20394
- const sequence = options.features.sequence;
20395
- const sequenceBpStart = options.features.bpStart;
20396
- const bpEnd = 1 + options.bpStart + (options.pixelWidth * options.bpPerPixel);
20405
+ let sequence = options.features.sequence;
20397
20406
 
20398
- let height = 15;
20399
- for (let bp = sequenceBpStart; bp <= bpEnd; bp++) {
20400
-
20401
- let seqOffsetBp = Math.floor(bp - sequenceBpStart);
20407
+ if (this.reversed) {
20408
+ sequence = sequence.split('').map(function (cv) {
20409
+ return complement[cv]
20410
+ }).join('');
20411
+ }
20402
20412
 
20403
- if (seqOffsetBp < sequence.length) {
20404
- 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);
20405
20415
 
20406
- if (this.reversed) {
20407
- letter = complement[letter] || "";
20408
- }
20416
+ for (let bp = Math.floor(options.bpStart); bp <= bpEnd; bp++) {
20409
20417
 
20410
- let offsetBP = bp - options.bpStart;
20411
- let aPixel = offsetBP / options.bpPerPixel;
20412
- let bPixel = (offsetBP + 1) / options.bpPerPixel;
20413
- let color = this.fillColor(letter);
20418
+ const seqIdx = Math.floor(bp - sequenceBpStart);
20414
20419
 
20415
- // 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);
20416
20426
 
20417
20427
  if (options.bpPerPixel > 1 / 10) {
20418
- IGVGraphics.fillRect(ctx, aPixel, 5, bPixel - aPixel, height - 5, {fillStyle: color});
20428
+ IGVGraphics.fillRect(ctx, aPixel, 5, pixelWidth, SEQUENCE_HEIGHT - 5, {fillStyle: color});
20419
20429
  } else {
20420
- let xPixel = 0.5 * (aPixel + bPixel - ctx.measureText(letter).width);
20421
- 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});
20422
20432
  }
20423
20433
  }
20424
20434
  }
20425
20435
 
20426
20436
  if (this.frameTranslate) {
20427
20437
 
20428
- let transSeq;
20429
- if (this.reversed) {
20430
- transSeq = sequence.split('').map(function (cv) {
20431
- return complement[cv]
20432
- });
20433
- transSeq = transSeq.join('');
20434
- } else {
20435
- transSeq = sequence;
20436
- }
20438
+ let y = SEQUENCE_HEIGHT + 2 * FRAME_BORDER;
20439
+ const translatedSequence = this.translateSequence(sequence);
20437
20440
 
20438
- let y = height;
20439
- let translatedSequence = this.translateSequence(transSeq);
20440
- for (let arr of translatedSequence) {
20441
+ for (let fNum = 0; fNum < translatedSequence.length; fNum++) { // == 3, 1 for each frame
20441
20442
 
20442
- let i = translatedSequence.indexOf(arr);
20443
- let fNum = i;
20444
- let h = 25;
20443
+ const aaSequence = translatedSequence[fNum]; // AA sequence for this frame
20445
20444
 
20446
- y = (i === 0) ? y + 10 : y + 30; //Little less room at first.
20445
+ for (let idx = 0; idx < aaSequence.length; idx++) {
20447
20446
 
20448
- for (let cv of arr) {
20449
-
20450
- let aaS;
20451
- let idx = arr.indexOf(cv);
20452
- let xSeed = (idx + fNum) + (2 * idx);
20453
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
+ }
20454
20461
 
20455
- let p0 = Math.floor(xSeed / options.bpPerPixel);
20456
- let p1 = Math.floor((xSeed + 3) / options.bpPerPixel);
20457
- let pc = Math.round((p0 + p1) / 2);
20458
-
20462
+ let aaLabel = cv.aminoA;
20459
20463
  if (cv.aminoA.indexOf('STOP') > -1) {
20460
20464
  color = 'rgb(255, 0, 0)';
20461
- aaS = 'STOP'; //Color blind accessible
20462
- } else {
20463
- aaS = cv.aminoA;
20464
- }
20465
-
20466
- if (cv.aminoA === 'M') {
20465
+ aaLabel = 'STOP'; //Color blind accessible
20466
+ } else if (cv.aminoA === 'M') {
20467
20467
  color = 'rgb(0, 153, 0)';
20468
- aaS = 'START'; //Color blind accessible
20468
+ aaLabel = 'START'; //Color blind accessible
20469
20469
  }
20470
20470
 
20471
- IGVGraphics.fillRect(ctx, p0, y, p1 - p0, h, {fillStyle: color});
20471
+ IGVGraphics.fillRect(ctx, p0, y, p1 - p0, FRAME_HEIGHT, {fillStyle: color});
20472
20472
 
20473
20473
  if (options.bpPerPixel <= 1 / 10) {
20474
- 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);
20475
20475
  }
20476
20476
  }
20477
+ y += (FRAME_HEIGHT + FRAME_BORDER);
20477
20478
  }
20478
20479
  }
20479
20480
  }
@@ -20484,6 +20485,7 @@ class SequenceTrack {
20484
20485
  }
20485
20486
 
20486
20487
  computePixelHeight(ignore) {
20488
+ this.height = this.frameTranslate ? TRANSLATED_HEIGHT : DEFAULT_HEIGHT;
20487
20489
  return this.height
20488
20490
  }
20489
20491
 
@@ -20496,8 +20498,21 @@ class SequenceTrack {
20496
20498
  } else {
20497
20499
  return 'rgb(0, 0, 150)'
20498
20500
  }
20501
+ }
20499
20502
 
20503
+ /**
20504
+ * Return the current state of the track. Used to create sessions and bookmarks.
20505
+ *
20506
+ * @returns {*|{}}
20507
+ */
20508
+ getState() {
20509
+
20510
+ const config = typeof super.getState === 'function' ? super.getState() : {};
20511
+ if (this.reversed) config.revealed = true;
20512
+ if (this.frameTranslate) config.frameTranslate = true;
20513
+ return config
20500
20514
  }
20515
+
20501
20516
  }
20502
20517
 
20503
20518
  /*
@@ -22333,7 +22348,9 @@ class FastaSequence {
22333
22348
 
22334
22349
  async getSequence(chr, start, end) {
22335
22350
 
22336
- if (!(this.interval && this.interval.contains(chr, start, end))) {
22351
+ const hasCachedSquence = this.interval && this.interval.contains(chr, start, end);
22352
+
22353
+ if (!hasCachedSquence) {
22337
22354
 
22338
22355
  // Expand query, to minimum of 50kb
22339
22356
  let qstart = start;
@@ -22792,7 +22809,7 @@ const Cytoband = function (start, end, name, typestain) {
22792
22809
  }
22793
22810
  };
22794
22811
 
22795
- const _version = "2.12.0";
22812
+ const _version = "2.12.3";
22796
22813
  function version() {
22797
22814
  return _version
22798
22815
  }
@@ -22940,7 +22957,7 @@ class Genome {
22940
22957
  constructor(config, sequence, ideograms, aliases) {
22941
22958
 
22942
22959
  this.config = config;
22943
- this.id = config.id;
22960
+ this.id = config.id || generateGenomeID(config);
22944
22961
  this.sequence = sequence;
22945
22962
  this.chromosomeNames = sequence.chromosomeNames;
22946
22963
  this.chromosomes = sequence.chromosomes; // An object (functions as a dictionary)
@@ -23262,6 +23279,18 @@ function constructWG(genome, config) {
23262
23279
 
23263
23280
  }
23264
23281
 
23282
+ function generateGenomeID(config) {
23283
+ if (config.id !== undefined) {
23284
+ return config.id
23285
+ } else if (config.fastaURL && isString$3(config.fastaURL)) {
23286
+ return config.fastaURL
23287
+ } else if (config.fastaURL && config.fastaURL.name) {
23288
+ return config.fastaURL.name
23289
+ } else {
23290
+ return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)
23291
+ }
23292
+ }
23293
+
23265
23294
  /**
23266
23295
  * Created by dat on 9/16/16.
23267
23296
  */
@@ -23444,6 +23473,17 @@ class TrackViewport extends Viewport {
23444
23473
  }
23445
23474
  }
23446
23475
 
23476
+ repaintDimensions() {
23477
+ const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23478
+ const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
23479
+ const bpPerPixel = this.referenceFrame.bpPerPixel;
23480
+ const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23481
+ const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23482
+ return {
23483
+ startBP, endBP, pixelWidth
23484
+ }
23485
+ }
23486
+
23447
23487
  /**
23448
23488
  * Repaint the canvas using the cached features
23449
23489
  *
@@ -23458,11 +23498,11 @@ class TrackViewport extends Viewport {
23458
23498
  //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
23459
23499
 
23460
23500
  // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
23461
- const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23501
+ GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
23462
23502
 
23463
23503
  // Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
23464
23504
  // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
23465
- const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
23505
+ const {startBP, endBP, pixelWidth} = this.repaintDimensions();
23466
23506
  const viewportHeight = this.$viewport.height();
23467
23507
  const contentHeight = this.getContentHeight();
23468
23508
  const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
@@ -23476,8 +23516,8 @@ class TrackViewport extends Viewport {
23476
23516
  const canvasTop = Math.max(0, -(this.$content.position().top) - viewportHeight);
23477
23517
 
23478
23518
  const bpPerPixel = this.referenceFrame.bpPerPixel;
23479
- const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23480
- const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
23519
+ //const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel)
23520
+ //const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel)
23481
23521
  const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
23482
23522
 
23483
23523
  const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
@@ -23740,10 +23780,9 @@ class TrackViewport extends Viewport {
23740
23780
  if (!this.featureCache) return true
23741
23781
  const referenceFrame = this.referenceFrame;
23742
23782
  const chr = this.referenceFrame.chr;
23743
- const start = referenceFrame.start;
23744
- const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
23745
23783
  const bpPerPixel = referenceFrame.bpPerPixel;
23746
- return (!this.featureCache.containsRange(chr, start, end, bpPerPixel))
23784
+ const {startBP, endBP} = this.repaintDimensions();
23785
+ return (!this.featureCache.containsRange(chr, startBP, endBP, bpPerPixel))
23747
23786
  }
23748
23787
 
23749
23788
  createZoomInNotice($parent) {
@@ -23847,7 +23886,7 @@ class TrackViewport extends Viewport {
23847
23886
 
23848
23887
  viewport.addEventListener('click', (event) => {
23849
23888
 
23850
- if (this.enableClick) {
23889
+ if (this.enableClick && this.canvas) {
23851
23890
  if (3 === event.which || event.ctrlKey) {
23852
23891
  return
23853
23892
  }
@@ -24381,6 +24420,10 @@ class PairedAlignment {
24381
24420
  return this.firstAlignment.mate.strand // Assumption is mate is first-of-pair
24382
24421
  }
24383
24422
  }
24423
+
24424
+ hasTag(str) {
24425
+ return this.firstAlignment.hasTag(str) || (this.secondAlignment && this.secondAlignment.hasTag(str))
24426
+ }
24384
24427
  }
24385
24428
 
24386
24429
  /*
@@ -29004,6 +29047,10 @@ class BamSource {
29004
29047
  * THE SOFTWARE.
29005
29048
  */
29006
29049
 
29050
+ const fixColor = (colorString) => {
29051
+ return (colorString.indexOf(",") > 0 && !colorString.startsWith("rgb")) ?
29052
+ `rgb(${colorString})` : colorString
29053
+ };
29007
29054
 
29008
29055
  /**
29009
29056
  * A collection of properties and methods shared by all (or most) track types.
@@ -29048,8 +29095,8 @@ class TrackBase {
29048
29095
 
29049
29096
  this.order = config.order;
29050
29097
 
29051
- this.color = config.color;
29052
- this.altColor = config.altColor;
29098
+ if(config.color) this.color = fixColor(config.color);
29099
+ if(config.altColor) this.altColor = fixColor(config.altColor);
29053
29100
  if ("civic-ws" === config.sourceType) { // Ugly proxy for specialized track type
29054
29101
  this.defaultColor = "rgb(155,20,20)";
29055
29102
  } else {
@@ -30944,7 +30991,7 @@ class ChordSetManager {
30944
30991
  return this.tracks.find(t => name === t.name)
30945
30992
  }
30946
30993
 
30947
- getChordset(name) {
30994
+ getChordSet(name) {
30948
30995
  return this.chordSets.find(cs => name === cs.name)
30949
30996
  }
30950
30997
 
@@ -31062,9 +31109,8 @@ class CircularView {
31062
31109
  buttonContainer.appendChild(this.showControlsButton);
31063
31110
  this.showControlsButton.innerText = 'none' === this.controlPanel.style.display ? 'Show Controls' : 'Hide Controls';
31064
31111
  this.showControlsButton.addEventListener('click', (event) => {
31065
- const trackPanelRows = this.controlPanel.querySelectorAll('div');
31066
- if (trackPanelRows.length > 0) {
31067
-
31112
+ const panelRows = this.controlPanel.querySelectorAll('div');
31113
+ if (panelRows.length > 0) {
31068
31114
  if ('none' === this.controlPanel.style.display) {
31069
31115
  this.controlPanel.style.display = 'flex';
31070
31116
  event.target.innerText = 'Hide Controls';
@@ -31148,10 +31194,10 @@ class CircularView {
31148
31194
  hideShowButton.innerText = true === chordSet.visible ? 'Hide' : 'Show';
31149
31195
  hideShowButton.addEventListener('click', event => {
31150
31196
  if (true === chordSet.visible) {
31151
- this.hideTrack(chordSet.name);
31197
+ this.hideChordSet(chordSet.name);
31152
31198
  event.target.innerText = "Show";
31153
31199
  } else {
31154
- this.showTrack(chordSet.name);
31200
+ this.showChordSet(chordSet.name);
31155
31201
  event.target.innerText = "Hide";
31156
31202
  }
31157
31203
  });
@@ -31175,7 +31221,7 @@ class CircularView {
31175
31221
  color: chordSet.color,
31176
31222
  onChange: ({rgbaString}) => {
31177
31223
  colorPickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
31178
- this.setTrackColor(chordSet.name, rgbaString);
31224
+ this.setColor(chordSet.name, rgbaString);
31179
31225
  alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
31180
31226
  }
31181
31227
  };
@@ -31193,7 +31239,7 @@ class CircularView {
31193
31239
  alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
31194
31240
  alphaSlider.oninput = () => {
31195
31241
  const v = valueToAlpha(alphaSlider.value);
31196
- this.setTrackColor(chordSet.name, setAlpha(chordSet.color, v));
31242
+ this.setColor(chordSet.name, setAlpha(chordSet.color, v));
31197
31243
  picker.setColor(chordSet.color);
31198
31244
  };
31199
31245
  row.appendChild(alphaSlider);
@@ -31213,11 +31259,13 @@ class CircularView {
31213
31259
  */
31214
31260
  setAssembly(igvGenome) {
31215
31261
 
31216
- if (this.genomeId === igvGenome.id) {
31262
+ const id = this.genomeId || guid();
31263
+
31264
+ if (this.genomeId === id) {
31217
31265
  return
31218
31266
  }
31219
31267
  this.chordManager.clearChords();
31220
- this.genomeId = igvGenome.id;
31268
+ this.genomeId = id;
31221
31269
  this.chrNames = new Set(igvGenome.chromosomes.map(chr => shortChrName$1(chr.name)));
31222
31270
 
31223
31271
  const regions = [];
@@ -31238,7 +31286,7 @@ class CircularView {
31238
31286
  this.assembly = {
31239
31287
  name: igvGenome.name,
31240
31288
  sequence: {
31241
- trackId: igvGenome.id,
31289
+ trackId: id,
31242
31290
  type: 'ReferenceSequenceTrack',
31243
31291
  adapter: {
31244
31292
  type: 'FromConfigSequenceAdapter',
@@ -31279,7 +31327,7 @@ class CircularView {
31279
31327
 
31280
31328
  addChords(newChords, options = {}) {
31281
31329
 
31282
- const tmp = options.track || options.name || "*";
31330
+ const tmp = options.name || options.track || "*";
31283
31331
  const trackName = tmp.split(' ')[0].replaceAll("%20", " ");
31284
31332
  const chordSetName = tmp.replaceAll("%20", " ");
31285
31333
 
@@ -31332,21 +31380,6 @@ class CircularView {
31332
31380
  this.viewState.pluginManager.rootModel.session.clearSelection();
31333
31381
  }
31334
31382
 
31335
- getFeature(featureId) {
31336
-
31337
- // TODO -- broken
31338
- // const display = this.viewState.pluginManager.rootModel.session.view.tracks[0].displays[0]
31339
- // const feature = display.data.features.get(featureId)
31340
- // return feature;
31341
-
31342
- const features = [...this.viewState.config.tracks[0].adapter.features.value];
31343
- for (let f of features) {
31344
- if (featureId === f.uniqueId) {
31345
- return f
31346
- }
31347
- }
31348
- }
31349
-
31350
31383
  /**
31351
31384
  * Deprecated, use "visible" property
31352
31385
  */
@@ -31369,23 +31402,23 @@ class CircularView {
31369
31402
  this.parent.style.display = isVisible ? 'block' : 'none';
31370
31403
  }
31371
31404
 
31372
- hideTrack(trackName) {
31373
- let track = this.getTrack(trackName);
31374
- if (track) {
31375
- track.visible = false;
31405
+ hideChordSet(trackName) {
31406
+ let cs = this.getChordSet(trackName);
31407
+ if (cs) {
31408
+ cs.visible = false;
31376
31409
  this.render();
31377
31410
  } else {
31378
31411
  console.warn(`No track with name: ${name}`);
31379
31412
  }
31380
31413
  }
31381
31414
 
31382
- showTrack(trackName) {
31383
- let track = this.getTrack(trackName);
31384
- if (track) {
31385
- track.visible = true;
31415
+ showChordSet(name) {
31416
+ let cs = this.getChordSet(name);
31417
+ if (cs) {
31418
+ cs.visible = true;
31386
31419
  this.render();
31387
31420
  } else {
31388
- console.warn(`No track with name: ${trackName}`);
31421
+ console.warn(`No track with name: ${name}`);
31389
31422
  }
31390
31423
  }
31391
31424
 
@@ -31411,12 +31444,12 @@ class CircularView {
31411
31444
  this.render();
31412
31445
  }
31413
31446
 
31414
- getTrack(name) {
31415
- return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordset(name)
31447
+ getChordSet(name) {
31448
+ return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordSet(name)
31416
31449
  }
31417
31450
 
31418
- setTrackColor(name, color) {
31419
- const t = this.getTrack(name);
31451
+ setColor(name, color) {
31452
+ const t = this.getChordSet(name);
31420
31453
  if (t) {
31421
31454
  t.color = color;
31422
31455
  const trackID = t.id;
@@ -31443,8 +31476,6 @@ class CircularView {
31443
31476
  // Remove all children from possible previous renders. React might do this for us when we render, but just in case.
31444
31477
  ReactDOM.unmountComponentAtNode(this.container);
31445
31478
 
31446
-
31447
-
31448
31479
  const visibleChordSets =
31449
31480
  (this.groupByTrack ? this.chordManager.tracks : this.chordManager.chordSets).filter(t => t.visible);
31450
31481
 
@@ -31523,7 +31554,7 @@ function guid() {
31523
31554
 
31524
31555
  function embedCSS$1() {
31525
31556
 
31526
- const css = '.igv-circview-container {\n z-index: 2048;\n position: absolute;\n width: fit-content;\n height: fit-content;\n box-sizing: content-box;\n color: dimgray;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n background-color: white;\n border-color: dimgray;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-circview-toolbar {\n position: relative;\n width: 100%;\n height: 32px;\n background-color: lightgrey;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n\n.igv-circview-toolbar-button-container {\n height: 100%;\n width: fit-content;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-toolbar-button-container > div {\n margin: 4px;\n}\n\n.igv-circview-track-panel {\n z-index: 1024;\n position: absolute;\n top: 33px;\n left: 0;\n width: 100%;\n height: fit-content;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n.igv-circview-track-panel > div {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-track-panel > div > div {\n margin: 4px;\n}\n\n.igv-circview-swatch-button {\n cursor: pointer;\n padding: 5px;\n width: 8px;\n height: 8px;\n border: 1px solid #8d8b8b;\n border-radius: 16px;\n}\n\n.igv-circview-button {\n cursor: pointer;\n padding: 5px;\n color: #444;\n vertical-align: middle;\n text-align: center;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n border: 1px solid #8d8b8b;\n border-radius: 4px;\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);\n}\n\n.igv-circview-button:hover {\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n.igv-circview-button:active {\n color: #007bff;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n/*# sourceMappingURL=circular-view.css.map */\n';
31557
+ const css = '.igv-circview-container {\n z-index: 2048;\n width: fit-content;\n height: fit-content;\n box-sizing: content-box;\n color: dimgray;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n background-color: white;\n border-color: dimgray;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-circview-toolbar {\n position: relative;\n width: 100%;\n height: 32px;\n background-color: lightgrey;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n\n.igv-circview-toolbar-button-container {\n height: 100%;\n width: fit-content;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-toolbar-button-container > div {\n margin: 4px;\n}\n\n.igv-circview-track-panel {\n z-index: 1024;\n position: absolute;\n top: 33px;\n left: 0;\n width: 100%;\n height: fit-content;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n.igv-circview-track-panel > div {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-track-panel > div > div {\n margin: 4px;\n}\n\n.igv-circview-swatch-button {\n cursor: pointer;\n padding: 5px;\n width: 8px;\n height: 8px;\n border: 1px solid #8d8b8b;\n border-radius: 16px;\n}\n\n.igv-circview-button {\n cursor: pointer;\n padding: 5px;\n color: #444;\n vertical-align: middle;\n text-align: center;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n border: 1px solid #8d8b8b;\n border-radius: 4px;\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);\n}\n\n.igv-circview-button:hover {\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n.igv-circview-button:active {\n color: #007bff;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n/*# sourceMappingURL=circular-view.css.map */\n';
31527
31558
 
31528
31559
  const style = document.createElement('style');
31529
31560
  style.setAttribute('type', 'text/css');
@@ -31562,27 +31593,45 @@ const makePairedAlignmentChords = (alignments) => {
31562
31593
 
31563
31594
  const chords = [];
31564
31595
  for (let a of alignments) {
31565
- const mate = a.mate;
31566
- if (mate && mate.chr && mate.position) {
31567
- chords.push({
31568
- uniqueId: a.readName,
31569
- refName: shortChrName(a.chr),
31570
- start: a.start,
31571
- end: a.end,
31572
- mate: {
31573
- refName: shortChrName(mate.chr),
31574
- start: mate.position - 1,
31575
- end: mate.position,
31576
- }
31577
- });
31596
+
31597
+ if(a.paired) {
31598
+ if(a.firstAlignment && a.secondAlignment) {
31599
+ chords.push({
31600
+ uniqueId: a.readName,
31601
+ refName: shortChrName(a.firstAlignment.chr),
31602
+ start: a.firstAlignment.start,
31603
+ end: a.firstAlignment.end,
31604
+ mate: {
31605
+ refName: shortChrName(a.secondAlignment.chr),
31606
+ start: a.secondAlignment.start,
31607
+ end: a.secondAlignment.end,
31608
+ }
31609
+ });
31610
+ }
31611
+ }
31612
+ else {
31613
+ const mate = a.mate;
31614
+ if (mate && mate.chr && mate.position) {
31615
+ chords.push({
31616
+ uniqueId: a.readName,
31617
+ refName: shortChrName(a.chr),
31618
+ start: a.start,
31619
+ end: a.end,
31620
+ mate: {
31621
+ refName: shortChrName(mate.chr),
31622
+ start: mate.position - 1,
31623
+ end: mate.position,
31624
+ }
31625
+ });
31626
+ }
31578
31627
  }
31579
31628
  }
31580
31629
  return chords
31581
31630
  };
31582
31631
 
31583
31632
  const makeSupplementalAlignmentChords = (alignments) => {
31584
- const chords = [];
31585
- for (let a of alignments) {
31633
+
31634
+ const makeChords = (a) => {
31586
31635
  const sa = a.tags()['SA'];
31587
31636
  const supAl = createSupplementaryAlignments(sa);
31588
31637
  let n = 0;
@@ -31601,6 +31650,18 @@ const makeSupplementalAlignmentChords = (alignments) => {
31601
31650
  });
31602
31651
  }
31603
31652
  }
31653
+ };
31654
+
31655
+ const chords = [];
31656
+ for (let a of alignments) {
31657
+ if(a.paired) {
31658
+ makeChords(a.firstAlignment);
31659
+ if(a.secondAlignment) {
31660
+ makeChords(a.secondAlignment);
31661
+ }
31662
+ } else {
31663
+ makeChords(a);
31664
+ }
31604
31665
  }
31605
31666
  return chords
31606
31667
  };
@@ -32322,11 +32383,18 @@ class BAMTrack extends TrackBase {
32322
32383
  const refFrame = viewport.referenceFrame;
32323
32384
  for (let a of viewport.cachedFeatures.allAlignments()) {
32324
32385
  if (a.end >= refFrame.start
32325
- && a.start <= refFrame.end
32326
- && a.mate
32327
- && a.mate.chr
32328
- && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
32329
- inView.push(a);
32386
+ && a.start <= refFrame.end) {
32387
+ if (a.paired) {
32388
+ if (a.end - a.start > maxTemplateLength) {
32389
+ inView.push(a);
32390
+ }
32391
+ } else {
32392
+ if (a.mate
32393
+ && a.mate.chr
32394
+ && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
32395
+ inView.push(a);
32396
+ }
32397
+ }
32330
32398
  }
32331
32399
  }
32332
32400
  const chords = makePairedAlignmentChords(inView);
@@ -33181,10 +33249,13 @@ class AlignmentTrack {
33181
33249
  case "pairOrientation":
33182
33250
 
33183
33251
  if (this.pairOrientation && alignment.pairOrientation) {
33184
- var oTypes = orientationTypes[this.pairOrientation];
33252
+ const oTypes = orientationTypes[this.pairOrientation];
33185
33253
  if (oTypes) {
33186
- var pairColor = this.pairColors[oTypes[alignment.pairOrientation]];
33187
- if (pairColor) color = pairColor;
33254
+ const pairColor = this.pairColors[oTypes[alignment.pairOrientation]];
33255
+ if (pairColor) {
33256
+ color = pairColor;
33257
+ break
33258
+ }
33188
33259
  }
33189
33260
  }
33190
33261
  if ("pairOrientation" === option) {
@@ -33902,7 +33973,7 @@ class SampleNameViewport {
33902
33973
  for (let {sampleNameViewport} of this.browser.trackViews) {
33903
33974
  sampleNameViewport.setWidth(this.browser.sampleNameViewportWidth);
33904
33975
  }
33905
- this.browser.resize();
33976
+ this.browser.layoutChange();
33906
33977
  }
33907
33978
  };
33908
33979
 
@@ -35793,7 +35864,7 @@ function decodeBed(tokens, header) {
35793
35864
  const eEnd = eStart + parseInt(exonSizes[i]);
35794
35865
  exons.push({start: eStart, end: eEnd});
35795
35866
  }
35796
- findUTRs(exons, feature.cdStart, feature.cdEnd);
35867
+ findUTRs$1(exons, feature.cdStart, feature.cdEnd);
35797
35868
  feature.exons = exons;
35798
35869
  }
35799
35870
 
@@ -35901,7 +35972,7 @@ function decodeGenePred(tokens, header) {
35901
35972
  const end = parseInt(exonEnds[i]);
35902
35973
  exons.push({start: start, end: end});
35903
35974
  }
35904
- findUTRs(exons, cdStart, cdEnd);
35975
+ findUTRs$1(exons, cdStart, cdEnd);
35905
35976
 
35906
35977
  feature.exons = exons;
35907
35978
 
@@ -35944,7 +36015,7 @@ function decodeGenePredExt(tokens, header) {
35944
36015
  const end = parseInt(exonEnds[i]);
35945
36016
  exons.push({start: start, end: end});
35946
36017
  }
35947
- findUTRs(exons, cdStart, cdEnd);
36018
+ findUTRs$1(exons, cdStart, cdEnd);
35948
36019
 
35949
36020
  feature.exons = exons;
35950
36021
 
@@ -35985,14 +36056,14 @@ function decodeReflat(tokens, header) {
35985
36056
  const end = parseInt(exonEnds[i]);
35986
36057
  exons.push({start: start, end: end});
35987
36058
  }
35988
- findUTRs(exons, cdStart, cdEnd);
36059
+ findUTRs$1(exons, cdStart, cdEnd);
35989
36060
 
35990
36061
  feature.exons = exons;
35991
36062
 
35992
36063
  return feature
35993
36064
  }
35994
36065
 
35995
- function findUTRs(exons, cdStart, cdEnd) {
36066
+ function findUTRs$1(exons, cdStart, cdEnd) {
35996
36067
 
35997
36068
  for (let exon of exons) {
35998
36069
  const end = exon.end;
@@ -39508,7 +39579,7 @@ class TextFeatureSource {
39508
39579
  this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
39509
39580
  this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
39510
39581
 
39511
- const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
39582
+ const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
39512
39583
 
39513
39584
  if (config.features && Array.isArray(config.features)) {
39514
39585
  // Explicit array of features
@@ -39928,11 +39999,9 @@ class BufferedReader {
39928
39999
  }
39929
40000
  }
39930
40001
 
39931
- //table chromatinInteract
39932
-
39933
40002
  function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
39934
40003
 
39935
- if (autoSql && 'chromatinInteract' === autoSql.table || "biginteract" === format) {
40004
+ if ("biginteract" === format || (autoSql && ('chromatinInteract' === autoSql.table) || 'interact' === autoSql.table)) {
39936
40005
  return decodeInteract
39937
40006
  } else {
39938
40007
  const standardFieldCount = definedFieldCount - 3;
@@ -39969,6 +40038,7 @@ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
39969
40038
  const eEnd = eStart + parseInt(exonSizes[i]);
39970
40039
  exons.push({start: eStart, end: eEnd});
39971
40040
  }
40041
+ findUTRs(exons, feature.cdStart, feature.cdEnd);
39972
40042
  feature.exons = exons;
39973
40043
  }
39974
40044
 
@@ -39986,6 +40056,28 @@ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
39986
40056
  }
39987
40057
  }
39988
40058
 
40059
+ //table chromatinInteract
40060
+ // "Chromatin interaction between two regions"
40061
+ // (
40062
+ // string chrom; "Chromosome (or contig, scaffold, etc.). For interchromosomal, use 2 records"
40063
+ // uint chromStart; "Start position of lower region. For interchromosomal, set to chromStart of this region"
40064
+ // uint chromEnd; "End position of upper region. For interchromosomal, set to chromEnd of this region"
40065
+ // string name; "Name of item, for display"
40066
+ // uint score; "Score from 0-1000"
40067
+ // double value; "Strength of interaction or other data value. Typically basis for score"
40068
+ // string exp; "Experiment name (metadata for filtering). Use . if not applicable"
40069
+ // string color; "Item color. Specified as r,g,b or hexadecimal #RRGGBB or html color name, as in //www.w3.org/TR/css3-color/#html4."
40070
+ // string region1Chrom; "Chromosome of lower region. For non-directional interchromosomal, chrom of this region."
40071
+ // uint region1Start; "Start position of lower/this region"
40072
+ // uint region1End; "End position in chromosome of lower/this region"
40073
+ // string region1Name; "Identifier of lower/this region"
40074
+ // string region1Strand; "Orientation of lower/this region: + or -. Use . if not applicable"
40075
+ // string region2Chrom; "Chromosome of upper region. For non-directional interchromosomal, chrom of other region"
40076
+ // uint region2Start; "Start position in chromosome of upper/this region"
40077
+ // uint region2End; "End position in chromosome of upper/this region"
40078
+ // string region2Name; "Identifier of upper/this region"
40079
+ // string region2Strand; "Orientation of upper/this region: + or -. Use . if not applicable"
40080
+ // )
39989
40081
  function decodeInteract(feature, tokens) {
39990
40082
 
39991
40083
  feature.chr1 = tokens[5];
@@ -40005,6 +40097,24 @@ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
40005
40097
  }
40006
40098
  }
40007
40099
 
40100
+ function findUTRs(exons, cdStart, cdEnd) {
40101
+
40102
+ for (let exon of exons) {
40103
+ const end = exon.end;
40104
+ const start = exon.start;
40105
+ if (end < cdStart || start > cdEnd) {
40106
+ exon.utr = true;
40107
+ } else {
40108
+ if (cdStart >= start && cdStart <= end) {
40109
+ exon.cdStart = cdStart;
40110
+ }
40111
+ if (cdEnd >= start && cdEnd <= end) {
40112
+ exon.cdEnd = cdEnd;
40113
+ }
40114
+ }
40115
+ }
40116
+ }
40117
+
40008
40118
  function scoreShade(score, color) {
40009
40119
  const alpha = Math.min(1, 0.11 + 0.89 * (score / 779));
40010
40120
  return alpha.toString()
@@ -41544,11 +41654,12 @@ function zoomLevelForScale(chr, bpPerPixel, genome) {
41544
41654
  * THE SOFTWARE.
41545
41655
  */
41546
41656
 
41657
+ const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
41547
41658
 
41548
41659
  function FeatureSource(config, genome) {
41549
41660
 
41550
41661
  const format = config.format ? config.format.toLowerCase() : undefined;
41551
- if ('bigwig' === format || 'bigbed' === format || 'bb' === format || "biginteract" === format) {
41662
+ if (bbFormats.has(format)) {
41552
41663
  return new BWSource(config, genome)
41553
41664
  } else if ("tdf" === format) {
41554
41665
  return new TDFSource(config, genome)
@@ -41796,10 +41907,9 @@ function renderFeatureLabel(ctx, feature, featureX, featureX1, featureY, referen
41796
41907
  const textBox = ctx.measureText(name);
41797
41908
  const xleft = centerX - textBox.width / 2;
41798
41909
  const xright = centerX + textBox.width / 2;
41799
- if (options.labelAllFeatures || xleft > options.rowLastX[feature.row] || gtexSelection) {
41800
- options.rowLastX[feature.row] = xright;
41910
+ if (options.labelAllFeatures || xleft > options.rowLastLabelX[feature.row] || gtexSelection) {
41911
+ options.rowLastLabelX[feature.row] = xright;
41801
41912
  IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform);
41802
-
41803
41913
  }
41804
41914
  } finally {
41805
41915
  ctx.restore();
@@ -42156,24 +42266,27 @@ class FeatureTrack extends TrackBase {
42156
42266
 
42157
42267
  const rowFeatureCount = [];
42158
42268
  options.rowLastX = [];
42269
+ options.rowLastLabelX = [];
42159
42270
  for (let feature of featureList) {
42160
- const row = feature.row || 0;
42161
- if (rowFeatureCount[row] === undefined) {
42162
- rowFeatureCount[row] = 1;
42163
- } else {
42164
- rowFeatureCount[row]++;
42271
+ if(feature.start > bpStart && feature.end < bpEnd) {
42272
+ const row = this.displayMode === "COLLAPSED" ? 0 : feature.row || 0;
42273
+ if (rowFeatureCount[row] === undefined) {
42274
+ rowFeatureCount[row] = 1;
42275
+ } else {
42276
+ rowFeatureCount[row]++;
42277
+ }
42278
+ options.rowLastX[row] = -Number.MAX_SAFE_INTEGER;
42279
+ options.rowLastLabelX[row] = -Number.MAX_SAFE_INTEGER;
42165
42280
  }
42166
- options.rowLastX[row] = -Number.MAX_SAFE_INTEGER;
42167
42281
  }
42282
+ const pixelsPerFeature = pixelWidth / Math.max(...rowFeatureCount);
42168
42283
 
42169
42284
  let lastPxEnd = [];
42170
42285
  for (let feature of featureList) {
42171
42286
  if (feature.end < bpStart) continue
42172
42287
  if (feature.start > bpEnd) break
42173
-
42174
42288
  const row = this.displayMode === 'COLLAPSED' ? 0 : feature.row;
42175
- const featureDensity = pixelWidth / rowFeatureCount[row];
42176
- options.drawLabel = options.labelAllFeatures || featureDensity > 10;
42289
+ options.drawLabel = options.labelAllFeatures || pixelsPerFeature > 10;
42177
42290
  const pxEnd = Math.ceil((feature.end - bpStart) / bpPerPixel);
42178
42291
  const last = lastPxEnd[row];
42179
42292
  if (!last || pxEnd > last) {
@@ -42187,7 +42300,6 @@ class FeatureTrack extends TrackBase {
42187
42300
  ctx.globalAlpha = 1.0;
42188
42301
  }
42189
42302
  lastPxEnd[row] = pxEnd;
42190
-
42191
42303
  }
42192
42304
  }
42193
42305
 
@@ -48594,7 +48706,7 @@ class SampleNameControl {
48594
48706
  }
48595
48707
  }
48596
48708
 
48597
- browser.resize();
48709
+ browser.layoutChange();
48598
48710
 
48599
48711
 
48600
48712
  });
@@ -50264,7 +50376,16 @@ class Browser {
50264
50376
 
50265
50377
  }
50266
50378
 
50379
+ /**
50380
+ * API function to signal that this browser visibility has changed, e.g. from hiding/showing in a tab interface.
50381
+ *
50382
+ * @returns {Promise<void>}
50383
+ */
50267
50384
  async visibilityChange() {
50385
+ this.layoutChange();
50386
+ }
50387
+
50388
+ async layoutChange() {
50268
50389
 
50269
50390
  const status = this.referenceFrameList.find(referenceFrame => referenceFrame.bpPerPixel < 0);
50270
50391
 
@@ -51005,6 +51126,7 @@ class Browser {
51005
51126
  }
51006
51127
 
51007
51128
  createCircularView(container, show) {
51129
+ show = show === true; // convert undefined to boolean
51008
51130
  this.circularView = createCircularView(container, this);
51009
51131
  this.circularViewControl = new CircularViewControl(this.$toggle_button_container.get(0), this);
51010
51132
  this.circularView.setAssembly({
@@ -51013,7 +51135,7 @@ class Browser {
51013
51135
  chromosomes: makeCircViewChromosomes(this.genome)
51014
51136
  });
51015
51137
  this.circularViewVisible = show;
51016
-
51138
+ return this.circularView
51017
51139
  }
51018
51140
 
51019
51141
  get circularViewVisible() {