igv 2.11.1 → 2.12.1
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 +566 -665
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +556 -607
- package/dist/igv.min.js +10 -10
- package/dist/igv.min.js.map +1 -1
- package/package.json +2 -2
package/dist/igv.esm.js
CHANGED
|
@@ -19712,6 +19712,8 @@ const knownFileExtensions = new Set([
|
|
|
19712
19712
|
"bb",
|
|
19713
19713
|
"bigbed",
|
|
19714
19714
|
"biginteract",
|
|
19715
|
+
"biggenepred",
|
|
19716
|
+
"bignarrowpeak",
|
|
19715
19717
|
"bw",
|
|
19716
19718
|
"bigwig",
|
|
19717
19719
|
"bam",
|
|
@@ -19854,6 +19856,7 @@ function inferTrackType(config) {
|
|
|
19854
19856
|
return "alignment"
|
|
19855
19857
|
case "bedpe":
|
|
19856
19858
|
case "bedpe-loop":
|
|
19859
|
+
case "biginteract":
|
|
19857
19860
|
return "interact"
|
|
19858
19861
|
case "bp":
|
|
19859
19862
|
return "arc"
|
|
@@ -19862,7 +19865,8 @@ function inferTrackType(config) {
|
|
|
19862
19865
|
case "bed":
|
|
19863
19866
|
case "bigbed":
|
|
19864
19867
|
case "bb":
|
|
19865
|
-
case "
|
|
19868
|
+
case "biggenepred":
|
|
19869
|
+
case "bignarrowpeak":
|
|
19866
19870
|
return "bedtype"
|
|
19867
19871
|
default:
|
|
19868
19872
|
return "annotation"
|
|
@@ -20113,6 +20117,38 @@ function isSecureContext() {
|
|
|
20113
20117
|
return window.location.protocol === "https:" || window.location.hostname === "localhost"
|
|
20114
20118
|
}
|
|
20115
20119
|
|
|
20120
|
+
const pairs =
|
|
20121
|
+
[
|
|
20122
|
+
['A', 'T'],
|
|
20123
|
+
['G', 'C'],
|
|
20124
|
+
['Y', 'R'],
|
|
20125
|
+
['W', 'S'],
|
|
20126
|
+
['K', 'M'],
|
|
20127
|
+
['D', 'H'],
|
|
20128
|
+
['B', 'V']
|
|
20129
|
+
];
|
|
20130
|
+
|
|
20131
|
+
const complements = new Map();
|
|
20132
|
+
for (let p of pairs) {
|
|
20133
|
+
const p1 = p[0];
|
|
20134
|
+
const p2 = p[1];
|
|
20135
|
+
complements.set(p1, p2);
|
|
20136
|
+
complements.set(p2, p1);
|
|
20137
|
+
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
20138
|
+
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
20139
|
+
}
|
|
20140
|
+
|
|
20141
|
+
function reverseComplementSequence(sequence) {
|
|
20142
|
+
|
|
20143
|
+
let comp = '';
|
|
20144
|
+
let idx = sequence.length;
|
|
20145
|
+
while (idx-- > 0) {
|
|
20146
|
+
const base = sequence[idx];
|
|
20147
|
+
comp += complements.has(base) ? complements.get(base) : base;
|
|
20148
|
+
}
|
|
20149
|
+
return comp
|
|
20150
|
+
}
|
|
20151
|
+
|
|
20116
20152
|
/*
|
|
20117
20153
|
* The MIT License (MIT)
|
|
20118
20154
|
*
|
|
@@ -20237,7 +20273,6 @@ class SequenceTrack {
|
|
|
20237
20273
|
}
|
|
20238
20274
|
|
|
20239
20275
|
menuItemList() {
|
|
20240
|
-
|
|
20241
20276
|
return [
|
|
20242
20277
|
{
|
|
20243
20278
|
name: this.reversed ? "Forward" : "Reverse",
|
|
@@ -20272,17 +20307,20 @@ class SequenceTrack {
|
|
|
20272
20307
|
contextMenuItemList(clickState) {
|
|
20273
20308
|
const viewport = clickState.viewport;
|
|
20274
20309
|
if (viewport.referenceFrame.bpPerPixel <= 1) {
|
|
20310
|
+
const pixelWidth = viewport.getWidth();
|
|
20311
|
+
const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
|
|
20312
|
+
const chr = viewport.referenceFrame.chr;
|
|
20313
|
+
const start = Math.floor(viewport.referenceFrame.start);
|
|
20314
|
+
const end = Math.ceil(start + bpWindow);
|
|
20275
20315
|
const items = [
|
|
20276
20316
|
{
|
|
20277
|
-
label: 'View visible sequence...',
|
|
20317
|
+
label: this.reversed ? 'View visible sequence (reversed)...' : 'View visible sequence...',
|
|
20278
20318
|
click: async () => {
|
|
20279
|
-
|
|
20280
|
-
|
|
20281
|
-
|
|
20282
|
-
|
|
20283
|
-
|
|
20284
|
-
const sequence = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20285
|
-
Alert.presentAlert(sequence);
|
|
20319
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20320
|
+
if (this.reversed) {
|
|
20321
|
+
seq = reverseComplementSequence(seq);
|
|
20322
|
+
}
|
|
20323
|
+
Alert.presentAlert(seq);
|
|
20286
20324
|
}
|
|
20287
20325
|
}
|
|
20288
20326
|
];
|
|
@@ -20290,14 +20328,18 @@ class SequenceTrack {
|
|
|
20290
20328
|
items.push({
|
|
20291
20329
|
label: 'Copy visible sequence',
|
|
20292
20330
|
click: async () => {
|
|
20293
|
-
|
|
20294
|
-
|
|
20295
|
-
|
|
20296
|
-
|
|
20297
|
-
|
|
20298
|
-
|
|
20299
|
-
|
|
20331
|
+
let seq = await this.browser.genome.sequence.getSequence(chr, start, end);
|
|
20332
|
+
if (this.reversed) {
|
|
20333
|
+
seq = reverseComplementSequence(seq);
|
|
20334
|
+
}
|
|
20335
|
+
try {
|
|
20336
|
+
await navigator.clipboard.writeText(seq);
|
|
20337
|
+
} catch (e) {
|
|
20338
|
+
console.error(e);
|
|
20339
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
20340
|
+
}
|
|
20300
20341
|
}
|
|
20342
|
+
|
|
20301
20343
|
});
|
|
20302
20344
|
}
|
|
20303
20345
|
items.push('<hr/>');
|
|
@@ -20441,7 +20483,7 @@ class SequenceTrack {
|
|
|
20441
20483
|
}
|
|
20442
20484
|
}
|
|
20443
20485
|
|
|
20444
|
-
supportsWholeGenome() {
|
|
20486
|
+
get supportsWholeGenome() {
|
|
20445
20487
|
return false
|
|
20446
20488
|
}
|
|
20447
20489
|
|
|
@@ -20515,13 +20557,13 @@ class Viewport {
|
|
|
20515
20557
|
this.$content.height(this.$viewport.height());
|
|
20516
20558
|
this.contentDiv = this.$content.get(0);
|
|
20517
20559
|
|
|
20518
|
-
this.$canvas =
|
|
20519
|
-
this.$content.append(this.$canvas)
|
|
20520
|
-
|
|
20521
|
-
this.canvas = this.$canvas.get(0)
|
|
20522
|
-
this.ctx = this.canvas.getContext("2d")
|
|
20560
|
+
// this.$canvas = $('<canvas>')
|
|
20561
|
+
// this.$content.append(this.$canvas)
|
|
20562
|
+
//
|
|
20563
|
+
// this.canvas = this.$canvas.get(0)
|
|
20564
|
+
// this.ctx = this.canvas.getContext("2d")
|
|
20523
20565
|
|
|
20524
|
-
this.
|
|
20566
|
+
this.$viewport.width(width);
|
|
20525
20567
|
|
|
20526
20568
|
this.initializationHelper();
|
|
20527
20569
|
|
|
@@ -20614,7 +20656,6 @@ class Viewport {
|
|
|
20614
20656
|
// Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
|
|
20615
20657
|
contentHeight = Math.min(contentHeight, 32000);
|
|
20616
20658
|
this.$content.height(contentHeight);
|
|
20617
|
-
if (this.canvas._data) this.canvas._data.invalidate = true;
|
|
20618
20659
|
}
|
|
20619
20660
|
|
|
20620
20661
|
isLoading() {
|
|
@@ -20660,8 +20701,6 @@ class Viewport {
|
|
|
20660
20701
|
this.popover.dispose();
|
|
20661
20702
|
}
|
|
20662
20703
|
|
|
20663
|
-
this.removeMouseHandlers();
|
|
20664
|
-
|
|
20665
20704
|
this.$viewport.get(0).remove();
|
|
20666
20705
|
|
|
20667
20706
|
// Null out all properties -- this should not be neccessary, but just in case there is a
|
|
@@ -22757,7 +22796,7 @@ const Cytoband = function (start, end, name, typestain) {
|
|
|
22757
22796
|
}
|
|
22758
22797
|
};
|
|
22759
22798
|
|
|
22760
|
-
const _version = "2.
|
|
22799
|
+
const _version = "2.12.1";
|
|
22761
22800
|
function version() {
|
|
22762
22801
|
return _version
|
|
22763
22802
|
}
|
|
@@ -22905,7 +22944,7 @@ class Genome {
|
|
|
22905
22944
|
constructor(config, sequence, ideograms, aliases) {
|
|
22906
22945
|
|
|
22907
22946
|
this.config = config;
|
|
22908
|
-
this.id = config.id;
|
|
22947
|
+
this.id = config.id || generateGenomeID(config);
|
|
22909
22948
|
this.sequence = sequence;
|
|
22910
22949
|
this.chromosomeNames = sequence.chromosomeNames;
|
|
22911
22950
|
this.chromosomes = sequence.chromosomes; // An object (functions as a dictionary)
|
|
@@ -23111,6 +23150,7 @@ class Genome {
|
|
|
23111
23150
|
}
|
|
23112
23151
|
|
|
23113
23152
|
async getSequence(chr, start, end) {
|
|
23153
|
+
chr = this.getChromosomeName(chr);
|
|
23114
23154
|
return this.sequence.getSequence(chr, start, end)
|
|
23115
23155
|
}
|
|
23116
23156
|
}
|
|
@@ -23226,6 +23266,18 @@ function constructWG(genome, config) {
|
|
|
23226
23266
|
|
|
23227
23267
|
}
|
|
23228
23268
|
|
|
23269
|
+
function generateGenomeID(config) {
|
|
23270
|
+
if (config.id !== undefined) {
|
|
23271
|
+
return config.id
|
|
23272
|
+
} else if (config.fastaURL && isString$3(config.fastaURL)) {
|
|
23273
|
+
return config.fastaURL
|
|
23274
|
+
} else if (config.fastaURL && config.fastaURL.name) {
|
|
23275
|
+
return config.fastaURL.name
|
|
23276
|
+
} else {
|
|
23277
|
+
return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)
|
|
23278
|
+
}
|
|
23279
|
+
}
|
|
23280
|
+
|
|
23229
23281
|
/**
|
|
23230
23282
|
* Created by dat on 9/16/16.
|
|
23231
23283
|
*/
|
|
@@ -23246,28 +23298,27 @@ class TrackViewport extends Viewport {
|
|
|
23246
23298
|
this.$viewport.append(this.$spinner);
|
|
23247
23299
|
this.$spinner.append($$1('<div>'));
|
|
23248
23300
|
|
|
23249
|
-
const
|
|
23250
|
-
|
|
23301
|
+
const track = this.trackView.track;
|
|
23251
23302
|
if ('sequence' !== track.type) {
|
|
23252
23303
|
this.$zoomInNotice = this.createZoomInNotice(this.$content);
|
|
23253
23304
|
}
|
|
23254
23305
|
|
|
23255
|
-
if (track.name && "sequence" !== track.
|
|
23256
|
-
|
|
23306
|
+
if (track.name && "sequence" !== track.id) {
|
|
23257
23307
|
this.$trackLabel = $$1('<div class="igv-track-label">');
|
|
23258
23308
|
this.$viewport.append(this.$trackLabel);
|
|
23259
23309
|
this.setTrackLabel(track.name);
|
|
23260
|
-
|
|
23261
23310
|
if (false === this.browser.trackLabelsVisible) {
|
|
23262
23311
|
this.$trackLabel.hide();
|
|
23263
23312
|
}
|
|
23264
|
-
|
|
23265
23313
|
}
|
|
23266
23314
|
|
|
23267
23315
|
this.stopSpinner();
|
|
23268
|
-
|
|
23269
23316
|
this.addMouseHandlers();
|
|
23317
|
+
}
|
|
23270
23318
|
|
|
23319
|
+
setContentHeight(contentHeight) {
|
|
23320
|
+
super.setContentHeight(contentHeight);
|
|
23321
|
+
if (this.featureCache) this.featureCache.redraw = true;
|
|
23271
23322
|
}
|
|
23272
23323
|
|
|
23273
23324
|
setTrackLabel(label) {
|
|
@@ -23289,10 +23340,17 @@ class TrackViewport extends Viewport {
|
|
|
23289
23340
|
}
|
|
23290
23341
|
}
|
|
23291
23342
|
|
|
23343
|
+
/**
|
|
23344
|
+
* Test to determine if we are zoomed in far enough to see features. Applicable to tracks with visibility windows.
|
|
23345
|
+
*
|
|
23346
|
+
* As a side effect the viewports canvas is removed if zoomed out.
|
|
23347
|
+
*
|
|
23348
|
+
* @returns {boolean} true if we are zoomed in past visibility window, false otherwise
|
|
23349
|
+
*/
|
|
23292
23350
|
checkZoomIn() {
|
|
23293
23351
|
|
|
23294
|
-
const
|
|
23295
|
-
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome
|
|
23352
|
+
const zoomedOutOfWindow = () => {
|
|
23353
|
+
if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome) {
|
|
23296
23354
|
return true
|
|
23297
23355
|
} else {
|
|
23298
23356
|
const visibilityWindow = this.trackView.track.visibilityWindow;
|
|
@@ -23303,7 +23361,9 @@ class TrackViewport extends Viewport {
|
|
|
23303
23361
|
};
|
|
23304
23362
|
|
|
23305
23363
|
if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
|
|
23306
|
-
|
|
23364
|
+
$$1(this.canvas).remove();
|
|
23365
|
+
this.canvas = undefined;
|
|
23366
|
+
//this.featureCache = undefined
|
|
23307
23367
|
return false
|
|
23308
23368
|
}
|
|
23309
23369
|
|
|
@@ -23311,29 +23371,30 @@ class TrackViewport extends Viewport {
|
|
|
23311
23371
|
return false
|
|
23312
23372
|
}
|
|
23313
23373
|
|
|
23314
|
-
if (this.$zoomInNotice) {
|
|
23315
|
-
if (showZoomInNotice()) {
|
|
23316
|
-
// Out of visibility window
|
|
23317
|
-
if (this.canvas) {
|
|
23318
|
-
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
23319
|
-
this.canvas._data = undefined;
|
|
23320
|
-
this.featureCache = undefined;
|
|
23321
|
-
}
|
|
23322
|
-
this.$zoomInNotice.show();
|
|
23323
23374
|
|
|
23324
|
-
|
|
23325
|
-
const minHeight = this.trackView.minHeight || 0;
|
|
23326
|
-
this.setContentHeight(minHeight);
|
|
23327
|
-
}
|
|
23375
|
+
if (zoomedOutOfWindow()) {
|
|
23328
23376
|
|
|
23329
|
-
|
|
23330
|
-
|
|
23377
|
+
// Out of visibility window
|
|
23378
|
+
if (this.canvas) {
|
|
23379
|
+
$$1(this.canvas).remove();
|
|
23380
|
+
this.canvas = undefined;
|
|
23381
|
+
//this.featureCache = undefined
|
|
23382
|
+
}
|
|
23383
|
+
if (this.trackView.track.autoHeight) {
|
|
23384
|
+
const minHeight = this.trackView.minHeight || 0;
|
|
23385
|
+
this.setContentHeight(minHeight);
|
|
23386
|
+
}
|
|
23387
|
+
if (this.$zoomInNotice) {
|
|
23388
|
+
this.$zoomInNotice.show();
|
|
23389
|
+
}
|
|
23390
|
+
return false
|
|
23391
|
+
} else {
|
|
23392
|
+
if (this.$zoomInNotice) {
|
|
23331
23393
|
this.$zoomInNotice.hide();
|
|
23332
|
-
return true
|
|
23333
23394
|
}
|
|
23395
|
+
return true
|
|
23334
23396
|
}
|
|
23335
23397
|
|
|
23336
|
-
return true
|
|
23337
23398
|
}
|
|
23338
23399
|
|
|
23339
23400
|
/**
|
|
@@ -23400,9 +23461,8 @@ class TrackViewport extends Viewport {
|
|
|
23400
23461
|
}
|
|
23401
23462
|
|
|
23402
23463
|
/**
|
|
23403
|
-
* Repaint the canvas
|
|
23464
|
+
* Repaint the canvas using the cached features
|
|
23404
23465
|
*
|
|
23405
|
-
* @returns {Promise<void>}
|
|
23406
23466
|
*/
|
|
23407
23467
|
repaint() {
|
|
23408
23468
|
|
|
@@ -23416,21 +23476,13 @@ class TrackViewport extends Viewport {
|
|
|
23416
23476
|
// const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)
|
|
23417
23477
|
const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
|
|
23418
23478
|
|
|
23419
|
-
|
|
23420
|
-
const startBP = this.featureCache.startBP;
|
|
23421
|
-
const endBP = this.featureCache.endBP;
|
|
23422
|
-
let bpPerPixel = this.referenceFrame.bpPerPixel;
|
|
23423
|
-
if (isWGV) {
|
|
23424
|
-
pixelWidth = this.$viewport.width();
|
|
23425
|
-
} else {
|
|
23426
|
-
pixelWidth = 3 * this.$viewport.width();
|
|
23427
|
-
}
|
|
23428
|
-
|
|
23479
|
+
// Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
|
|
23429
23480
|
// For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position
|
|
23481
|
+
const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
|
|
23430
23482
|
const viewportHeight = this.$viewport.height();
|
|
23431
23483
|
const contentHeight = this.getContentHeight();
|
|
23432
23484
|
const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.
|
|
23433
|
-
|
|
23485
|
+
const pixelHeight = Math.min(minHeight, 3 * viewportHeight);
|
|
23434
23486
|
if (0 === pixelWidth || 0 === pixelHeight) {
|
|
23435
23487
|
if (this.canvas) {
|
|
23436
23488
|
$$1(this.canvas).remove();
|
|
@@ -23439,30 +23491,25 @@ class TrackViewport extends Viewport {
|
|
|
23439
23491
|
}
|
|
23440
23492
|
const canvasTop = Math.max(0, -(this.$content.position().top) - viewportHeight);
|
|
23441
23493
|
|
|
23442
|
-
|
|
23443
|
-
|
|
23444
|
-
|
|
23445
|
-
|
|
23446
|
-
} else {
|
|
23447
|
-
devicePixelRatio = (this.trackView.track.supportHiDPI === false) ? 1 : window.devicePixelRatio;
|
|
23448
|
-
}
|
|
23449
|
-
|
|
23450
|
-
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / this.referenceFrame.bpPerPixel);
|
|
23494
|
+
const bpPerPixel = this.referenceFrame.bpPerPixel;
|
|
23495
|
+
const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23496
|
+
const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
|
|
23497
|
+
const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
|
|
23451
23498
|
|
|
23452
23499
|
const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
|
|
23453
|
-
const ctx = newCanvas.getContext("2d");
|
|
23454
|
-
|
|
23455
23500
|
newCanvas.style.width = pixelWidth + "px";
|
|
23456
23501
|
newCanvas.style.height = pixelHeight + "px";
|
|
23502
|
+
newCanvas.style.left = pixelXOffset + "px";
|
|
23503
|
+
newCanvas.style.top = canvasTop + "px";
|
|
23457
23504
|
|
|
23505
|
+
// Always use high DPI if in "FILL" display mode, otherwise use track setting;
|
|
23506
|
+
const devicePixelRatio = ("FILL" === this.trackView.track.displayMode || this.trackView.track.supportHiDPI !== false) ?
|
|
23507
|
+
window.devicePixelRatio : 1;
|
|
23458
23508
|
newCanvas.width = devicePixelRatio * pixelWidth;
|
|
23459
23509
|
newCanvas.height = devicePixelRatio * pixelHeight;
|
|
23460
23510
|
|
|
23511
|
+
const ctx = newCanvas.getContext("2d");
|
|
23461
23512
|
ctx.scale(devicePixelRatio, devicePixelRatio);
|
|
23462
|
-
|
|
23463
|
-
newCanvas.style.left = pixelXOffset + "px";
|
|
23464
|
-
newCanvas.style.top = canvasTop + "px";
|
|
23465
|
-
|
|
23466
23513
|
ctx.translate(0, -canvasTop);
|
|
23467
23514
|
|
|
23468
23515
|
const drawConfiguration =
|
|
@@ -23483,16 +23530,16 @@ class TrackViewport extends Viewport {
|
|
|
23483
23530
|
|
|
23484
23531
|
this.draw(drawConfiguration, features, roiFeatures);
|
|
23485
23532
|
|
|
23486
|
-
|
|
23487
|
-
|
|
23488
|
-
chr: this.referenceFrame.chr, startBP, endBP, bpPerPixel, top: canvasTop, bottom: canvasTop + pixelHeight
|
|
23489
|
-
};
|
|
23533
|
+
this.featureCache.canvasTop = canvasTop;
|
|
23534
|
+
this.featureCache.height = pixelHeight;
|
|
23490
23535
|
|
|
23491
23536
|
if (this.canvas) {
|
|
23492
23537
|
$$1(this.canvas).remove();
|
|
23493
23538
|
}
|
|
23539
|
+
newCanvas._data = {
|
|
23540
|
+
chr: this.featureCache.chr, bpPerPixel, startBP, endBP, pixelHeight, pixelTop: canvasTop
|
|
23541
|
+
};
|
|
23494
23542
|
this.canvas = newCanvas;
|
|
23495
|
-
this.ctx = ctx;
|
|
23496
23543
|
this.$content.append($$1(newCanvas));
|
|
23497
23544
|
|
|
23498
23545
|
}
|
|
@@ -23534,17 +23581,18 @@ class TrackViewport extends Viewport {
|
|
|
23534
23581
|
|
|
23535
23582
|
savePNG() {
|
|
23536
23583
|
|
|
23537
|
-
if (!this.
|
|
23584
|
+
if (!this.canvas) return
|
|
23538
23585
|
|
|
23539
|
-
const canvasMetadata = this.
|
|
23540
|
-
const canvasTop = canvasMetadata ? canvasMetadata.
|
|
23586
|
+
const canvasMetadata = this.featureCache;
|
|
23587
|
+
const canvasTop = canvasMetadata ? canvasMetadata.canvasTop : 0;
|
|
23541
23588
|
const devicePixelRatio = window.devicePixelRatio;
|
|
23542
23589
|
const w = this.$viewport.width() * devicePixelRatio;
|
|
23543
23590
|
const h = this.$viewport.height() * devicePixelRatio;
|
|
23544
23591
|
const x = -$$1(this.canvas).position().left * devicePixelRatio;
|
|
23545
23592
|
const y = (-this.$content.position().top - canvasTop) * devicePixelRatio;
|
|
23546
23593
|
|
|
23547
|
-
const
|
|
23594
|
+
const ctx = this.canvas.getContext("2d");
|
|
23595
|
+
const imageData = ctx.getImageData(x, y, w, h);
|
|
23548
23596
|
const exportCanvas = document.createElement('canvas');
|
|
23549
23597
|
const exportCtx = exportCanvas.getContext('2d');
|
|
23550
23598
|
exportCanvas.width = imageData.width;
|
|
@@ -23692,6 +23740,28 @@ class TrackViewport extends Viewport {
|
|
|
23692
23740
|
}
|
|
23693
23741
|
}
|
|
23694
23742
|
|
|
23743
|
+
needsRepaint() {
|
|
23744
|
+
|
|
23745
|
+
if (!this.canvas) return true
|
|
23746
|
+
|
|
23747
|
+
const data = this.canvas._data;
|
|
23748
|
+
return !data ||
|
|
23749
|
+
this.referenceFrame.start < data.startBP ||
|
|
23750
|
+
this.referenceFrame.end > data.endBP ||
|
|
23751
|
+
this.referenceFrame.chr !== data.chr ||
|
|
23752
|
+
this.referenceFrame.bpPerPixel != data.bpPerPixel
|
|
23753
|
+
}
|
|
23754
|
+
|
|
23755
|
+
needsReload() {
|
|
23756
|
+
if (!this.featureCache) return true
|
|
23757
|
+
const referenceFrame = this.referenceFrame;
|
|
23758
|
+
const chr = this.referenceFrame.chr;
|
|
23759
|
+
const start = referenceFrame.start;
|
|
23760
|
+
const end = start + referenceFrame.toBP($$1(this.contentDiv).width());
|
|
23761
|
+
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
23762
|
+
return (!this.featureCache.containsRange(chr, start, end, bpPerPixel))
|
|
23763
|
+
}
|
|
23764
|
+
|
|
23695
23765
|
createZoomInNotice($parent) {
|
|
23696
23766
|
|
|
23697
23767
|
const $container = $$1('<div>', {class: 'igv-zoom-in-notice-container'});
|
|
@@ -23713,15 +23783,33 @@ class TrackViewport extends Viewport {
|
|
|
23713
23783
|
|
|
23714
23784
|
addMouseHandlers() {
|
|
23715
23785
|
|
|
23716
|
-
this
|
|
23786
|
+
const viewport = this.$viewport.get(0);
|
|
23717
23787
|
|
|
23718
|
-
this.
|
|
23788
|
+
this.addViewportContextMenuHandler(viewport);
|
|
23719
23789
|
|
|
23720
|
-
|
|
23721
|
-
|
|
23722
|
-
|
|
23790
|
+
const md = (event) => {
|
|
23791
|
+
this.enableClick = true;
|
|
23792
|
+
this.browser.mouseDownOnViewport(event, this);
|
|
23793
|
+
pageCoordinates$1(event);
|
|
23794
|
+
};
|
|
23795
|
+
viewport.addEventListener('mousedown', md);
|
|
23796
|
+
viewport.addEventListener('touchstart', md);
|
|
23797
|
+
|
|
23798
|
+
const mu = (event) => {
|
|
23799
|
+
// Any mouse up cancels drag and scrolling
|
|
23800
|
+
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
23801
|
+
this.browser.cancelTrackPan();
|
|
23802
|
+
// event.preventDefault();
|
|
23803
|
+
// event.stopPropagation();
|
|
23804
|
+
this.enableClick = false; // Until next mouse down
|
|
23805
|
+
} else {
|
|
23806
|
+
this.browser.cancelTrackPan();
|
|
23807
|
+
this.browser.endTrackDrag();
|
|
23808
|
+
}
|
|
23809
|
+
};
|
|
23723
23810
|
|
|
23724
|
-
|
|
23811
|
+
viewport.addEventListener('mouseup', mu);
|
|
23812
|
+
viewport.addEventListener('touchend', mu);
|
|
23725
23813
|
|
|
23726
23814
|
this.addViewportClickHandler(this.$viewport.get(0));
|
|
23727
23815
|
|
|
@@ -23731,32 +23819,9 @@ class TrackViewport extends Viewport {
|
|
|
23731
23819
|
|
|
23732
23820
|
}
|
|
23733
23821
|
|
|
23734
|
-
removeMouseHandlers() {
|
|
23735
|
-
|
|
23736
|
-
this.removeViewportContextMenuHandler(this.$viewport.get(0));
|
|
23737
|
-
|
|
23738
|
-
this.removeViewportMouseDownHandler(this.$viewport.get(0));
|
|
23739
|
-
|
|
23740
|
-
this.removeViewportTouchStartHandler(this.$viewport.get(0));
|
|
23741
|
-
|
|
23742
|
-
this.removeViewportMouseUpHandler(this.$viewport.get(0));
|
|
23743
|
-
|
|
23744
|
-
this.removeViewportTouchEndHandler(this.$viewport.get(0));
|
|
23745
|
-
|
|
23746
|
-
this.removeViewportClickHandler(this.$viewport.get(0));
|
|
23747
|
-
|
|
23748
|
-
if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
|
|
23749
|
-
this.removeTrackLabelClickHandler(this.$trackLabel.get(0));
|
|
23750
|
-
}
|
|
23751
|
-
|
|
23752
|
-
}
|
|
23753
|
-
|
|
23754
23822
|
addViewportContextMenuHandler(viewport) {
|
|
23755
23823
|
|
|
23756
|
-
|
|
23757
|
-
viewport.addEventListener('contextmenu', this.boundContextMenuHandler);
|
|
23758
|
-
|
|
23759
|
-
function contextMenuHandler(event) {
|
|
23824
|
+
viewport.addEventListener('contextmenu', (event) => {
|
|
23760
23825
|
|
|
23761
23826
|
// Ignore if we are doing a drag. This can happen with touch events.
|
|
23762
23827
|
if (this.browser.dragObject) {
|
|
@@ -23789,58 +23854,16 @@ class TrackViewport extends Viewport {
|
|
|
23789
23854
|
menuItems.push({label: 'Save Image (SVG)', click: () => this.saveSVG()});
|
|
23790
23855
|
|
|
23791
23856
|
this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
|
|
23792
|
-
}
|
|
23793
|
-
|
|
23794
|
-
}
|
|
23795
|
-
|
|
23796
|
-
removeViewportContextMenuHandler(viewport) {
|
|
23797
|
-
viewport.removeEventListener('contextmenu', this.boundContextMenuHandler);
|
|
23798
|
-
}
|
|
23799
|
-
|
|
23800
|
-
addViewportMouseDownHandler(viewport) {
|
|
23801
|
-
this.boundMouseDownHandler = mouseDownHandler.bind(this);
|
|
23802
|
-
viewport.addEventListener('mousedown', this.boundMouseDownHandler);
|
|
23803
|
-
}
|
|
23804
|
-
|
|
23805
|
-
removeViewportMouseDownHandler(viewport) {
|
|
23806
|
-
viewport.removeEventListener('mousedown', this.boundMouseDownHandler);
|
|
23807
|
-
}
|
|
23808
|
-
|
|
23809
|
-
addViewportTouchStartHandler(viewport) {
|
|
23810
|
-
this.boundTouchStartHandler = mouseDownHandler.bind(this);
|
|
23811
|
-
viewport.addEventListener('touchstart', this.boundTouchStartHandler);
|
|
23812
|
-
}
|
|
23813
|
-
|
|
23814
|
-
removeViewportTouchStartHandler(viewport) {
|
|
23815
|
-
viewport.removeEventListener('touchstart', this.boundTouchStartHandler);
|
|
23816
|
-
}
|
|
23857
|
+
});
|
|
23817
23858
|
|
|
23818
|
-
addViewportMouseUpHandler(viewport) {
|
|
23819
|
-
this.boundMouseUpHandler = mouseUpHandler.bind(this);
|
|
23820
|
-
viewport.addEventListener('mouseup', this.boundMouseUpHandler);
|
|
23821
23859
|
}
|
|
23822
23860
|
|
|
23823
|
-
removeViewportMouseUpHandler(viewport) {
|
|
23824
|
-
viewport.removeEventListener('mouseup', this.boundMouseUpHandler);
|
|
23825
|
-
}
|
|
23826
|
-
|
|
23827
|
-
addViewportTouchEndHandler(viewport) {
|
|
23828
|
-
this.boundTouchEndHandler = mouseUpHandler.bind(this);
|
|
23829
|
-
viewport.addEventListener('touchend', this.boundTouchEndHandler);
|
|
23830
|
-
}
|
|
23831
|
-
|
|
23832
|
-
removeViewportTouchEndHandler(viewport) {
|
|
23833
|
-
viewport.removeEventListener('touchend', this.boundTouchEndHandler);
|
|
23834
|
-
}
|
|
23835
23861
|
|
|
23836
23862
|
addViewportClickHandler(viewport) {
|
|
23837
23863
|
|
|
23838
|
-
|
|
23839
|
-
viewport.addEventListener('click', this.boundClickHandler);
|
|
23840
|
-
|
|
23841
|
-
function clickHandler(event) {
|
|
23864
|
+
viewport.addEventListener('click', (event) => {
|
|
23842
23865
|
|
|
23843
|
-
if (this.enableClick) {
|
|
23866
|
+
if (this.enableClick && this.canvas) {
|
|
23844
23867
|
if (3 === event.which || event.ctrlKey) {
|
|
23845
23868
|
return
|
|
23846
23869
|
}
|
|
@@ -23923,19 +23946,12 @@ class TrackViewport extends Viewport {
|
|
|
23923
23946
|
lastClickTime = time;
|
|
23924
23947
|
|
|
23925
23948
|
}
|
|
23926
|
-
}
|
|
23927
|
-
}
|
|
23928
|
-
|
|
23929
|
-
removeViewportClickHandler(viewport) {
|
|
23930
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
23949
|
+
});
|
|
23931
23950
|
}
|
|
23932
23951
|
|
|
23933
23952
|
addTrackLabelClickHandler(trackLabel) {
|
|
23934
23953
|
|
|
23935
|
-
|
|
23936
|
-
trackLabel.addEventListener('click', this.boundTrackLabelClickHandler);
|
|
23937
|
-
|
|
23938
|
-
function clickHandler(event) {
|
|
23954
|
+
trackLabel.addEventListener('click', (event) => {
|
|
23939
23955
|
|
|
23940
23956
|
event.stopPropagation();
|
|
23941
23957
|
|
|
@@ -23955,35 +23971,11 @@ class TrackViewport extends Viewport {
|
|
|
23955
23971
|
this.popover = new Popover(this.browser.columnContainer, (track.name || ''));
|
|
23956
23972
|
this.popover.presentContentWithEvent(event, str);
|
|
23957
23973
|
}
|
|
23958
|
-
}
|
|
23959
|
-
}
|
|
23960
|
-
|
|
23961
|
-
removeTrackLabelClickHandler(trackLabel) {
|
|
23962
|
-
trackLabel.removeEventListener('click', this.boundTrackLabelClickHandler);
|
|
23974
|
+
});
|
|
23963
23975
|
}
|
|
23964
23976
|
|
|
23965
23977
|
}
|
|
23966
23978
|
|
|
23967
|
-
function mouseDownHandler(event) {
|
|
23968
|
-
this.enableClick = true;
|
|
23969
|
-
this.browser.mouseDownOnViewport(event, this);
|
|
23970
|
-
pageCoordinates$1(event);
|
|
23971
|
-
}
|
|
23972
|
-
|
|
23973
|
-
function mouseUpHandler(event) {
|
|
23974
|
-
|
|
23975
|
-
// Any mouse up cancels drag and scrolling
|
|
23976
|
-
if (this.browser.dragObject || this.browser.isScrolling) {
|
|
23977
|
-
this.browser.cancelTrackPan();
|
|
23978
|
-
// event.preventDefault();
|
|
23979
|
-
// event.stopPropagation();
|
|
23980
|
-
this.enableClick = false; // Until next mouse down
|
|
23981
|
-
} else {
|
|
23982
|
-
this.browser.cancelTrackPan();
|
|
23983
|
-
this.browser.endTrackDrag();
|
|
23984
|
-
}
|
|
23985
|
-
}
|
|
23986
|
-
|
|
23987
23979
|
function createClickState(event, viewport) {
|
|
23988
23980
|
|
|
23989
23981
|
const referenceFrame = viewport.referenceFrame;
|
|
@@ -24239,11 +24231,13 @@ class RulerSweeper {
|
|
|
24239
24231
|
|
|
24240
24232
|
validateLocusExtent(this.rulerViewport.browser.genome.getChromosome(this.rulerViewport.referenceFrame.chr).bpLength, extent, this.rulerViewport.browser.minimumBases());
|
|
24241
24233
|
|
|
24242
|
-
|
|
24243
|
-
|
|
24244
|
-
this.rulerViewport.referenceFrame.
|
|
24234
|
+
const newStart = Math.round(extent.start);
|
|
24235
|
+
const newEnd = Math.round(extent.end);
|
|
24236
|
+
this.rulerViewport.referenceFrame.bpPerPixel = (newEnd - newStart) / this.rulerViewport.contentDiv.clientWidth;
|
|
24237
|
+
this.rulerViewport.referenceFrame.start = newStart;
|
|
24238
|
+
this.rulerViewport.referenceFrame.end = newEnd;
|
|
24245
24239
|
|
|
24246
|
-
this.rulerViewport.browser.updateViews(
|
|
24240
|
+
this.rulerViewport.browser.updateViews();
|
|
24247
24241
|
}
|
|
24248
24242
|
|
|
24249
24243
|
}
|
|
@@ -24389,7 +24383,7 @@ class PairedAlignment {
|
|
|
24389
24383
|
return this.firstAlignment.isProperPair()
|
|
24390
24384
|
}
|
|
24391
24385
|
|
|
24392
|
-
get
|
|
24386
|
+
get fragmentLength() {
|
|
24393
24387
|
return Math.abs(this.firstAlignment.fragmentLength)
|
|
24394
24388
|
}
|
|
24395
24389
|
|
|
@@ -25832,7 +25826,7 @@ const BamUtils = {
|
|
|
25832
25826
|
const lseq = readInt(ba, offset + 20);
|
|
25833
25827
|
const mateChrIdx = readInt(ba, offset + 24);
|
|
25834
25828
|
const matePos = readInt(ba, offset + 28);
|
|
25835
|
-
const
|
|
25829
|
+
const fragmentLength = readInt(ba, offset + 32);
|
|
25836
25830
|
|
|
25837
25831
|
let readName = [];
|
|
25838
25832
|
for (let j = 0; j < nl - 1; ++j) {
|
|
@@ -25873,7 +25867,7 @@ const BamUtils = {
|
|
|
25873
25867
|
alignment.readName = readName;
|
|
25874
25868
|
alignment.cigar = cigar;
|
|
25875
25869
|
alignment.lengthOnRef = lengthOnRef;
|
|
25876
|
-
alignment.fragmentLength =
|
|
25870
|
+
alignment.fragmentLength = fragmentLength;
|
|
25877
25871
|
alignment.mq = mq;
|
|
25878
25872
|
|
|
25879
25873
|
BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);
|
|
@@ -29026,6 +29020,10 @@ class BamSource {
|
|
|
29026
29020
|
* THE SOFTWARE.
|
|
29027
29021
|
*/
|
|
29028
29022
|
|
|
29023
|
+
const fixColor = (colorString) => {
|
|
29024
|
+
return (colorString.indexOf(",") > 0 && !colorString.startsWith("rgb")) ?
|
|
29025
|
+
`rgb(${colorString})` : colorString
|
|
29026
|
+
};
|
|
29029
29027
|
|
|
29030
29028
|
/**
|
|
29031
29029
|
* A collection of properties and methods shared by all (or most) track types.
|
|
@@ -29070,8 +29068,8 @@ class TrackBase {
|
|
|
29070
29068
|
|
|
29071
29069
|
this.order = config.order;
|
|
29072
29070
|
|
|
29073
|
-
this.color = config.color;
|
|
29074
|
-
this.altColor = config.altColor;
|
|
29071
|
+
if(config.color) this.color = fixColor(config.color);
|
|
29072
|
+
if(config.altColor) this.altColor = fixColor(config.altColor);
|
|
29075
29073
|
if ("civic-ws" === config.sourceType) { // Ugly proxy for specialized track type
|
|
29076
29074
|
this.defaultColor = "rgb(155,20,20)";
|
|
29077
29075
|
} else {
|
|
@@ -29177,7 +29175,7 @@ class TrackBase {
|
|
|
29177
29175
|
return state
|
|
29178
29176
|
}
|
|
29179
29177
|
|
|
29180
|
-
supportsWholeGenome() {
|
|
29178
|
+
get supportsWholeGenome() {
|
|
29181
29179
|
return false
|
|
29182
29180
|
}
|
|
29183
29181
|
|
|
@@ -30966,7 +30964,7 @@ class ChordSetManager {
|
|
|
30966
30964
|
return this.tracks.find(t => name === t.name)
|
|
30967
30965
|
}
|
|
30968
30966
|
|
|
30969
|
-
|
|
30967
|
+
getChordSet(name) {
|
|
30970
30968
|
return this.chordSets.find(cs => name === cs.name)
|
|
30971
30969
|
}
|
|
30972
30970
|
|
|
@@ -31084,9 +31082,8 @@ class CircularView {
|
|
|
31084
31082
|
buttonContainer.appendChild(this.showControlsButton);
|
|
31085
31083
|
this.showControlsButton.innerText = 'none' === this.controlPanel.style.display ? 'Show Controls' : 'Hide Controls';
|
|
31086
31084
|
this.showControlsButton.addEventListener('click', (event) => {
|
|
31087
|
-
const
|
|
31088
|
-
if (
|
|
31089
|
-
|
|
31085
|
+
const panelRows = this.controlPanel.querySelectorAll('div');
|
|
31086
|
+
if (panelRows.length > 0) {
|
|
31090
31087
|
if ('none' === this.controlPanel.style.display) {
|
|
31091
31088
|
this.controlPanel.style.display = 'flex';
|
|
31092
31089
|
event.target.innerText = 'Hide Controls';
|
|
@@ -31170,10 +31167,10 @@ class CircularView {
|
|
|
31170
31167
|
hideShowButton.innerText = true === chordSet.visible ? 'Hide' : 'Show';
|
|
31171
31168
|
hideShowButton.addEventListener('click', event => {
|
|
31172
31169
|
if (true === chordSet.visible) {
|
|
31173
|
-
this.
|
|
31170
|
+
this.hideChordSet(chordSet.name);
|
|
31174
31171
|
event.target.innerText = "Show";
|
|
31175
31172
|
} else {
|
|
31176
|
-
this.
|
|
31173
|
+
this.showChordSet(chordSet.name);
|
|
31177
31174
|
event.target.innerText = "Hide";
|
|
31178
31175
|
}
|
|
31179
31176
|
});
|
|
@@ -31197,7 +31194,7 @@ class CircularView {
|
|
|
31197
31194
|
color: chordSet.color,
|
|
31198
31195
|
onChange: ({rgbaString}) => {
|
|
31199
31196
|
colorPickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
|
|
31200
|
-
this.
|
|
31197
|
+
this.setColor(chordSet.name, rgbaString);
|
|
31201
31198
|
alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
|
|
31202
31199
|
}
|
|
31203
31200
|
};
|
|
@@ -31215,7 +31212,7 @@ class CircularView {
|
|
|
31215
31212
|
alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
|
|
31216
31213
|
alphaSlider.oninput = () => {
|
|
31217
31214
|
const v = valueToAlpha(alphaSlider.value);
|
|
31218
|
-
this.
|
|
31215
|
+
this.setColor(chordSet.name, setAlpha(chordSet.color, v));
|
|
31219
31216
|
picker.setColor(chordSet.color);
|
|
31220
31217
|
};
|
|
31221
31218
|
row.appendChild(alphaSlider);
|
|
@@ -31235,11 +31232,13 @@ class CircularView {
|
|
|
31235
31232
|
*/
|
|
31236
31233
|
setAssembly(igvGenome) {
|
|
31237
31234
|
|
|
31238
|
-
|
|
31235
|
+
const id = this.genomeId || guid();
|
|
31236
|
+
|
|
31237
|
+
if (this.genomeId === id) {
|
|
31239
31238
|
return
|
|
31240
31239
|
}
|
|
31241
31240
|
this.chordManager.clearChords();
|
|
31242
|
-
this.genomeId =
|
|
31241
|
+
this.genomeId = id;
|
|
31243
31242
|
this.chrNames = new Set(igvGenome.chromosomes.map(chr => shortChrName$1(chr.name)));
|
|
31244
31243
|
|
|
31245
31244
|
const regions = [];
|
|
@@ -31260,7 +31259,7 @@ class CircularView {
|
|
|
31260
31259
|
this.assembly = {
|
|
31261
31260
|
name: igvGenome.name,
|
|
31262
31261
|
sequence: {
|
|
31263
|
-
trackId:
|
|
31262
|
+
trackId: id,
|
|
31264
31263
|
type: 'ReferenceSequenceTrack',
|
|
31265
31264
|
adapter: {
|
|
31266
31265
|
type: 'FromConfigSequenceAdapter',
|
|
@@ -31301,7 +31300,7 @@ class CircularView {
|
|
|
31301
31300
|
|
|
31302
31301
|
addChords(newChords, options = {}) {
|
|
31303
31302
|
|
|
31304
|
-
const tmp = options.
|
|
31303
|
+
const tmp = options.name || options.track || "*";
|
|
31305
31304
|
const trackName = tmp.split(' ')[0].replaceAll("%20", " ");
|
|
31306
31305
|
const chordSetName = tmp.replaceAll("%20", " ");
|
|
31307
31306
|
|
|
@@ -31354,21 +31353,6 @@ class CircularView {
|
|
|
31354
31353
|
this.viewState.pluginManager.rootModel.session.clearSelection();
|
|
31355
31354
|
}
|
|
31356
31355
|
|
|
31357
|
-
getFeature(featureId) {
|
|
31358
|
-
|
|
31359
|
-
// TODO -- broken
|
|
31360
|
-
// const display = this.viewState.pluginManager.rootModel.session.view.tracks[0].displays[0]
|
|
31361
|
-
// const feature = display.data.features.get(featureId)
|
|
31362
|
-
// return feature;
|
|
31363
|
-
|
|
31364
|
-
const features = [...this.viewState.config.tracks[0].adapter.features.value];
|
|
31365
|
-
for (let f of features) {
|
|
31366
|
-
if (featureId === f.uniqueId) {
|
|
31367
|
-
return f
|
|
31368
|
-
}
|
|
31369
|
-
}
|
|
31370
|
-
}
|
|
31371
|
-
|
|
31372
31356
|
/**
|
|
31373
31357
|
* Deprecated, use "visible" property
|
|
31374
31358
|
*/
|
|
@@ -31391,23 +31375,23 @@ class CircularView {
|
|
|
31391
31375
|
this.parent.style.display = isVisible ? 'block' : 'none';
|
|
31392
31376
|
}
|
|
31393
31377
|
|
|
31394
|
-
|
|
31395
|
-
let
|
|
31396
|
-
if (
|
|
31397
|
-
|
|
31378
|
+
hideChordSet(trackName) {
|
|
31379
|
+
let cs = this.getChordSet(trackName);
|
|
31380
|
+
if (cs) {
|
|
31381
|
+
cs.visible = false;
|
|
31398
31382
|
this.render();
|
|
31399
31383
|
} else {
|
|
31400
31384
|
console.warn(`No track with name: ${name}`);
|
|
31401
31385
|
}
|
|
31402
31386
|
}
|
|
31403
31387
|
|
|
31404
|
-
|
|
31405
|
-
let
|
|
31406
|
-
if (
|
|
31407
|
-
|
|
31388
|
+
showChordSet(name) {
|
|
31389
|
+
let cs = this.getChordSet(name);
|
|
31390
|
+
if (cs) {
|
|
31391
|
+
cs.visible = true;
|
|
31408
31392
|
this.render();
|
|
31409
31393
|
} else {
|
|
31410
|
-
console.warn(`No track with name: ${
|
|
31394
|
+
console.warn(`No track with name: ${name}`);
|
|
31411
31395
|
}
|
|
31412
31396
|
}
|
|
31413
31397
|
|
|
@@ -31433,12 +31417,12 @@ class CircularView {
|
|
|
31433
31417
|
this.render();
|
|
31434
31418
|
}
|
|
31435
31419
|
|
|
31436
|
-
|
|
31437
|
-
return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.
|
|
31420
|
+
getChordSet(name) {
|
|
31421
|
+
return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordSet(name)
|
|
31438
31422
|
}
|
|
31439
31423
|
|
|
31440
|
-
|
|
31441
|
-
const t = this.
|
|
31424
|
+
setColor(name, color) {
|
|
31425
|
+
const t = this.getChordSet(name);
|
|
31442
31426
|
if (t) {
|
|
31443
31427
|
t.color = color;
|
|
31444
31428
|
const trackID = t.id;
|
|
@@ -31465,8 +31449,6 @@ class CircularView {
|
|
|
31465
31449
|
// Remove all children from possible previous renders. React might do this for us when we render, but just in case.
|
|
31466
31450
|
ReactDOM.unmountComponentAtNode(this.container);
|
|
31467
31451
|
|
|
31468
|
-
|
|
31469
|
-
|
|
31470
31452
|
const visibleChordSets =
|
|
31471
31453
|
(this.groupByTrack ? this.chordManager.tracks : this.chordManager.chordSets).filter(t => t.visible);
|
|
31472
31454
|
|
|
@@ -31545,7 +31527,7 @@ function guid() {
|
|
|
31545
31527
|
|
|
31546
31528
|
function embedCSS$1() {
|
|
31547
31529
|
|
|
31548
|
-
const css = '.igv-circview-container {\n z-index: 2048;\n
|
|
31530
|
+
const css = '.igv-circview-container {\n z-index: 2048;\n width: fit-content;\n height: fit-content;\n box-sizing: content-box;\n color: dimgray;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n background-color: white;\n border-color: dimgray;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-circview-toolbar {\n position: relative;\n width: 100%;\n height: 32px;\n background-color: lightgrey;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n\n.igv-circview-toolbar-button-container {\n height: 100%;\n width: fit-content;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-toolbar-button-container > div {\n margin: 4px;\n}\n\n.igv-circview-track-panel {\n z-index: 1024;\n position: absolute;\n top: 33px;\n left: 0;\n width: 100%;\n height: fit-content;\n border-bottom-style: solid;\n border-bottom-color: dimgray;\n border-bottom-width: thin;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n}\n.igv-circview-track-panel > div {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-circview-track-panel > div > div {\n margin: 4px;\n}\n\n.igv-circview-swatch-button {\n cursor: pointer;\n padding: 5px;\n width: 8px;\n height: 8px;\n border: 1px solid #8d8b8b;\n border-radius: 16px;\n}\n\n.igv-circview-button {\n cursor: pointer;\n padding: 5px;\n color: #444;\n vertical-align: middle;\n text-align: center;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n border: 1px solid #8d8b8b;\n border-radius: 4px;\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);\n}\n\n.igv-circview-button:hover {\n background: #efefef;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n.igv-circview-button:active {\n color: #007bff;\n box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n/*# sourceMappingURL=circular-view.css.map */\n';
|
|
31549
31531
|
|
|
31550
31532
|
const style = document.createElement('style');
|
|
31551
31533
|
style.setAttribute('type', 'text/css');
|
|
@@ -31707,6 +31689,9 @@ function sendChords(chords, track, refFrame, alpha) {
|
|
|
31707
31689
|
`${encodedName} ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
|
|
31708
31690
|
track.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor});
|
|
31709
31691
|
|
|
31692
|
+
// show circular view if hidden
|
|
31693
|
+
if(!track.browser.circularViewVisible) track.browser.circularViewVisible = true;
|
|
31694
|
+
|
|
31710
31695
|
}
|
|
31711
31696
|
|
|
31712
31697
|
|
|
@@ -31724,21 +31709,24 @@ function createCircularView(el, browser) {
|
|
|
31724
31709
|
function addFrameForFeature(feature) {
|
|
31725
31710
|
|
|
31726
31711
|
feature.chr = browser.genome.getChromosomeName(feature.refName);
|
|
31727
|
-
|
|
31728
|
-
for (let referenceFrame of browser.
|
|
31712
|
+
let frameFound = false;
|
|
31713
|
+
for (let referenceFrame of browser.referenceFrameList) {
|
|
31729
31714
|
const l = Locus.fromLocusString(referenceFrame.getLocusString());
|
|
31730
31715
|
if (l.contains(feature)) {
|
|
31716
|
+
frameFound = true;
|
|
31731
31717
|
break
|
|
31732
31718
|
} else if (l.overlaps(feature)) {
|
|
31733
31719
|
referenceFrame.extend(feature);
|
|
31734
|
-
|
|
31735
|
-
} else {
|
|
31736
|
-
const flanking = 2000;
|
|
31737
|
-
const center = (feature.start + feature.end) / 2;
|
|
31738
|
-
browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
|
|
31720
|
+
frameFound = true;
|
|
31739
31721
|
break
|
|
31740
31722
|
}
|
|
31741
31723
|
}
|
|
31724
|
+
if (!frameFound) {
|
|
31725
|
+
const flanking = 2000;
|
|
31726
|
+
const center = (feature.start + feature.end) / 2;
|
|
31727
|
+
browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
|
|
31728
|
+
|
|
31729
|
+
}
|
|
31742
31730
|
}
|
|
31743
31731
|
}
|
|
31744
31732
|
});
|
|
@@ -32169,7 +32157,7 @@ class BAMTrack extends TrackBase {
|
|
|
32169
32157
|
}
|
|
32170
32158
|
|
|
32171
32159
|
// Add chords to JBrowse circular view, if present
|
|
32172
|
-
if (this.browser.circularView &&
|
|
32160
|
+
if (this.browser.circularView &&
|
|
32173
32161
|
(this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
|
|
32174
32162
|
menuItems.push('<hr/>');
|
|
32175
32163
|
if (this.alignmentTrack.hasPairs) {
|
|
@@ -32694,7 +32682,7 @@ class AlignmentTrack {
|
|
|
32694
32682
|
for (let alignment of alignmentRow.alignments) {
|
|
32695
32683
|
|
|
32696
32684
|
this.hasPairs = this.hasPairs || alignment.isPaired();
|
|
32697
|
-
if (this.browser.circularView
|
|
32685
|
+
if (this.browser.circularView) {
|
|
32698
32686
|
// This is an expensive check, only do it if needed
|
|
32699
32687
|
this.hasSupplemental = this.hasSupplemental || alignment.hasTag('SA');
|
|
32700
32688
|
}
|
|
@@ -33053,10 +33041,7 @@ class AlignmentTrack {
|
|
|
33053
33041
|
list.push({
|
|
33054
33042
|
label: 'View read sequence',
|
|
33055
33043
|
click: () => {
|
|
33056
|
-
const
|
|
33057
|
-
if (!alignment) return
|
|
33058
|
-
|
|
33059
|
-
const seqstring = alignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33044
|
+
const seqstring = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33060
33045
|
if (!seqstring || "*" === seqstring) {
|
|
33061
33046
|
Alert.presentAlert("Read sequence: *");
|
|
33062
33047
|
} else {
|
|
@@ -33068,11 +33053,16 @@ class AlignmentTrack {
|
|
|
33068
33053
|
if (isSecureContext()) {
|
|
33069
33054
|
list.push({
|
|
33070
33055
|
label: 'Copy read sequence',
|
|
33071
|
-
click: () => {
|
|
33072
|
-
const
|
|
33073
|
-
|
|
33074
|
-
|
|
33075
|
-
|
|
33056
|
+
click: async () => {
|
|
33057
|
+
const seq = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");
|
|
33058
|
+
try {
|
|
33059
|
+
//console.log(`seq: ${seq}`)
|
|
33060
|
+
await navigator.clipboard.writeText(seq);
|
|
33061
|
+
} catch (e) {
|
|
33062
|
+
console.error(e);
|
|
33063
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
33064
|
+
}
|
|
33065
|
+
|
|
33076
33066
|
}
|
|
33077
33067
|
});
|
|
33078
33068
|
}
|
|
@@ -33082,8 +33072,7 @@ class AlignmentTrack {
|
|
|
33082
33072
|
}
|
|
33083
33073
|
|
|
33084
33074
|
// Experimental JBrowse feature
|
|
33085
|
-
if (this.browser.circularView &&
|
|
33086
|
-
&& (this.hasPairs || this.hasSupplemental)) {
|
|
33075
|
+
if (this.browser.circularView && (this.hasPairs || this.hasSupplemental)) {
|
|
33087
33076
|
if (this.hasPairs) {
|
|
33088
33077
|
list.push({
|
|
33089
33078
|
label: 'Add discordant pairs to circular view',
|
|
@@ -33196,10 +33185,13 @@ class AlignmentTrack {
|
|
|
33196
33185
|
case "pairOrientation":
|
|
33197
33186
|
|
|
33198
33187
|
if (this.pairOrientation && alignment.pairOrientation) {
|
|
33199
|
-
|
|
33188
|
+
const oTypes = orientationTypes[this.pairOrientation];
|
|
33200
33189
|
if (oTypes) {
|
|
33201
|
-
|
|
33202
|
-
if (pairColor)
|
|
33190
|
+
const pairColor = this.pairColors[oTypes[alignment.pairOrientation]];
|
|
33191
|
+
if (pairColor) {
|
|
33192
|
+
color = pairColor;
|
|
33193
|
+
break
|
|
33194
|
+
}
|
|
33203
33195
|
}
|
|
33204
33196
|
}
|
|
33205
33197
|
if ("pairOrientation" === option) {
|
|
@@ -33540,7 +33532,9 @@ class RulerViewport extends TrackViewport {
|
|
|
33540
33532
|
currentViewport = this;
|
|
33541
33533
|
this.$tooltip.show();
|
|
33542
33534
|
} else if (currentViewport.guid !== this.guid) {
|
|
33543
|
-
currentViewport.$tooltip
|
|
33535
|
+
if (currentViewport.$tooltip) {
|
|
33536
|
+
currentViewport.$tooltip.hide();
|
|
33537
|
+
}
|
|
33544
33538
|
this.$tooltip.show();
|
|
33545
33539
|
currentViewport = this;
|
|
33546
33540
|
} else {
|
|
@@ -33567,7 +33561,9 @@ class RulerViewport extends TrackViewport {
|
|
|
33567
33561
|
|
|
33568
33562
|
// hide tooltip when movement stops
|
|
33569
33563
|
clearTimeout(timer);
|
|
33570
|
-
timer = setTimeout(() =>
|
|
33564
|
+
timer = setTimeout(() => {
|
|
33565
|
+
if (this.$tooltip) this.$tooltip.hide();
|
|
33566
|
+
}, toolTipTimeout);
|
|
33571
33567
|
|
|
33572
33568
|
}
|
|
33573
33569
|
|
|
@@ -33586,69 +33582,6 @@ class RulerViewport extends TrackViewport {
|
|
|
33586
33582
|
|
|
33587
33583
|
}
|
|
33588
33584
|
|
|
33589
|
-
const viewportColumnManager =
|
|
33590
|
-
{
|
|
33591
|
-
createColumns: (columnContainer, count) => {
|
|
33592
|
-
|
|
33593
|
-
for (let i = 0; i < count; i++) {
|
|
33594
|
-
if (0 === i) {
|
|
33595
|
-
createColumn(columnContainer, 'igv-column');
|
|
33596
|
-
} else {
|
|
33597
|
-
columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
|
|
33598
|
-
createColumn(columnContainer, 'igv-column');
|
|
33599
|
-
}
|
|
33600
|
-
}
|
|
33601
|
-
|
|
33602
|
-
},
|
|
33603
|
-
|
|
33604
|
-
removeColumnAtIndex: (i, column) => {
|
|
33605
|
-
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
33606
|
-
column.remove();
|
|
33607
|
-
shim.remove();
|
|
33608
|
-
},
|
|
33609
|
-
|
|
33610
|
-
insertAfter: referenceElement => {
|
|
33611
|
-
|
|
33612
|
-
const shim = div$1({class: 'igv-column-shim'});
|
|
33613
|
-
insertElementAfter(shim, referenceElement);
|
|
33614
|
-
|
|
33615
|
-
const column = div$1({class: 'igv-column'});
|
|
33616
|
-
insertElementAfter(column, shim);
|
|
33617
|
-
|
|
33618
|
-
return column
|
|
33619
|
-
},
|
|
33620
|
-
|
|
33621
|
-
insertBefore: (referenceElement, count) => {
|
|
33622
|
-
|
|
33623
|
-
for (let i = 0; i < count; i++) {
|
|
33624
|
-
|
|
33625
|
-
const column = div$1({class: 'igv-column'});
|
|
33626
|
-
insertElementBefore(column, referenceElement);
|
|
33627
|
-
|
|
33628
|
-
if (count > 1 && i > 0) {
|
|
33629
|
-
const columnShim = div$1({class: 'igv-column-shim'});
|
|
33630
|
-
insertElementBefore(columnShim, column);
|
|
33631
|
-
}
|
|
33632
|
-
|
|
33633
|
-
}
|
|
33634
|
-
|
|
33635
|
-
},
|
|
33636
|
-
|
|
33637
|
-
indexOfColumn: (columnContainer, column) => {
|
|
33638
|
-
|
|
33639
|
-
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
33640
|
-
|
|
33641
|
-
for (let i = 0; i < allColumns.length; i++) {
|
|
33642
|
-
const c = allColumns[ i ];
|
|
33643
|
-
if (c === column) {
|
|
33644
|
-
return i
|
|
33645
|
-
}
|
|
33646
|
-
}
|
|
33647
|
-
|
|
33648
|
-
return undefined
|
|
33649
|
-
},
|
|
33650
|
-
};
|
|
33651
|
-
|
|
33652
33585
|
/*
|
|
33653
33586
|
* The MIT License (MIT)
|
|
33654
33587
|
*
|
|
@@ -33682,61 +33615,28 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33682
33615
|
|
|
33683
33616
|
initializationHelper() {
|
|
33684
33617
|
|
|
33685
|
-
this
|
|
33686
|
-
this
|
|
33687
|
-
|
|
33688
|
-
|
|
33689
|
-
this.ideogram_ctx = canvas.getContext('2d');
|
|
33690
|
-
|
|
33691
|
-
this.$canvas.remove();
|
|
33692
|
-
this.canvas = undefined;
|
|
33693
|
-
this.ctx = undefined;
|
|
33618
|
+
this.canvas = document.createElement('canvas');
|
|
33619
|
+
this.canvas.className = 'igv-ideogram-canvas';
|
|
33620
|
+
this.$content.append($$1(this.canvas));
|
|
33621
|
+
this.ideogram_ctx = this.canvas.getContext('2d');
|
|
33694
33622
|
|
|
33695
33623
|
this.addMouseHandlers();
|
|
33696
|
-
|
|
33697
33624
|
}
|
|
33698
33625
|
|
|
33699
33626
|
addMouseHandlers() {
|
|
33700
|
-
this.addBrowserObserver();
|
|
33701
33627
|
this.addViewportClickHandler(this.$viewport.get(0));
|
|
33702
33628
|
}
|
|
33703
33629
|
|
|
33704
|
-
removeMouseHandlers() {
|
|
33705
|
-
this.removeBrowserObserver();
|
|
33706
|
-
this.removeViewportClickHandler(this.$viewport.get(0));
|
|
33707
|
-
|
|
33708
|
-
}
|
|
33709
|
-
|
|
33710
|
-
addBrowserObserver() {
|
|
33711
|
-
|
|
33712
|
-
function observerHandler(referenceFrameList) {
|
|
33713
|
-
const column = this.$viewport.get(0).parentElement;
|
|
33714
|
-
if (null !== column) {
|
|
33715
|
-
const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
|
|
33716
|
-
// console.log(`ideogram-viewport - locus-change-handler index(${ index }) ${ referenceFrameList[ index ].getLocusString() } ${ Date.now() } `)
|
|
33717
|
-
this.update(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height(), referenceFrameList[ index ]);
|
|
33718
|
-
}
|
|
33719
|
-
}
|
|
33720
|
-
|
|
33721
|
-
this.boundObserverHandler = observerHandler.bind(this);
|
|
33722
|
-
this.browser.on('locuschange', this.boundObserverHandler);
|
|
33723
|
-
}
|
|
33724
|
-
|
|
33725
|
-
removeBrowserObserver() {
|
|
33726
|
-
this.browser.off('locuschange', this.boundObserverHandler);
|
|
33727
|
-
}
|
|
33728
|
-
|
|
33729
33630
|
addViewportClickHandler(viewport) {
|
|
33730
33631
|
|
|
33731
|
-
|
|
33632
|
+
this.boundClickHandler = clickHandler.bind(this);
|
|
33633
|
+
viewport.addEventListener('click', this.boundClickHandler);
|
|
33732
33634
|
|
|
33733
|
-
|
|
33734
|
-
const index = viewportColumnManager.indexOfColumn(this.browser.columnContainer, column);
|
|
33735
|
-
const referenceFrame = this.browser.referenceFrameList[ index ];
|
|
33635
|
+
function clickHandler(event) {
|
|
33736
33636
|
|
|
33737
33637
|
const {xNormalized, width} = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
|
|
33738
|
-
const {bpLength} = this.browser.genome.getChromosome(referenceFrame.chr);
|
|
33739
|
-
const locusLength = referenceFrame.bpPerPixel * width;
|
|
33638
|
+
const {bpLength} = this.browser.genome.getChromosome(this.referenceFrame.chr);
|
|
33639
|
+
const locusLength = this.referenceFrame.bpPerPixel * width;
|
|
33740
33640
|
const chrCoveragePercentage = locusLength / bpLength;
|
|
33741
33641
|
|
|
33742
33642
|
let xPercentage = xNormalized;
|
|
@@ -33751,21 +33651,14 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33751
33651
|
const ss = Math.round((xPercentage - (chrCoveragePercentage / 2.0)) * bpLength);
|
|
33752
33652
|
const ee = Math.round((xPercentage + (chrCoveragePercentage / 2.0)) * bpLength);
|
|
33753
33653
|
|
|
33754
|
-
referenceFrame.start = ss;
|
|
33755
|
-
referenceFrame.end = ee;
|
|
33756
|
-
referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
33654
|
+
this.referenceFrame.start = ss;
|
|
33655
|
+
this.referenceFrame.end = ee;
|
|
33656
|
+
this.referenceFrame.bpPerPixel = (ee - ss) / width;
|
|
33757
33657
|
|
|
33758
|
-
this.browser.updateViews(true);
|
|
33658
|
+
this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
|
|
33759
33659
|
|
|
33760
33660
|
}
|
|
33761
33661
|
|
|
33762
|
-
this.boundClickHandler = clickHandler.bind(this);
|
|
33763
|
-
viewport.addEventListener('click', this.boundClickHandler);
|
|
33764
|
-
|
|
33765
|
-
}
|
|
33766
|
-
|
|
33767
|
-
removeViewportClickHandler(viewport) {
|
|
33768
|
-
viewport.removeEventListener('click', this.boundClickHandler);
|
|
33769
33662
|
}
|
|
33770
33663
|
|
|
33771
33664
|
setWidth(width) {
|
|
@@ -33786,10 +33679,20 @@ class IdeogramViewport extends TrackViewport {
|
|
|
33786
33679
|
context.restore();
|
|
33787
33680
|
}
|
|
33788
33681
|
|
|
33789
|
-
|
|
33790
|
-
this
|
|
33791
|
-
|
|
33792
|
-
|
|
33682
|
+
repaint() {
|
|
33683
|
+
this.draw({referenceFrame: this.referenceFrame});
|
|
33684
|
+
}
|
|
33685
|
+
|
|
33686
|
+
draw({referenceFrame}) {
|
|
33687
|
+
|
|
33688
|
+
IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
|
|
33689
|
+
|
|
33690
|
+
this.trackView.track.draw({
|
|
33691
|
+
context: this.ideogram_ctx,
|
|
33692
|
+
referenceFrame,
|
|
33693
|
+
pixelWidth: this.$viewport.width(),
|
|
33694
|
+
pixelHeight: this.$viewport.height()
|
|
33695
|
+
});
|
|
33793
33696
|
}
|
|
33794
33697
|
|
|
33795
33698
|
startSpinner() {
|
|
@@ -33830,7 +33733,7 @@ function createViewport(trackView, column, referenceFrame, width) {
|
|
|
33830
33733
|
|
|
33831
33734
|
if ('ruler' === trackView.track.type) {
|
|
33832
33735
|
return new RulerViewport(trackView, column, referenceFrame, width)
|
|
33833
|
-
} else if ('ideogram' === trackView.track.
|
|
33736
|
+
} else if ('ideogram' === trackView.track.id) {
|
|
33834
33737
|
return new IdeogramViewport(trackView, column, referenceFrame, width)
|
|
33835
33738
|
} else {
|
|
33836
33739
|
return new TrackViewport(trackView, column, referenceFrame, width)
|
|
@@ -34353,7 +34256,6 @@ const colorPickerExclusionTypes = new Set(['ruler', 'sequence', 'ideogram']);
|
|
|
34353
34256
|
class TrackView {
|
|
34354
34257
|
|
|
34355
34258
|
constructor(browser, columnContainer, track) {
|
|
34356
|
-
this.namespace = `trackview-${guid$2()}`;
|
|
34357
34259
|
this.browser = browser;
|
|
34358
34260
|
this.track = track;
|
|
34359
34261
|
track.trackView = this;
|
|
@@ -34381,7 +34283,7 @@ class TrackView {
|
|
|
34381
34283
|
// Axis
|
|
34382
34284
|
this.axis = this.createAxis(browser, this.track);
|
|
34383
34285
|
|
|
34384
|
-
//
|
|
34286
|
+
// Create a viewport for each reference frame
|
|
34385
34287
|
this.viewports = [];
|
|
34386
34288
|
const viewportWidth = browser.calculateViewportWidth(referenceFrameList.length);
|
|
34387
34289
|
const viewportColumns = columnContainer.querySelectorAll('.igv-column');
|
|
@@ -34454,7 +34356,6 @@ class TrackView {
|
|
|
34454
34356
|
|
|
34455
34357
|
// Track Viewports
|
|
34456
34358
|
for (let viewport of this.viewports) {
|
|
34457
|
-
viewport.removeMouseHandlers();
|
|
34458
34359
|
viewport.$viewport.remove();
|
|
34459
34360
|
}
|
|
34460
34361
|
|
|
@@ -34572,9 +34473,8 @@ class TrackView {
|
|
|
34572
34473
|
|
|
34573
34474
|
this.sampleNameViewport.viewport.style.height = `${newHeight}px`;
|
|
34574
34475
|
|
|
34575
|
-
// If the track does not manage its own content height set it here
|
|
34476
|
+
// If the track does not manage its own content height set it equal to the viewport height here
|
|
34576
34477
|
if (typeof this.track.computePixelHeight !== "function") {
|
|
34577
|
-
|
|
34578
34478
|
for (let vp of this.viewports) {
|
|
34579
34479
|
vp.setContentHeight(newHeight);
|
|
34580
34480
|
}
|
|
@@ -34627,17 +34527,8 @@ class TrackView {
|
|
|
34627
34527
|
|
|
34628
34528
|
isLoading() {
|
|
34629
34529
|
for (let viewport of this.viewports) {
|
|
34630
|
-
if (viewport.isLoading()) return true
|
|
34631
|
-
}
|
|
34632
|
-
}
|
|
34633
|
-
|
|
34634
|
-
resize(viewportWidth) {
|
|
34635
|
-
|
|
34636
|
-
for (let viewport of this.viewports) {
|
|
34637
|
-
viewport.setWidth(viewportWidth);
|
|
34530
|
+
if (viewport.isLoading()) return true
|
|
34638
34531
|
}
|
|
34639
|
-
|
|
34640
|
-
this.updateViews(true);
|
|
34641
34532
|
}
|
|
34642
34533
|
|
|
34643
34534
|
/**
|
|
@@ -34647,7 +34538,7 @@ class TrackView {
|
|
|
34647
34538
|
repaintViews() {
|
|
34648
34539
|
|
|
34649
34540
|
for (let viewport of this.viewports) {
|
|
34650
|
-
if(viewport.isVisible()) {
|
|
34541
|
+
if (viewport.isVisible()) {
|
|
34651
34542
|
viewport.repaint();
|
|
34652
34543
|
}
|
|
34653
34544
|
}
|
|
@@ -34658,7 +34549,6 @@ class TrackView {
|
|
|
34658
34549
|
|
|
34659
34550
|
// Repaint sample names last
|
|
34660
34551
|
this.repaintSamples();
|
|
34661
|
-
|
|
34662
34552
|
}
|
|
34663
34553
|
|
|
34664
34554
|
repaintSamples() {
|
|
@@ -34674,13 +34564,25 @@ class TrackView {
|
|
|
34674
34564
|
this.viewports.forEach(viewport => viewport.setTrackLabel(name));
|
|
34675
34565
|
}
|
|
34676
34566
|
|
|
34567
|
+
/**
|
|
34568
|
+
* Called in response to a window resize event, change in # of multilocus panels, or other event that changes
|
|
34569
|
+
* the width of the track view.
|
|
34570
|
+
*
|
|
34571
|
+
* @param viewportWidth The width of each viewport in this track view.
|
|
34572
|
+
*/
|
|
34573
|
+
resize(viewportWidth) {
|
|
34574
|
+
for (let viewport of this.viewports) {
|
|
34575
|
+
viewport.setWidth(viewportWidth);
|
|
34576
|
+
}
|
|
34577
|
+
}
|
|
34578
|
+
|
|
34677
34579
|
/**
|
|
34678
34580
|
* Update viewports to reflect current genomic state, possibly loading additional data.
|
|
34679
34581
|
*
|
|
34680
34582
|
* @param force - if true, force a repaint even if no new data is loaded
|
|
34681
34583
|
* @returns {Promise<void>}
|
|
34682
34584
|
*/
|
|
34683
|
-
async updateViews(
|
|
34585
|
+
async updateViews() {
|
|
34684
34586
|
|
|
34685
34587
|
if (!(this.browser && this.browser.referenceFrameList)) return
|
|
34686
34588
|
|
|
@@ -34689,18 +34591,25 @@ class TrackView {
|
|
|
34689
34591
|
// Shift viewports left/right to current genomic state (pans canvas)
|
|
34690
34592
|
visibleViewports.forEach(viewport => viewport.shift());
|
|
34691
34593
|
|
|
34692
|
-
|
|
34693
|
-
|
|
34694
|
-
if (isDragging) {
|
|
34594
|
+
// If dragging (panning) return
|
|
34595
|
+
if (this.browser.dragObject) {
|
|
34695
34596
|
return
|
|
34696
34597
|
}
|
|
34697
34598
|
|
|
34698
|
-
//
|
|
34699
|
-
|
|
34599
|
+
// Get viewports to repaint
|
|
34600
|
+
let viewportsToRepaint = (this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler') ?
|
|
34601
|
+
visibleViewports :
|
|
34602
|
+
visibleViewports.filter(vp => vp.needsRepaint());
|
|
34603
|
+
|
|
34604
|
+
// Filter zoomed out views. This has the side effect or turning off or no the zoomed out notice
|
|
34605
|
+
viewportsToRepaint = viewportsToRepaint.filter(viewport => viewport.checkZoomIn());
|
|
34606
|
+
|
|
34607
|
+
// Get viewports that require a data load
|
|
34608
|
+
const viewportsToReload = viewportsToRepaint.filter(viewport => viewport.needsReload());
|
|
34700
34609
|
|
|
34701
34610
|
// Trigger viewport to load features needed to cover current genomic range
|
|
34702
34611
|
// NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
|
|
34703
|
-
for (let viewport of
|
|
34612
|
+
for (let viewport of viewportsToReload) {
|
|
34704
34613
|
await viewport.loadFeatures();
|
|
34705
34614
|
}
|
|
34706
34615
|
|
|
@@ -34709,7 +34618,7 @@ class TrackView {
|
|
|
34709
34618
|
// Special case for variant tracks in multilocus view. The # of rows to allocate to the variant (site)
|
|
34710
34619
|
// section depends on data from all the views. We only need to adjust this however if any data was loaded
|
|
34711
34620
|
// (i.e. reloadableViewports.length > 0)
|
|
34712
|
-
if (this.track && typeof this.track.variantRowCount === 'function' &&
|
|
34621
|
+
if (this.track && typeof this.track.variantRowCount === 'function' && viewportsToReload.length > 0) {
|
|
34713
34622
|
let maxRow = 0;
|
|
34714
34623
|
for (let viewport of this.viewports) {
|
|
34715
34624
|
if (viewport.featureCache && viewport.featureCache.features) {
|
|
@@ -34747,19 +34656,8 @@ class TrackView {
|
|
|
34747
34656
|
}
|
|
34748
34657
|
}
|
|
34749
34658
|
|
|
34750
|
-
|
|
34751
|
-
|
|
34752
|
-
for (let vp of visibleViewports) {
|
|
34753
|
-
vp.repaint();
|
|
34754
|
-
}
|
|
34755
|
-
} else {
|
|
34756
|
-
const reloadedViewports = new Set(reloadableViewports);
|
|
34757
|
-
for (let vp of visibleViewports) {
|
|
34758
|
-
const invalid = vp.canvas && vp.canvas._data && vp.canvas._data.invalidate;
|
|
34759
|
-
if (invalid || reloadedViewports.has(vp)) {
|
|
34760
|
-
vp.repaint();
|
|
34761
|
-
}
|
|
34762
|
-
}
|
|
34659
|
+
for (let vp of viewportsToRepaint) {
|
|
34660
|
+
vp.repaint();
|
|
34763
34661
|
}
|
|
34764
34662
|
|
|
34765
34663
|
this.adjustTrackHeight();
|
|
@@ -34787,7 +34685,7 @@ class TrackView {
|
|
|
34787
34685
|
}
|
|
34788
34686
|
|
|
34789
34687
|
/**
|
|
34790
|
-
* Return a promise to get all in-view features. Used for group autoscaling.
|
|
34688
|
+
* Return a promise to get all in-view features across all viewports. Used for group autoscaling.
|
|
34791
34689
|
*/
|
|
34792
34690
|
async getInViewFeatures() {
|
|
34793
34691
|
|
|
@@ -34795,16 +34693,11 @@ class TrackView {
|
|
|
34795
34693
|
return []
|
|
34796
34694
|
}
|
|
34797
34695
|
|
|
34798
|
-
// List of viewports that need reloading
|
|
34799
|
-
const rpV = this.viewportsToReload();
|
|
34800
|
-
const promises = rpV.map(function (vp) {
|
|
34801
|
-
return vp.loadFeatures()
|
|
34802
|
-
});
|
|
34803
|
-
|
|
34804
|
-
await Promise.all(promises);
|
|
34805
|
-
|
|
34806
34696
|
let allFeatures = [];
|
|
34807
34697
|
for (let vp of this.viewports) {
|
|
34698
|
+
if (vp.needsReload()) {
|
|
34699
|
+
await vp.loadFeatures();
|
|
34700
|
+
}
|
|
34808
34701
|
if (vp.featureCache && vp.featureCache.features) {
|
|
34809
34702
|
const referenceFrame = vp.referenceFrame;
|
|
34810
34703
|
const start = referenceFrame.start;
|
|
@@ -34855,27 +34748,6 @@ class TrackView {
|
|
|
34855
34748
|
}
|
|
34856
34749
|
}
|
|
34857
34750
|
|
|
34858
|
-
viewportsToReload(force) {
|
|
34859
|
-
|
|
34860
|
-
// List of viewports that need reloading
|
|
34861
|
-
const viewports = this.viewports.filter(viewport => {
|
|
34862
|
-
if (!viewport.isVisible()) {
|
|
34863
|
-
return false
|
|
34864
|
-
}
|
|
34865
|
-
if (!viewport.checkZoomIn()) {
|
|
34866
|
-
return false
|
|
34867
|
-
} else {
|
|
34868
|
-
const referenceFrame = viewport.referenceFrame;
|
|
34869
|
-
const chr = viewport.referenceFrame.chr;
|
|
34870
|
-
const start = referenceFrame.start;
|
|
34871
|
-
const end = start + referenceFrame.toBP($$1(viewport.contentDiv).width());
|
|
34872
|
-
const bpPerPixel = referenceFrame.bpPerPixel;
|
|
34873
|
-
return (!viewport.featureCache || !viewport.featureCache.containsRange(chr, start, end, bpPerPixel))
|
|
34874
|
-
}
|
|
34875
|
-
});
|
|
34876
|
-
return viewports
|
|
34877
|
-
}
|
|
34878
|
-
|
|
34879
34751
|
createTrackScrollbar(browser) {
|
|
34880
34752
|
|
|
34881
34753
|
const outerScroll = div$1();
|
|
@@ -34988,7 +34860,7 @@ class TrackView {
|
|
|
34988
34860
|
|
|
34989
34861
|
addTrackDragMouseHandlers(browser) {
|
|
34990
34862
|
|
|
34991
|
-
if ('ideogram' === this.track.
|
|
34863
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
34992
34864
|
|
|
34993
34865
|
let currentDragHandle = undefined;
|
|
34994
34866
|
|
|
@@ -35065,7 +34937,7 @@ class TrackView {
|
|
|
35065
34937
|
|
|
35066
34938
|
removeTrackDragMouseHandlers() {
|
|
35067
34939
|
|
|
35068
|
-
if ('ideogram' === this.track.
|
|
34940
|
+
if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
|
|
35069
34941
|
this.dragHandle.removeEventListener('mousedown', this.boundTrackDragMouseDownHandler);
|
|
35070
34942
|
document.removeEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);
|
|
35071
34943
|
this.dragHandle.removeEventListener('mouseup', this.boundTrackDragMouseEnterHandler);
|
|
@@ -35928,7 +35800,7 @@ function decodeBed(tokens, header) {
|
|
|
35928
35800
|
const eEnd = eStart + parseInt(exonSizes[i]);
|
|
35929
35801
|
exons.push({start: eStart, end: eEnd});
|
|
35930
35802
|
}
|
|
35931
|
-
findUTRs(exons, feature.cdStart, feature.cdEnd);
|
|
35803
|
+
findUTRs$1(exons, feature.cdStart, feature.cdEnd);
|
|
35932
35804
|
feature.exons = exons;
|
|
35933
35805
|
}
|
|
35934
35806
|
|
|
@@ -36036,7 +35908,7 @@ function decodeGenePred(tokens, header) {
|
|
|
36036
35908
|
const end = parseInt(exonEnds[i]);
|
|
36037
35909
|
exons.push({start: start, end: end});
|
|
36038
35910
|
}
|
|
36039
|
-
findUTRs(exons, cdStart, cdEnd);
|
|
35911
|
+
findUTRs$1(exons, cdStart, cdEnd);
|
|
36040
35912
|
|
|
36041
35913
|
feature.exons = exons;
|
|
36042
35914
|
|
|
@@ -36079,7 +35951,7 @@ function decodeGenePredExt(tokens, header) {
|
|
|
36079
35951
|
const end = parseInt(exonEnds[i]);
|
|
36080
35952
|
exons.push({start: start, end: end});
|
|
36081
35953
|
}
|
|
36082
|
-
findUTRs(exons, cdStart, cdEnd);
|
|
35954
|
+
findUTRs$1(exons, cdStart, cdEnd);
|
|
36083
35955
|
|
|
36084
35956
|
feature.exons = exons;
|
|
36085
35957
|
|
|
@@ -36120,14 +35992,14 @@ function decodeReflat(tokens, header) {
|
|
|
36120
35992
|
const end = parseInt(exonEnds[i]);
|
|
36121
35993
|
exons.push({start: start, end: end});
|
|
36122
35994
|
}
|
|
36123
|
-
findUTRs(exons, cdStart, cdEnd);
|
|
35995
|
+
findUTRs$1(exons, cdStart, cdEnd);
|
|
36124
35996
|
|
|
36125
35997
|
feature.exons = exons;
|
|
36126
35998
|
|
|
36127
35999
|
return feature
|
|
36128
36000
|
}
|
|
36129
36001
|
|
|
36130
|
-
function findUTRs(exons, cdStart, cdEnd) {
|
|
36002
|
+
function findUTRs$1(exons, cdStart, cdEnd) {
|
|
36131
36003
|
|
|
36132
36004
|
for (let exon of exons) {
|
|
36133
36005
|
const end = exon.end;
|
|
@@ -39643,7 +39515,7 @@ class TextFeatureSource {
|
|
|
39643
39515
|
this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
|
|
39644
39516
|
this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
|
|
39645
39517
|
|
|
39646
|
-
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "tdf"]);
|
|
39518
|
+
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
39647
39519
|
|
|
39648
39520
|
if (config.features && Array.isArray(config.features)) {
|
|
39649
39521
|
// Explicit array of features
|
|
@@ -40063,11 +39935,9 @@ class BufferedReader {
|
|
|
40063
39935
|
}
|
|
40064
39936
|
}
|
|
40065
39937
|
|
|
40066
|
-
//table chromatinInteract
|
|
40067
|
-
|
|
40068
39938
|
function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
40069
39939
|
|
|
40070
|
-
if (autoSql && 'chromatinInteract' === autoSql.table ||
|
|
39940
|
+
if ("biginteract" === format || (autoSql && ('chromatinInteract' === autoSql.table) || 'interact' === autoSql.table)) {
|
|
40071
39941
|
return decodeInteract
|
|
40072
39942
|
} else {
|
|
40073
39943
|
const standardFieldCount = definedFieldCount - 3;
|
|
@@ -40104,6 +39974,7 @@ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
|
40104
39974
|
const eEnd = eStart + parseInt(exonSizes[i]);
|
|
40105
39975
|
exons.push({start: eStart, end: eEnd});
|
|
40106
39976
|
}
|
|
39977
|
+
findUTRs(exons, feature.cdStart, feature.cdEnd);
|
|
40107
39978
|
feature.exons = exons;
|
|
40108
39979
|
}
|
|
40109
39980
|
|
|
@@ -40121,6 +39992,28 @@ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
|
40121
39992
|
}
|
|
40122
39993
|
}
|
|
40123
39994
|
|
|
39995
|
+
//table chromatinInteract
|
|
39996
|
+
// "Chromatin interaction between two regions"
|
|
39997
|
+
// (
|
|
39998
|
+
// string chrom; "Chromosome (or contig, scaffold, etc.). For interchromosomal, use 2 records"
|
|
39999
|
+
// uint chromStart; "Start position of lower region. For interchromosomal, set to chromStart of this region"
|
|
40000
|
+
// uint chromEnd; "End position of upper region. For interchromosomal, set to chromEnd of this region"
|
|
40001
|
+
// string name; "Name of item, for display"
|
|
40002
|
+
// uint score; "Score from 0-1000"
|
|
40003
|
+
// double value; "Strength of interaction or other data value. Typically basis for score"
|
|
40004
|
+
// string exp; "Experiment name (metadata for filtering). Use . if not applicable"
|
|
40005
|
+
// string color; "Item color. Specified as r,g,b or hexadecimal #RRGGBB or html color name, as in //www.w3.org/TR/css3-color/#html4."
|
|
40006
|
+
// string region1Chrom; "Chromosome of lower region. For non-directional interchromosomal, chrom of this region."
|
|
40007
|
+
// uint region1Start; "Start position of lower/this region"
|
|
40008
|
+
// uint region1End; "End position in chromosome of lower/this region"
|
|
40009
|
+
// string region1Name; "Identifier of lower/this region"
|
|
40010
|
+
// string region1Strand; "Orientation of lower/this region: + or -. Use . if not applicable"
|
|
40011
|
+
// string region2Chrom; "Chromosome of upper region. For non-directional interchromosomal, chrom of other region"
|
|
40012
|
+
// uint region2Start; "Start position in chromosome of upper/this region"
|
|
40013
|
+
// uint region2End; "End position in chromosome of upper/this region"
|
|
40014
|
+
// string region2Name; "Identifier of upper/this region"
|
|
40015
|
+
// string region2Strand; "Orientation of upper/this region: + or -. Use . if not applicable"
|
|
40016
|
+
// )
|
|
40124
40017
|
function decodeInteract(feature, tokens) {
|
|
40125
40018
|
|
|
40126
40019
|
feature.chr1 = tokens[5];
|
|
@@ -40140,6 +40033,24 @@ function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
|
40140
40033
|
}
|
|
40141
40034
|
}
|
|
40142
40035
|
|
|
40036
|
+
function findUTRs(exons, cdStart, cdEnd) {
|
|
40037
|
+
|
|
40038
|
+
for (let exon of exons) {
|
|
40039
|
+
const end = exon.end;
|
|
40040
|
+
const start = exon.start;
|
|
40041
|
+
if (end < cdStart || start > cdEnd) {
|
|
40042
|
+
exon.utr = true;
|
|
40043
|
+
} else {
|
|
40044
|
+
if (cdStart >= start && cdStart <= end) {
|
|
40045
|
+
exon.cdStart = cdStart;
|
|
40046
|
+
}
|
|
40047
|
+
if (cdEnd >= start && cdEnd <= end) {
|
|
40048
|
+
exon.cdEnd = cdEnd;
|
|
40049
|
+
}
|
|
40050
|
+
}
|
|
40051
|
+
}
|
|
40052
|
+
}
|
|
40053
|
+
|
|
40143
40054
|
function scoreShade(score, color) {
|
|
40144
40055
|
const alpha = Math.min(1, 0.11 + 0.89 * (score / 779));
|
|
40145
40056
|
return alpha.toString()
|
|
@@ -41571,7 +41482,7 @@ class TDFSource {
|
|
|
41571
41482
|
return features
|
|
41572
41483
|
}
|
|
41573
41484
|
|
|
41574
|
-
supportsWholeGenome() {
|
|
41485
|
+
get supportsWholeGenome() {
|
|
41575
41486
|
return true
|
|
41576
41487
|
}
|
|
41577
41488
|
}
|
|
@@ -41679,11 +41590,12 @@ function zoomLevelForScale(chr, bpPerPixel, genome) {
|
|
|
41679
41590
|
* THE SOFTWARE.
|
|
41680
41591
|
*/
|
|
41681
41592
|
|
|
41593
|
+
const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);
|
|
41682
41594
|
|
|
41683
41595
|
function FeatureSource(config, genome) {
|
|
41684
41596
|
|
|
41685
41597
|
const format = config.format ? config.format.toLowerCase() : undefined;
|
|
41686
|
-
if (
|
|
41598
|
+
if (bbFormats.has(format)) {
|
|
41687
41599
|
return new BWSource(config, genome)
|
|
41688
41600
|
} else if ("tdf" === format) {
|
|
41689
41601
|
return new TDFSource(config, genome)
|
|
@@ -41692,38 +41604,6 @@ function FeatureSource(config, genome) {
|
|
|
41692
41604
|
}
|
|
41693
41605
|
}
|
|
41694
41606
|
|
|
41695
|
-
const pairs =
|
|
41696
|
-
[
|
|
41697
|
-
['A', 'T'],
|
|
41698
|
-
['G', 'C'],
|
|
41699
|
-
['Y', 'R'],
|
|
41700
|
-
['W', 'S'],
|
|
41701
|
-
['K', 'M'],
|
|
41702
|
-
['D', 'H'],
|
|
41703
|
-
['B', 'V']
|
|
41704
|
-
];
|
|
41705
|
-
|
|
41706
|
-
const complements = new Map();
|
|
41707
|
-
for (let p of pairs) {
|
|
41708
|
-
const p1 = p[0];
|
|
41709
|
-
const p2 = p[1];
|
|
41710
|
-
complements.set(p1, p2);
|
|
41711
|
-
complements.set(p2, p1);
|
|
41712
|
-
complements.set(p1.toLowerCase(), p2.toLowerCase());
|
|
41713
|
-
complements.set(p2.toLowerCase(), p1.toLowerCase());
|
|
41714
|
-
}
|
|
41715
|
-
|
|
41716
|
-
function reverseComplementSequence(sequence) {
|
|
41717
|
-
|
|
41718
|
-
let comp = '';
|
|
41719
|
-
let idx = sequence.length;
|
|
41720
|
-
while (idx-- > 0) {
|
|
41721
|
-
const base = sequence[idx];
|
|
41722
|
-
comp += complements.has(base) ? complements.get(base) : base;
|
|
41723
|
-
}
|
|
41724
|
-
return comp
|
|
41725
|
-
}
|
|
41726
|
-
|
|
41727
41607
|
const GtexUtils = {
|
|
41728
41608
|
|
|
41729
41609
|
getTissueInfo: function (datasetId, baseURL) {
|
|
@@ -41824,7 +41704,7 @@ function renderFeature(feature, bpStart, xScale, pixelHeight, ctx, options) {
|
|
|
41824
41704
|
// single-exon transcript
|
|
41825
41705
|
const xLeft = Math.max(0, coord.px);
|
|
41826
41706
|
const xRight = Math.min(pixelWidth, coord.px1);
|
|
41827
|
-
const width =
|
|
41707
|
+
const width = xRight - xLeft;
|
|
41828
41708
|
ctx.fillRect(xLeft, py, width, h);
|
|
41829
41709
|
|
|
41830
41710
|
// Arrows
|
|
@@ -41963,10 +41843,9 @@ function renderFeatureLabel(ctx, feature, featureX, featureX1, featureY, referen
|
|
|
41963
41843
|
const textBox = ctx.measureText(name);
|
|
41964
41844
|
const xleft = centerX - textBox.width / 2;
|
|
41965
41845
|
const xright = centerX + textBox.width / 2;
|
|
41966
|
-
if (options.labelAllFeatures || xleft > options.
|
|
41967
|
-
options.
|
|
41846
|
+
if (options.labelAllFeatures || xleft > options.rowLastLabelX[feature.row] || gtexSelection) {
|
|
41847
|
+
options.rowLastLabelX[feature.row] = xright;
|
|
41968
41848
|
IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform);
|
|
41969
|
-
|
|
41970
41849
|
}
|
|
41971
41850
|
} finally {
|
|
41972
41851
|
ctx.restore();
|
|
@@ -42267,7 +42146,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42267
42146
|
|
|
42268
42147
|
}
|
|
42269
42148
|
|
|
42270
|
-
supportsWholeGenome() {
|
|
42149
|
+
get supportsWholeGenome() {
|
|
42271
42150
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false
|
|
42272
42151
|
}
|
|
42273
42152
|
|
|
@@ -42323,24 +42202,27 @@ class FeatureTrack extends TrackBase {
|
|
|
42323
42202
|
|
|
42324
42203
|
const rowFeatureCount = [];
|
|
42325
42204
|
options.rowLastX = [];
|
|
42205
|
+
options.rowLastLabelX = [];
|
|
42326
42206
|
for (let feature of featureList) {
|
|
42327
|
-
|
|
42328
|
-
|
|
42329
|
-
rowFeatureCount[row]
|
|
42330
|
-
|
|
42331
|
-
|
|
42207
|
+
if(feature.start > bpStart && feature.end < bpEnd) {
|
|
42208
|
+
const row = this.displayMode === "COLLAPSED" ? 0 : feature.row || 0;
|
|
42209
|
+
if (rowFeatureCount[row] === undefined) {
|
|
42210
|
+
rowFeatureCount[row] = 1;
|
|
42211
|
+
} else {
|
|
42212
|
+
rowFeatureCount[row]++;
|
|
42213
|
+
}
|
|
42214
|
+
options.rowLastX[row] = -Number.MAX_SAFE_INTEGER;
|
|
42215
|
+
options.rowLastLabelX[row] = -Number.MAX_SAFE_INTEGER;
|
|
42332
42216
|
}
|
|
42333
|
-
options.rowLastX[row] = -Number.MAX_SAFE_INTEGER;
|
|
42334
42217
|
}
|
|
42218
|
+
const pixelsPerFeature = pixelWidth / Math.max(...rowFeatureCount);
|
|
42335
42219
|
|
|
42336
42220
|
let lastPxEnd = [];
|
|
42337
42221
|
for (let feature of featureList) {
|
|
42338
42222
|
if (feature.end < bpStart) continue
|
|
42339
42223
|
if (feature.start > bpEnd) break
|
|
42340
|
-
|
|
42341
42224
|
const row = this.displayMode === 'COLLAPSED' ? 0 : feature.row;
|
|
42342
|
-
|
|
42343
|
-
options.drawLabel = options.labelAllFeatures || featureDensity > 10;
|
|
42225
|
+
options.drawLabel = options.labelAllFeatures || pixelsPerFeature > 10;
|
|
42344
42226
|
const pxEnd = Math.ceil((feature.end - bpStart) / bpPerPixel);
|
|
42345
42227
|
const last = lastPxEnd[row];
|
|
42346
42228
|
if (!last || pxEnd > last) {
|
|
@@ -42354,7 +42236,6 @@ class FeatureTrack extends TrackBase {
|
|
|
42354
42236
|
ctx.globalAlpha = 1.0;
|
|
42355
42237
|
}
|
|
42356
42238
|
lastPxEnd[row] = pxEnd;
|
|
42357
|
-
|
|
42358
42239
|
}
|
|
42359
42240
|
}
|
|
42360
42241
|
|
|
@@ -42416,21 +42297,18 @@ class FeatureTrack extends TrackBase {
|
|
|
42416
42297
|
const infoURL = this.infoURL || this.config.infoURL;
|
|
42417
42298
|
for (let fd of featureData) {
|
|
42418
42299
|
data.push(fd);
|
|
42419
|
-
if (infoURL
|
|
42420
|
-
|
|
42421
|
-
|
|
42422
|
-
|
|
42423
|
-
|
|
42424
|
-
|
|
42425
|
-
|
|
42426
|
-
|
|
42427
|
-
const url = this.infoURL || this.config.infoURL;
|
|
42428
|
-
const href = url.replace("$$", feature.name);
|
|
42429
|
-
data.push({name: "Info", value: `<a target="_blank" href=${href}>${fd.value}</a>`});
|
|
42430
|
-
}
|
|
42300
|
+
if (infoURL &&
|
|
42301
|
+
fd.name &&
|
|
42302
|
+
fd.name.toLowerCase() === "name" &&
|
|
42303
|
+
fd.value &&
|
|
42304
|
+
isString$3(fd.value) &&
|
|
42305
|
+
!fd.value.startsWith("<")) {
|
|
42306
|
+
const href = infoURL.replace("$$", feature.name);
|
|
42307
|
+
fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
|
|
42431
42308
|
}
|
|
42432
42309
|
}
|
|
42433
42310
|
|
|
42311
|
+
|
|
42434
42312
|
//Array.prototype.push.apply(data, featureData);
|
|
42435
42313
|
|
|
42436
42314
|
// If we have clicked over an exon number it.
|
|
@@ -42459,7 +42337,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42459
42337
|
}
|
|
42460
42338
|
|
|
42461
42339
|
menuItemList() {
|
|
42462
|
-
|
|
42340
|
+
|
|
42463
42341
|
const menuItems = [];
|
|
42464
42342
|
|
|
42465
42343
|
if (this.render === renderSnp) {
|
|
@@ -42487,7 +42365,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42487
42365
|
menuItems.push(
|
|
42488
42366
|
{
|
|
42489
42367
|
object: $$1(createCheckbox$1(lut[displayMode], displayMode === this.displayMode)),
|
|
42490
|
-
click:
|
|
42368
|
+
click: () => {
|
|
42491
42369
|
this.displayMode = displayMode;
|
|
42492
42370
|
this.config.displayMode = displayMode;
|
|
42493
42371
|
this.trackView.checkContentHeight();
|
|
@@ -42503,14 +42381,28 @@ class FeatureTrack extends TrackBase {
|
|
|
42503
42381
|
|
|
42504
42382
|
contextMenuItemList(clickState) {
|
|
42505
42383
|
|
|
42506
|
-
|
|
42507
|
-
|
|
42508
|
-
|
|
42509
|
-
|
|
42510
|
-
|
|
42511
|
-
|
|
42512
|
-
|
|
42513
|
-
|
|
42384
|
+
const features = this.clickedFeatures(clickState);
|
|
42385
|
+
if (features.length > 1) {
|
|
42386
|
+
features.sort((a, b) => (b.end - b.start) - (a.end - a.start));
|
|
42387
|
+
}
|
|
42388
|
+
const f = features[0]; // The shortest clicked feature
|
|
42389
|
+
|
|
42390
|
+
if ((f.end - f.start) <= 1000000) {
|
|
42391
|
+
const list = [{
|
|
42392
|
+
label: 'View feature sequence',
|
|
42393
|
+
click: async () => {
|
|
42394
|
+
let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);
|
|
42395
|
+
if (f.strand === '-') {
|
|
42396
|
+
seq = reverseComplementSequence(seq);
|
|
42397
|
+
}
|
|
42398
|
+
if (!seq) seq = "Unknown sequence";
|
|
42399
|
+
Alert.presentAlert(seq);
|
|
42400
|
+
|
|
42401
|
+
}
|
|
42402
|
+
}];
|
|
42403
|
+
|
|
42404
|
+
if (isSecureContext() && navigator.clipboard !== undefined) {
|
|
42405
|
+
list.push(
|
|
42514
42406
|
{
|
|
42515
42407
|
label: 'Copy feature sequence',
|
|
42516
42408
|
click: async () => {
|
|
@@ -42518,17 +42410,23 @@ class FeatureTrack extends TrackBase {
|
|
|
42518
42410
|
if (f.strand === '-') {
|
|
42519
42411
|
seq = reverseComplementSequence(seq);
|
|
42520
42412
|
}
|
|
42521
|
-
|
|
42413
|
+
try {
|
|
42414
|
+
await navigator.clipboard.writeText(seq);
|
|
42415
|
+
} catch (e) {
|
|
42416
|
+
console.error(e);
|
|
42417
|
+
Alert.presentAlert(`error copying sequence to clipboard ${e}`);
|
|
42418
|
+
}
|
|
42522
42419
|
}
|
|
42523
|
-
}
|
|
42524
|
-
|
|
42525
|
-
]
|
|
42420
|
+
}
|
|
42421
|
+
);
|
|
42526
42422
|
}
|
|
42527
|
-
|
|
42423
|
+
list.push('<hr/>');
|
|
42424
|
+
return list
|
|
42425
|
+
} else {
|
|
42528
42426
|
|
|
42529
|
-
|
|
42530
|
-
return undefined
|
|
42427
|
+
return undefined
|
|
42531
42428
|
|
|
42429
|
+
}
|
|
42532
42430
|
}
|
|
42533
42431
|
|
|
42534
42432
|
description() {
|
|
@@ -42549,7 +42447,7 @@ class FeatureTrack extends TrackBase {
|
|
|
42549
42447
|
desc += "</html>";
|
|
42550
42448
|
return desc
|
|
42551
42449
|
} else {
|
|
42552
|
-
return super.description()
|
|
42450
|
+
return super.description()
|
|
42553
42451
|
}
|
|
42554
42452
|
|
|
42555
42453
|
};
|
|
@@ -42870,7 +42768,7 @@ class WigTrack extends TrackBase {
|
|
|
42870
42768
|
}
|
|
42871
42769
|
}
|
|
42872
42770
|
|
|
42873
|
-
supportsWholeGenome() {
|
|
42771
|
+
get supportsWholeGenome() {
|
|
42874
42772
|
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
42875
42773
|
}
|
|
42876
42774
|
|
|
@@ -43442,7 +43340,7 @@ class SegTrack extends TrackBase {
|
|
|
43442
43340
|
|
|
43443
43341
|
}
|
|
43444
43342
|
|
|
43445
|
-
supportsWholeGenome() {
|
|
43343
|
+
get supportsWholeGenome() {
|
|
43446
43344
|
return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false
|
|
43447
43345
|
}
|
|
43448
43346
|
|
|
@@ -43665,7 +43563,7 @@ class MergedTrack extends TrackBase {
|
|
|
43665
43563
|
}
|
|
43666
43564
|
|
|
43667
43565
|
|
|
43668
|
-
supportsWholeGenome() {
|
|
43566
|
+
get supportsWholeGenome() {
|
|
43669
43567
|
return this.tracks.every(track => track.supportsWholeGenome())
|
|
43670
43568
|
}
|
|
43671
43569
|
}
|
|
@@ -43790,7 +43688,7 @@ class InteractionTrack extends TrackBase {
|
|
|
43790
43688
|
return this
|
|
43791
43689
|
}
|
|
43792
43690
|
|
|
43793
|
-
supportsWholeGenome() {
|
|
43691
|
+
get supportsWholeGenome() {
|
|
43794
43692
|
return true
|
|
43795
43693
|
}
|
|
43796
43694
|
|
|
@@ -44176,7 +44074,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44176
44074
|
items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
|
|
44177
44075
|
}
|
|
44178
44076
|
|
|
44179
|
-
if (this.browser.circularView
|
|
44077
|
+
if (this.browser.circularView) {
|
|
44180
44078
|
items.push('<hr/>');
|
|
44181
44079
|
items.push({
|
|
44182
44080
|
label: 'Add interactions to circular view',
|
|
@@ -44194,7 +44092,7 @@ class InteractionTrack extends TrackBase {
|
|
|
44194
44092
|
contextMenuItemList(clickState) {
|
|
44195
44093
|
|
|
44196
44094
|
// Experimental JBrowse feature
|
|
44197
|
-
if (this.browser.circularView
|
|
44095
|
+
if (this.browser.circularView ) {
|
|
44198
44096
|
const viewport = clickState.viewport;
|
|
44199
44097
|
const list = [];
|
|
44200
44098
|
|
|
@@ -44225,7 +44123,6 @@ class InteractionTrack extends TrackBase {
|
|
|
44225
44123
|
const inView = cachedFeatures.filter(f => f.drawState);
|
|
44226
44124
|
if(inView.length === 0) return;
|
|
44227
44125
|
|
|
44228
|
-
this.browser.circularViewVisible = true;
|
|
44229
44126
|
const chords = makeBedPEChords(inView);
|
|
44230
44127
|
sendChords(chords, this, refFrame, 0.5);
|
|
44231
44128
|
//
|
|
@@ -44638,7 +44535,7 @@ class VariantTrack extends TrackBase {
|
|
|
44638
44535
|
|
|
44639
44536
|
}
|
|
44640
44537
|
|
|
44641
|
-
supportsWholeGenome() {
|
|
44538
|
+
get supportsWholeGenome() {
|
|
44642
44539
|
return this.config.indexed === false || this.config.supportsWholeGenome === true
|
|
44643
44540
|
}
|
|
44644
44541
|
|
|
@@ -45071,7 +44968,7 @@ class VariantTrack extends TrackBase {
|
|
|
45071
44968
|
}
|
|
45072
44969
|
|
|
45073
44970
|
// Experimental JBrowse circular view integration
|
|
45074
|
-
if (this.browser.circularView
|
|
44971
|
+
if (this.browser.circularView) {
|
|
45075
44972
|
|
|
45076
44973
|
menuItems.push('<hr>');
|
|
45077
44974
|
menuItems.push({
|
|
@@ -45091,7 +44988,7 @@ class VariantTrack extends TrackBase {
|
|
|
45091
44988
|
contextMenuItemList(clickState) {
|
|
45092
44989
|
|
|
45093
44990
|
// Experimental JBrowse circular view integration
|
|
45094
|
-
if (this.browser.circularView
|
|
44991
|
+
if (this.browser.circularView) {
|
|
45095
44992
|
const viewport = clickState.viewport;
|
|
45096
44993
|
const list = [];
|
|
45097
44994
|
|
|
@@ -45632,7 +45529,7 @@ class GWASTrack extends TrackBase {
|
|
|
45632
45529
|
}
|
|
45633
45530
|
|
|
45634
45531
|
|
|
45635
|
-
supportsWholeGenome() {
|
|
45532
|
+
get supportsWholeGenome() {
|
|
45636
45533
|
return true
|
|
45637
45534
|
}
|
|
45638
45535
|
|
|
@@ -46146,7 +46043,7 @@ class GCNVTrack extends TrackBase {
|
|
|
46146
46043
|
return items
|
|
46147
46044
|
}
|
|
46148
46045
|
|
|
46149
|
-
supportsWholeGenome() {
|
|
46046
|
+
get supportsWholeGenome() {
|
|
46150
46047
|
return false
|
|
46151
46048
|
}
|
|
46152
46049
|
}
|
|
@@ -46533,21 +46430,18 @@ class RNAFeatureSource {
|
|
|
46533
46430
|
* THE SOFTWARE.
|
|
46534
46431
|
*/
|
|
46535
46432
|
|
|
46433
|
+
/**
|
|
46434
|
+
* Class represents an ideogram of a chromsome cytobands. It is used for the header of a track panel.
|
|
46435
|
+
*
|
|
46436
|
+
*/
|
|
46536
46437
|
class IdeogramTrack {
|
|
46537
46438
|
constructor(browser) {
|
|
46538
|
-
|
|
46539
46439
|
this.browser = browser;
|
|
46540
|
-
|
|
46541
46440
|
this.type = 'ideogram';
|
|
46542
|
-
this.id = this.type;
|
|
46543
|
-
|
|
46544
46441
|
this.height = 16;
|
|
46545
|
-
|
|
46546
46442
|
this.order = Number.MIN_SAFE_INTEGER;
|
|
46547
|
-
|
|
46548
46443
|
this.disableButtons = true;
|
|
46549
46444
|
this.ignoreTrackMenu = true;
|
|
46550
|
-
|
|
46551
46445
|
}
|
|
46552
46446
|
|
|
46553
46447
|
async getFeatures(chr, start, end) {
|
|
@@ -46814,7 +46708,7 @@ class SpliceJunctionTrack extends TrackBase {
|
|
|
46814
46708
|
|
|
46815
46709
|
}
|
|
46816
46710
|
|
|
46817
|
-
supportsWholeGenome() {
|
|
46711
|
+
get supportsWholeGenome() {
|
|
46818
46712
|
return false
|
|
46819
46713
|
}
|
|
46820
46714
|
|
|
@@ -47879,30 +47773,6 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
|
|
|
47879
47773
|
})
|
|
47880
47774
|
}
|
|
47881
47775
|
|
|
47882
|
-
function adjustReferenceFrame(scaleFactor, referenceFrame, viewportWidth, alignmentStart, alignmentLength) {
|
|
47883
|
-
|
|
47884
|
-
referenceFrame.bpPerPixel *= scaleFactor;
|
|
47885
|
-
|
|
47886
|
-
const alignmentEE = alignmentStart + alignmentLength;
|
|
47887
|
-
const alignmentCC = (alignmentStart + alignmentEE) / 2;
|
|
47888
|
-
|
|
47889
|
-
referenceFrame.start = alignmentCC - (referenceFrame.bpPerPixel * (viewportWidth / 2));
|
|
47890
|
-
referenceFrame.end = referenceFrame.start + (referenceFrame.bpPerPixel * viewportWidth);
|
|
47891
|
-
referenceFrame.locusSearchString = referenceFrame.getLocusString();
|
|
47892
|
-
}
|
|
47893
|
-
|
|
47894
|
-
function createReferenceFrameWithAlignment(genome, chromosomeName, bpp, viewportWidth, alignmentStart, alignmentLength) {
|
|
47895
|
-
|
|
47896
|
-
const alignmentEE = alignmentStart + alignmentLength;
|
|
47897
|
-
const alignmentCC = (alignmentStart + alignmentEE) / 2;
|
|
47898
|
-
|
|
47899
|
-
const ss = alignmentCC - (bpp * (viewportWidth / 2));
|
|
47900
|
-
const ee = ss + (bpp * viewportWidth);
|
|
47901
|
-
|
|
47902
|
-
return new ReferenceFrame(genome, chromosomeName, ss, ee, bpp)
|
|
47903
|
-
|
|
47904
|
-
}
|
|
47905
|
-
|
|
47906
47776
|
const defaultNucleotideColors = {
|
|
47907
47777
|
"A": "rgb( 0, 200, 0)",
|
|
47908
47778
|
"C": "rgb( 0,0,200)",
|
|
@@ -49002,6 +48872,69 @@ const SVGSaveControl = function (parent, browser) {
|
|
|
49002
48872
|
button.addEventListener('click', () => browser.saveSVGtoFile({}));
|
|
49003
48873
|
};
|
|
49004
48874
|
|
|
48875
|
+
const viewportColumnManager =
|
|
48876
|
+
{
|
|
48877
|
+
createColumns: (columnContainer, count) => {
|
|
48878
|
+
|
|
48879
|
+
for (let i = 0; i < count; i++) {
|
|
48880
|
+
if (0 === i) {
|
|
48881
|
+
createColumn(columnContainer, 'igv-column');
|
|
48882
|
+
} else {
|
|
48883
|
+
columnContainer.appendChild(div$1({class: 'igv-column-shim'}));
|
|
48884
|
+
createColumn(columnContainer, 'igv-column');
|
|
48885
|
+
}
|
|
48886
|
+
}
|
|
48887
|
+
|
|
48888
|
+
},
|
|
48889
|
+
|
|
48890
|
+
removeColumnAtIndex: (i, column) => {
|
|
48891
|
+
const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
|
|
48892
|
+
column.remove();
|
|
48893
|
+
shim.remove();
|
|
48894
|
+
},
|
|
48895
|
+
|
|
48896
|
+
insertAfter: referenceElement => {
|
|
48897
|
+
|
|
48898
|
+
const shim = div$1({class: 'igv-column-shim'});
|
|
48899
|
+
insertElementAfter(shim, referenceElement);
|
|
48900
|
+
|
|
48901
|
+
const column = div$1({class: 'igv-column'});
|
|
48902
|
+
insertElementAfter(column, shim);
|
|
48903
|
+
|
|
48904
|
+
return column
|
|
48905
|
+
},
|
|
48906
|
+
|
|
48907
|
+
insertBefore: (referenceElement, count) => {
|
|
48908
|
+
|
|
48909
|
+
for (let i = 0; i < count; i++) {
|
|
48910
|
+
|
|
48911
|
+
const column = div$1({class: 'igv-column'});
|
|
48912
|
+
insertElementBefore(column, referenceElement);
|
|
48913
|
+
|
|
48914
|
+
if (count > 1 && i > 0) {
|
|
48915
|
+
const columnShim = div$1({class: 'igv-column-shim'});
|
|
48916
|
+
insertElementBefore(columnShim, column);
|
|
48917
|
+
}
|
|
48918
|
+
|
|
48919
|
+
}
|
|
48920
|
+
|
|
48921
|
+
},
|
|
48922
|
+
|
|
48923
|
+
indexOfColumn: (columnContainer, column) => {
|
|
48924
|
+
|
|
48925
|
+
const allColumns = columnContainer.querySelectorAll('.igv-column');
|
|
48926
|
+
|
|
48927
|
+
for (let i = 0; i < allColumns.length; i++) {
|
|
48928
|
+
const c = allColumns[ i ];
|
|
48929
|
+
if (c === column) {
|
|
48930
|
+
return i
|
|
48931
|
+
}
|
|
48932
|
+
}
|
|
48933
|
+
|
|
48934
|
+
return undefined
|
|
48935
|
+
},
|
|
48936
|
+
};
|
|
48937
|
+
|
|
49005
48938
|
/*
|
|
49006
48939
|
* The MIT License (MIT)
|
|
49007
48940
|
*
|
|
@@ -49234,7 +49167,7 @@ class RulerTrack {
|
|
|
49234
49167
|
}
|
|
49235
49168
|
}
|
|
49236
49169
|
|
|
49237
|
-
supportsWholeGenome() {
|
|
49170
|
+
get supportsWholeGenome() {
|
|
49238
49171
|
return true
|
|
49239
49172
|
};
|
|
49240
49173
|
|
|
@@ -49811,7 +49744,9 @@ class Browser {
|
|
|
49811
49744
|
// Create ideogram and ruler track. Really this belongs in browser initialization, but creation is
|
|
49812
49745
|
// deferred because ideogram and ruler are treated as "tracks", and tracks require a reference frame
|
|
49813
49746
|
if (false !== session.showIdeogram) {
|
|
49814
|
-
|
|
49747
|
+
const ideogramTrack = new IdeogramTrack(this);
|
|
49748
|
+
ideogramTrack.id = 'ideogram';
|
|
49749
|
+
this.trackViews.push(new TrackView(this, this.columnContainer, ideogramTrack));
|
|
49815
49750
|
}
|
|
49816
49751
|
|
|
49817
49752
|
if (false !== session.showRuler) {
|
|
@@ -49856,8 +49791,8 @@ class Browser {
|
|
|
49856
49791
|
|
|
49857
49792
|
await this.loadTrackList(trackConfigurations);
|
|
49858
49793
|
|
|
49859
|
-
// The ruler
|
|
49860
|
-
for (let rtv of this.trackViews.filter((tv) => tv.track.type === 'ruler')) {
|
|
49794
|
+
// The ruler and ideogram tracks are not explicitly loaded, but needs updated nonetheless.
|
|
49795
|
+
for (let rtv of this.trackViews.filter((tv) => tv.track.type === 'ruler' || tv.track.type === 'ideogram')) {
|
|
49861
49796
|
rtv.updateViews();
|
|
49862
49797
|
}
|
|
49863
49798
|
|
|
@@ -50393,10 +50328,11 @@ class Browser {
|
|
|
50393
50328
|
this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
|
|
50394
50329
|
}
|
|
50395
50330
|
|
|
50396
|
-
|
|
50331
|
+
resize.call(this);
|
|
50332
|
+
await this.updateViews();
|
|
50397
50333
|
}
|
|
50398
50334
|
|
|
50399
|
-
async updateViews(
|
|
50335
|
+
async updateViews() {
|
|
50400
50336
|
|
|
50401
50337
|
const trackViews = this.trackViews;
|
|
50402
50338
|
|
|
@@ -50409,7 +50345,7 @@ class Browser {
|
|
|
50409
50345
|
// Don't autoscale while dragging.
|
|
50410
50346
|
if (this.dragObject) {
|
|
50411
50347
|
for (let trackView of trackViews) {
|
|
50412
|
-
await trackView.updateViews(
|
|
50348
|
+
await trackView.updateViews();
|
|
50413
50349
|
}
|
|
50414
50350
|
} else {
|
|
50415
50351
|
// Group autoscale
|
|
@@ -50454,14 +50390,14 @@ class Browser {
|
|
|
50454
50390
|
for (let trackView of groupTrackViews) {
|
|
50455
50391
|
trackView.track.dataRange = dataRange;
|
|
50456
50392
|
trackView.track.autoscale = false;
|
|
50457
|
-
p.push(trackView.updateViews(
|
|
50393
|
+
p.push(trackView.updateViews());
|
|
50458
50394
|
}
|
|
50459
50395
|
await Promise.all(p);
|
|
50460
50396
|
}
|
|
50461
50397
|
|
|
50462
50398
|
}
|
|
50463
50399
|
|
|
50464
|
-
await Promise.all(otherTracks.map(tv => tv.updateViews(
|
|
50400
|
+
await Promise.all(otherTracks.map(tv => tv.updateViews()));
|
|
50465
50401
|
// for (let trackView of otherTracks) {
|
|
50466
50402
|
// await trackView.updateViews(force);
|
|
50467
50403
|
// }
|
|
@@ -50539,50 +50475,6 @@ class Browser {
|
|
|
50539
50475
|
}
|
|
50540
50476
|
}
|
|
50541
50477
|
|
|
50542
|
-
async presentMultiLocusPanel(alignment, referenceFrameLeft) {
|
|
50543
|
-
|
|
50544
|
-
// account for reduced viewport width as a result of adding right mate pair panel
|
|
50545
|
-
const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
50546
|
-
|
|
50547
|
-
const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
50548
|
-
adjustReferenceFrame(scaleFactor, referenceFrameLeft, viewportWidth, alignment.start, alignment.lengthOnRef);
|
|
50549
|
-
|
|
50550
|
-
// create right mate pair reference frame
|
|
50551
|
-
const mateChrName = this.genome.getChromosomeName(alignment.mate.chr);
|
|
50552
|
-
|
|
50553
|
-
const referenceFrameRight = createReferenceFrameWithAlignment(this.genome, mateChrName, referenceFrameLeft.bpPerPixel, viewportWidth, alignment.mate.position, alignment.lengthOnRef);
|
|
50554
|
-
|
|
50555
|
-
// add right mate panel beside left mate panel
|
|
50556
|
-
const indexLeft = this.referenceFrameList.indexOf(referenceFrameLeft);
|
|
50557
|
-
const indexRight = 1 + (this.referenceFrameList.indexOf(referenceFrameLeft));
|
|
50558
|
-
|
|
50559
|
-
const {$viewport} = this.trackViews[0].viewports[indexLeft];
|
|
50560
|
-
const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);
|
|
50561
|
-
|
|
50562
|
-
if (indexRight === this.referenceFrameList.length) {
|
|
50563
|
-
|
|
50564
|
-
this.referenceFrameList.push(referenceFrameRight);
|
|
50565
|
-
|
|
50566
|
-
for (let trackView of this.trackViews) {
|
|
50567
|
-
const viewport = createViewport(trackView, viewportColumn, referenceFrameRight);
|
|
50568
|
-
trackView.viewports.push(viewport);
|
|
50569
|
-
}
|
|
50570
|
-
|
|
50571
|
-
} else {
|
|
50572
|
-
|
|
50573
|
-
this.referenceFrameList.splice(indexRight, 0, referenceFrameRight);
|
|
50574
|
-
|
|
50575
|
-
for (let trackView of this.trackViews) {
|
|
50576
|
-
const viewport = createViewport(trackView, viewportColumn, referenceFrameRight);
|
|
50577
|
-
trackView.viewports.splice(indexRight, 0, viewport);
|
|
50578
|
-
}
|
|
50579
|
-
}
|
|
50580
|
-
|
|
50581
|
-
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
50582
|
-
|
|
50583
|
-
await resize.call(this);
|
|
50584
|
-
}
|
|
50585
|
-
|
|
50586
50478
|
/**
|
|
50587
50479
|
* Add a new multi-locus panel for the specified region
|
|
50588
50480
|
* @param chr
|
|
@@ -50590,7 +50482,7 @@ class Browser {
|
|
|
50590
50482
|
* @param end
|
|
50591
50483
|
* @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
|
|
50592
50484
|
*/
|
|
50593
|
-
|
|
50485
|
+
async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
|
|
50594
50486
|
|
|
50595
50487
|
// account for reduced viewport width as a result of adding right mate pair panel
|
|
50596
50488
|
const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
|
|
@@ -50626,6 +50518,7 @@ class Browser {
|
|
|
50626
50518
|
this.centerLineList = this.createCenterLineList(this.columnContainer);
|
|
50627
50519
|
|
|
50628
50520
|
resize.call(this);
|
|
50521
|
+
await this.updateViews(true);
|
|
50629
50522
|
}
|
|
50630
50523
|
|
|
50631
50524
|
async removeMultiLocusPanel(referenceFrame) {
|
|
@@ -50955,11 +50848,6 @@ class Browser {
|
|
|
50955
50848
|
return surl
|
|
50956
50849
|
}
|
|
50957
50850
|
|
|
50958
|
-
currentReferenceFrames() {
|
|
50959
|
-
const anyTrackView = this.trackViews[0];
|
|
50960
|
-
return anyTrackView.viewports.map(vp => vp.referenceFrame)
|
|
50961
|
-
}
|
|
50962
|
-
|
|
50963
50851
|
/**
|
|
50964
50852
|
* Record a mouse click on a specific viewport. This might be the start of a drag operation. Dragging
|
|
50965
50853
|
* (panning) is handled here so that the mouse can move out of a specific viewport (e.g. stray into another
|
|
@@ -50997,12 +50885,22 @@ class Browser {
|
|
|
50997
50885
|
|
|
50998
50886
|
}
|
|
50999
50887
|
|
|
50888
|
+
/**
|
|
50889
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
50890
|
+
*
|
|
50891
|
+
* @param trackView
|
|
50892
|
+
*/
|
|
51000
50893
|
startTrackDrag(trackView) {
|
|
51001
50894
|
|
|
51002
50895
|
this.dragTrack = trackView;
|
|
51003
50896
|
|
|
51004
50897
|
}
|
|
51005
50898
|
|
|
50899
|
+
/**
|
|
50900
|
+
* Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
|
|
50901
|
+
*
|
|
50902
|
+
* @param dragDestination
|
|
50903
|
+
*/
|
|
51006
50904
|
updateTrackDrag(dragDestination) {
|
|
51007
50905
|
|
|
51008
50906
|
if (dragDestination && this.dragTrack) {
|
|
@@ -51155,6 +51053,7 @@ class Browser {
|
|
|
51155
51053
|
}
|
|
51156
51054
|
|
|
51157
51055
|
createCircularView(container, show) {
|
|
51056
|
+
show = show === true; // convert undefined to boolean
|
|
51158
51057
|
this.circularView = createCircularView(container, this);
|
|
51159
51058
|
this.circularViewControl = new CircularViewControl(this.$toggle_button_container.get(0), this);
|
|
51160
51059
|
this.circularView.setAssembly({
|
|
@@ -51162,8 +51061,8 @@ class Browser {
|
|
|
51162
51061
|
id: this.genome.id,
|
|
51163
51062
|
chromosomes: makeCircViewChromosomes(this.genome)
|
|
51164
51063
|
});
|
|
51165
|
-
this.circularViewVisible = show
|
|
51166
|
-
|
|
51064
|
+
this.circularViewVisible = show;
|
|
51065
|
+
return this.circularView
|
|
51167
51066
|
}
|
|
51168
51067
|
|
|
51169
51068
|
get circularViewVisible() {
|
|
@@ -51216,7 +51115,9 @@ async function resize() {
|
|
|
51216
51115
|
|
|
51217
51116
|
this.updateUIWithReferenceFrameList();
|
|
51218
51117
|
|
|
51219
|
-
|
|
51118
|
+
//TODO -- update view only if needed. Reducing size never needed. Increasing size maybe
|
|
51119
|
+
|
|
51120
|
+
await this.updateViews(true);
|
|
51220
51121
|
}
|
|
51221
51122
|
|
|
51222
51123
|
|