igv 2.11.1 → 2.11.2
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 +10 -23
- package/dist/igv.esm.js +601 -624
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +582 -633
- package/dist/igv.min.js +6 -6
- package/dist/igv.min.js.map +1 -1
- package/package.json +4 -4
package/dist/igv.esm.js
CHANGED
|
@@ -17624,7 +17624,7 @@ function Node(interval) {
|
|
|
17624
17624
|
* @constructor
|
|
17625
17625
|
*/
|
|
17626
17626
|
|
|
17627
|
-
class FeatureCache
|
|
17627
|
+
class FeatureCache {
|
|
17628
17628
|
|
|
17629
17629
|
constructor(featureList, genome, range) {
|
|
17630
17630
|
|
|
@@ -20588,13 +20588,14 @@ class Viewport {
|
|
|
20588
20588
|
console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
|
|
20589
20589
|
}
|
|
20590
20590
|
|
|
20591
|
-
checkContentHeight(
|
|
20591
|
+
checkContentHeight() {
|
|
20592
20592
|
|
|
20593
20593
|
let track = this.trackView.track;
|
|
20594
|
-
|
|
20594
|
+
|
|
20595
20595
|
if ("FILL" === track.displayMode) {
|
|
20596
20596
|
this.setContentHeight(this.$viewport.height());
|
|
20597
20597
|
} else if (typeof track.computePixelHeight === 'function') {
|
|
20598
|
+
let features = this.cachedFeatures;
|
|
20598
20599
|
if (features && features.length > 0) {
|
|
20599
20600
|
let requiredContentHeight = track.computePixelHeight(features);
|
|
20600
20601
|
let currentContentHeight = this.$content.height();
|
|
@@ -20610,11 +20611,12 @@ class Viewport {
|
|
|
20610
20611
|
}
|
|
20611
20612
|
|
|
20612
20613
|
setContentHeight(contentHeight) {
|
|
20613
|
-
|
|
20614
20614
|
// Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
|
|
20615
20615
|
contentHeight = Math.min(contentHeight, 32000);
|
|
20616
|
+
|
|
20616
20617
|
this.$content.height(contentHeight);
|
|
20617
|
-
|
|
20618
|
+
|
|
20619
|
+
if (this.tile) this.tile.invalidate = true;
|
|
20618
20620
|
}
|
|
20619
20621
|
|
|
20620
20622
|
isLoading() {
|
|
@@ -20631,6 +20633,8 @@ class Viewport {
|
|
|
20631
20633
|
|
|
20632
20634
|
setWidth(width) {
|
|
20633
20635
|
this.$viewport.width(width);
|
|
20636
|
+
this.canvas.style.width = (`${width}px`);
|
|
20637
|
+
this.canvas.setAttribute('width', width);
|
|
20634
20638
|
}
|
|
20635
20639
|
|
|
20636
20640
|
getWidth() {
|
|
@@ -22757,7 +22761,7 @@ const Cytoband = function (start, end, name, typestain) {
|
|
|
22757
22761
|
}
|
|
22758
22762
|
};
|
|
22759
22763
|
|
|
22760
|
-
const _version = "2.11.
|
|
22764
|
+
const _version = "2.11.2";
|
|
22761
22765
|
function version() {
|
|
22762
22766
|
return _version
|
|
22763
22767
|
}
|
|
@@ -23292,21 +23296,17 @@ class TrackViewport extends Viewport {
|
|
|
23292
23296
|
checkZoomIn() {
|
|
23293
23297
|
|
|
23294
23298
|
const showZoomInNotice = () => {
|
|
23299
|
+
const referenceFrame = this.referenceFrame;
|
|
23295
23300
|
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome()) {
|
|
23296
23301
|
return true
|
|
23297
23302
|
} else {
|
|
23298
23303
|
const visibilityWindow = this.trackView.track.visibilityWindow;
|
|
23299
23304
|
return (
|
|
23300
23305
|
visibilityWindow !== undefined && visibilityWindow > 0 &&
|
|
23301
|
-
(
|
|
23306
|
+
(referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow))
|
|
23302
23307
|
}
|
|
23303
23308
|
};
|
|
23304
23309
|
|
|
23305
|
-
if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
|
|
23306
|
-
if (this.canvas) ;
|
|
23307
|
-
return false
|
|
23308
|
-
}
|
|
23309
|
-
|
|
23310
23310
|
if (!(this.viewIsReady())) {
|
|
23311
23311
|
return false
|
|
23312
23312
|
}
|
|
@@ -23316,8 +23316,7 @@ class TrackViewport extends Viewport {
|
|
|
23316
23316
|
// Out of visibility window
|
|
23317
23317
|
if (this.canvas) {
|
|
23318
23318
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
23319
|
-
this.
|
|
23320
|
-
this.featureCache = undefined;
|
|
23319
|
+
this.tile = undefined;
|
|
23321
23320
|
}
|
|
23322
23321
|
this.$zoomInNotice.show();
|
|
23323
23322
|
|
|
@@ -23334,19 +23333,21 @@ class TrackViewport extends Viewport {
|
|
|
23334
23333
|
}
|
|
23335
23334
|
|
|
23336
23335
|
return true
|
|
23336
|
+
|
|
23337
|
+
|
|
23337
23338
|
}
|
|
23338
23339
|
|
|
23339
|
-
/**
|
|
23340
|
-
* Adjust the canvas to the current genomic state.
|
|
23341
|
-
*/
|
|
23342
23340
|
shift() {
|
|
23343
|
-
const
|
|
23344
|
-
|
|
23345
|
-
|
|
23346
|
-
|
|
23347
|
-
|
|
23348
|
-
|
|
23349
|
-
|
|
23341
|
+
const self = this;
|
|
23342
|
+
const referenceFrame = self.referenceFrame;
|
|
23343
|
+
|
|
23344
|
+
if (self.canvas &&
|
|
23345
|
+
self.tile &&
|
|
23346
|
+
self.tile.chr === self.referenceFrame.chr &&
|
|
23347
|
+
self.tile.bpPerPixel === referenceFrame.bpPerPixel) {
|
|
23348
|
+
|
|
23349
|
+
const pixelOffset = Math.round((self.tile.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
|
|
23350
|
+
self.canvas.style.left = pixelOffset + "px";
|
|
23350
23351
|
}
|
|
23351
23352
|
}
|
|
23352
23353
|
|
|
@@ -23369,23 +23370,22 @@ class TrackViewport extends Viewport {
|
|
|
23369
23370
|
this.startSpinner();
|
|
23370
23371
|
|
|
23371
23372
|
try {
|
|
23372
|
-
const
|
|
23373
|
-
const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23373
|
+
const features = await this.getFeatures(this.trackView.track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23374
23374
|
let roiFeatures = [];
|
|
23375
|
-
const roi = mergeArrays(this.browser.roi, track.roi);
|
|
23375
|
+
const roi = mergeArrays(this.browser.roi, this.trackView.track.roi);
|
|
23376
23376
|
if (roi) {
|
|
23377
23377
|
for (let r of roi) {
|
|
23378
|
-
const f = await
|
|
23378
|
+
const f = await
|
|
23379
|
+
r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
|
|
23379
23380
|
roiFeatures.push({track: r, features: f});
|
|
23380
23381
|
}
|
|
23381
23382
|
}
|
|
23382
23383
|
|
|
23383
|
-
|
|
23384
|
-
this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
|
|
23384
|
+
this.tile = new Tile(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures);
|
|
23385
23385
|
this.loading = false;
|
|
23386
23386
|
this.hideMessage();
|
|
23387
23387
|
this.stopSpinner();
|
|
23388
|
-
return this.
|
|
23388
|
+
return this.tile
|
|
23389
23389
|
} catch (error) {
|
|
23390
23390
|
// Track might have been removed during load
|
|
23391
23391
|
if (this.trackView && this.trackView.disposed !== true) {
|
|
@@ -23399,31 +23399,25 @@ class TrackViewport extends Viewport {
|
|
|
23399
23399
|
}
|
|
23400
23400
|
}
|
|
23401
23401
|
|
|
23402
|
-
|
|
23403
|
-
* Repaint the canvas for the current genomic state.
|
|
23404
|
-
*
|
|
23405
|
-
* @returns {Promise<void>}
|
|
23406
|
-
*/
|
|
23407
|
-
repaint() {
|
|
23402
|
+
async repaint() {
|
|
23408
23403
|
|
|
23409
|
-
if (undefined === this.
|
|
23404
|
+
if (undefined === this.tile) {
|
|
23410
23405
|
return
|
|
23411
23406
|
}
|
|
23412
23407
|
|
|
23413
|
-
let {features, roiFeatures} = this.
|
|
23414
|
-
//this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
|
|
23408
|
+
let {features, roiFeatures, bpPerPixel, startBP, endBP} = this.tile;
|
|
23415
23409
|
|
|
23416
23410
|
// const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
|
|
23417
23411
|
const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
|
|
23418
|
-
|
|
23419
23412
|
let pixelWidth;
|
|
23420
|
-
|
|
23421
|
-
const endBP = this.featureCache.endBP;
|
|
23422
|
-
let bpPerPixel = this.referenceFrame.bpPerPixel;
|
|
23413
|
+
|
|
23423
23414
|
if (isWGV) {
|
|
23415
|
+
bpPerPixel = this.referenceFrame.end / this.$viewport.width();
|
|
23416
|
+
startBP = 0;
|
|
23417
|
+
endBP = this.referenceFrame.end;
|
|
23424
23418
|
pixelWidth = this.$viewport.width();
|
|
23425
23419
|
} else {
|
|
23426
|
-
pixelWidth =
|
|
23420
|
+
pixelWidth = Math.ceil((endBP - startBP) / bpPerPixel);
|
|
23427
23421
|
}
|
|
23428
23422
|
|
|
23429
23423
|
// For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
|
|
@@ -23483,27 +23477,17 @@ class TrackViewport extends Viewport {
|
|
|
23483
23477
|
|
|
23484
23478
|
this.draw(drawConfiguration, features, roiFeatures);
|
|
23485
23479
|
|
|
23486
|
-
|
|
23487
|
-
newCanvas._data = {
|
|
23488
|
-
chr: this.referenceFrame.chr, startBP, endBP, bpPerPixel, top: canvasTop, bottom: canvasTop + pixelHeight
|
|
23489
|
-
};
|
|
23480
|
+
this.canvasVerticalRange = {top: canvasTop, bottom: canvasTop + pixelHeight};
|
|
23490
23481
|
|
|
23491
|
-
if (this
|
|
23492
|
-
|
|
23482
|
+
if (this.$canvas) {
|
|
23483
|
+
this.$canvas.remove();
|
|
23493
23484
|
}
|
|
23485
|
+
this.$canvas = $$1(newCanvas);
|
|
23486
|
+
this.$content.append(this.$canvas);
|
|
23494
23487
|
this.canvas = newCanvas;
|
|
23495
23488
|
this.ctx = ctx;
|
|
23496
|
-
this.$content.append($$1(newCanvas));
|
|
23497
|
-
|
|
23498
23489
|
}
|
|
23499
23490
|
|
|
23500
|
-
/**
|
|
23501
|
-
* Draw the associated track.
|
|
23502
|
-
*
|
|
23503
|
-
* @param drawConfiguration
|
|
23504
|
-
* @param features
|
|
23505
|
-
* @param roiFeatures
|
|
23506
|
-
*/
|
|
23507
23491
|
draw(drawConfiguration, features, roiFeatures) {
|
|
23508
23492
|
|
|
23509
23493
|
// console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
|
|
@@ -23520,6 +23504,60 @@ class TrackViewport extends Viewport {
|
|
|
23520
23504
|
}
|
|
23521
23505
|
}
|
|
23522
23506
|
|
|
23507
|
+
// TODO: Nolonger used. Will discard
|
|
23508
|
+
async toSVG(tile) {
|
|
23509
|
+
|
|
23510
|
+
// Nothing to do if zoomInNotice is active
|
|
23511
|
+
if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) {
|
|
23512
|
+
return
|
|
23513
|
+
}
|
|
23514
|
+
|
|
23515
|
+
const referenceFrame = this.referenceFrame;
|
|
23516
|
+
const bpPerPixel = tile.bpPerPixel;
|
|
23517
|
+
const features = tile.features;
|
|
23518
|
+
const roiFeatures = tile.roiFeatures;
|
|
23519
|
+
const pixelWidth = this.$viewport.width();
|
|
23520
|
+
const pixelHeight = this.$viewport.height();
|
|
23521
|
+
const bpStart = referenceFrame.start;
|
|
23522
|
+
const bpEnd = referenceFrame.start + pixelWidth * referenceFrame.bpPerPixel;
|
|
23523
|
+
|
|
23524
|
+
const ctx$1 = new ctx(
|
|
23525
|
+
{
|
|
23526
|
+
// svg
|
|
23527
|
+
width: pixelWidth,
|
|
23528
|
+
height: pixelHeight,
|
|
23529
|
+
viewbox:
|
|
23530
|
+
{
|
|
23531
|
+
x: 0,
|
|
23532
|
+
y: -this.$content.position().top,
|
|
23533
|
+
width: pixelWidth,
|
|
23534
|
+
height: pixelHeight
|
|
23535
|
+
}
|
|
23536
|
+
|
|
23537
|
+
});
|
|
23538
|
+
|
|
23539
|
+
const drawConfiguration =
|
|
23540
|
+
{
|
|
23541
|
+
viewport: this,
|
|
23542
|
+
context: ctx$1,
|
|
23543
|
+
top: -this.$content.position().top,
|
|
23544
|
+
pixelTop: 0, // for compatibility with canvas draw
|
|
23545
|
+
pixelWidth,
|
|
23546
|
+
pixelHeight,
|
|
23547
|
+
bpStart,
|
|
23548
|
+
bpEnd,
|
|
23549
|
+
bpPerPixel,
|
|
23550
|
+
referenceFrame: this.referenceFrame,
|
|
23551
|
+
selection: this.selection,
|
|
23552
|
+
viewportWidth: pixelWidth,
|
|
23553
|
+
};
|
|
23554
|
+
|
|
23555
|
+
this.draw(drawConfiguration, features, roiFeatures);
|
|
23556
|
+
|
|
23557
|
+
return ctx$1.getSerializedSvg(true)
|
|
23558
|
+
|
|
23559
|
+
}
|
|
23560
|
+
|
|
23523
23561
|
containsPosition(chr, position) {
|
|
23524
23562
|
if (this.referenceFrame.chr === chr && position >= this.referenceFrame.start) {
|
|
23525
23563
|
return position <= this.referenceFrame.calculateEnd(this.getWidth())
|
|
@@ -23532,12 +23570,11 @@ class TrackViewport extends Viewport {
|
|
|
23532
23570
|
return this.loading
|
|
23533
23571
|
}
|
|
23534
23572
|
|
|
23535
|
-
|
|
23573
|
+
saveImage() {
|
|
23536
23574
|
|
|
23537
23575
|
if (!this.ctx) return
|
|
23538
23576
|
|
|
23539
|
-
const
|
|
23540
|
-
const canvasTop = canvasMetadata ? canvasMetadata.top : 0;
|
|
23577
|
+
const canvasTop = this.canvasVerticalRange ? this.canvasVerticalRange.top : 0;
|
|
23541
23578
|
const devicePixelRatio = window.devicePixelRatio;
|
|
23542
23579
|
const w = this.$viewport.width() * devicePixelRatio;
|
|
23543
23580
|
const h = this.$viewport.height() * devicePixelRatio;
|
|
@@ -23667,25 +23704,26 @@ class TrackViewport extends Viewport {
|
|
|
23667
23704
|
selection: this.selection
|
|
23668
23705
|
};
|
|
23669
23706
|
|
|
23670
|
-
const features = this.
|
|
23671
|
-
const roiFeatures = this.
|
|
23707
|
+
const features = this.tile ? this.tile.features : [];
|
|
23708
|
+
const roiFeatures = this.tile ? this.tile.roiFeatures : undefined;
|
|
23672
23709
|
this.draw(config, features, roiFeatures);
|
|
23673
23710
|
|
|
23674
23711
|
context.restore();
|
|
23675
23712
|
|
|
23676
23713
|
}
|
|
23677
23714
|
|
|
23678
|
-
|
|
23679
|
-
return this.
|
|
23715
|
+
getCachedFeatures() {
|
|
23716
|
+
return this.tile ? this.tile.features : []
|
|
23680
23717
|
}
|
|
23681
23718
|
|
|
23682
23719
|
async getFeatures(track, chr, start, end, bpPerPixel) {
|
|
23683
23720
|
|
|
23684
|
-
if (this.
|
|
23685
|
-
return this.
|
|
23721
|
+
if (this.tile && this.tile.containsRange(chr, start, end, bpPerPixel)) {
|
|
23722
|
+
return this.tile.features
|
|
23686
23723
|
} else if (typeof track.getFeatures === "function") {
|
|
23687
23724
|
const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
|
|
23688
|
-
this.
|
|
23725
|
+
this.cachedFeatures = features;
|
|
23726
|
+
this.checkContentHeight();
|
|
23689
23727
|
return features
|
|
23690
23728
|
} else {
|
|
23691
23729
|
return undefined
|
|
@@ -23923,7 +23961,9 @@ class TrackViewport extends Viewport {
|
|
|
23923
23961
|
lastClickTime = time;
|
|
23924
23962
|
|
|
23925
23963
|
}
|
|
23964
|
+
|
|
23926
23965
|
}
|
|
23966
|
+
|
|
23927
23967
|
}
|
|
23928
23968
|
|
|
23929
23969
|
removeViewportClickHandler(viewport) {
|
|
@@ -23987,10 +24027,16 @@ function mouseUpHandler(event) {
|
|
|
23987
24027
|
function createClickState(event, viewport) {
|
|
23988
24028
|
|
|
23989
24029
|
const referenceFrame = viewport.referenceFrame;
|
|
24030
|
+
|
|
23990
24031
|
const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
|
|
23991
24032
|
const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
|
|
24033
|
+
|
|
23992
24034
|
const genomicLocation = ((referenceFrame.start) + referenceFrame.toBP(viewportCoords.x));
|
|
23993
24035
|
|
|
24036
|
+
if (undefined === genomicLocation || null === viewport.tile) {
|
|
24037
|
+
return undefined
|
|
24038
|
+
}
|
|
24039
|
+
|
|
23994
24040
|
return {
|
|
23995
24041
|
event,
|
|
23996
24042
|
viewport,
|
|
@@ -24051,30 +24097,22 @@ function formatPopoverText(nameValues) {
|
|
|
24051
24097
|
return rows.join('')
|
|
24052
24098
|
}
|
|
24053
24099
|
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
|
|
24060
|
-
|
|
24061
|
-
|
|
24062
|
-
this.roiFeatures = roiFeatures;
|
|
24063
|
-
this.multiresolution = multiresolution;
|
|
24064
|
-
}
|
|
24065
|
-
|
|
24066
|
-
containsRange(chr, start, end, bpPerPixel) {
|
|
24067
|
-
|
|
24068
|
-
// For multi-resolution tracks allow for a 2X change in bpPerPixel
|
|
24069
|
-
const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
|
|
24100
|
+
var Tile = function (chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures) {
|
|
24101
|
+
this.chr = chr;
|
|
24102
|
+
this.startBP = tileStart;
|
|
24103
|
+
this.endBP = tileEnd;
|
|
24104
|
+
this.bpPerPixel = bpPerPixel;
|
|
24105
|
+
this.features = features;
|
|
24106
|
+
this.roiFeatures = roiFeatures;
|
|
24107
|
+
};
|
|
24070
24108
|
|
|
24071
|
-
|
|
24072
|
-
|
|
24109
|
+
Tile.prototype.containsRange = function (chr, start, end, bpPerPixel) {
|
|
24110
|
+
return this.bpPerPixel === bpPerPixel && start >= this.startBP && end <= this.endBP && chr === this.chr
|
|
24111
|
+
};
|
|
24073
24112
|
|
|
24074
|
-
|
|
24075
|
-
|
|
24076
|
-
|
|
24077
|
-
}
|
|
24113
|
+
Tile.prototype.overlapsRange = function (chr, start, end) {
|
|
24114
|
+
return this.chr === chr && end >= this.startBP && start <= this.endBP
|
|
24115
|
+
};
|
|
24078
24116
|
|
|
24079
24117
|
|
|
24080
24118
|
/**
|
|
@@ -24381,18 +24419,6 @@ class PairedAlignment {
|
|
|
24381
24419
|
return true // By definition
|
|
24382
24420
|
}
|
|
24383
24421
|
|
|
24384
|
-
isMateMapped() {
|
|
24385
|
-
return true // By definition
|
|
24386
|
-
}
|
|
24387
|
-
|
|
24388
|
-
isProperPair() {
|
|
24389
|
-
return this.firstAlignment.isProperPair()
|
|
24390
|
-
}
|
|
24391
|
-
|
|
24392
|
-
get tlen() {
|
|
24393
|
-
return Math.abs(this.firstAlignment.fragmentLength)
|
|
24394
|
-
}
|
|
24395
|
-
|
|
24396
24422
|
firstOfPairStrand() {
|
|
24397
24423
|
|
|
24398
24424
|
if (this.firstAlignment.isFirstOfPair()) {
|
|
@@ -24734,10 +24760,7 @@ function packAlignmentRows(alignments, start, end, showSoftClips) {
|
|
|
24734
24760
|
|
|
24735
24761
|
|
|
24736
24762
|
class AlignmentContainer {
|
|
24737
|
-
|
|
24738
|
-
// this.config.samplingWindowSize, this.config.samplingDepth,
|
|
24739
|
-
// this.config.pairsSupported, this.config.alleleFreqThreshold)
|
|
24740
|
-
constructor(chr, start, end, {samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold}) {
|
|
24763
|
+
constructor(chr, start, end, samplingWindowSize, samplingDepth, pairsSupported, alleleFreqThreshold) {
|
|
24741
24764
|
|
|
24742
24765
|
this.chr = chr;
|
|
24743
24766
|
this.start = Math.floor(start);
|
|
@@ -24765,12 +24788,17 @@ class AlignmentContainer {
|
|
|
24765
24788
|
return alignment.isMapped() && !alignment.isFailsVendorQualityCheck()
|
|
24766
24789
|
};
|
|
24767
24790
|
|
|
24791
|
+
this.pairedEndStats = new PairedEndStats();
|
|
24768
24792
|
}
|
|
24769
24793
|
|
|
24770
24794
|
push(alignment) {
|
|
24771
24795
|
|
|
24772
24796
|
if (this.filter(alignment) === false) return
|
|
24773
24797
|
|
|
24798
|
+
if (alignment.isPaired()) {
|
|
24799
|
+
this.pairedEndStats.push(alignment);
|
|
24800
|
+
}
|
|
24801
|
+
|
|
24774
24802
|
this.coverageMap.incCounts(alignment); // Count coverage before any downsampling
|
|
24775
24803
|
|
|
24776
24804
|
if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
|
|
@@ -24802,6 +24830,8 @@ class AlignmentContainer {
|
|
|
24802
24830
|
|
|
24803
24831
|
this.pairsCache = undefined;
|
|
24804
24832
|
this.downsampledReads = undefined;
|
|
24833
|
+
|
|
24834
|
+
this.pairedEndStats.compute();
|
|
24805
24835
|
}
|
|
24806
24836
|
|
|
24807
24837
|
contains(chr, start, end) {
|
|
@@ -25112,6 +25142,69 @@ class DownsampledInterval {
|
|
|
25112
25142
|
}
|
|
25113
25143
|
}
|
|
25114
25144
|
|
|
25145
|
+
class PairedEndStats {
|
|
25146
|
+
|
|
25147
|
+
constructor(lowerPercentile, upperPercentile) {
|
|
25148
|
+
this.totalCount = 0;
|
|
25149
|
+
this.frCount = 0;
|
|
25150
|
+
this.rfCount = 0;
|
|
25151
|
+
this.ffCount = 0;
|
|
25152
|
+
this.sumF = 0;
|
|
25153
|
+
this.sumF2 = 0;
|
|
25154
|
+
//this.lp = lowerPercentile === undefined ? 0.005 : lowerPercentile;
|
|
25155
|
+
//this.up = upperPercentile === undefined ? 0.995 : upperPercentile;
|
|
25156
|
+
//this.digest = new Digest();
|
|
25157
|
+
}
|
|
25158
|
+
|
|
25159
|
+
push(alignment) {
|
|
25160
|
+
|
|
25161
|
+
if (alignment.isProperPair()) {
|
|
25162
|
+
|
|
25163
|
+
var fragmentLength = Math.abs(alignment.fragmentLength);
|
|
25164
|
+
//this.digest.push(fragmentLength);
|
|
25165
|
+
this.sumF += fragmentLength;
|
|
25166
|
+
this.sumF2 += fragmentLength * fragmentLength;
|
|
25167
|
+
|
|
25168
|
+
var po = alignment.pairOrientation;
|
|
25169
|
+
|
|
25170
|
+
if (typeof po === "string" && po.length === 4) {
|
|
25171
|
+
var tmp = '' + po.charAt(0) + po.charAt(2);
|
|
25172
|
+
switch (tmp) {
|
|
25173
|
+
case 'FF':
|
|
25174
|
+
case 'RR':
|
|
25175
|
+
this.ffCount++;
|
|
25176
|
+
break
|
|
25177
|
+
case "FR":
|
|
25178
|
+
this.frCount++;
|
|
25179
|
+
break
|
|
25180
|
+
case"RF":
|
|
25181
|
+
this.rfCount++;
|
|
25182
|
+
}
|
|
25183
|
+
}
|
|
25184
|
+
this.totalCount++;
|
|
25185
|
+
}
|
|
25186
|
+
}
|
|
25187
|
+
|
|
25188
|
+
compute() {
|
|
25189
|
+
|
|
25190
|
+
if (this.totalCount > 100) {
|
|
25191
|
+
if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";
|
|
25192
|
+
else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";
|
|
25193
|
+
else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
|
|
25194
|
+
|
|
25195
|
+
|
|
25196
|
+
var fMean = this.sumF / this.totalCount;
|
|
25197
|
+
var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount));
|
|
25198
|
+
this.lowerFragmentLength = fMean - 3 * stdDev;
|
|
25199
|
+
this.upperFragmentLength = fMean + 3 * stdDev;
|
|
25200
|
+
|
|
25201
|
+
//this.lowerFragmentLength = this.digest.percentile(this.lp);
|
|
25202
|
+
//this.upperFragmentLength = this.digest.percentile(this.up);
|
|
25203
|
+
//this.digest = undefined;
|
|
25204
|
+
}
|
|
25205
|
+
}
|
|
25206
|
+
}
|
|
25207
|
+
|
|
25115
25208
|
class SupplementaryAlignment {
|
|
25116
25209
|
|
|
25117
25210
|
constructor(rec) {
|
|
@@ -26301,7 +26394,7 @@ class BamReaderNonIndexed {
|
|
|
26301
26394
|
const header = this.header;
|
|
26302
26395
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
26303
26396
|
const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
26304
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
26397
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
|
|
26305
26398
|
for (let a of qAlignments) {
|
|
26306
26399
|
alignmentContainer.push(a);
|
|
26307
26400
|
}
|
|
@@ -26328,13 +26421,13 @@ class BamReaderNonIndexed {
|
|
|
26328
26421
|
const alignments = [];
|
|
26329
26422
|
this.header = BamUtils.decodeBamHeader(data);
|
|
26330
26423
|
BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
|
|
26331
|
-
this.alignmentCache = new FeatureCache
|
|
26424
|
+
this.alignmentCache = new FeatureCache(alignments, this.genome);
|
|
26332
26425
|
}
|
|
26333
26426
|
|
|
26334
26427
|
fetchAlignments(chr, bpStart, bpEnd) {
|
|
26335
26428
|
const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
|
|
26336
26429
|
const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
|
|
26337
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.
|
|
26430
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.samplingWindowSize, this.samplingDepth, this.pairsSupported);
|
|
26338
26431
|
for (let feature of features) {
|
|
26339
26432
|
alignmentContainer.push(feature);
|
|
26340
26433
|
}
|
|
@@ -27325,7 +27418,9 @@ class BamReader {
|
|
|
27325
27418
|
const chrToIndex = await this.getChrIndex();
|
|
27326
27419
|
const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
|
|
27327
27420
|
const chrId = chrToIndex[queryChr];
|
|
27328
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
|
|
27421
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
|
|
27422
|
+
this.config.samplingWindowSize, this.config.samplingDepth,
|
|
27423
|
+
this.config.pairsSupported, this.config.alleleFreqThreshold);
|
|
27329
27424
|
|
|
27330
27425
|
if (chrId === undefined) {
|
|
27331
27426
|
return alignmentContainer
|
|
@@ -27457,7 +27552,7 @@ class ShardedBamReader {
|
|
|
27457
27552
|
async readAlignments(chr, start, end) {
|
|
27458
27553
|
|
|
27459
27554
|
if (!this.bamReaders.hasOwnProperty(chr)) {
|
|
27460
|
-
return new AlignmentContainer(chr, start, end
|
|
27555
|
+
return new AlignmentContainer(chr, start, end)
|
|
27461
27556
|
} else {
|
|
27462
27557
|
|
|
27463
27558
|
let reader = this.bamReaders[chr];
|
|
@@ -27548,7 +27643,7 @@ BamWebserviceReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
|
|
|
27548
27643
|
|
|
27549
27644
|
header.chrToIndex[queryChr];
|
|
27550
27645
|
|
|
27551
|
-
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
27646
|
+
alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold);
|
|
27552
27647
|
|
|
27553
27648
|
BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
|
|
27554
27649
|
|
|
@@ -27802,7 +27897,7 @@ class HtsgetBamReader extends HtsgetReader {
|
|
|
27802
27897
|
const ba = unbgzf(compressedData.buffer);
|
|
27803
27898
|
|
|
27804
27899
|
const chrIdx = this.header.chrToIndex[chr];
|
|
27805
|
-
const alignmentContainer = new AlignmentContainer(chr, start, end, this.
|
|
27900
|
+
const alignmentContainer = new AlignmentContainer(chr, start, end, this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
|
|
27806
27901
|
BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
|
|
27807
27902
|
alignmentContainer.finish();
|
|
27808
27903
|
|
|
@@ -27968,7 +28063,8 @@ class CramReader {
|
|
|
27968
28063
|
const header = await this.getHeader();
|
|
27969
28064
|
const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
|
|
27970
28065
|
const chrIdx = header.chrToIndex[queryChr];
|
|
27971
|
-
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
|
|
28066
|
+
const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd,
|
|
28067
|
+
this.samplingWindowSize, this.samplingDepth, this.pairsSupported, this.alleleFreqThreshold);
|
|
27972
28068
|
|
|
27973
28069
|
if (chrIdx === undefined) {
|
|
27974
28070
|
return alignmentContainer
|
|
@@ -28584,7 +28680,7 @@ Ga4ghAlignmentReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
|
|
|
28584
28680
|
"pageSize": "10000"
|
|
28585
28681
|
},
|
|
28586
28682
|
decode: decodeGa4ghReads,
|
|
28587
|
-
results: new AlignmentContainer(chr, bpStart, bpEnd, self.
|
|
28683
|
+
results: new AlignmentContainer(chr, bpStart, bpEnd, self.samplingWindowSize, self.samplingDepth, self.pairsSupported, self.alleleFreqThreshold)
|
|
28588
28684
|
})
|
|
28589
28685
|
})
|
|
28590
28686
|
|
|
@@ -28917,6 +29013,7 @@ class BamSource {
|
|
|
28917
29013
|
|
|
28918
29014
|
this.config = config;
|
|
28919
29015
|
this.genome = genome;
|
|
29016
|
+
this.alignmentContainer = undefined;
|
|
28920
29017
|
|
|
28921
29018
|
if (isDataURL(config.url)) {
|
|
28922
29019
|
if ("cram" === config.format) {
|
|
@@ -28964,11 +29061,19 @@ class BamSource {
|
|
|
28964
29061
|
}
|
|
28965
29062
|
|
|
28966
29063
|
setViewAsPairs(bool) {
|
|
28967
|
-
|
|
29064
|
+
|
|
29065
|
+
if (this.viewAsPairs !== bool) {
|
|
29066
|
+
this.viewAsPairs = bool;
|
|
29067
|
+
// if (this.alignmentContainer) {
|
|
29068
|
+
// this.alignmentContainer.setViewAsPairs(bool);
|
|
29069
|
+
// }
|
|
29070
|
+
}
|
|
28968
29071
|
}
|
|
28969
29072
|
|
|
28970
29073
|
setShowSoftClips(bool) {
|
|
28971
|
-
this.showSoftClips
|
|
29074
|
+
if (this.showSoftClips !== bool) {
|
|
29075
|
+
this.showSoftClips = bool;
|
|
29076
|
+
}
|
|
28972
29077
|
}
|
|
28973
29078
|
|
|
28974
29079
|
async getAlignments(chr, bpStart, bpEnd) {
|
|
@@ -28976,28 +29081,33 @@ class BamSource {
|
|
|
28976
29081
|
const genome = this.genome;
|
|
28977
29082
|
const showSoftClips = this.showSoftClips;
|
|
28978
29083
|
|
|
28979
|
-
|
|
28980
|
-
|
|
28981
|
-
if (!this.viewAsPairs) {
|
|
28982
|
-
alignments = unpairAlignments([{alignments: alignments}]);
|
|
28983
|
-
}
|
|
28984
|
-
const hasAlignments = alignments.length > 0;
|
|
28985
|
-
alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
|
|
28986
|
-
|
|
28987
|
-
this.alignmentContainer = alignmentContainer;
|
|
29084
|
+
if (this.alignmentContainer && this.alignmentContainer.contains(chr, bpStart, bpEnd)) {
|
|
29085
|
+
return this.alignmentContainer
|
|
28988
29086
|
|
|
28989
|
-
|
|
28990
|
-
const
|
|
28991
|
-
|
|
28992
|
-
|
|
28993
|
-
|
|
28994
|
-
|
|
28995
|
-
|
|
28996
|
-
|
|
29087
|
+
} else {
|
|
29088
|
+
const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
|
|
29089
|
+
let alignments = alignmentContainer.alignments;
|
|
29090
|
+
if (!this.viewAsPairs) {
|
|
29091
|
+
alignments = unpairAlignments([{alignments: alignments}]);
|
|
29092
|
+
}
|
|
29093
|
+
const hasAlignments = alignments.length > 0;
|
|
29094
|
+
alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
|
|
29095
|
+
alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
|
|
29096
|
+
|
|
29097
|
+
this.alignmentContainer = alignmentContainer;
|
|
29098
|
+
|
|
29099
|
+
if (hasAlignments) {
|
|
29100
|
+
const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);
|
|
29101
|
+
if (sequence) {
|
|
29102
|
+
alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this
|
|
29103
|
+
alignmentContainer.sequence = sequence; // TODO -- fix this
|
|
29104
|
+
return alignmentContainer
|
|
29105
|
+
} else {
|
|
29106
|
+
console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
|
|
29107
|
+
}
|
|
28997
29108
|
}
|
|
29109
|
+
return alignmentContainer
|
|
28998
29110
|
}
|
|
28999
|
-
return alignmentContainer
|
|
29000
|
-
|
|
29001
29111
|
}
|
|
29002
29112
|
}
|
|
29003
29113
|
|
|
@@ -29315,7 +29425,7 @@ class TrackBase {
|
|
|
29315
29425
|
|
|
29316
29426
|
// We use the cached features rather than method to avoid async load. If the
|
|
29317
29427
|
// feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
|
|
29318
|
-
if (!features) features = clickState.viewport.
|
|
29428
|
+
if (!features) features = clickState.viewport.getCachedFeatures();
|
|
29319
29429
|
|
|
29320
29430
|
if (!features || features.length === 0) {
|
|
29321
29431
|
return []
|
|
@@ -29819,7 +29929,8 @@ class Locus {
|
|
|
29819
29929
|
}
|
|
29820
29930
|
|
|
29821
29931
|
overlaps(locus) {
|
|
29822
|
-
return locus.chr === this.chr
|
|
29932
|
+
return locus.chr === this.chr
|
|
29933
|
+
&& !(locus.end < this.start || locus.start > this.end)
|
|
29823
29934
|
}
|
|
29824
29935
|
|
|
29825
29936
|
extend(l) {
|
|
@@ -31696,19 +31807,6 @@ function makeCircViewChromosomes(genome) {
|
|
|
31696
31807
|
return regions
|
|
31697
31808
|
}
|
|
31698
31809
|
|
|
31699
|
-
function sendChords(chords, track, refFrame, alpha) {
|
|
31700
|
-
|
|
31701
|
-
const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
|
|
31702
|
-
const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha);
|
|
31703
|
-
|
|
31704
|
-
// name the chord set to include locus and filtering information
|
|
31705
|
-
const encodedName = track.name.replaceAll(' ', '%20');
|
|
31706
|
-
const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
31707
|
-
`${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
|
|
31708
|
-
track.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
|
|
31709
|
-
|
|
31710
|
-
}
|
|
31711
|
-
|
|
31712
31810
|
|
|
31713
31811
|
function createCircularView(el, browser) {
|
|
31714
31812
|
|
|
@@ -31718,102 +31816,42 @@ function createCircularView(el, browser) {
|
|
|
31718
31816
|
|
|
31719
31817
|
const f1 = feature.data;
|
|
31720
31818
|
const f2 = f1.mate;
|
|
31721
|
-
|
|
31722
|
-
addFrameForFeature(f2);
|
|
31723
|
-
|
|
31724
|
-
function addFrameForFeature(feature) {
|
|
31725
|
-
|
|
31726
|
-
feature.chr = browser.genome.getChromosomeName(feature.refName);
|
|
31727
|
-
|
|
31728
|
-
for (let referenceFrame of browser.currentReferenceFrames()) {
|
|
31729
|
-
const l = Locus.fromLocusString(referenceFrame.getLocusString());
|
|
31730
|
-
if (l.contains(feature)) {
|
|
31731
|
-
break
|
|
31732
|
-
} else if (l.overlaps(feature)) {
|
|
31733
|
-
referenceFrame.extend(feature);
|
|
31734
|
-
break
|
|
31735
|
-
} else {
|
|
31736
|
-
const flanking = 2000;
|
|
31737
|
-
const center = (feature.start + feature.end) / 2;
|
|
31738
|
-
browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
|
|
31739
|
-
break
|
|
31740
|
-
}
|
|
31741
|
-
}
|
|
31742
|
-
}
|
|
31743
|
-
}
|
|
31744
|
-
});
|
|
31819
|
+
const flanking = 2000;
|
|
31745
31820
|
|
|
31746
|
-
|
|
31747
|
-
}
|
|
31821
|
+
const l1 = new Locus({chr: browser.genome.getChromosomeName(f1.refName), start: f1.start, end: f1.end});
|
|
31822
|
+
const l2 = new Locus({chr: browser.genome.getChromosomeName(f2.refName), start: f2.start, end: f2.end});
|
|
31748
31823
|
|
|
31749
|
-
|
|
31824
|
+
let loci;
|
|
31750
31825
|
|
|
31751
|
-
|
|
31752
|
-
this.totalCount = 0;
|
|
31753
|
-
this.frCount = 0;
|
|
31754
|
-
this.rfCount = 0;
|
|
31755
|
-
this.ffCount = 0;
|
|
31756
|
-
this.sumF = 0;
|
|
31757
|
-
this.sumF2 = 0;
|
|
31758
|
-
this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
|
|
31759
|
-
this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
|
|
31760
|
-
this.isizes = [];
|
|
31761
|
-
this.compute(alignments);
|
|
31762
|
-
}
|
|
31826
|
+
// If there is overlap with current loci
|
|
31763
31827
|
|
|
31764
|
-
|
|
31828
|
+
loci = browser.currentLoci().map(str => Locus.fromLocusString(str));
|
|
31765
31829
|
|
|
31766
|
-
|
|
31767
|
-
|
|
31768
|
-
|
|
31769
|
-
|
|
31770
|
-
|
|
31771
|
-
|
|
31772
|
-
|
|
31773
|
-
|
|
31774
|
-
|
|
31775
|
-
if (typeof po === "string" && po.length === 4) {
|
|
31776
|
-
var tmp = '' + po.charAt(0) + po.charAt(2);
|
|
31777
|
-
switch (tmp) {
|
|
31778
|
-
case 'FF':
|
|
31779
|
-
case 'RR':
|
|
31780
|
-
this.ffCount++;
|
|
31781
|
-
break
|
|
31782
|
-
case "FR":
|
|
31783
|
-
this.frCount++;
|
|
31784
|
-
break
|
|
31785
|
-
case"RF":
|
|
31786
|
-
this.rfCount++;
|
|
31830
|
+
if (loci.some(locus => locus.contains(l1)) || loci.some(locus => locus.contains(l2))) {
|
|
31831
|
+
for (let l of [l1, l2]) {
|
|
31832
|
+
if (!loci.some(locus => {
|
|
31833
|
+
return locus.contains(l)
|
|
31834
|
+
})) {
|
|
31835
|
+
// add flanking
|
|
31836
|
+
l.start = Math.max(0, l.start - flanking);
|
|
31837
|
+
l.end += flanking;
|
|
31838
|
+
loci.push(l);
|
|
31787
31839
|
}
|
|
31788
31840
|
}
|
|
31789
|
-
|
|
31841
|
+
} else {
|
|
31842
|
+
l1.start = Math.max(0, l1.start - flanking);
|
|
31843
|
+
l1.end += flanking;
|
|
31844
|
+
l2.start = Math.max(0, l2.start - flanking);
|
|
31845
|
+
l2.end += flanking;
|
|
31846
|
+
loci = [l1, l2];
|
|
31790
31847
|
}
|
|
31791
|
-
}
|
|
31792
|
-
|
|
31793
|
-
if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";
|
|
31794
|
-
else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";
|
|
31795
|
-
else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
|
|
31796
31848
|
|
|
31797
|
-
|
|
31798
|
-
|
|
31799
|
-
|
|
31800
|
-
// var fMean = this.sumF / this.totalCount
|
|
31801
|
-
// var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
|
|
31802
|
-
// this.minTLEN = fMean - 3 * stdDev
|
|
31803
|
-
// this.maxTLEN = fMean + 3 * stdDev
|
|
31804
|
-
|
|
31805
|
-
}
|
|
31806
|
-
}
|
|
31807
|
-
|
|
31808
|
-
function percentile(array, p) {
|
|
31809
|
-
|
|
31810
|
-
if (array.length === 0) return undefined
|
|
31811
|
-
var k = Math.floor(array.length * (p / 100));
|
|
31812
|
-
array.sort(function (a, b) {
|
|
31813
|
-
return a - b
|
|
31849
|
+
const searchString = loci.map(l => l.getLocusString()).join(" ");
|
|
31850
|
+
browser.search(searchString);
|
|
31851
|
+
}
|
|
31814
31852
|
});
|
|
31815
|
-
return array[k]
|
|
31816
31853
|
|
|
31854
|
+
return circularView
|
|
31817
31855
|
}
|
|
31818
31856
|
|
|
31819
31857
|
/*
|
|
@@ -31880,6 +31918,8 @@ class BAMTrack extends TrackBase {
|
|
|
31880
31918
|
this.showMismatches = false !== config.showMismatches;
|
|
31881
31919
|
this.color = config.color;
|
|
31882
31920
|
this.coverageColor = config.coverageColor;
|
|
31921
|
+
this.minFragmentLength = config.minFragmentLength; // Optional, might be undefined
|
|
31922
|
+
this.maxFragmentLength = config.maxFragmentLength || 1000;
|
|
31883
31923
|
|
|
31884
31924
|
// The sort object can be an array in the case of multi-locus view, however if multiple sort positions
|
|
31885
31925
|
// are present for a given reference frame the last one will take precedence
|
|
@@ -31907,24 +31947,12 @@ class BAMTrack extends TrackBase {
|
|
|
31907
31947
|
return this._height
|
|
31908
31948
|
}
|
|
31909
31949
|
|
|
31910
|
-
get minTemplateLength() {
|
|
31911
|
-
const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
|
|
31912
|
-
return (configMinTLEN !== undefined) ? configMinTLEN :
|
|
31913
|
-
this._pairedEndStats ? this._pairedEndStats.minTLEN : 0
|
|
31914
|
-
}
|
|
31915
|
-
|
|
31916
|
-
get maxTemplateLength() {
|
|
31917
|
-
const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
|
|
31918
|
-
return (configMaxTLEN !== undefined) ? configMaxTLEN :
|
|
31919
|
-
this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000
|
|
31920
|
-
}
|
|
31921
|
-
|
|
31922
31950
|
sort(options) {
|
|
31923
31951
|
options = this.assignSort(options);
|
|
31924
31952
|
|
|
31925
31953
|
for (let vp of this.trackView.viewports) {
|
|
31926
31954
|
if (vp.containsPosition(options.chr, options.position)) {
|
|
31927
|
-
const alignmentContainer = vp.
|
|
31955
|
+
const alignmentContainer = vp.getCachedFeatures();
|
|
31928
31956
|
if (alignmentContainer) {
|
|
31929
31957
|
sortAlignmentRows(options, alignmentContainer);
|
|
31930
31958
|
vp.repaint();
|
|
@@ -31959,13 +31987,14 @@ class BAMTrack extends TrackBase {
|
|
|
31959
31987
|
|
|
31960
31988
|
const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);
|
|
31961
31989
|
|
|
31962
|
-
if (alignmentContainer.
|
|
31963
|
-
|
|
31964
|
-
|
|
31965
|
-
|
|
31990
|
+
if (alignmentContainer.alignments && alignmentContainer.alignments.length > 99) {
|
|
31991
|
+
if (undefined === this.minFragmentLength) {
|
|
31992
|
+
this.minFragmentLength = alignmentContainer.pairedEndStats.lowerFragmentLength;
|
|
31993
|
+
}
|
|
31994
|
+
if (undefined === this.maxFragmentLength) {
|
|
31995
|
+
this.maxFragmentLength = alignmentContainer.pairedEndStats.upperFragmentLength;
|
|
31966
31996
|
}
|
|
31967
31997
|
}
|
|
31968
|
-
alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore
|
|
31969
31998
|
|
|
31970
31999
|
const sort = this.sortObject;
|
|
31971
32000
|
if (sort) {
|
|
@@ -32062,7 +32091,7 @@ class BAMTrack extends TrackBase {
|
|
|
32062
32091
|
if (this.alignmentTrack.hasPairs) {
|
|
32063
32092
|
colorByMenuItems.push({key: 'firstOfPairStrand', label: 'first-of-pair strand'});
|
|
32064
32093
|
colorByMenuItems.push({key: 'pairOrientation', label: 'pair orientation'});
|
|
32065
|
-
colorByMenuItems.push({key: '
|
|
32094
|
+
colorByMenuItems.push({key: 'fragmentLength', label: 'insert size (TLEN)'});
|
|
32066
32095
|
colorByMenuItems.push({key: 'unexpectedPair', label: 'pair orientation & insert size (TLEN)'});
|
|
32067
32096
|
}
|
|
32068
32097
|
const tagLabel = 'tag' + (this.alignmentTrack.colorByTag ? ' (' + this.alignmentTrack.colorByTag + ')' : '');
|
|
@@ -32168,7 +32197,7 @@ class BAMTrack extends TrackBase {
|
|
|
32168
32197
|
});
|
|
32169
32198
|
}
|
|
32170
32199
|
|
|
32171
|
-
//
|
|
32200
|
+
// Experimental JBrowse feature
|
|
32172
32201
|
if (this.browser.circularView && true === this.browser.circularViewVisible &&
|
|
32173
32202
|
(this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
|
|
32174
32203
|
menuItems.push('<hr/>');
|
|
@@ -32176,9 +32205,24 @@ class BAMTrack extends TrackBase {
|
|
|
32176
32205
|
menuItems.push({
|
|
32177
32206
|
label: 'Add discordant pairs to circular view',
|
|
32178
32207
|
click: () => {
|
|
32208
|
+
const maxFragmentLength = this.maxFragmentLength;
|
|
32209
|
+
const inView = [];
|
|
32179
32210
|
for (let viewport of this.trackView.viewports) {
|
|
32180
|
-
|
|
32211
|
+
for (let a of viewport.getCachedFeatures().allAlignments()) {
|
|
32212
|
+
const referenceFrame = viewport.referenceFrame;
|
|
32213
|
+
if (a.end >= referenceFrame.start
|
|
32214
|
+
&& a.start <= referenceFrame.end
|
|
32215
|
+
&& a.mate
|
|
32216
|
+
&& a.mate.chr
|
|
32217
|
+
&& (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength)) {
|
|
32218
|
+
inView.push(a);
|
|
32219
|
+
}
|
|
32220
|
+
}
|
|
32181
32221
|
}
|
|
32222
|
+
this.browser.circularViewVisible = true;
|
|
32223
|
+
const chords = makePairedAlignmentChords(inView);
|
|
32224
|
+
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02);
|
|
32225
|
+
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
32182
32226
|
}
|
|
32183
32227
|
});
|
|
32184
32228
|
}
|
|
@@ -32186,9 +32230,19 @@ class BAMTrack extends TrackBase {
|
|
|
32186
32230
|
menuItems.push({
|
|
32187
32231
|
label: 'Add split reads to circular view',
|
|
32188
32232
|
click: () => {
|
|
32233
|
+
const inView = [];
|
|
32189
32234
|
for (let viewport of this.trackView.viewports) {
|
|
32190
|
-
|
|
32235
|
+
for (let a of viewport.getCachedFeatures().allAlignments()) {
|
|
32236
|
+
const referenceFrame = viewport.referenceFrame;
|
|
32237
|
+
const sa = a.hasTag('SA');
|
|
32238
|
+
if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
|
|
32239
|
+
inView.push(a);
|
|
32240
|
+
}
|
|
32241
|
+
}
|
|
32191
32242
|
}
|
|
32243
|
+
const chords = makeSupplementalAlignmentChords(inView);
|
|
32244
|
+
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
|
|
32245
|
+
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
32192
32246
|
}
|
|
32193
32247
|
});
|
|
32194
32248
|
}
|
|
@@ -32300,7 +32354,7 @@ class BAMTrack extends TrackBase {
|
|
|
32300
32354
|
}
|
|
32301
32355
|
|
|
32302
32356
|
getCachedAlignmentContainers() {
|
|
32303
|
-
return this.trackView.viewports.map(vp => vp.
|
|
32357
|
+
return this.trackView.viewports.map(vp => vp.getCachedFeatures())
|
|
32304
32358
|
}
|
|
32305
32359
|
|
|
32306
32360
|
get dataRange() {
|
|
@@ -32326,62 +32380,6 @@ class BAMTrack extends TrackBase {
|
|
|
32326
32380
|
set autoscale(autoscale) {
|
|
32327
32381
|
this.coverageTrack.autoscale = autoscale;
|
|
32328
32382
|
}
|
|
32329
|
-
|
|
32330
|
-
/**
|
|
32331
|
-
* Add chords to the circular view for the given viewport, represented by its reference frame
|
|
32332
|
-
* @param refFrame
|
|
32333
|
-
*/
|
|
32334
|
-
addPairedChordsForViewport(viewport) {
|
|
32335
|
-
|
|
32336
|
-
const maxTemplateLength = this.maxTemplateLength;
|
|
32337
|
-
const inView = [];
|
|
32338
|
-
const refFrame = viewport.referenceFrame;
|
|
32339
|
-
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
32340
|
-
if (a.end >= refFrame.start
|
|
32341
|
-
&& a.start <= refFrame.end
|
|
32342
|
-
&& a.mate
|
|
32343
|
-
&& a.mate.chr
|
|
32344
|
-
&& (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
|
|
32345
|
-
inView.push(a);
|
|
32346
|
-
}
|
|
32347
|
-
}
|
|
32348
|
-
const chords = makePairedAlignmentChords(inView);
|
|
32349
|
-
sendChords(chords, this, refFrame, 0.02);
|
|
32350
|
-
|
|
32351
|
-
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
32352
|
-
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
32353
|
-
//
|
|
32354
|
-
// // name the chord set to include track name and locus
|
|
32355
|
-
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
32356
|
-
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
32357
|
-
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
32358
|
-
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
32359
|
-
}
|
|
32360
|
-
|
|
32361
|
-
addSplitChordsForViewport(viewport) {
|
|
32362
|
-
|
|
32363
|
-
const inView = [];
|
|
32364
|
-
const refFrame = viewport.referenceFrame;
|
|
32365
|
-
for (let a of viewport.cachedFeatures.allAlignments()) {
|
|
32366
|
-
|
|
32367
|
-
const sa = a.hasTag('SA');
|
|
32368
|
-
if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
|
|
32369
|
-
inView.push(a);
|
|
32370
|
-
}
|
|
32371
|
-
}
|
|
32372
|
-
|
|
32373
|
-
const chords = makeSupplementalAlignmentChords(inView);
|
|
32374
|
-
sendChords(chords, this, refFrame, 0.02);
|
|
32375
|
-
|
|
32376
|
-
// const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
|
|
32377
|
-
// const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
|
|
32378
|
-
//
|
|
32379
|
-
// // name the chord set to include track name and locus
|
|
32380
|
-
// const encodedName = this.name.replaceAll(' ', '%20')
|
|
32381
|
-
// const chordSetName = "all" === refFrame.chr ? encodedName :
|
|
32382
|
-
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
|
|
32383
|
-
// this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
32384
|
-
}
|
|
32385
32383
|
}
|
|
32386
32384
|
|
|
32387
32385
|
|
|
@@ -32502,7 +32500,7 @@ class CoverageTrack {
|
|
|
32502
32500
|
|
|
32503
32501
|
getClickedObject(clickState) {
|
|
32504
32502
|
|
|
32505
|
-
let features = clickState.viewport.
|
|
32503
|
+
let features = clickState.viewport.getCachedFeatures();
|
|
32506
32504
|
if (!features || features.length === 0) return
|
|
32507
32505
|
|
|
32508
32506
|
const genomicLocation = Math.floor(clickState.genomicLocation);
|
|
@@ -32578,8 +32576,8 @@ class AlignmentTrack {
|
|
|
32578
32576
|
this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
|
|
32579
32577
|
this.pairConnectorColor = config.pairConnectorColor;
|
|
32580
32578
|
|
|
32581
|
-
this.
|
|
32582
|
-
this.
|
|
32579
|
+
this.smallFragmentLengthColor = config.smallFragmentLengthColor || "rgb(0, 0, 150)";
|
|
32580
|
+
this.largeFragmentLengthColor = config.largeFragmentLengthColor || "rgb(200, 0, 0)";
|
|
32583
32581
|
|
|
32584
32582
|
this.pairOrientation = config.pairOrienation || 'fr';
|
|
32585
32583
|
this.pairColors = {};
|
|
@@ -32587,7 +32585,7 @@ class AlignmentTrack {
|
|
|
32587
32585
|
this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
|
|
32588
32586
|
this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
|
|
32589
32587
|
|
|
32590
|
-
this.colorBy = config.colorBy || "
|
|
32588
|
+
this.colorBy = config.colorBy || "pairOrientation";
|
|
32591
32589
|
this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
|
|
32592
32590
|
this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
|
|
32593
32591
|
|
|
@@ -32979,7 +32977,7 @@ class AlignmentTrack {
|
|
|
32979
32977
|
direction: direction
|
|
32980
32978
|
};
|
|
32981
32979
|
this.parent.sortObject = newSortObject;
|
|
32982
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
32980
|
+
sortAlignmentRows(newSortObject, viewport.getCachedFeatures());
|
|
32983
32981
|
viewport.repaint();
|
|
32984
32982
|
};
|
|
32985
32983
|
list.push('<b>Sort by...</b>');
|
|
@@ -33009,7 +33007,7 @@ class AlignmentTrack {
|
|
|
33009
33007
|
};
|
|
33010
33008
|
this.sortByTag = tag;
|
|
33011
33009
|
this.parent.sortObject = newSortObject;
|
|
33012
|
-
sortAlignmentRows(newSortObject, viewport.
|
|
33010
|
+
sortAlignmentRows(newSortObject, viewport.getCachedFeatures());
|
|
33013
33011
|
viewport.repaint();
|
|
33014
33012
|
}
|
|
33015
33013
|
}
|
|
@@ -33036,11 +33034,7 @@ class AlignmentTrack {
|
|
|
33036
33034
|
const referenceFrame = clickState.viewport.referenceFrame;
|
|
33037
33035
|
if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
|
|
33038
33036
|
this.highlightedAlignmentReadNamed = clickedAlignment.readName;
|
|
33039
|
-
|
|
33040
|
-
const bpWidth = referenceFrame.end - referenceFrame.start;
|
|
33041
|
-
const frameStart = clickedAlignment.mate.position - bpWidth / 2;
|
|
33042
|
-
const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
|
|
33043
|
-
this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
|
|
33037
|
+
this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame);
|
|
33044
33038
|
} else {
|
|
33045
33039
|
Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
|
|
33046
33040
|
}
|
|
@@ -33088,7 +33082,19 @@ class AlignmentTrack {
|
|
|
33088
33082
|
list.push({
|
|
33089
33083
|
label: 'Add discordant pairs to circular view',
|
|
33090
33084
|
click: () => {
|
|
33091
|
-
this.parent.
|
|
33085
|
+
const maxFragmentLength = this.parent.maxFragmentLength;
|
|
33086
|
+
const {referenceFrame} = viewport;
|
|
33087
|
+
const inView = viewport.getCachedFeatures().allAlignments().filter(a => {
|
|
33088
|
+
return a.end >= referenceFrame.start
|
|
33089
|
+
&& a.start <= referenceFrame.end
|
|
33090
|
+
&& a.mate
|
|
33091
|
+
&& a.mate.chr
|
|
33092
|
+
&& (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxFragmentLength)
|
|
33093
|
+
});
|
|
33094
|
+
this.browser.circularViewVisible = true;
|
|
33095
|
+
const chords = makePairedAlignmentChords(inView);
|
|
33096
|
+
const color = IGVColor.addAlpha(this.parent.color || 'rgb(0,0,255)', 0.02);
|
|
33097
|
+
this.browser.circularView.addChords(chords, {track: this.parent.name, color: color});
|
|
33092
33098
|
}
|
|
33093
33099
|
});
|
|
33094
33100
|
}
|
|
@@ -33096,7 +33102,17 @@ class AlignmentTrack {
|
|
|
33096
33102
|
list.push({
|
|
33097
33103
|
label: 'Add split reads to circular view',
|
|
33098
33104
|
click: () => {
|
|
33099
|
-
|
|
33105
|
+
const inView = [];
|
|
33106
|
+
for (let a of viewport.getCachedFeatures().allAlignments()) {
|
|
33107
|
+
const referenceFrame = viewport.referenceFrame;
|
|
33108
|
+
const sa = a.hasTag('SA');
|
|
33109
|
+
if (a.end >= referenceFrame.start && a.start <= referenceFrame.end && sa) {
|
|
33110
|
+
inView.push(a);
|
|
33111
|
+
}
|
|
33112
|
+
}
|
|
33113
|
+
const chords = makeSupplementalAlignmentChords(inView);
|
|
33114
|
+
const color = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.1);
|
|
33115
|
+
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
33100
33116
|
}
|
|
33101
33117
|
});
|
|
33102
33118
|
}
|
|
@@ -33115,7 +33131,7 @@ class AlignmentTrack {
|
|
|
33115
33131
|
|
|
33116
33132
|
const showSoftClips = this.parent.showSoftClips;
|
|
33117
33133
|
|
|
33118
|
-
let features = viewport.
|
|
33134
|
+
let features = viewport.getCachedFeatures();
|
|
33119
33135
|
if (!features || features.length === 0) return
|
|
33120
33136
|
|
|
33121
33137
|
let packedAlignmentRows = features.packedAlignmentRows;
|
|
@@ -33206,17 +33222,14 @@ class AlignmentTrack {
|
|
|
33206
33222
|
break
|
|
33207
33223
|
}
|
|
33208
33224
|
|
|
33209
|
-
case "tlen":
|
|
33210
33225
|
case "fragmentLength":
|
|
33211
33226
|
|
|
33212
|
-
if (alignment.mate && alignment.isMateMapped()) {
|
|
33213
|
-
|
|
33214
|
-
|
|
33215
|
-
|
|
33216
|
-
|
|
33217
|
-
|
|
33218
|
-
color = this.largeTLENColor;
|
|
33219
|
-
}
|
|
33227
|
+
if (alignment.mate && alignment.isMateMapped() && alignment.mate.chr !== alignment.chr) {
|
|
33228
|
+
color = getChrColor(alignment.mate.chr);
|
|
33229
|
+
} else if (this.parent.minFragmentLength && Math.abs(alignment.fragmentLength) < this.parent.minFragmentLength) {
|
|
33230
|
+
color = this.smallFragmentLengthColor;
|
|
33231
|
+
} else if (this.parent.maxFragmentLength && Math.abs(alignment.fragmentLength) > this.parent.maxFragmentLength) {
|
|
33232
|
+
color = this.largeFragmentLengthColor;
|
|
33220
33233
|
}
|
|
33221
33234
|
break
|
|
33222
33235
|
|
|
@@ -33258,6 +33271,11 @@ function sortAlignmentRows(options, alignmentContainer) {
|
|
|
33258
33271
|
return true === direction ? i : -i
|
|
33259
33272
|
});
|
|
33260
33273
|
|
|
33274
|
+
// For debugging
|
|
33275
|
+
// for(let r of alignmentContainer.packedAlignmentRows) {
|
|
33276
|
+
// console.log(r.score);
|
|
33277
|
+
// }
|
|
33278
|
+
|
|
33261
33279
|
}
|
|
33262
33280
|
|
|
33263
33281
|
function shadedBaseColor(qual, baseColor) {
|
|
@@ -33417,7 +33435,7 @@ class RulerViewport extends TrackViewport {
|
|
|
33417
33435
|
|
|
33418
33436
|
this.$rulerLabel.click(async () => {
|
|
33419
33437
|
|
|
33420
|
-
await this.browser.
|
|
33438
|
+
await this.browser.selectMultiLocusPanel(this.referenceFrame);
|
|
33421
33439
|
|
|
33422
33440
|
// const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
|
|
33423
33441
|
// for (let referenceFrame of removals) {
|
|
@@ -33755,7 +33773,7 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33755
33773
|
referenceFrame.end = ee;
|
|
33756
33774
|
referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
33757
33775
|
|
|
33758
|
-
this.browser.updateViews(true);
|
|
33776
|
+
this.browser.updateViews(referenceFrame, this.browser.trackViews, true);
|
|
33759
33777
|
|
|
33760
33778
|
}
|
|
33761
33779
|
|
|
@@ -34353,11 +34371,15 @@ const colorPickerExclusionTypes = new Set(['ruler', 'sequence', 'ideogram']);
|
|
|
34353
34371
|
class TrackView {
|
|
34354
34372
|
|
|
34355
34373
|
constructor(browser, columnContainer, track) {
|
|
34374
|
+
|
|
34356
34375
|
this.namespace = `trackview-${guid$2()}`;
|
|
34376
|
+
|
|
34357
34377
|
this.browser = browser;
|
|
34358
34378
|
this.track = track;
|
|
34359
34379
|
track.trackView = this;
|
|
34380
|
+
|
|
34360
34381
|
this.addDOMToColumnContainer(browser, columnContainer, browser.referenceFrameList);
|
|
34382
|
+
|
|
34361
34383
|
}
|
|
34362
34384
|
|
|
34363
34385
|
/**
|
|
@@ -34520,14 +34542,19 @@ class TrackView {
|
|
|
34520
34542
|
if (false === colorPickerExclusionTypes.has(this.track.type)) {
|
|
34521
34543
|
|
|
34522
34544
|
const trackColors = [];
|
|
34545
|
+
|
|
34523
34546
|
const color = this.track.color || this.track.defaultColor;
|
|
34547
|
+
|
|
34524
34548
|
if (isString$3(color)) {
|
|
34525
34549
|
trackColors.push(color);
|
|
34526
34550
|
}
|
|
34551
|
+
|
|
34527
34552
|
if (this.track.altColor && isString$3(this.track.altColor)) {
|
|
34528
34553
|
trackColors.push(this.track.altColor);
|
|
34529
34554
|
}
|
|
34555
|
+
|
|
34530
34556
|
const defaultColors = trackColors.map(c => c.startsWith("#") ? c : c.startsWith("rgb(") ? IGVColor.rgbToHex(c) : IGVColor.colorNameToHex(c));
|
|
34557
|
+
|
|
34531
34558
|
const colorHandlers =
|
|
34532
34559
|
{
|
|
34533
34560
|
color: color => {
|
|
@@ -34540,6 +34567,7 @@ class TrackView {
|
|
|
34540
34567
|
}
|
|
34541
34568
|
|
|
34542
34569
|
};
|
|
34570
|
+
|
|
34543
34571
|
this.browser.genericColorPicker.configure(defaultColors, colorHandlers);
|
|
34544
34572
|
this.browser.genericColorPicker.setActiveColorHandler(key);
|
|
34545
34573
|
this.browser.genericColorPicker.show();
|
|
@@ -34553,6 +34581,7 @@ class TrackView {
|
|
|
34553
34581
|
if (this.track.minHeight) {
|
|
34554
34582
|
newHeight = Math.max(this.track.minHeight, newHeight);
|
|
34555
34583
|
}
|
|
34584
|
+
|
|
34556
34585
|
if (this.track.maxHeight) {
|
|
34557
34586
|
newHeight = Math.min(this.track.maxHeight, newHeight);
|
|
34558
34587
|
}
|
|
@@ -34647,9 +34676,7 @@ class TrackView {
|
|
|
34647
34676
|
repaintViews() {
|
|
34648
34677
|
|
|
34649
34678
|
for (let viewport of this.viewports) {
|
|
34650
|
-
|
|
34651
|
-
viewport.repaint();
|
|
34652
|
-
}
|
|
34679
|
+
viewport.repaint();
|
|
34653
34680
|
}
|
|
34654
34681
|
|
|
34655
34682
|
if (typeof this.track.paintAxis === 'function') {
|
|
@@ -34676,9 +34703,6 @@ class TrackView {
|
|
|
34676
34703
|
|
|
34677
34704
|
/**
|
|
34678
34705
|
* Update viewports to reflect current genomic state, possibly loading additional data.
|
|
34679
|
-
*
|
|
34680
|
-
* @param force - if true, force a repaint even if no new data is loaded
|
|
34681
|
-
* @returns {Promise<void>}
|
|
34682
34706
|
*/
|
|
34683
34707
|
async updateViews(force) {
|
|
34684
34708
|
|
|
@@ -34696,24 +34720,24 @@ class TrackView {
|
|
|
34696
34720
|
}
|
|
34697
34721
|
|
|
34698
34722
|
// rpv: viewports whose image (canvas) does not fully cover current genomic range
|
|
34699
|
-
const reloadableViewports =
|
|
34723
|
+
const reloadableViewports = this.viewportsToReload(force);
|
|
34700
34724
|
|
|
34701
34725
|
// Trigger viewport to load features needed to cover current genomic range
|
|
34702
34726
|
// NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
|
|
34703
34727
|
for (let viewport of reloadableViewports) {
|
|
34704
34728
|
await viewport.loadFeatures();
|
|
34705
34729
|
}
|
|
34706
|
-
|
|
34730
|
+
|
|
34707
34731
|
if (this.disposed) return // Track was removed during load
|
|
34708
34732
|
|
|
34709
|
-
//
|
|
34733
|
+
// Very special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
|
|
34710
34734
|
// section depends on data from all the views. We only need to adjust this however if any data was loaded
|
|
34711
34735
|
// (i.e. reloadableViewports.length > 0)
|
|
34712
34736
|
if (this.track && typeof this.track.variantRowCount === 'function' && reloadableViewports.length > 0) {
|
|
34713
34737
|
let maxRow = 0;
|
|
34714
34738
|
for (let viewport of this.viewports) {
|
|
34715
|
-
if (viewport.
|
|
34716
|
-
maxRow = Math.max(maxRow, viewport.
|
|
34739
|
+
if (viewport.tile && viewport.tile.features) {
|
|
34740
|
+
maxRow = Math.max(maxRow, viewport.tile.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
|
|
34717
34741
|
}
|
|
34718
34742
|
}
|
|
34719
34743
|
const current = this.track.nVariantRows;
|
|
@@ -34725,18 +34749,19 @@ class TrackView {
|
|
|
34725
34749
|
}
|
|
34726
34750
|
}
|
|
34727
34751
|
|
|
34752
|
+
|
|
34728
34753
|
if (this.track.autoscale) {
|
|
34729
34754
|
let allFeatures = [];
|
|
34730
34755
|
for (let visibleViewport of visibleViewports) {
|
|
34731
34756
|
const referenceFrame = visibleViewport.referenceFrame;
|
|
34732
34757
|
const start = referenceFrame.start;
|
|
34733
34758
|
const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());
|
|
34734
|
-
if (visibleViewport.
|
|
34735
|
-
if (typeof visibleViewport.
|
|
34736
|
-
const max = visibleViewport.
|
|
34759
|
+
if (visibleViewport.tile && visibleViewport.tile.features) {
|
|
34760
|
+
if (typeof visibleViewport.tile.features.getMax === 'function') {
|
|
34761
|
+
const max = visibleViewport.tile.features.getMax(start, end);
|
|
34737
34762
|
allFeatures.push({value: max});
|
|
34738
34763
|
} else {
|
|
34739
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.
|
|
34764
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.tile.features, start, end));
|
|
34740
34765
|
}
|
|
34741
34766
|
}
|
|
34742
34767
|
}
|
|
@@ -34747,18 +34772,14 @@ class TrackView {
|
|
|
34747
34772
|
}
|
|
34748
34773
|
}
|
|
34749
34774
|
|
|
34750
|
-
// Must repaint all viewports if autoscaling
|
|
34751
|
-
if (this.track.autoscale || this.track.autoscaleGroup
|
|
34752
|
-
for (let
|
|
34753
|
-
|
|
34775
|
+
// Must repaint all viewports if autoscaling
|
|
34776
|
+
if (!isDragging && (this.track.autoscale || this.track.autoscaleGroup)) {
|
|
34777
|
+
for (let visibleViewport of visibleViewports) {
|
|
34778
|
+
visibleViewport.repaint();
|
|
34754
34779
|
}
|
|
34755
34780
|
} else {
|
|
34756
|
-
|
|
34757
|
-
|
|
34758
|
-
const invalid = vp.canvas && vp.canvas._data && vp.canvas._data.invalidate;
|
|
34759
|
-
if (invalid || reloadedViewports.has(vp)) {
|
|
34760
|
-
vp.repaint();
|
|
34761
|
-
}
|
|
34781
|
+
for (let vp of reloadableViewports) {
|
|
34782
|
+
vp.repaint();
|
|
34762
34783
|
}
|
|
34763
34784
|
}
|
|
34764
34785
|
|
|
@@ -34789,14 +34810,14 @@ class TrackView {
|
|
|
34789
34810
|
/**
|
|
34790
34811
|
* Return a promise to get all in-view features. Used for group autoscaling.
|
|
34791
34812
|
*/
|
|
34792
|
-
async getInViewFeatures() {
|
|
34813
|
+
async getInViewFeatures(force) {
|
|
34793
34814
|
|
|
34794
34815
|
if (!(this.browser && this.browser.referenceFrameList)) {
|
|
34795
34816
|
return []
|
|
34796
34817
|
}
|
|
34797
34818
|
|
|
34798
34819
|
// List of viewports that need reloading
|
|
34799
|
-
const rpV = this.viewportsToReload();
|
|
34820
|
+
const rpV = this.viewportsToReload(force);
|
|
34800
34821
|
const promises = rpV.map(function (vp) {
|
|
34801
34822
|
return vp.loadFeatures()
|
|
34802
34823
|
});
|
|
@@ -34805,16 +34826,16 @@ class TrackView {
|
|
|
34805
34826
|
|
|
34806
34827
|
let allFeatures = [];
|
|
34807
34828
|
for (let vp of this.viewports) {
|
|
34808
|
-
if (vp.
|
|
34829
|
+
if (vp.tile && vp.tile.features) {
|
|
34809
34830
|
const referenceFrame = vp.referenceFrame;
|
|
34810
34831
|
const start = referenceFrame.start;
|
|
34811
34832
|
const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());
|
|
34812
34833
|
|
|
34813
|
-
if (typeof vp.
|
|
34814
|
-
const max = vp.
|
|
34834
|
+
if (typeof vp.tile.features.getMax === 'function') {
|
|
34835
|
+
const max = vp.tile.features.getMax(start, end);
|
|
34815
34836
|
allFeatures.push({value: max});
|
|
34816
34837
|
} else {
|
|
34817
|
-
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.
|
|
34838
|
+
allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.tile.features, start, end));
|
|
34818
34839
|
}
|
|
34819
34840
|
}
|
|
34820
34841
|
}
|
|
@@ -34870,7 +34891,7 @@ class TrackView {
|
|
|
34870
34891
|
const start = referenceFrame.start;
|
|
34871
34892
|
const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
|
|
34872
34893
|
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
34873
|
-
return (!viewport.
|
|
34894
|
+
return force || (!viewport.tile || viewport.tile.invalidate || !viewport.tile.containsRange(chr, start, end, bpPerPixel))
|
|
34874
34895
|
}
|
|
34875
34896
|
});
|
|
34876
34897
|
return viewports
|
|
@@ -39653,7 +39674,7 @@ class TextFeatureSource {
|
|
|
39653
39674
|
mapProperties(features, config.mappings);
|
|
39654
39675
|
}
|
|
39655
39676
|
this.queryable = false;
|
|
39656
|
-
this.featureCache = new FeatureCache
|
|
39677
|
+
this.featureCache = new FeatureCache(features, genome);
|
|
39657
39678
|
} else if (config.reader) {
|
|
39658
39679
|
// Explicit reader implementation
|
|
39659
39680
|
this.reader = config.reader;
|
|
@@ -39820,14 +39841,14 @@ class TextFeatureSource {
|
|
|
39820
39841
|
}
|
|
39821
39842
|
|
|
39822
39843
|
// Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
|
|
39823
|
-
this.featureCache = new FeatureCache
|
|
39844
|
+
this.featureCache = new FeatureCache(features, this.genome, genomicInterval);
|
|
39824
39845
|
|
|
39825
39846
|
// If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
39826
39847
|
if (this.config.searchable || this.config.searchableFields) {
|
|
39827
39848
|
this.addFeaturesToDB(features);
|
|
39828
39849
|
}
|
|
39829
39850
|
} else {
|
|
39830
|
-
this.featureCache = new FeatureCache
|
|
39851
|
+
this.featureCache = new FeatureCache([], genomicInterval); // Empty cache
|
|
39831
39852
|
}
|
|
39832
39853
|
}
|
|
39833
39854
|
|
|
@@ -41824,7 +41845,7 @@ function renderFeature(feature, bpStart, xScale, pixelHeight, ctx, options) {
|
|
|
41824
41845
|
// single-exon transcript
|
|
41825
41846
|
const xLeft = Math.max(0, coord.px);
|
|
41826
41847
|
const xRight = Math.min(pixelWidth, coord.px1);
|
|
41827
|
-
const width =
|
|
41848
|
+
const width = xRight - xLeft;
|
|
41828
41849
|
ctx.fillRect(xLeft, py, width, h);
|
|
41829
41850
|
|
|
41830
41851
|
// Arrows
|
|
@@ -42630,11 +42651,13 @@ class WigTrack extends TrackBase {
|
|
|
42630
42651
|
this.paintAxis = paintAxis;
|
|
42631
42652
|
|
|
42632
42653
|
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
42633
|
-
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42634
|
-
this.logScale = config.logScale ? config.logScale : false;
|
|
42635
42654
|
if ("bigwig" === format) {
|
|
42655
|
+
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42656
|
+
this.logScale = config.logScale ? config.logScale : false;
|
|
42636
42657
|
this.featureSource = new BWSource(config, this.browser.genome);
|
|
42637
42658
|
} else if ("tdf" === format) {
|
|
42659
|
+
this.flipAxis = config.flipAxis ? config.flipAxis : false;
|
|
42660
|
+
this.logScale = config.logScale ? config.logScale : false;
|
|
42638
42661
|
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
42639
42662
|
} else {
|
|
42640
42663
|
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
@@ -42686,7 +42709,7 @@ class WigTrack extends TrackBase {
|
|
|
42686
42709
|
let items = [];
|
|
42687
42710
|
if (this.flipAxis !== undefined) {
|
|
42688
42711
|
items.push({
|
|
42689
|
-
label:
|
|
42712
|
+
label:"Flip y-axis",
|
|
42690
42713
|
click: () => {
|
|
42691
42714
|
this.flipAxis = !this.flipAxis;
|
|
42692
42715
|
this.trackView.repaintViews();
|
|
@@ -43414,7 +43437,7 @@ class SegTrack extends TrackBase {
|
|
|
43414
43437
|
|
|
43415
43438
|
const sortHandler = (sort) => {
|
|
43416
43439
|
const viewport = clickState.viewport;
|
|
43417
|
-
const features = viewport.
|
|
43440
|
+
const features = viewport.getCachedFeatures();
|
|
43418
43441
|
this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
|
|
43419
43442
|
};
|
|
43420
43443
|
|
|
@@ -43532,16 +43555,10 @@ const MUT_COLORS = {
|
|
|
43532
43555
|
* THE SOFTWARE.
|
|
43533
43556
|
*/
|
|
43534
43557
|
|
|
43535
|
-
/**
|
|
43536
|
-
* Represents 2 or more wig tracks overlaid on a common viewport.
|
|
43537
|
-
*/
|
|
43538
43558
|
class MergedTrack extends TrackBase {
|
|
43539
43559
|
|
|
43540
43560
|
constructor(config, browser) {
|
|
43541
43561
|
super(config, browser);
|
|
43542
|
-
this.type = "merged";
|
|
43543
|
-
this.featureType = 'numeric';
|
|
43544
|
-
this.paintAxis = paintAxis;
|
|
43545
43562
|
}
|
|
43546
43563
|
|
|
43547
43564
|
init(config) {
|
|
@@ -43552,6 +43569,21 @@ class MergedTrack extends TrackBase {
|
|
|
43552
43569
|
super.init(config);
|
|
43553
43570
|
}
|
|
43554
43571
|
|
|
43572
|
+
get height() {
|
|
43573
|
+
return this._height
|
|
43574
|
+
}
|
|
43575
|
+
|
|
43576
|
+
set height(h) {
|
|
43577
|
+
this._height = h;
|
|
43578
|
+
if (this.tracks) {
|
|
43579
|
+
for (let t of this.tracks) {
|
|
43580
|
+
t.height = h;
|
|
43581
|
+
t.config.height = h;
|
|
43582
|
+
}
|
|
43583
|
+
}
|
|
43584
|
+
}
|
|
43585
|
+
|
|
43586
|
+
|
|
43555
43587
|
async postInit() {
|
|
43556
43588
|
|
|
43557
43589
|
this.tracks = [];
|
|
@@ -43571,55 +43603,11 @@ class MergedTrack extends TrackBase {
|
|
|
43571
43603
|
}
|
|
43572
43604
|
}
|
|
43573
43605
|
|
|
43574
|
-
this.
|
|
43575
|
-
this.logScale = this.config.logScale ? this.config.logScale : false;
|
|
43576
|
-
this.autoscale = this.config.autoscale || this.config.max === undefined;
|
|
43577
|
-
if (!this.autoscale) {
|
|
43578
|
-
this.dataRange = {
|
|
43579
|
-
min: this.config.min || 0,
|
|
43580
|
-
max: this.config.max
|
|
43581
|
-
};
|
|
43582
|
-
}
|
|
43583
|
-
for (let t of this.tracks) {
|
|
43584
|
-
t.autoscale = false;
|
|
43585
|
-
t.dataRange = this.dataRange;
|
|
43586
|
-
}
|
|
43587
|
-
|
|
43588
|
-
this.height = this.config.height || 50;
|
|
43606
|
+
this.height = this.config.height || 100;
|
|
43589
43607
|
|
|
43590
43608
|
return Promise.all(p)
|
|
43591
43609
|
}
|
|
43592
43610
|
|
|
43593
|
-
get height() {
|
|
43594
|
-
return this._height
|
|
43595
|
-
}
|
|
43596
|
-
|
|
43597
|
-
set height(h) {
|
|
43598
|
-
this._height = h;
|
|
43599
|
-
if (this.tracks) {
|
|
43600
|
-
for (let t of this.tracks) {
|
|
43601
|
-
t.height = h;
|
|
43602
|
-
t.config.height = h;
|
|
43603
|
-
}
|
|
43604
|
-
}
|
|
43605
|
-
}
|
|
43606
|
-
|
|
43607
|
-
menuItemList() {
|
|
43608
|
-
let items = [];
|
|
43609
|
-
if (this.flipAxis !== undefined) {
|
|
43610
|
-
items.push({
|
|
43611
|
-
label: "Flip y-axis",
|
|
43612
|
-
click: () => {
|
|
43613
|
-
this.flipAxis = !this.flipAxis;
|
|
43614
|
-
this.trackView.repaintViews();
|
|
43615
|
-
}
|
|
43616
|
-
});
|
|
43617
|
-
}
|
|
43618
|
-
|
|
43619
|
-
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
43620
|
-
|
|
43621
|
-
return items
|
|
43622
|
-
}
|
|
43623
43611
|
|
|
43624
43612
|
async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
|
|
43625
43613
|
|
|
@@ -43629,26 +43617,44 @@ class MergedTrack extends TrackBase {
|
|
|
43629
43617
|
|
|
43630
43618
|
draw(options) {
|
|
43631
43619
|
|
|
43632
|
-
|
|
43620
|
+
var i, len, mergedFeatures, trackOptions, dataRange;
|
|
43633
43621
|
|
|
43634
|
-
|
|
43635
|
-
|
|
43636
|
-
|
|
43622
|
+
mergedFeatures = options.features; // Array of feature arrays, 1 for each track
|
|
43623
|
+
|
|
43624
|
+
dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
|
|
43637
43625
|
|
|
43638
|
-
|
|
43639
|
-
|
|
43626
|
+
//IGVGraphics.fillRect(options.context, 0, options.pixelTop, options.pixelWidth, options.pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
|
|
43627
|
+
|
|
43628
|
+
for (i = 0, len = this.tracks.length; i < len; i++) {
|
|
43629
|
+
|
|
43630
|
+
trackOptions = Object.assign({}, options);
|
|
43640
43631
|
trackOptions.features = mergedFeatures[i];
|
|
43641
|
-
this.tracks[i].dataRange =
|
|
43642
|
-
this.tracks[i].flipAxis = this.flipAxis;
|
|
43643
|
-
this.tracks[i].logScale = this.logScale;
|
|
43644
|
-
this.tracks[i].graphType = this.graphType;
|
|
43632
|
+
this.tracks[i].dataRange = dataRange;
|
|
43645
43633
|
this.tracks[i].draw(trackOptions);
|
|
43646
43634
|
}
|
|
43635
|
+
|
|
43636
|
+
}
|
|
43637
|
+
|
|
43638
|
+
paintAxis(ctx, pixelWidth, pixelHeight) {
|
|
43639
|
+
|
|
43640
|
+
var i, len, autoscale, track;
|
|
43641
|
+
|
|
43642
|
+
autoscale = true; // Hardcoded for now
|
|
43643
|
+
|
|
43644
|
+
for (i = 0, len = this.tracks.length; i < len; i++) {
|
|
43645
|
+
|
|
43646
|
+
track = this.tracks[i];
|
|
43647
|
+
|
|
43648
|
+
if (typeof track.paintAxis === 'function') {
|
|
43649
|
+
track.paintAxis(ctx, pixelWidth, pixelHeight);
|
|
43650
|
+
if (autoscale) break
|
|
43651
|
+
}
|
|
43652
|
+
}
|
|
43647
43653
|
}
|
|
43648
43654
|
|
|
43649
43655
|
popupData(clickState, features) {
|
|
43650
43656
|
|
|
43651
|
-
const featuresArray = features || clickState.viewport.
|
|
43657
|
+
const featuresArray = features || clickState.viewport.getCachedFeatures();
|
|
43652
43658
|
|
|
43653
43659
|
if (featuresArray && featuresArray.length === this.tracks.length) {
|
|
43654
43660
|
// Array of feature arrays, 1 for each track
|
|
@@ -43666,22 +43672,41 @@ class MergedTrack extends TrackBase {
|
|
|
43666
43672
|
|
|
43667
43673
|
|
|
43668
43674
|
supportsWholeGenome() {
|
|
43669
|
-
|
|
43675
|
+
const b = this.tracks.every(track => track.supportsWholeGenome());
|
|
43676
|
+
return b
|
|
43670
43677
|
}
|
|
43671
43678
|
}
|
|
43672
43679
|
|
|
43673
43680
|
function autoscale(chr, featureArrays) {
|
|
43674
43681
|
|
|
43675
|
-
|
|
43676
|
-
|
|
43677
|
-
|
|
43678
|
-
|
|
43682
|
+
|
|
43683
|
+
var min = 0,
|
|
43684
|
+
max = -Number.MAX_VALUE;
|
|
43685
|
+
|
|
43686
|
+
// if (chr === 'all') {
|
|
43687
|
+
// allValues = [];
|
|
43688
|
+
// featureArrays.forEach(function (features) {
|
|
43689
|
+
// features.forEach(function (f) {
|
|
43690
|
+
// if (!Number.isNaN(f.value)) {
|
|
43691
|
+
// allValues.push(f.value);
|
|
43692
|
+
// }
|
|
43693
|
+
// });
|
|
43694
|
+
// });
|
|
43695
|
+
//
|
|
43696
|
+
// min = Math.min(0, IGVMath.percentile(allValues, .1));
|
|
43697
|
+
// max = IGVMath.percentile(allValues, 99.9);
|
|
43698
|
+
//
|
|
43699
|
+
// }
|
|
43700
|
+
// else {
|
|
43701
|
+
featureArrays.forEach(function (features, i) {
|
|
43702
|
+
features.forEach(function (f) {
|
|
43679
43703
|
if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
|
|
43680
43704
|
min = Math.min(min, f.value);
|
|
43681
43705
|
max = Math.max(max, f.value);
|
|
43682
43706
|
}
|
|
43683
|
-
}
|
|
43684
|
-
}
|
|
43707
|
+
});
|
|
43708
|
+
});
|
|
43709
|
+
// }
|
|
43685
43710
|
return {min: min, max: max}
|
|
43686
43711
|
}
|
|
43687
43712
|
|
|
@@ -44223,23 +44248,21 @@ class InteractionTrack extends TrackBase {
|
|
|
44223
44248
|
|
|
44224
44249
|
// inView features are simply features that have been drawn, i.e. have a drawState
|
|
44225
44250
|
const inView = cachedFeatures.filter(f => f.drawState);
|
|
44226
|
-
if(inView.length === 0)
|
|
44251
|
+
if(inView.length === 0) erturn;
|
|
44227
44252
|
|
|
44228
44253
|
this.browser.circularViewVisible = true;
|
|
44229
44254
|
const chords = makeBedPEChords(inView);
|
|
44230
|
-
|
|
44231
|
-
//
|
|
44232
|
-
|
|
44233
|
-
|
|
44234
|
-
|
|
44235
|
-
//
|
|
44236
|
-
|
|
44237
|
-
|
|
44238
|
-
|
|
44239
|
-
|
|
44240
|
-
|
|
44241
|
-
// `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
|
|
44242
|
-
// this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
|
|
44255
|
+
|
|
44256
|
+
// for filtered set, distinguishing the chromosomes is more critical than tracks
|
|
44257
|
+
const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5);
|
|
44258
|
+
const trackColor = IGVColor.addAlpha(this.color, 0.5);
|
|
44259
|
+
|
|
44260
|
+
// name the chord set to include filtering information
|
|
44261
|
+
const encodedName = this.name.replaceAll(' ', '%20');
|
|
44262
|
+
const chordSetName = "all" === refFrame.chr ?
|
|
44263
|
+
encodedName :
|
|
44264
|
+
`${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`;
|
|
44265
|
+
this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
|
|
44243
44266
|
}
|
|
44244
44267
|
|
|
44245
44268
|
doAutoscale(features) {
|
|
@@ -44304,7 +44327,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44304
44327
|
|
|
44305
44328
|
// We use the cached features rather than method to avoid async load. If the
|
|
44306
44329
|
// feature is not already loaded this won't work, but the user wouldn't be mousing over it either.
|
|
44307
|
-
const featureList = features || clickState.viewport.
|
|
44330
|
+
const featureList = features || clickState.viewport.getCachedFeatures();
|
|
44308
44331
|
const candidates = [];
|
|
44309
44332
|
if (featureList) {
|
|
44310
44333
|
const proportional = (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView");
|
|
@@ -44830,7 +44853,7 @@ class VariantTrack extends TrackBase {
|
|
|
44830
44853
|
}
|
|
44831
44854
|
|
|
44832
44855
|
} else if (this._color) {
|
|
44833
|
-
variantColor = this.
|
|
44856
|
+
variantColor = (typeof this._color === "function") ? this._color(v) : this._color;
|
|
44834
44857
|
} else if ("NONVARIANT" === v.type) {
|
|
44835
44858
|
variantColor = this.nonRefColor;
|
|
44836
44859
|
} else if ("MIXED" === v.type) {
|
|
@@ -44841,10 +44864,6 @@ class VariantTrack extends TrackBase {
|
|
|
44841
44864
|
return variantColor
|
|
44842
44865
|
}
|
|
44843
44866
|
|
|
44844
|
-
get color() {
|
|
44845
|
-
return this._color ? ((typeof this._color === "function") ? this._color(v) : this._color) : this.defaultColor
|
|
44846
|
-
}
|
|
44847
|
-
|
|
44848
44867
|
clickedFeatures(clickState, features) {
|
|
44849
44868
|
|
|
44850
44869
|
let featureList = super.clickedFeatures(clickState, features);
|
|
@@ -45077,9 +45096,19 @@ class VariantTrack extends TrackBase {
|
|
|
45077
45096
|
menuItems.push({
|
|
45078
45097
|
label: 'Add SVs to circular view',
|
|
45079
45098
|
click: () => {
|
|
45099
|
+
const inView = [];
|
|
45080
45100
|
for (let viewport of this.trackView.viewports) {
|
|
45081
|
-
|
|
45101
|
+
const refFrame = viewport.referenceFrame;
|
|
45102
|
+
for (let f of viewport.getCachedFeatures()) {
|
|
45103
|
+
if (f.end >= refFrame.start && f.start <= refFrame.end) {
|
|
45104
|
+
inView.push(f);
|
|
45105
|
+
}
|
|
45106
|
+
}
|
|
45082
45107
|
}
|
|
45108
|
+
|
|
45109
|
+
const chords = makeVCFChords(inView);
|
|
45110
|
+
const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
|
|
45111
|
+
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
45083
45112
|
}
|
|
45084
45113
|
});
|
|
45085
45114
|
}
|
|
@@ -45098,7 +45127,13 @@ class VariantTrack extends TrackBase {
|
|
|
45098
45127
|
list.push({
|
|
45099
45128
|
label: 'Add SVs to Circular View',
|
|
45100
45129
|
click: () => {
|
|
45101
|
-
|
|
45130
|
+
const refFrame = viewport.referenceFrame;
|
|
45131
|
+
const inView = "all" === refFrame.chr ?
|
|
45132
|
+
this.featureSource.getAllFeatures() :
|
|
45133
|
+
this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
|
|
45134
|
+
const chords = makeVCFChords(inView);
|
|
45135
|
+
const color = IGVColor.addAlpha(this._color || this.defaultColor, 0.5);
|
|
45136
|
+
this.browser.circularView.addChords(chords, {track: this.name, color: color});
|
|
45102
45137
|
}
|
|
45103
45138
|
});
|
|
45104
45139
|
|
|
@@ -45108,15 +45143,6 @@ class VariantTrack extends TrackBase {
|
|
|
45108
45143
|
}
|
|
45109
45144
|
|
|
45110
45145
|
|
|
45111
|
-
sendChordsForViewport(viewport) {
|
|
45112
|
-
const refFrame = viewport.referenceFrame;
|
|
45113
|
-
const inView = "all" === refFrame.chr ?
|
|
45114
|
-
this.featureSource.getAllFeatures() :
|
|
45115
|
-
this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
|
|
45116
|
-
const chords = makeVCFChords(inView);
|
|
45117
|
-
sendChords(chords, this, refFrame, 0.5);
|
|
45118
|
-
}
|
|
45119
|
-
|
|
45120
45146
|
/**
|
|
45121
45147
|
* Create a "color by" checkbox menu item, optionally initially checked
|
|
45122
45148
|
* @param menuItem
|
|
@@ -45413,7 +45439,7 @@ class EqtlTrack extends TrackBase {
|
|
|
45413
45439
|
*/
|
|
45414
45440
|
popupData(clickState) {
|
|
45415
45441
|
|
|
45416
|
-
let features = clickState.viewport.
|
|
45442
|
+
let features = clickState.viewport.getCachedFeatures();
|
|
45417
45443
|
if (!features || features.length === 0) return []
|
|
45418
45444
|
|
|
45419
45445
|
const tolerance = 3;
|
|
@@ -45743,7 +45769,7 @@ class GWASTrack extends TrackBase {
|
|
|
45743
45769
|
|
|
45744
45770
|
let data = [];
|
|
45745
45771
|
const track = clickState.viewport.trackView.track;
|
|
45746
|
-
const features = clickState.viewport.
|
|
45772
|
+
const features = clickState.viewport.getCachedFeatures();
|
|
45747
45773
|
|
|
45748
45774
|
if (features) {
|
|
45749
45775
|
let count = 0;
|
|
@@ -46426,7 +46452,7 @@ class RNAFeatureSource {
|
|
|
46426
46452
|
|
|
46427
46453
|
const data = await igvxhr.loadString(this.config.url, options);
|
|
46428
46454
|
|
|
46429
|
-
this.featureCache = new FeatureCache
|
|
46455
|
+
this.featureCache = new FeatureCache(parseBP(data), genome);
|
|
46430
46456
|
|
|
46431
46457
|
return this.featureCache.queryFeatures(chr, start, end)
|
|
46432
46458
|
|
|
@@ -47708,15 +47734,6 @@ class ReferenceFrame {
|
|
|
47708
47734
|
this.id = guid$2();
|
|
47709
47735
|
}
|
|
47710
47736
|
|
|
47711
|
-
extend(locus) {
|
|
47712
|
-
const newStart = Math.min(locus.start, this.start);
|
|
47713
|
-
const newEnd = Math.max(locus.end, this.end);
|
|
47714
|
-
const ratio = (newEnd - newStart) / (this.end - this.start);
|
|
47715
|
-
this.start = newStart;
|
|
47716
|
-
this.end = newEnd;
|
|
47717
|
-
this.bpPerPixel *= ratio;
|
|
47718
|
-
}
|
|
47719
|
-
|
|
47720
47737
|
calculateEnd(pixels) {
|
|
47721
47738
|
return this.start + this.bpPerPixel * pixels
|
|
47722
47739
|
}
|
|
@@ -47803,7 +47820,7 @@ class ReferenceFrame {
|
|
|
47803
47820
|
|
|
47804
47821
|
const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;
|
|
47805
47822
|
if (viewChanged) {
|
|
47806
|
-
await browser.updateViews(
|
|
47823
|
+
await browser.updateViews(this);
|
|
47807
47824
|
}
|
|
47808
47825
|
|
|
47809
47826
|
}
|
|
@@ -49605,8 +49622,8 @@ class Browser {
|
|
|
49605
49622
|
this.svgSaveControl = new SVGSaveControl($toggle_button_container.get(0), this);
|
|
49606
49623
|
}
|
|
49607
49624
|
|
|
49608
|
-
if
|
|
49609
|
-
for
|
|
49625
|
+
if(config.customButtons) {
|
|
49626
|
+
for(let b of config.customButtons) {
|
|
49610
49627
|
new CustomButton($toggle_button_container.get(0), this, b);
|
|
49611
49628
|
}
|
|
49612
49629
|
}
|
|
@@ -49767,6 +49784,7 @@ class Browser {
|
|
|
49767
49784
|
return undefined
|
|
49768
49785
|
}
|
|
49769
49786
|
}
|
|
49787
|
+
|
|
49770
49788
|
}
|
|
49771
49789
|
}
|
|
49772
49790
|
|
|
@@ -49856,11 +49874,6 @@ class Browser {
|
|
|
49856
49874
|
|
|
49857
49875
|
await this.loadTrackList(trackConfigurations);
|
|
49858
49876
|
|
|
49859
|
-
// The ruler track is not explicitly loaded, but needs updated nonetheless.
|
|
49860
|
-
for (let rtv of this.trackViews.filter((tv) => tv.track.type === 'ruler')) {
|
|
49861
|
-
rtv.updateViews();
|
|
49862
|
-
}
|
|
49863
|
-
|
|
49864
49877
|
this.updateUIWithReferenceFrameList();
|
|
49865
49878
|
|
|
49866
49879
|
}
|
|
@@ -50039,19 +50052,23 @@ class Browser {
|
|
|
50039
50052
|
|
|
50040
50053
|
async loadTrackList(configList) {
|
|
50041
50054
|
|
|
50042
|
-
|
|
50043
|
-
|
|
50044
|
-
|
|
50045
|
-
|
|
50055
|
+
try {
|
|
50056
|
+
const promises = [];
|
|
50057
|
+
for (let config of configList) {
|
|
50058
|
+
promises.push(this.loadTrack(config, false));
|
|
50059
|
+
}
|
|
50046
50060
|
|
|
50047
|
-
|
|
50048
|
-
|
|
50049
|
-
|
|
50050
|
-
|
|
50051
|
-
|
|
50052
|
-
|
|
50061
|
+
const loadedTracks = await Promise.all(promises);
|
|
50062
|
+
const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
|
|
50063
|
+
return trackView.track.autoscaleGroup
|
|
50064
|
+
});
|
|
50065
|
+
if (groupAutoscaleViews.length > 0) {
|
|
50066
|
+
this.updateViews(groupAutoscaleViews);
|
|
50067
|
+
}
|
|
50068
|
+
return loadedTracks
|
|
50069
|
+
} finally {
|
|
50070
|
+
await this.resize();
|
|
50053
50071
|
}
|
|
50054
|
-
return loadedTracks
|
|
50055
50072
|
}
|
|
50056
50073
|
|
|
50057
50074
|
async loadROI(config) {
|
|
@@ -50065,8 +50082,6 @@ class Browser {
|
|
|
50065
50082
|
} else {
|
|
50066
50083
|
this.roi.push(new ROI(config, this.genome));
|
|
50067
50084
|
}
|
|
50068
|
-
// Force reload all views (force = true) to insure ROI features are loaded. Wasteful but this function is
|
|
50069
|
-
// rarely called.
|
|
50070
50085
|
await this.updateViews(true);
|
|
50071
50086
|
}
|
|
50072
50087
|
|
|
@@ -50078,14 +50093,14 @@ class Browser {
|
|
|
50078
50093
|
}
|
|
50079
50094
|
}
|
|
50080
50095
|
for (let tv of this.trackViews) {
|
|
50081
|
-
tv.
|
|
50096
|
+
tv.updateViews(true);
|
|
50082
50097
|
}
|
|
50083
50098
|
}
|
|
50084
50099
|
|
|
50085
50100
|
clearROIs() {
|
|
50086
50101
|
this.roi = [];
|
|
50087
50102
|
for (let tv of this.trackViews) {
|
|
50088
|
-
tv.
|
|
50103
|
+
tv.updateViews(true);
|
|
50089
50104
|
}
|
|
50090
50105
|
}
|
|
50091
50106
|
|
|
@@ -50097,12 +50112,24 @@ class Browser {
|
|
|
50097
50112
|
/**
|
|
50098
50113
|
* Return a promise to load a track.
|
|
50099
50114
|
*
|
|
50115
|
+
* Each track is associated with the following DOM elements
|
|
50116
|
+
*
|
|
50117
|
+
* leftHandGutter - div on the left for track controls and legend
|
|
50118
|
+
* contentDiv - a div element wrapping all the track content. Height can be > viewportDiv height
|
|
50119
|
+
* viewportDiv - a div element through which the track is viewed. This might have a vertical scrollbar
|
|
50120
|
+
* canvas - canvas element upon which the track is drawn. Child of contentDiv
|
|
50121
|
+
*
|
|
50122
|
+
* The width of all elements should be equal. Height of the viewportDiv is controlled by the user, but never
|
|
50123
|
+
* greater than the contentDiv height. Height of contentDiv and canvas are equal, and governed by the data
|
|
50124
|
+
* loaded.
|
|
50125
|
+
*
|
|
50126
|
+
*
|
|
50100
50127
|
* @param config
|
|
50101
50128
|
* @param doResize - undefined by default
|
|
50102
50129
|
* @returns {*}
|
|
50103
50130
|
*/
|
|
50104
50131
|
|
|
50105
|
-
async loadTrack(config) {
|
|
50132
|
+
async loadTrack(config, doResize) {
|
|
50106
50133
|
|
|
50107
50134
|
|
|
50108
50135
|
// config might be json
|
|
@@ -50170,6 +50197,11 @@ class Browser {
|
|
|
50170
50197
|
}
|
|
50171
50198
|
msg += (": " + config.url);
|
|
50172
50199
|
Alert.presentAlert(new Error(msg), undefined);
|
|
50200
|
+
} finally {
|
|
50201
|
+
// TODO: If loadTrack() is called individually - not via loadTrackList() - call this.resize()
|
|
50202
|
+
if (false === doResize) ; else {
|
|
50203
|
+
await this.resize();
|
|
50204
|
+
}
|
|
50173
50205
|
}
|
|
50174
50206
|
}
|
|
50175
50207
|
|
|
@@ -50393,7 +50425,41 @@ class Browser {
|
|
|
50393
50425
|
this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
|
|
50394
50426
|
}
|
|
50395
50427
|
|
|
50396
|
-
await resize
|
|
50428
|
+
await this.resize();
|
|
50429
|
+
}
|
|
50430
|
+
|
|
50431
|
+
async resize() {
|
|
50432
|
+
|
|
50433
|
+
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
50434
|
+
|
|
50435
|
+
for (let referenceFrame of this.referenceFrameList) {
|
|
50436
|
+
|
|
50437
|
+
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
50438
|
+
|
|
50439
|
+
const {chr, genome} = referenceFrame;
|
|
50440
|
+
|
|
50441
|
+
const {bpLength} = genome.getChromosome(referenceFrame.chr);
|
|
50442
|
+
|
|
50443
|
+
const viewportWidthBP = referenceFrame.toBP(viewportWidth);
|
|
50444
|
+
|
|
50445
|
+
// viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
50446
|
+
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
50447
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
50448
|
+
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
50449
|
+
} else {
|
|
50450
|
+
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
50451
|
+
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
50452
|
+
}
|
|
50453
|
+
|
|
50454
|
+
for (let {viewports} of this.trackViews) {
|
|
50455
|
+
viewports[index].setWidth(viewportWidth);
|
|
50456
|
+
}
|
|
50457
|
+
|
|
50458
|
+
}
|
|
50459
|
+
|
|
50460
|
+
await this.updateViews(true);
|
|
50461
|
+
|
|
50462
|
+
this.updateUIWithReferenceFrameList();
|
|
50397
50463
|
}
|
|
50398
50464
|
|
|
50399
50465
|
async updateViews(force) {
|
|
@@ -50469,12 +50535,6 @@ class Browser {
|
|
|
50469
50535
|
|
|
50470
50536
|
}
|
|
50471
50537
|
|
|
50472
|
-
repaintViews() {
|
|
50473
|
-
for (let trackView of this.trackViews) {
|
|
50474
|
-
trackView.repaintViews();
|
|
50475
|
-
}
|
|
50476
|
-
}
|
|
50477
|
-
|
|
50478
50538
|
updateLocusSearchWidget() {
|
|
50479
50539
|
|
|
50480
50540
|
const referenceFrameList = this.referenceFrameList;
|
|
@@ -50576,56 +50636,12 @@ class Browser {
|
|
|
50576
50636
|
const viewport = createViewport(trackView, viewportColumn, referenceFrameRight);
|
|
50577
50637
|
trackView.viewports.splice(indexRight, 0, viewport);
|
|
50578
50638
|
}
|
|
50579
|
-
}
|
|
50580
|
-
|
|
50581
|
-
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
50582
|
-
|
|
50583
|
-
await resize.call(this);
|
|
50584
|
-
}
|
|
50585
|
-
|
|
50586
|
-
/**
|
|
50587
|
-
* Add a new multi-locus panel for the specified region
|
|
50588
|
-
* @param chr
|
|
50589
|
-
* @param start
|
|
50590
|
-
* @param end
|
|
50591
|
-
* @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
|
|
50592
|
-
*/
|
|
50593
|
-
async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
|
|
50594
|
-
|
|
50595
|
-
// account for reduced viewport width as a result of adding right mate pair panel
|
|
50596
|
-
const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
50597
|
-
const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
50598
|
-
for (let refFrame of this.referenceFrameList) {
|
|
50599
|
-
refFrame.bpPerPixel *= scaleFactor;
|
|
50600
|
-
}
|
|
50601
|
-
|
|
50602
|
-
const bpp = (end - start) / viewportWidth;
|
|
50603
|
-
const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
|
|
50604
|
-
const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
|
|
50605
|
-
const indexRight = 1 + indexLeft;
|
|
50606
50639
|
|
|
50607
|
-
// TODO -- this is really ugly
|
|
50608
|
-
const {$viewport} = this.trackViews[0].viewports[indexLeft];
|
|
50609
|
-
const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
|
|
50610
|
-
|
|
50611
|
-
if (indexRight === this.referenceFrameList.length) {
|
|
50612
|
-
this.referenceFrameList.push(newReferenceFrame);
|
|
50613
|
-
for (let trackView of this.trackViews) {
|
|
50614
|
-
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
50615
|
-
trackView.viewports.push(viewport);
|
|
50616
|
-
}
|
|
50617
|
-
} else {
|
|
50618
|
-
this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);
|
|
50619
|
-
for (let trackView of this.trackViews) {
|
|
50620
|
-
const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
|
|
50621
|
-
trackView.viewports.splice(indexRight, 0, viewport);
|
|
50622
|
-
}
|
|
50623
50640
|
}
|
|
50624
50641
|
|
|
50625
|
-
|
|
50626
50642
|
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
50627
50643
|
|
|
50628
|
-
resize
|
|
50644
|
+
await this.resize();
|
|
50629
50645
|
}
|
|
50630
50646
|
|
|
50631
50647
|
async removeMultiLocusPanel(referenceFrame) {
|
|
@@ -50654,13 +50670,7 @@ class Browser {
|
|
|
50654
50670
|
|
|
50655
50671
|
}
|
|
50656
50672
|
|
|
50657
|
-
|
|
50658
|
-
* Goto the locus represented by the selected referenceFrame, discarding all other panels
|
|
50659
|
-
*
|
|
50660
|
-
* @param referenceFrame
|
|
50661
|
-
* @returns {Promise<void>}
|
|
50662
|
-
*/
|
|
50663
|
-
async gotoMultilocusPanel(referenceFrame) {
|
|
50673
|
+
async selectMultiLocusPanel(referenceFrame) {
|
|
50664
50674
|
|
|
50665
50675
|
const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame);
|
|
50666
50676
|
|
|
@@ -50714,7 +50724,7 @@ class Browser {
|
|
|
50714
50724
|
|
|
50715
50725
|
this.updateUIWithReferenceFrameList();
|
|
50716
50726
|
|
|
50717
|
-
await this.updateViews();
|
|
50727
|
+
await this.updateViews(true);
|
|
50718
50728
|
|
|
50719
50729
|
}
|
|
50720
50730
|
|
|
@@ -50937,6 +50947,7 @@ class Browser {
|
|
|
50937
50947
|
}
|
|
50938
50948
|
|
|
50939
50949
|
|
|
50950
|
+
|
|
50940
50951
|
json["tracks"] = trackJson;
|
|
50941
50952
|
|
|
50942
50953
|
return json // This is an object, not a json string
|
|
@@ -50955,9 +50966,14 @@ class Browser {
|
|
|
50955
50966
|
return surl
|
|
50956
50967
|
}
|
|
50957
50968
|
|
|
50958
|
-
|
|
50969
|
+
currentLoci() {
|
|
50970
|
+
const loci = [];
|
|
50959
50971
|
const anyTrackView = this.trackViews[0];
|
|
50960
|
-
|
|
50972
|
+
for (let {referenceFrame} of anyTrackView.viewports) {
|
|
50973
|
+
const locusString = referenceFrame.getLocusString();
|
|
50974
|
+
loci.push(locusString);
|
|
50975
|
+
}
|
|
50976
|
+
return loci
|
|
50961
50977
|
}
|
|
50962
50978
|
|
|
50963
50979
|
/**
|
|
@@ -51077,9 +51093,12 @@ class Browser {
|
|
|
51077
51093
|
}
|
|
51078
51094
|
|
|
51079
51095
|
addWindowResizeHandler() {
|
|
51080
|
-
|
|
51081
|
-
this.boundWindowResizeHandler = resize.bind(this);
|
|
51096
|
+
this.boundWindowResizeHandler = windowResizeHandler.bind(this);
|
|
51082
51097
|
window.addEventListener('resize', this.boundWindowResizeHandler);
|
|
51098
|
+
|
|
51099
|
+
function windowResizeHandler() {
|
|
51100
|
+
this.resize();
|
|
51101
|
+
}
|
|
51083
51102
|
}
|
|
51084
51103
|
|
|
51085
51104
|
removeWindowResizeHandler() {
|
|
@@ -51178,48 +51197,6 @@ class Browser {
|
|
|
51178
51197
|
}
|
|
51179
51198
|
}
|
|
51180
51199
|
|
|
51181
|
-
/**
|
|
51182
|
-
* Function called win window is resized, or visibility changed (e.g. "show" from a tab). This is a function rather
|
|
51183
|
-
* than class method because it needs to be copied and bound to specific instances of browser to support listener
|
|
51184
|
-
* removal
|
|
51185
|
-
*
|
|
51186
|
-
* @returns {Promise<void>}
|
|
51187
|
-
*/
|
|
51188
|
-
async function resize() {
|
|
51189
|
-
|
|
51190
|
-
const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
|
|
51191
|
-
|
|
51192
|
-
for (let referenceFrame of this.referenceFrameList) {
|
|
51193
|
-
|
|
51194
|
-
const index = this.referenceFrameList.indexOf(referenceFrame);
|
|
51195
|
-
|
|
51196
|
-
const {chr, genome} = referenceFrame;
|
|
51197
|
-
|
|
51198
|
-
const {bpLength} = genome.getChromosome(referenceFrame.chr);
|
|
51199
|
-
|
|
51200
|
-
const viewportWidthBP = referenceFrame.toBP(viewportWidth);
|
|
51201
|
-
|
|
51202
|
-
// viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser
|
|
51203
|
-
if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
|
|
51204
|
-
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
|
|
51205
|
-
referenceFrame.bpPerPixel = bpLength / viewportWidth;
|
|
51206
|
-
} else {
|
|
51207
|
-
// console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
|
|
51208
|
-
referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
|
|
51209
|
-
}
|
|
51210
|
-
|
|
51211
|
-
for (let {viewports} of this.trackViews) {
|
|
51212
|
-
viewports[index].setWidth(viewportWidth);
|
|
51213
|
-
}
|
|
51214
|
-
|
|
51215
|
-
}
|
|
51216
|
-
|
|
51217
|
-
this.updateUIWithReferenceFrameList();
|
|
51218
|
-
|
|
51219
|
-
await this.updateViews(true);
|
|
51220
|
-
}
|
|
51221
|
-
|
|
51222
|
-
|
|
51223
51200
|
function handleMouseMove(e) {
|
|
51224
51201
|
|
|
51225
51202
|
e.preventDefault();
|