igv 3.0.1 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/igv.esm.js +1804 -1753
- package/dist/igv.esm.min.js +10 -10
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +1804 -1753
- package/dist/igv.min.js +10 -10
- package/dist/igv.min.js.map +1 -1
- package/package.json +1 -1
package/dist/igv.esm.js
CHANGED
|
@@ -19015,13 +19015,13 @@ class DataRangeDialog {
|
|
|
19015
19015
|
if (Array.isArray(trackViewOrTrackViewList)) {
|
|
19016
19016
|
dataRange = { min: Number.MAX_SAFE_INTEGER, max:-Number.MAX_SAFE_INTEGER };
|
|
19017
19017
|
for (const trackView of trackViewOrTrackViewList) {
|
|
19018
|
-
if (trackView.dataRange) {
|
|
19019
|
-
dataRange.min = Math.min(trackView.dataRange.min, dataRange.min);
|
|
19020
|
-
dataRange.max = Math.max(trackView.dataRange.max, dataRange.max);
|
|
19018
|
+
if (trackView.track.dataRange) {
|
|
19019
|
+
dataRange.min = Math.min(trackView.track.dataRange.min, dataRange.min);
|
|
19020
|
+
dataRange.max = Math.max(trackView.track.dataRange.max, dataRange.max);
|
|
19021
19021
|
}
|
|
19022
19022
|
}
|
|
19023
19023
|
} else {
|
|
19024
|
-
dataRange = trackViewOrTrackViewList.dataRange;
|
|
19024
|
+
dataRange = trackViewOrTrackViewList.track.dataRange;
|
|
19025
19025
|
}
|
|
19026
19026
|
|
|
19027
19027
|
if (dataRange) {
|
|
@@ -19063,7 +19063,7 @@ class DataRangeDialog {
|
|
|
19063
19063
|
} else {
|
|
19064
19064
|
const list = Array.isArray(trackViewOfTrackViewList) ? trackViewOfTrackViewList : [ trackViewOfTrackViewList ];
|
|
19065
19065
|
for (const trackView of list) {
|
|
19066
|
-
trackView.
|
|
19066
|
+
trackView.track.setDataRange({ min, max });
|
|
19067
19067
|
}
|
|
19068
19068
|
|
|
19069
19069
|
}
|
|
@@ -26696,6 +26696,14 @@ class TrackBase {
|
|
|
26696
26696
|
return menuItems
|
|
26697
26697
|
}
|
|
26698
26698
|
|
|
26699
|
+
setDataRange({ min, max }) {
|
|
26700
|
+
|
|
26701
|
+
this.dataRange = { min, max };
|
|
26702
|
+
this.autoscale = false;
|
|
26703
|
+
this.autoscaleGroup = undefined;
|
|
26704
|
+
this.trackView.repaintViews();
|
|
26705
|
+
}
|
|
26706
|
+
|
|
26699
26707
|
/**
|
|
26700
26708
|
* Return the first feature in this track whose start position is > position
|
|
26701
26709
|
* @param chr
|
|
@@ -27063,7 +27071,7 @@ class Variant {
|
|
|
27063
27071
|
this.filter = tokens[6];
|
|
27064
27072
|
this.info = {};
|
|
27065
27073
|
const infoStr = tokens[7];
|
|
27066
|
-
if (infoStr) {
|
|
27074
|
+
if (infoStr && infoStr !== '.') {
|
|
27067
27075
|
for (let elem of infoStr.split(';')) {
|
|
27068
27076
|
var element = elem.split('=');
|
|
27069
27077
|
this.info[element[0]] = element[1];
|
|
@@ -27222,9 +27230,10 @@ class Variant {
|
|
|
27222
27230
|
}
|
|
27223
27231
|
}
|
|
27224
27232
|
|
|
27225
|
-
|
|
27233
|
+
const infoKeys = Object.keys(this.info);
|
|
27234
|
+
if (this.info && infoKeys.length > 0) {
|
|
27226
27235
|
fields.push({html: '<hr style="border-top: dotted 1px;border-color: #c9c3ba" />'});
|
|
27227
|
-
for (let key of
|
|
27236
|
+
for (let key of infoKeys) {
|
|
27228
27237
|
fields.push({name: key, value: arrayToString(decodeURIComponent(this.info[key]))});
|
|
27229
27238
|
}
|
|
27230
27239
|
}
|
|
@@ -27291,7 +27300,7 @@ class SVComplement {
|
|
|
27291
27300
|
}
|
|
27292
27301
|
|
|
27293
27302
|
getAttributeValue(key) {
|
|
27294
|
-
|
|
27303
|
+
return this.mate.getAttributeValue(key)
|
|
27295
27304
|
}
|
|
27296
27305
|
|
|
27297
27306
|
getInfo(tag) {
|
|
@@ -27314,7 +27323,7 @@ class SVComplement {
|
|
|
27314
27323
|
popupData.push({name: 'Pos', value: `${numberFormatter$1(this.pos)}`});
|
|
27315
27324
|
popupData.push({html: '<hr style="border-top: dotted 1px;border-color: #c9c3ba" />'});
|
|
27316
27325
|
popupData.push("SV");
|
|
27317
|
-
popupData.push(...
|
|
27326
|
+
popupData.push(...this.mate.popupData(genomicLocation, genomeId));
|
|
27318
27327
|
|
|
27319
27328
|
return popupData
|
|
27320
27329
|
}
|
|
@@ -30735,346 +30744,746 @@ class BaseFeatureSource {
|
|
|
30735
30744
|
|
|
30736
30745
|
}
|
|
30737
30746
|
|
|
30738
|
-
|
|
30739
|
-
|
|
30740
|
-
/**
|
|
30741
|
-
* feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
|
|
30747
|
+
/*
|
|
30748
|
+
* The MIT License (MIT)
|
|
30742
30749
|
*
|
|
30743
|
-
*
|
|
30744
|
-
*
|
|
30750
|
+
* Copyright (c) 2016 University of California San Diego
|
|
30751
|
+
* Author: Jim Robinson
|
|
30752
|
+
*
|
|
30753
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
30754
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
30755
|
+
* in the Software without restriction, including without limitation the rights
|
|
30756
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
30757
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
30758
|
+
* furnished to do so, subject to the following conditions:
|
|
30759
|
+
*
|
|
30760
|
+
* The above copyright notice and this permission notice shall be included in
|
|
30761
|
+
* all copies or substantial portions of the Software.
|
|
30762
|
+
*
|
|
30763
|
+
*
|
|
30764
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30765
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30766
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30767
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30768
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30769
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
30770
|
+
* THE SOFTWARE.
|
|
30745
30771
|
*/
|
|
30746
|
-
class TextFeatureSource extends BaseFeatureSource {
|
|
30747
30772
|
|
|
30748
|
-
|
|
30773
|
+
const GZIP_FLAG = 0x1;
|
|
30749
30774
|
|
|
30750
|
-
|
|
30775
|
+
class TDFReader {
|
|
30751
30776
|
|
|
30752
|
-
|
|
30777
|
+
constructor(config, genome) {
|
|
30778
|
+
this.config = config;
|
|
30753
30779
|
this.genome = genome;
|
|
30754
|
-
this.
|
|
30755
|
-
this.
|
|
30756
|
-
this.
|
|
30780
|
+
this.path = config.url;
|
|
30781
|
+
this.groupCache = {};
|
|
30782
|
+
this.datasetCache = {};
|
|
30783
|
+
}
|
|
30757
30784
|
|
|
30758
|
-
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
30759
30785
|
|
|
30760
|
-
|
|
30761
|
-
|
|
30762
|
-
|
|
30763
|
-
this
|
|
30764
|
-
this.queryable = config.queryable !== false;
|
|
30765
|
-
} else if (config.sourceType === "ga4gh") {
|
|
30766
|
-
throw Error("Unsupported source type 'ga4gh'")
|
|
30767
|
-
} else if ((config.type === "eqtl" || config.type === "qtl") && config.sourceType === "gtex-ws") {
|
|
30768
|
-
this.reader = new GtexReader(config);
|
|
30769
|
-
this.queryable = true;
|
|
30770
|
-
} else if ("htsget" === config.sourceType) {
|
|
30771
|
-
this.reader = new HtsgetVariantReader(config, genome);
|
|
30772
|
-
this.queryable = true;
|
|
30773
|
-
} else if (config.sourceType === 'ucscservice') {
|
|
30774
|
-
this.reader = new UCSCServiceReader(config.source);
|
|
30775
|
-
this.queryable = true;
|
|
30776
|
-
} else if (config.sourceType === 'custom') {
|
|
30777
|
-
this.reader = new CustomServiceReader(config.source);
|
|
30778
|
-
this.queryable = false !== config.source.queryable;
|
|
30779
|
-
} else if ('service' === config.sourceType) {
|
|
30780
|
-
this.reader = new FeatureFileReader(config, genome);
|
|
30781
|
-
this.queryable = true;
|
|
30782
|
-
} else {
|
|
30783
|
-
// File of some type (i.e. not a webservice)
|
|
30784
|
-
this.reader = new FeatureFileReader(config, genome);
|
|
30785
|
-
if (config.queryable !== undefined) {
|
|
30786
|
-
this.queryable = config.queryable;
|
|
30787
|
-
} else if (queryableFormats.has(config.format) || this.reader.indexed) {
|
|
30788
|
-
this.queryable = true;
|
|
30789
|
-
} else ;
|
|
30786
|
+
async readHeader() {
|
|
30787
|
+
|
|
30788
|
+
if (this.magic !== undefined) {
|
|
30789
|
+
return this // Already read
|
|
30790
30790
|
}
|
|
30791
30791
|
|
|
30792
|
-
|
|
30793
|
-
|
|
30792
|
+
let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {range: {start: 0, size: 64000}}));
|
|
30793
|
+
let binaryParser = new BinaryParser$1(new DataView(data));
|
|
30794
|
+
this.magic = binaryParser.getInt();
|
|
30795
|
+
this.version = binaryParser.getInt();
|
|
30796
|
+
this.indexPos = binaryParser.getLong();
|
|
30797
|
+
this.indexSize = binaryParser.getInt();
|
|
30798
|
+
binaryParser.getInt();
|
|
30794
30799
|
|
|
30795
|
-
}
|
|
30796
30800
|
|
|
30797
|
-
|
|
30798
|
-
|
|
30799
|
-
|
|
30801
|
+
if (this.version >= 2) {
|
|
30802
|
+
let nWindowFunctions = binaryParser.getInt();
|
|
30803
|
+
this.windowFunctions = [];
|
|
30804
|
+
while (nWindowFunctions-- > 0) {
|
|
30805
|
+
this.windowFunctions.push(binaryParser.getString());
|
|
30806
|
+
}
|
|
30800
30807
|
}
|
|
30801
|
-
}
|
|
30802
30808
|
|
|
30803
|
-
|
|
30804
|
-
|
|
30805
|
-
if (header) {
|
|
30806
|
-
return header.type
|
|
30807
|
-
} else {
|
|
30808
|
-
return undefined // Convention for unknown or unspecified
|
|
30809
|
-
}
|
|
30810
|
-
}
|
|
30809
|
+
this.trackType = binaryParser.getString();
|
|
30810
|
+
this.trackLine = binaryParser.getString();
|
|
30811
30811
|
|
|
30812
|
-
|
|
30813
|
-
|
|
30812
|
+
let nTracks = binaryParser.getInt();
|
|
30813
|
+
this.trackNames = [];
|
|
30814
|
+
while (nTracks-- > 0) {
|
|
30815
|
+
this.trackNames.push(binaryParser.getString());
|
|
30816
|
+
}
|
|
30817
|
+
this.genomeID = binaryParser.getString();
|
|
30818
|
+
this.flags = binaryParser.getInt();
|
|
30819
|
+
this.compressed = (this.flags & GZIP_FLAG) !== 0;
|
|
30814
30820
|
|
|
30815
|
-
|
|
30816
|
-
|
|
30817
|
-
|
|
30818
|
-
|
|
30819
|
-
|
|
30820
|
-
this.config.format = header.format;
|
|
30821
|
-
}
|
|
30822
|
-
} else {
|
|
30823
|
-
this.header = {};
|
|
30824
|
-
}
|
|
30825
|
-
} else {
|
|
30826
|
-
this.header = {};
|
|
30821
|
+
// Now read index
|
|
30822
|
+
data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30823
|
+
range: {
|
|
30824
|
+
start: this.indexPos,
|
|
30825
|
+
size: this.indexSize
|
|
30827
30826
|
}
|
|
30827
|
+
}));
|
|
30828
|
+
binaryParser = new BinaryParser$1(new DataView(data));
|
|
30829
|
+
this.datasetIndex = {};
|
|
30830
|
+
let nEntries = binaryParser.getInt();
|
|
30831
|
+
while (nEntries-- > 0) {
|
|
30832
|
+
const name = binaryParser.getString();
|
|
30833
|
+
const pos = binaryParser.getLong();
|
|
30834
|
+
const size = binaryParser.getInt();
|
|
30835
|
+
this.datasetIndex[name] = {position: pos, size: size};
|
|
30828
30836
|
}
|
|
30829
|
-
|
|
30837
|
+
|
|
30838
|
+
this.groupIndex = {};
|
|
30839
|
+
nEntries = binaryParser.getInt();
|
|
30840
|
+
while (nEntries-- > 0) {
|
|
30841
|
+
const name = binaryParser.getString();
|
|
30842
|
+
const pos = binaryParser.getLong();
|
|
30843
|
+
const size = binaryParser.getInt();
|
|
30844
|
+
this.groupIndex[name] = {position: pos, size: size};
|
|
30845
|
+
}
|
|
30846
|
+
|
|
30847
|
+
return this
|
|
30830
30848
|
}
|
|
30831
30849
|
|
|
30832
|
-
|
|
30833
|
-
* Required function for all data source objects. Fetches features for the
|
|
30834
|
-
* range requested.
|
|
30835
|
-
*
|
|
30836
|
-
* This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
|
|
30837
|
-
* some not.
|
|
30838
|
-
*
|
|
30839
|
-
* @param chr
|
|
30840
|
-
* @param start
|
|
30841
|
-
* @param end
|
|
30842
|
-
* @param bpPerPixel
|
|
30843
|
-
*/
|
|
30844
|
-
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
|
|
30850
|
+
async readDataset(chr, windowFunction, zoom) {
|
|
30845
30851
|
|
|
30846
|
-
const
|
|
30852
|
+
const key = chr + "_" + windowFunction + "_" + zoom;
|
|
30847
30853
|
|
|
30848
|
-
|
|
30849
|
-
|
|
30854
|
+
if (this.datasetCache[key]) {
|
|
30855
|
+
return this.datasetCache[key]
|
|
30850
30856
|
|
|
30851
|
-
|
|
30852
|
-
|
|
30853
|
-
|
|
30854
|
-
|
|
30855
|
-
// const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))
|
|
30856
|
-
if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) ||
|
|
30857
|
-
this.config.disableCache ||
|
|
30858
|
-
!this.featureCache ||
|
|
30859
|
-
!this.featureCache.containsRange(new GenomicInterval(chr, start, end))) {
|
|
30860
|
-
await this.loadFeatures(chr, start, end, visibilityWindow);
|
|
30861
|
-
}
|
|
30857
|
+
} else {
|
|
30858
|
+
await this.readHeader();
|
|
30859
|
+
const wf = (this.version < 2) ? "" : "/" + windowFunction;
|
|
30860
|
+
const zoomString = (chr.toLowerCase() === "all" || zoom === undefined) ? "0" : zoom.toString();
|
|
30862
30861
|
|
|
30863
|
-
|
|
30864
|
-
if (
|
|
30865
|
-
|
|
30866
|
-
|
|
30867
|
-
|
|
30868
|
-
|
|
30862
|
+
let dsName;
|
|
30863
|
+
if (windowFunction === "raw") {
|
|
30864
|
+
dsName = "/" + chr + "/raw";
|
|
30865
|
+
} else {
|
|
30866
|
+
dsName = "/" + chr + "/z" + zoomString + wf;
|
|
30867
|
+
}
|
|
30868
|
+
const indexEntry = this.datasetIndex[dsName];
|
|
30869
|
+
|
|
30870
|
+
if (indexEntry === undefined) {
|
|
30871
|
+
return undefined
|
|
30872
|
+
}
|
|
30873
|
+
|
|
30874
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30875
|
+
range: {
|
|
30876
|
+
start: indexEntry.position,
|
|
30877
|
+
size: indexEntry.size
|
|
30869
30878
|
}
|
|
30879
|
+
}));
|
|
30880
|
+
|
|
30881
|
+
if (!data) {
|
|
30882
|
+
return undefined
|
|
30870
30883
|
}
|
|
30871
|
-
return this.wgFeatures
|
|
30872
|
-
} else {
|
|
30873
|
-
return this.featureCache.queryFeatures(chr, start, end)
|
|
30874
|
-
}
|
|
30875
|
-
}
|
|
30876
30884
|
|
|
30877
|
-
|
|
30878
|
-
|
|
30879
|
-
|
|
30885
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
30886
|
+
let nAttributes = binaryParser.getInt();
|
|
30887
|
+
const attributes = {};
|
|
30888
|
+
while (nAttributes-- > 0) {
|
|
30889
|
+
attributes[binaryParser.getString()] = binaryParser.getString();
|
|
30890
|
+
}
|
|
30891
|
+
const dataType = binaryParser.getString();
|
|
30892
|
+
const tileWidth = binaryParser.getFloat();
|
|
30893
|
+
let nTiles = binaryParser.getInt();
|
|
30894
|
+
const tiles = [];
|
|
30895
|
+
while (nTiles-- > 0) {
|
|
30896
|
+
tiles.push({position: binaryParser.getLong(), size: binaryParser.getInt()});
|
|
30897
|
+
}
|
|
30880
30898
|
|
|
30881
|
-
|
|
30882
|
-
|
|
30883
|
-
|
|
30899
|
+
const dataset = {
|
|
30900
|
+
name: dsName,
|
|
30901
|
+
attributes: attributes,
|
|
30902
|
+
dataType: dataType,
|
|
30903
|
+
tileWidth: tileWidth,
|
|
30904
|
+
tiles: tiles
|
|
30905
|
+
};
|
|
30884
30906
|
|
|
30885
|
-
|
|
30886
|
-
|
|
30887
|
-
if (this.queryable || !this.featureCache) { // queryable sources don't support all features
|
|
30888
|
-
return []
|
|
30889
|
-
} else {
|
|
30890
|
-
return this.featureCache.getAllFeatures()
|
|
30907
|
+
this.datasetCache[key] = dataset;
|
|
30908
|
+
return dataset
|
|
30891
30909
|
}
|
|
30892
30910
|
}
|
|
30893
30911
|
|
|
30912
|
+
async readRootGroup() {
|
|
30894
30913
|
|
|
30895
|
-
|
|
30914
|
+
const genome = this.genome;
|
|
30915
|
+
const rootGroup = this.groupCache["/"];
|
|
30916
|
+
if (rootGroup) {
|
|
30917
|
+
return rootGroup
|
|
30918
|
+
} else {
|
|
30896
30919
|
|
|
30897
|
-
|
|
30920
|
+
const group = await this.readGroup("/");
|
|
30921
|
+
const names = group["chromosomes"];
|
|
30922
|
+
const maxZoomString = group["maxZoom"];
|
|
30898
30923
|
|
|
30899
|
-
|
|
30900
|
-
|
|
30901
|
-
|
|
30924
|
+
// Now parse out interesting attributes.
|
|
30925
|
+
if (maxZoomString) {
|
|
30926
|
+
this.maxZoom = Number(maxZoomString);
|
|
30927
|
+
}
|
|
30902
30928
|
|
|
30903
|
-
|
|
30904
|
-
|
|
30905
|
-
|
|
30906
|
-
|
|
30907
|
-
}
|
|
30908
|
-
if (this.chrAliasManager) {
|
|
30909
|
-
queryChr = await this.chrAliasManager.getAliasName(chr);
|
|
30910
|
-
}
|
|
30929
|
+
const totalCountString = group["totalCount"];
|
|
30930
|
+
if (totalCountString) {
|
|
30931
|
+
group.totalCount = Number(totalCountString);
|
|
30932
|
+
}
|
|
30911
30933
|
|
|
30912
|
-
|
|
30913
|
-
|
|
30914
|
-
|
|
30915
|
-
|
|
30916
|
-
|
|
30917
|
-
|
|
30918
|
-
|
|
30919
|
-
intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
|
|
30920
|
-
} else if (visibilityWindow > (end - start) && this.config.expandQuery !== false) {
|
|
30921
|
-
let expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
|
|
30922
|
-
if(this.config.minQuerySize && expansionWindow < this.config.minQuerySize) {
|
|
30923
|
-
expansionWindow = this.config.minQuerySize;
|
|
30934
|
+
// Chromosome names
|
|
30935
|
+
const chrAliasTable = {};
|
|
30936
|
+
if (names) {
|
|
30937
|
+
names.split(",").forEach(function (chr) {
|
|
30938
|
+
const canonicalName = genome.getChromosomeName(chr);
|
|
30939
|
+
chrAliasTable[canonicalName] = chr;
|
|
30940
|
+
});
|
|
30924
30941
|
}
|
|
30925
|
-
|
|
30926
|
-
intervalEnd = intervalStart + expansionWindow;
|
|
30927
|
-
}
|
|
30942
|
+
this.chrAliasTable = chrAliasTable;
|
|
30928
30943
|
|
|
30929
|
-
|
|
30930
|
-
|
|
30931
|
-
this.queryable = reader.indexed;
|
|
30944
|
+
this.groupCache["/"] = group;
|
|
30945
|
+
return group
|
|
30932
30946
|
}
|
|
30947
|
+
}
|
|
30933
30948
|
|
|
30934
|
-
|
|
30935
|
-
new GenomicInterval(chr, intervalStart, intervalEnd) :
|
|
30936
|
-
undefined;
|
|
30949
|
+
async readGroup(name) {
|
|
30937
30950
|
|
|
30938
|
-
|
|
30951
|
+
const group = this.groupCache[name];
|
|
30952
|
+
if (group) {
|
|
30953
|
+
return group
|
|
30954
|
+
} else {
|
|
30939
30955
|
|
|
30940
|
-
|
|
30941
|
-
|
|
30942
|
-
|
|
30943
|
-
|
|
30956
|
+
await this.readHeader();
|
|
30957
|
+
const indexEntry = this.groupIndex[name];
|
|
30958
|
+
if (indexEntry === undefined) {
|
|
30959
|
+
return undefined
|
|
30944
30960
|
}
|
|
30945
30961
|
|
|
30946
|
-
|
|
30947
|
-
|
|
30962
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30963
|
+
range: {
|
|
30964
|
+
start: indexEntry.position,
|
|
30965
|
+
size: indexEntry.size
|
|
30966
|
+
}
|
|
30967
|
+
}));
|
|
30948
30968
|
|
|
30949
|
-
|
|
30950
|
-
|
|
30951
|
-
this.addFeaturesToDB(features, this.config);
|
|
30969
|
+
if (!data) {
|
|
30970
|
+
return undefined
|
|
30952
30971
|
}
|
|
30953
|
-
} else {
|
|
30954
|
-
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
30955
|
-
}
|
|
30956
|
-
}
|
|
30957
30972
|
|
|
30958
|
-
|
|
30959
|
-
|
|
30960
|
-
|
|
30961
|
-
|
|
30962
|
-
|
|
30963
|
-
|
|
30964
|
-
|
|
30965
|
-
let key;
|
|
30966
|
-
if (typeof feature.getAttributeValue === 'function') {
|
|
30967
|
-
key = feature.getAttributeValue(field);
|
|
30968
|
-
}
|
|
30969
|
-
if (key) {
|
|
30970
|
-
key = key.replaceAll(' ', '+').toUpperCase();
|
|
30971
|
-
// If feature is already present keep largest one
|
|
30972
|
-
if (this.featureMap.has(key)) {
|
|
30973
|
-
const f2 = this.featureMap.get(key);
|
|
30974
|
-
if (feature.end - feature.start < f2.end - f2.start) {
|
|
30975
|
-
continue
|
|
30976
|
-
}
|
|
30977
|
-
}
|
|
30978
|
-
this.featureMap.set(key, feature);
|
|
30979
|
-
}
|
|
30973
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
30974
|
+
const group = {name: name};
|
|
30975
|
+
let nAttributes = binaryParser.getInt();
|
|
30976
|
+
while (nAttributes-- > 0) {
|
|
30977
|
+
const key = binaryParser.getString();
|
|
30978
|
+
const value = binaryParser.getString();
|
|
30979
|
+
group[key] = value;
|
|
30980
30980
|
}
|
|
30981
|
+
this.groupCache[name] = group;
|
|
30982
|
+
return group
|
|
30981
30983
|
}
|
|
30982
30984
|
}
|
|
30983
30985
|
|
|
30984
|
-
|
|
30985
|
-
if (this.featureMap) {
|
|
30986
|
-
return this.featureMap.get(term.toUpperCase())
|
|
30987
|
-
}
|
|
30988
|
-
|
|
30989
|
-
}
|
|
30990
|
-
}
|
|
30991
|
-
|
|
30992
|
-
/**
|
|
30993
|
-
* A ChromTree parses a UCSC bigbed/bigwig "chromosomeTree" section of the header to produce 2 maps,
|
|
30994
|
-
* (1) ID -> chromosome names, and its
|
|
30995
|
-
* (2) chromsome name -> ID
|
|
30996
|
-
*
|
|
30997
|
-
* Both maps are needed by IGV
|
|
30998
|
-
*
|
|
30999
|
-
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
31000
|
-
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
31001
|
-
* leaving only the mapps.
|
|
31002
|
-
*/
|
|
31003
|
-
class ChromTree {
|
|
30986
|
+
async readTiles(tileIndeces, nTracks) {
|
|
31004
30987
|
|
|
31005
|
-
|
|
31006
|
-
|
|
31007
|
-
|
|
31008
|
-
this.idToName = valueToKey;
|
|
31009
|
-
this.sumLengths = sumLengths;
|
|
31010
|
-
}
|
|
30988
|
+
tileIndeces.sort(function (a, b) {
|
|
30989
|
+
return a.position - b.position
|
|
30990
|
+
});
|
|
31011
30991
|
|
|
31012
|
-
|
|
31013
|
-
|
|
31014
|
-
|
|
31015
|
-
const blockSize = binaryParser.getInt();
|
|
31016
|
-
const keySize = binaryParser.getInt();
|
|
31017
|
-
const valSize = binaryParser.getInt();
|
|
31018
|
-
const itemCount = binaryParser.getLong();
|
|
31019
|
-
const reserved = binaryParser.getLong();
|
|
30992
|
+
tileIndeces = tileIndeces.filter(function (idx) {
|
|
30993
|
+
return idx.size > 0
|
|
30994
|
+
});
|
|
31020
30995
|
|
|
31021
|
-
|
|
31022
|
-
|
|
31023
|
-
|
|
31024
|
-
let sumLengths = 0;
|
|
31025
|
-
const readTreeNode = (offset) => {
|
|
30996
|
+
if (tileIndeces.length === 0) {
|
|
30997
|
+
return []
|
|
30998
|
+
}
|
|
31026
30999
|
|
|
31027
|
-
|
|
31028
|
-
const type = binaryParser.getByte();
|
|
31029
|
-
binaryParser.getByte();
|
|
31030
|
-
const count = binaryParser.getUShort();
|
|
31000
|
+
const tiles = [];
|
|
31031
31001
|
|
|
31032
|
-
|
|
31033
|
-
// Leaf node
|
|
31034
|
-
for (let i = 0; i < count; i++) {
|
|
31035
|
-
let key = binaryParser.getFixedLengthString(keySize);
|
|
31036
|
-
let value;
|
|
31037
|
-
if (valSize === 8) {
|
|
31038
|
-
value = binaryParser.getInt();
|
|
31039
|
-
const chromSize = binaryParser.getInt();
|
|
31040
|
-
sumLengths += chromSize;
|
|
31041
|
-
if (genome) key = genome.getChromosomeName(key); // Translate to canonical chr name
|
|
31042
|
-
nameToId.set(key, value);
|
|
31043
|
-
idToName[value] = key;
|
|
31002
|
+
for (let indexEntry of tileIndeces) {
|
|
31044
31003
|
|
|
31045
|
-
|
|
31046
|
-
|
|
31047
|
-
|
|
31048
|
-
|
|
31049
|
-
} else {
|
|
31050
|
-
// non-leaf
|
|
31051
|
-
for (let i = 0; i < count; i++) {
|
|
31052
|
-
binaryParser.getFixedLengthString(keySize);
|
|
31053
|
-
const childOffset = binaryParser.getLong();
|
|
31054
|
-
const bufferOffset = childOffset - startOffset;
|
|
31055
|
-
const currOffset = binaryParser.position;
|
|
31056
|
-
readTreeNode(bufferOffset);
|
|
31057
|
-
binaryParser.position = currOffset;
|
|
31058
|
-
}
|
|
31004
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
31005
|
+
range: {
|
|
31006
|
+
start: indexEntry.position,
|
|
31007
|
+
size: indexEntry.size
|
|
31059
31008
|
}
|
|
31060
|
-
};
|
|
31009
|
+
}));
|
|
31061
31010
|
|
|
31062
|
-
|
|
31063
|
-
|
|
31011
|
+
let tileData;
|
|
31012
|
+
try {
|
|
31013
|
+
tileData = this.compressed ? inflate_1$3(data).buffer : data;
|
|
31014
|
+
} catch (e) {
|
|
31015
|
+
console.error(e);
|
|
31016
|
+
continue
|
|
31017
|
+
}
|
|
31064
31018
|
|
|
31065
|
-
|
|
31019
|
+
const binaryParser = new BinaryParser$1(new DataView(tileData));
|
|
31020
|
+
const type = binaryParser.getString();
|
|
31021
|
+
let tile;
|
|
31022
|
+
switch (type) {
|
|
31023
|
+
case "fixedStep":
|
|
31024
|
+
tile = createFixedStep(binaryParser, nTracks);
|
|
31025
|
+
break
|
|
31026
|
+
case "variableStep":
|
|
31027
|
+
tile = createVariableStep(binaryParser, nTracks);
|
|
31028
|
+
break
|
|
31029
|
+
case "bed":
|
|
31030
|
+
case "bedWithName":
|
|
31031
|
+
tile = createBed(binaryParser, nTracks, type);
|
|
31032
|
+
break
|
|
31033
|
+
default:
|
|
31034
|
+
throw "Unknown tile type: " + type
|
|
31035
|
+
}
|
|
31036
|
+
tiles.push(tile);
|
|
31066
31037
|
}
|
|
31038
|
+
return tiles
|
|
31067
31039
|
}
|
|
31068
31040
|
|
|
31069
|
-
|
|
31070
|
-
|
|
31071
|
-
const RPTREE_HEADER_SIZE = 48;
|
|
31072
|
-
const RPTREE_NODE_LEAF_ITEM_SIZE = 32; // leaf item size
|
|
31073
|
-
const RPTREE_NODE_CHILD_ITEM_SIZE = 24; // child item size
|
|
31041
|
+
async readTile(indexEntry, nTracks) {
|
|
31074
31042
|
|
|
31075
|
-
|
|
31043
|
+
let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
31044
|
+
range: {
|
|
31045
|
+
start: indexEntry.position,
|
|
31046
|
+
size: indexEntry.size
|
|
31047
|
+
}
|
|
31048
|
+
}));
|
|
31076
31049
|
|
|
31077
|
-
|
|
31050
|
+
if (this.compressed) {
|
|
31051
|
+
const plain = inflate_1$3(data);
|
|
31052
|
+
data = plain.buffer;
|
|
31053
|
+
}
|
|
31054
|
+
|
|
31055
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
31056
|
+
const type = binaryParser.getString();
|
|
31057
|
+
switch (type) {
|
|
31058
|
+
case "fixedStep":
|
|
31059
|
+
return createFixedStep(binaryParser, nTracks)
|
|
31060
|
+
case "variableStep":
|
|
31061
|
+
return createVariableStep(binaryParser, nTracks)
|
|
31062
|
+
case "bed":
|
|
31063
|
+
case "bedWithName":
|
|
31064
|
+
return createBed(binaryParser, nTracks, type)
|
|
31065
|
+
default:
|
|
31066
|
+
throw "Unknown tile type: " + type
|
|
31067
|
+
}
|
|
31068
|
+
}
|
|
31069
|
+
|
|
31070
|
+
}
|
|
31071
|
+
|
|
31072
|
+
function createFixedStep(binaryParser, nTracks) {
|
|
31073
|
+
const nPositions = binaryParser.getInt();
|
|
31074
|
+
const start = binaryParser.getInt();
|
|
31075
|
+
const span = binaryParser.getFloat();
|
|
31076
|
+
|
|
31077
|
+
const data = [];
|
|
31078
|
+
let nt = nTracks;
|
|
31079
|
+
while (nt-- > 0) {
|
|
31080
|
+
let np = nPositions;
|
|
31081
|
+
const dtrack = [];
|
|
31082
|
+
while (np-- > 0) {
|
|
31083
|
+
dtrack.push(binaryParser.getFloat());
|
|
31084
|
+
}
|
|
31085
|
+
data.push(dtrack);
|
|
31086
|
+
}
|
|
31087
|
+
|
|
31088
|
+
return {
|
|
31089
|
+
type: "fixedStep",
|
|
31090
|
+
start: start,
|
|
31091
|
+
span: span,
|
|
31092
|
+
data: data,
|
|
31093
|
+
nTracks: nTracks,
|
|
31094
|
+
nPositions: nPositions
|
|
31095
|
+
}
|
|
31096
|
+
}
|
|
31097
|
+
|
|
31098
|
+
function createVariableStep(binaryParser, nTracks) {
|
|
31099
|
+
|
|
31100
|
+
const tileStart = binaryParser.getInt();
|
|
31101
|
+
const span = binaryParser.getFloat();
|
|
31102
|
+
const nPositions = binaryParser.getInt();
|
|
31103
|
+
const start = [];
|
|
31104
|
+
|
|
31105
|
+
let np = nPositions;
|
|
31106
|
+
while (np-- > 0) {
|
|
31107
|
+
start.push(binaryParser.getInt());
|
|
31108
|
+
}
|
|
31109
|
+
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
31110
|
+
|
|
31111
|
+
const data = [];
|
|
31112
|
+
let nt = nTracks;
|
|
31113
|
+
while (nt-- > 0) {
|
|
31114
|
+
np = nPositions;
|
|
31115
|
+
const dtrack = [];
|
|
31116
|
+
while (np-- > 0) {
|
|
31117
|
+
dtrack.push(binaryParser.getFloat());
|
|
31118
|
+
}
|
|
31119
|
+
data.push(dtrack);
|
|
31120
|
+
}
|
|
31121
|
+
|
|
31122
|
+
return {
|
|
31123
|
+
type: "variableStep",
|
|
31124
|
+
tileStart: tileStart,
|
|
31125
|
+
span: span,
|
|
31126
|
+
start: start,
|
|
31127
|
+
data: data,
|
|
31128
|
+
nTracks: nTracks,
|
|
31129
|
+
nPositions: nPositions
|
|
31130
|
+
}
|
|
31131
|
+
}
|
|
31132
|
+
|
|
31133
|
+
function createBed(binaryParser, nTracks, type) {
|
|
31134
|
+
|
|
31135
|
+
const nPositions = binaryParser.getInt();
|
|
31136
|
+
|
|
31137
|
+
let n = nPositions;
|
|
31138
|
+
const start = [];
|
|
31139
|
+
while (n-- > 0) {
|
|
31140
|
+
start.push(binaryParser.getInt());
|
|
31141
|
+
}
|
|
31142
|
+
|
|
31143
|
+
n = nPositions;
|
|
31144
|
+
const end = [];
|
|
31145
|
+
while (n-- > 0) {
|
|
31146
|
+
end.push(binaryParser.getInt());
|
|
31147
|
+
}
|
|
31148
|
+
|
|
31149
|
+
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
31150
|
+
const data = [];
|
|
31151
|
+
let nt = nTracks;
|
|
31152
|
+
while (nt-- > 0) {
|
|
31153
|
+
let np = nPositions;
|
|
31154
|
+
const dtrack = [];
|
|
31155
|
+
while (np-- > 0) {
|
|
31156
|
+
dtrack.push(binaryParser.getFloat());
|
|
31157
|
+
}
|
|
31158
|
+
data.push(dtrack);
|
|
31159
|
+
}
|
|
31160
|
+
|
|
31161
|
+
if (type === "bedWithName") {
|
|
31162
|
+
n = nPositions;
|
|
31163
|
+
const name = [];
|
|
31164
|
+
while (n-- > 0) {
|
|
31165
|
+
name.push(binaryParser.getString());
|
|
31166
|
+
}
|
|
31167
|
+
}
|
|
31168
|
+
|
|
31169
|
+
return {
|
|
31170
|
+
type: type,
|
|
31171
|
+
start: start,
|
|
31172
|
+
end: end,
|
|
31173
|
+
data: data,
|
|
31174
|
+
nTracks: nTracks,
|
|
31175
|
+
nPositions: nPositions
|
|
31176
|
+
}
|
|
31177
|
+
}
|
|
31178
|
+
|
|
31179
|
+
/*
|
|
31180
|
+
* The MIT License (MIT)
|
|
31181
|
+
*
|
|
31182
|
+
* Copyright (c) 2016 University of California San Diego
|
|
31183
|
+
* Author: Jim Robinson
|
|
31184
|
+
*
|
|
31185
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
31186
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
31187
|
+
* in the Software without restriction, including without limitation the rights
|
|
31188
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31189
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
31190
|
+
* furnished to do so, subject to the following conditions:
|
|
31191
|
+
*
|
|
31192
|
+
* The above copyright notice and this permission notice shall be included in
|
|
31193
|
+
* all copies or substantial portions of the Software.
|
|
31194
|
+
*
|
|
31195
|
+
*
|
|
31196
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
31197
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31198
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
31199
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31200
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31201
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
31202
|
+
* THE SOFTWARE.
|
|
31203
|
+
*/
|
|
31204
|
+
|
|
31205
|
+
class TDFSource extends BaseFeatureSource {
|
|
31206
|
+
|
|
31207
|
+
#wgValues = {}
|
|
31208
|
+
searchable = false
|
|
31209
|
+
|
|
31210
|
+
|
|
31211
|
+
constructor(config, genome) {
|
|
31212
|
+
super(genome);
|
|
31213
|
+
this.genome = genome;
|
|
31214
|
+
this.reader = new TDFReader(config, genome);
|
|
31215
|
+
this.queryable = true;
|
|
31216
|
+
}
|
|
31217
|
+
|
|
31218
|
+
async getFeatures({chr, start, end, bpPerPixel, windowFunction = "mean"}) {
|
|
31219
|
+
|
|
31220
|
+
if (chr.toLowerCase() === "all") {
|
|
31221
|
+
return this.getWGValues(windowFunction, bpPerPixel)
|
|
31222
|
+
} else {
|
|
31223
|
+
return this._getFeatures(chr, start, end, bpPerPixel, windowFunction)
|
|
31224
|
+
}
|
|
31225
|
+
}
|
|
31226
|
+
async _getFeatures(chr, start, end, bpPerPixel, windowFunction) {
|
|
31227
|
+
const genomicInterval = new GenomicInterval(chr, start, end);
|
|
31228
|
+
const genome = this.genome;
|
|
31229
|
+
|
|
31230
|
+
|
|
31231
|
+
if (!this.rootGroup) {
|
|
31232
|
+
this.rootGroup = await this.reader.readRootGroup();
|
|
31233
|
+
if (!this.normalizationFactor) {
|
|
31234
|
+
const totalCount = this.rootGroup.totalCount;
|
|
31235
|
+
if (totalCount) {
|
|
31236
|
+
this.normalizationFactor = 1.0e6 / totalCount;
|
|
31237
|
+
}
|
|
31238
|
+
}
|
|
31239
|
+
}
|
|
31240
|
+
|
|
31241
|
+
genomicInterval.bpPerPixel = bpPerPixel;
|
|
31242
|
+
const zoom = zoomLevelForScale$1(chr, bpPerPixel, genome);
|
|
31243
|
+
let queryChr = this.reader.chrAliasTable[chr];
|
|
31244
|
+
let maxZoom = this.reader.maxZoom;
|
|
31245
|
+
if (queryChr === undefined) queryChr = chr;
|
|
31246
|
+
if (maxZoom === undefined) maxZoom = -1;
|
|
31247
|
+
|
|
31248
|
+
const wf = zoom > maxZoom ? "raw" : windowFunction;
|
|
31249
|
+
const dataset = await this.reader.readDataset(queryChr, wf, zoom);
|
|
31250
|
+
if (dataset == null) {
|
|
31251
|
+
return []
|
|
31252
|
+
}
|
|
31253
|
+
|
|
31254
|
+
const tileWidth = dataset.tileWidth;
|
|
31255
|
+
const startTile = Math.floor(start / tileWidth);
|
|
31256
|
+
const endTile = Math.floor(end / tileWidth);
|
|
31257
|
+
const NTRACKS = 1; // TODO read this
|
|
31258
|
+
const tiles = await this.reader.readTiles(dataset.tiles.slice(startTile, endTile + 1), NTRACKS);
|
|
31259
|
+
const features = [];
|
|
31260
|
+
for (let tile of tiles) {
|
|
31261
|
+
switch (tile.type) {
|
|
31262
|
+
case "bed":
|
|
31263
|
+
decodeBedTile(tile, chr, start, end, bpPerPixel, features);
|
|
31264
|
+
break
|
|
31265
|
+
case "variableStep":
|
|
31266
|
+
decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
|
|
31267
|
+
break
|
|
31268
|
+
case "fixedStep":
|
|
31269
|
+
decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
|
|
31270
|
+
break
|
|
31271
|
+
default:
|
|
31272
|
+
throw ("Unknown tile type: " + tile.type)
|
|
31273
|
+
}
|
|
31274
|
+
}
|
|
31275
|
+
features.sort(function (a, b) {
|
|
31276
|
+
return a.start - b.start
|
|
31277
|
+
});
|
|
31278
|
+
|
|
31279
|
+
return features
|
|
31280
|
+
}
|
|
31281
|
+
|
|
31282
|
+
get supportsWholeGenome() {
|
|
31283
|
+
return true
|
|
31284
|
+
}
|
|
31285
|
+
|
|
31286
|
+
get windowFunctions() {
|
|
31287
|
+
return this.reader.windowFunctions
|
|
31288
|
+
}
|
|
31289
|
+
|
|
31290
|
+
async getWGValues(windowFunction, bpPerPixel) {
|
|
31291
|
+
|
|
31292
|
+
const cached = this.#wgValues[windowFunction];
|
|
31293
|
+
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
31294
|
+
return cached.values
|
|
31295
|
+
} else {
|
|
31296
|
+
const wgFeatures = [];
|
|
31297
|
+
const genome = this.genome;
|
|
31298
|
+
const chrNames = this.genome.wgChromosomeNames;
|
|
31299
|
+
if (chrNames) {
|
|
31300
|
+
for (let c of genome.wgChromosomeNames) {
|
|
31301
|
+
const len = genome.getChromosome(c).bpLength;
|
|
31302
|
+
bpPerPixel = len / 1000;
|
|
31303
|
+
const chrFeatures = await this._getFeatures(c, 0, len, bpPerPixel, windowFunction);
|
|
31304
|
+
if (chrFeatures) {
|
|
31305
|
+
for (let f of chrFeatures) {
|
|
31306
|
+
const wg = Object.assign({}, f);
|
|
31307
|
+
wg.chr = "all";
|
|
31308
|
+
wg.start = genome.getGenomeCoordinate(f.chr, f.start);
|
|
31309
|
+
wg.end = genome.getGenomeCoordinate(f.chr, f.end);
|
|
31310
|
+
wg._f = f;
|
|
31311
|
+
wgFeatures.push(wg);
|
|
31312
|
+
}
|
|
31313
|
+
}
|
|
31314
|
+
}
|
|
31315
|
+
}
|
|
31316
|
+
this.#wgValues[windowFunction] = {values: wgFeatures, bpPerPixel};
|
|
31317
|
+
return wgFeatures
|
|
31318
|
+
}
|
|
31319
|
+
}
|
|
31320
|
+
|
|
31321
|
+
}
|
|
31322
|
+
|
|
31323
|
+
function decodeBedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31324
|
+
|
|
31325
|
+
const nPositions = tile.nPositions;
|
|
31326
|
+
const starts = tile.start;
|
|
31327
|
+
const ends = tile.end;
|
|
31328
|
+
const data = tile.data[0]; // Single track for now
|
|
31329
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31330
|
+
const s = starts[i];
|
|
31331
|
+
const e = ends[i];
|
|
31332
|
+
if (e < bpStart) continue
|
|
31333
|
+
if (s > bpEnd) break
|
|
31334
|
+
features.push({
|
|
31335
|
+
chr: chr,
|
|
31336
|
+
start: s,
|
|
31337
|
+
end: e,
|
|
31338
|
+
value: data[i]
|
|
31339
|
+
});
|
|
31340
|
+
}
|
|
31341
|
+
}
|
|
31342
|
+
|
|
31343
|
+
function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31344
|
+
|
|
31345
|
+
const nPositions = tile.nPositions;
|
|
31346
|
+
const starts = tile.start;
|
|
31347
|
+
const span = tile.span;
|
|
31348
|
+
const data = tile.data[0]; // Single track for now
|
|
31349
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31350
|
+
const s = starts[i];
|
|
31351
|
+
const e = s + span;
|
|
31352
|
+
if (e < bpStart) continue
|
|
31353
|
+
if (s > bpEnd) break
|
|
31354
|
+
features.push({
|
|
31355
|
+
chr: chr,
|
|
31356
|
+
start: s,
|
|
31357
|
+
end: e,
|
|
31358
|
+
value: data[i]
|
|
31359
|
+
});
|
|
31360
|
+
}
|
|
31361
|
+
}
|
|
31362
|
+
|
|
31363
|
+
function decodeFixedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31364
|
+
|
|
31365
|
+
const nPositions = tile.nPositions;
|
|
31366
|
+
let s = tile.start;
|
|
31367
|
+
const span = tile.span;
|
|
31368
|
+
const data = tile.data[0]; // Single track for now
|
|
31369
|
+
|
|
31370
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31371
|
+
const e = s + span;
|
|
31372
|
+
if (s > bpEnd) break
|
|
31373
|
+
if (e >= bpStart) {
|
|
31374
|
+
if (!Number.isNaN(data[i])) {
|
|
31375
|
+
features.push({
|
|
31376
|
+
chr: chr,
|
|
31377
|
+
start: s,
|
|
31378
|
+
end: e,
|
|
31379
|
+
value: data[i]
|
|
31380
|
+
});
|
|
31381
|
+
}
|
|
31382
|
+
}
|
|
31383
|
+
s = e;
|
|
31384
|
+
}
|
|
31385
|
+
}
|
|
31386
|
+
|
|
31387
|
+
|
|
31388
|
+
var log2 = Math.log(2);
|
|
31389
|
+
|
|
31390
|
+
function zoomLevelForScale$1(chr, bpPerPixel, genome) {
|
|
31391
|
+
|
|
31392
|
+
// Convert bpPerPixel to IGV "zoom" level. This is a bit convoluted, TDF is computed zoom levels assuming
|
|
31393
|
+
// display in a 700 pixel window. The fully zoomed out view of a chromosome is zoom level "0".
|
|
31394
|
+
// Zoom level 1 is magnified 2X, and so forth
|
|
31395
|
+
|
|
31396
|
+
var chrSize = genome.getChromosome(chr).bpLength;
|
|
31397
|
+
|
|
31398
|
+
return Math.ceil(Math.log(Math.max(0, (chrSize / (bpPerPixel * 700)))) / log2)
|
|
31399
|
+
}
|
|
31400
|
+
|
|
31401
|
+
/**
|
|
31402
|
+
* A ChromTree parses a UCSC bigbed/bigwig "chromosomeTree" section of the header to produce 2 maps,
|
|
31403
|
+
* (1) ID -> chromosome names, and its
|
|
31404
|
+
* (2) chromsome name -> ID
|
|
31405
|
+
*
|
|
31406
|
+
* Both maps are needed by IGV
|
|
31407
|
+
*
|
|
31408
|
+
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
31409
|
+
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
31410
|
+
* leaving only the mapps.
|
|
31411
|
+
*/
|
|
31412
|
+
class ChromTree {
|
|
31413
|
+
|
|
31414
|
+
constructor(header, nameToID, valueToKey, sumLengths) {
|
|
31415
|
+
this.header = header;
|
|
31416
|
+
this.nameToId = nameToID;
|
|
31417
|
+
this.idToName = valueToKey;
|
|
31418
|
+
this.sumLengths = sumLengths;
|
|
31419
|
+
}
|
|
31420
|
+
|
|
31421
|
+
static parseTree(binaryParser, startOffset, genome = false) {
|
|
31422
|
+
{
|
|
31423
|
+
const magic = binaryParser.getInt();
|
|
31424
|
+
const blockSize = binaryParser.getInt();
|
|
31425
|
+
const keySize = binaryParser.getInt();
|
|
31426
|
+
const valSize = binaryParser.getInt();
|
|
31427
|
+
const itemCount = binaryParser.getLong();
|
|
31428
|
+
const reserved = binaryParser.getLong();
|
|
31429
|
+
|
|
31430
|
+
const header = {magic, blockSize, keySize, valSize, itemCount, reserved};
|
|
31431
|
+
const nameToId = new Map();
|
|
31432
|
+
const idToName = [];
|
|
31433
|
+
let sumLengths = 0;
|
|
31434
|
+
const readTreeNode = (offset) => {
|
|
31435
|
+
|
|
31436
|
+
if (offset >= 0) binaryParser.position = offset;
|
|
31437
|
+
const type = binaryParser.getByte();
|
|
31438
|
+
binaryParser.getByte();
|
|
31439
|
+
const count = binaryParser.getUShort();
|
|
31440
|
+
|
|
31441
|
+
if (type === 1) {
|
|
31442
|
+
// Leaf node
|
|
31443
|
+
for (let i = 0; i < count; i++) {
|
|
31444
|
+
let key = binaryParser.getFixedLengthString(keySize);
|
|
31445
|
+
let value;
|
|
31446
|
+
if (valSize === 8) {
|
|
31447
|
+
value = binaryParser.getInt();
|
|
31448
|
+
const chromSize = binaryParser.getInt();
|
|
31449
|
+
sumLengths += chromSize;
|
|
31450
|
+
if (genome) key = genome.getChromosomeName(key); // Translate to canonical chr name
|
|
31451
|
+
nameToId.set(key, value);
|
|
31452
|
+
idToName[value] = key;
|
|
31453
|
+
|
|
31454
|
+
} else {
|
|
31455
|
+
throw Error(`Unexpected "valSize" value in chromosome tree. Expected 8, actual value is ${valSize}`)
|
|
31456
|
+
}
|
|
31457
|
+
}
|
|
31458
|
+
} else {
|
|
31459
|
+
// non-leaf
|
|
31460
|
+
for (let i = 0; i < count; i++) {
|
|
31461
|
+
binaryParser.getFixedLengthString(keySize);
|
|
31462
|
+
const childOffset = binaryParser.getLong();
|
|
31463
|
+
const bufferOffset = childOffset - startOffset;
|
|
31464
|
+
const currOffset = binaryParser.position;
|
|
31465
|
+
readTreeNode(bufferOffset);
|
|
31466
|
+
binaryParser.position = currOffset;
|
|
31467
|
+
}
|
|
31468
|
+
}
|
|
31469
|
+
};
|
|
31470
|
+
|
|
31471
|
+
// Recursively walk tree to populate dictionary
|
|
31472
|
+
readTreeNode(binaryParser);
|
|
31473
|
+
|
|
31474
|
+
return new ChromTree(header, nameToId, idToName, sumLengths)
|
|
31475
|
+
}
|
|
31476
|
+
}
|
|
31477
|
+
|
|
31478
|
+
}
|
|
31479
|
+
|
|
31480
|
+
const RPTREE_HEADER_SIZE = 48;
|
|
31481
|
+
const RPTREE_NODE_LEAF_ITEM_SIZE = 32; // leaf item size
|
|
31482
|
+
const RPTREE_NODE_CHILD_ITEM_SIZE = 24; // child item size
|
|
31483
|
+
|
|
31484
|
+
class RPTree {
|
|
31485
|
+
|
|
31486
|
+
static magic = 610839776
|
|
31078
31487
|
littleEndian = true
|
|
31079
31488
|
nodeCache = new Map()
|
|
31080
31489
|
|
|
@@ -31210,7 +31619,7 @@ function overlaps(item, chrIdx1, startBase, chrIdx2, endBase) {
|
|
|
31210
31619
|
|
|
31211
31620
|
function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
|
|
31212
31621
|
|
|
31213
|
-
if ("biginteract" === format || (autoSql && ('chromatinInteract' === autoSql.table
|
|
31622
|
+
if ("biginteract" === format || (autoSql && ('chromatinInteract' === autoSql.table || 'interact' === autoSql.table))) {
|
|
31214
31623
|
return decodeInteract
|
|
31215
31624
|
} else {
|
|
31216
31625
|
const standardFieldCount = definedFieldCount - 3;
|
|
@@ -31560,7 +31969,7 @@ class BWReader {
|
|
|
31560
31969
|
if (this.type === "bigwig") {
|
|
31561
31970
|
// Select a biwig "zoom level" appropriate for the current resolution.
|
|
31562
31971
|
const zoomLevelHeaders = await this.getZoomHeaders();
|
|
31563
|
-
let zoomLevelHeader = bpPerPixel ? zoomLevelForScale
|
|
31972
|
+
let zoomLevelHeader = bpPerPixel ? zoomLevelForScale(bpPerPixel, zoomLevelHeaders) : undefined;
|
|
31564
31973
|
if (zoomLevelHeader) {
|
|
31565
31974
|
treeOffset = zoomLevelHeader.indexOffset;
|
|
31566
31975
|
decodeFunction = decodeZoomData;
|
|
@@ -32016,7 +32425,7 @@ function computeStats() {
|
|
|
32016
32425
|
}
|
|
32017
32426
|
}
|
|
32018
32427
|
|
|
32019
|
-
function zoomLevelForScale
|
|
32428
|
+
function zoomLevelForScale(bpPerPixel, zoomLevelHeaders) {
|
|
32020
32429
|
let level;
|
|
32021
32430
|
for (let i = 0; i < zoomLevelHeaders.length; i++) {
|
|
32022
32431
|
const zl = zoomLevelHeaders[i];
|
|
@@ -32204,7 +32613,7 @@ class DataBuffer {
|
|
|
32204
32613
|
class BWSource extends BaseFeatureSource {
|
|
32205
32614
|
|
|
32206
32615
|
queryable = true
|
|
32207
|
-
wgValues = {}
|
|
32616
|
+
#wgValues = {}
|
|
32208
32617
|
windowFunctions = ["mean", "min", "max"]
|
|
32209
32618
|
|
|
32210
32619
|
constructor(config, genome) {
|
|
@@ -32216,12 +32625,15 @@ class BWSource extends BaseFeatureSource {
|
|
|
32216
32625
|
|
|
32217
32626
|
async getFeatures({chr, start, end, bpPerPixel, windowFunction}) {
|
|
32218
32627
|
|
|
32219
|
-
await
|
|
32628
|
+
await this.reader.loadHeader();
|
|
32220
32629
|
const isBigWig = this.reader.type === "bigwig";
|
|
32221
32630
|
|
|
32222
|
-
|
|
32223
|
-
|
|
32224
|
-
await this.
|
|
32631
|
+
let features;
|
|
32632
|
+
if ("all" === chr.toLowerCase()) {
|
|
32633
|
+
features = isBigWig ? await this.getWGValues(windowFunction, bpPerPixel) : [];
|
|
32634
|
+
} else {
|
|
32635
|
+
features = await this.reader.readFeatures(chr, start, chr, end, bpPerPixel, windowFunction);
|
|
32636
|
+
}
|
|
32225
32637
|
|
|
32226
32638
|
if (!isBigWig) {
|
|
32227
32639
|
pack(features);
|
|
@@ -32237,26 +32649,25 @@ class BWSource extends BaseFeatureSource {
|
|
|
32237
32649
|
if (this.reader.type === "bigwig") {
|
|
32238
32650
|
return -1
|
|
32239
32651
|
} else {
|
|
32240
|
-
return this.reader.featureDensity ?
|
|
32652
|
+
return this.reader.featureDensity ? Math.floor(10000 / this.reader.featureDensity) : -1
|
|
32241
32653
|
}
|
|
32242
32654
|
|
|
32243
32655
|
}
|
|
32244
32656
|
|
|
32245
|
-
async getWGValues(windowFunction) {
|
|
32657
|
+
async getWGValues(windowFunction, bpPerPixel) {
|
|
32246
32658
|
|
|
32247
|
-
const numberOfBins = 1000; // This doesn't need to be precise
|
|
32248
32659
|
const genome = this.genome;
|
|
32249
|
-
|
|
32250
|
-
if (
|
|
32251
|
-
return
|
|
32660
|
+
const cached = this.#wgValues[windowFunction];
|
|
32661
|
+
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
32662
|
+
return cached.values
|
|
32252
32663
|
} else {
|
|
32253
32664
|
|
|
32254
|
-
const bpPerPixel = genome.getGenomeLength() / numberOfBins;
|
|
32255
32665
|
const features = await this.reader.readWGFeatures(bpPerPixel, windowFunction);
|
|
32256
32666
|
let wgValues = [];
|
|
32257
32667
|
for (let f of features) {
|
|
32258
32668
|
const chr = f.chr;
|
|
32259
32669
|
const offset = genome.getCumulativeOffset(chr);
|
|
32670
|
+
if (undefined === offset) continue
|
|
32260
32671
|
const wgFeature = Object.assign({}, f);
|
|
32261
32672
|
wgFeature.chr = "all";
|
|
32262
32673
|
wgFeature.start = offset + f.start;
|
|
@@ -32265,7 +32676,7 @@ class BWSource extends BaseFeatureSource {
|
|
|
32265
32676
|
wgValues.push(wgFeature);
|
|
32266
32677
|
}
|
|
32267
32678
|
wgValues.sort((a, b) => a.start - b.start);
|
|
32268
|
-
this
|
|
32679
|
+
this.#wgValues[windowFunction] = {values: wgValues, bpPerPixel};
|
|
32269
32680
|
return wgValues
|
|
32270
32681
|
}
|
|
32271
32682
|
}
|
|
@@ -32287,658 +32698,780 @@ class BWSource extends BaseFeatureSource {
|
|
|
32287
32698
|
}
|
|
32288
32699
|
}
|
|
32289
32700
|
|
|
32290
|
-
|
|
32291
|
-
|
|
32292
|
-
|
|
32293
|
-
|
|
32294
|
-
* Author: Jim Robinson
|
|
32295
|
-
*
|
|
32296
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32297
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
32298
|
-
* in the Software without restriction, including without limitation the rights
|
|
32299
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32300
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
32301
|
-
* furnished to do so, subject to the following conditions:
|
|
32302
|
-
*
|
|
32303
|
-
* The above copyright notice and this permission notice shall be included in
|
|
32304
|
-
* all copies or substantial portions of the Software.
|
|
32305
|
-
*
|
|
32306
|
-
*
|
|
32307
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32308
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32309
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32310
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32311
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32312
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32313
|
-
* THE SOFTWARE.
|
|
32314
|
-
*/
|
|
32315
|
-
|
|
32316
|
-
const GZIP_FLAG = 0x1;
|
|
32701
|
+
const shim = .01;
|
|
32702
|
+
const colorStripWidth = 4;
|
|
32703
|
+
const axesXOffset = colorStripWidth + 1;
|
|
32704
|
+
function paintAxis(ctx, width, height, colorOrUndefined) {
|
|
32317
32705
|
|
|
32318
|
-
|
|
32706
|
+
if (undefined === this.dataRange || undefined === this.dataRange.max || undefined === this.dataRange.min) {
|
|
32707
|
+
return
|
|
32708
|
+
}
|
|
32319
32709
|
|
|
32320
|
-
|
|
32321
|
-
|
|
32322
|
-
|
|
32323
|
-
this.path = config.url;
|
|
32324
|
-
this.groupCache = {};
|
|
32325
|
-
this.datasetCache = {};
|
|
32710
|
+
IGVGraphics.fillRect(ctx, 0, 0, width, height, { fillStyle: 'white' });
|
|
32711
|
+
if (colorOrUndefined) {
|
|
32712
|
+
IGVGraphics.fillRect(ctx, width - colorStripWidth - 2, 0, colorStripWidth, height, { fillStyle: colorOrUndefined });
|
|
32326
32713
|
}
|
|
32327
32714
|
|
|
32715
|
+
const flipAxis = (undefined === this.flipAxis) ? false : this.flipAxis;
|
|
32328
32716
|
|
|
32329
|
-
|
|
32717
|
+
const xTickStart = 0.95 * width - 8 - axesXOffset;
|
|
32718
|
+
const xTickEnd = 0.95 * width - axesXOffset;
|
|
32330
32719
|
|
|
32331
|
-
|
|
32332
|
-
|
|
32333
|
-
|
|
32720
|
+
const properties =
|
|
32721
|
+
{
|
|
32722
|
+
font: 'normal 10px Arial',
|
|
32723
|
+
textAlign: 'right',
|
|
32724
|
+
fillStyle: 'black',
|
|
32725
|
+
strokeStyle: 'black',
|
|
32726
|
+
};
|
|
32334
32727
|
|
|
32335
|
-
|
|
32336
|
-
|
|
32337
|
-
|
|
32338
|
-
this.version = binaryParser.getInt();
|
|
32339
|
-
this.indexPos = binaryParser.getLong();
|
|
32340
|
-
this.indexSize = binaryParser.getInt();
|
|
32341
|
-
binaryParser.getInt();
|
|
32728
|
+
// tick
|
|
32729
|
+
IGVGraphics.strokeLine(ctx, xTickStart, shim * height, xTickEnd, shim * height, properties);
|
|
32730
|
+
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), xTickStart + 4, shim * height + 12, properties);
|
|
32342
32731
|
|
|
32732
|
+
const y = (1.0 - shim) * height;
|
|
32343
32733
|
|
|
32344
|
-
|
|
32345
|
-
|
|
32346
|
-
|
|
32347
|
-
while (nWindowFunctions-- > 0) {
|
|
32348
|
-
this.windowFunctions.push(binaryParser.getString());
|
|
32349
|
-
}
|
|
32350
|
-
}
|
|
32734
|
+
// tick
|
|
32735
|
+
IGVGraphics.strokeLine(ctx, xTickStart, y, xTickEnd, y, properties);
|
|
32736
|
+
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), xTickStart + 4, y - 4, properties);
|
|
32351
32737
|
|
|
32352
|
-
|
|
32353
|
-
|
|
32738
|
+
// vertical axis
|
|
32739
|
+
IGVGraphics.strokeLine(ctx, xTickEnd, shim * height, xTickEnd, y, properties);
|
|
32354
32740
|
|
|
32355
|
-
|
|
32356
|
-
this.trackNames = [];
|
|
32357
|
-
while (nTracks-- > 0) {
|
|
32358
|
-
this.trackNames.push(binaryParser.getString());
|
|
32359
|
-
}
|
|
32360
|
-
this.genomeID = binaryParser.getString();
|
|
32361
|
-
this.flags = binaryParser.getInt();
|
|
32362
|
-
this.compressed = (this.flags & GZIP_FLAG) !== 0;
|
|
32741
|
+
function prettyPrint(number) {
|
|
32363
32742
|
|
|
32364
|
-
|
|
32365
|
-
|
|
32366
|
-
|
|
32367
|
-
|
|
32368
|
-
|
|
32369
|
-
|
|
32370
|
-
}))
|
|
32371
|
-
|
|
32372
|
-
|
|
32373
|
-
|
|
32374
|
-
while (nEntries-- > 0) {
|
|
32375
|
-
const name = binaryParser.getString();
|
|
32376
|
-
const pos = binaryParser.getLong();
|
|
32377
|
-
const size = binaryParser.getInt();
|
|
32378
|
-
this.datasetIndex[name] = {position: pos, size: size};
|
|
32743
|
+
if (number === 0) {
|
|
32744
|
+
return "0"
|
|
32745
|
+
} else if (Math.abs(number) >= 10) {
|
|
32746
|
+
return number.toFixed()
|
|
32747
|
+
} else if (Math.abs(number) >= 1) {
|
|
32748
|
+
return number.toFixed(1)
|
|
32749
|
+
} else if (Math.abs(number) >= 0.1) {
|
|
32750
|
+
return number.toFixed(2)
|
|
32751
|
+
} else {
|
|
32752
|
+
return number.toExponential(1)
|
|
32379
32753
|
}
|
|
32754
|
+
}
|
|
32755
|
+
}
|
|
32380
32756
|
|
|
32381
|
-
|
|
32382
|
-
nEntries = binaryParser.getInt();
|
|
32383
|
-
while (nEntries-- > 0) {
|
|
32384
|
-
const name = binaryParser.getString();
|
|
32385
|
-
const pos = binaryParser.getLong();
|
|
32386
|
-
const size = binaryParser.getInt();
|
|
32387
|
-
this.groupIndex[name] = {position: pos, size: size};
|
|
32388
|
-
}
|
|
32757
|
+
const DEFAULT_COLOR$2 = 'rgb(150, 150, 150)';
|
|
32389
32758
|
|
|
32390
|
-
|
|
32759
|
+
|
|
32760
|
+
class WigTrack extends TrackBase {
|
|
32761
|
+
|
|
32762
|
+
static defaults = {
|
|
32763
|
+
height: 50,
|
|
32764
|
+
flipAxis: false,
|
|
32765
|
+
logScale: false,
|
|
32766
|
+
windowFunction: 'mean',
|
|
32767
|
+
graphType: 'bar',
|
|
32768
|
+
normalize: undefined,
|
|
32769
|
+
scaleFactor: undefined,
|
|
32770
|
+
overflowColor: `rgb(255, 32, 255)`,
|
|
32771
|
+
baselineColor: 'lightGray',
|
|
32772
|
+
summarize: true
|
|
32391
32773
|
}
|
|
32392
32774
|
|
|
32393
|
-
|
|
32775
|
+
constructor(config, browser) {
|
|
32776
|
+
super(config, browser);
|
|
32777
|
+
}
|
|
32394
32778
|
|
|
32395
|
-
|
|
32779
|
+
init(config) {
|
|
32396
32780
|
|
|
32397
|
-
|
|
32398
|
-
|
|
32781
|
+
super.init(config);
|
|
32782
|
+
|
|
32783
|
+
this.type = "wig";
|
|
32784
|
+
this.featureType = 'numeric';
|
|
32785
|
+
this.resolutionAware = true;
|
|
32786
|
+
this.paintAxis = paintAxis;
|
|
32399
32787
|
|
|
32788
|
+
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
32789
|
+
if (config.featureSource) {
|
|
32790
|
+
this.featureSource = config.featureSource;
|
|
32791
|
+
delete config.featureSource;
|
|
32792
|
+
} else if ("bigwig" === format) {
|
|
32793
|
+
this.featureSource = new BWSource(config, this.browser.genome);
|
|
32794
|
+
} else if ("tdf" === format) {
|
|
32795
|
+
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
32400
32796
|
} else {
|
|
32401
|
-
|
|
32402
|
-
|
|
32403
|
-
const zoomString = (chr.toLowerCase() === "all" || zoom === undefined) ? "0" : zoom.toString();
|
|
32797
|
+
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
32798
|
+
}
|
|
32404
32799
|
|
|
32405
|
-
let dsName;
|
|
32406
|
-
if (windowFunction === "raw") {
|
|
32407
|
-
dsName = "/" + chr + "/raw";
|
|
32408
|
-
} else {
|
|
32409
|
-
dsName = "/" + chr + "/z" + zoomString + wf;
|
|
32410
|
-
}
|
|
32411
|
-
const indexEntry = this.datasetIndex[dsName];
|
|
32412
32800
|
|
|
32413
|
-
|
|
32414
|
-
|
|
32415
|
-
|
|
32801
|
+
// Override autoscale default
|
|
32802
|
+
if (config.max === undefined || config.autoscale === true) {
|
|
32803
|
+
this.autoscale = true;
|
|
32804
|
+
} else {
|
|
32805
|
+
this.dataRange = {
|
|
32806
|
+
min: config.min || 0,
|
|
32807
|
+
max: config.max
|
|
32808
|
+
};
|
|
32809
|
+
}
|
|
32810
|
+
}
|
|
32416
32811
|
|
|
32417
|
-
|
|
32418
|
-
|
|
32419
|
-
|
|
32420
|
-
|
|
32421
|
-
|
|
32422
|
-
}));
|
|
32812
|
+
async postInit() {
|
|
32813
|
+
const header = await this.getHeader();
|
|
32814
|
+
if (this.disposed) return // This track was removed during async load
|
|
32815
|
+
if (header) this.setTrackProperties(header);
|
|
32816
|
+
}
|
|
32423
32817
|
|
|
32424
|
-
|
|
32425
|
-
return undefined
|
|
32426
|
-
}
|
|
32818
|
+
async getFeatures(chr, start, end, bpPerPixel) {
|
|
32427
32819
|
|
|
32428
|
-
|
|
32429
|
-
|
|
32430
|
-
|
|
32431
|
-
|
|
32432
|
-
|
|
32820
|
+
const windowFunction = this.windowFunction;
|
|
32821
|
+
|
|
32822
|
+
const features = await this.featureSource.getFeatures({
|
|
32823
|
+
chr,
|
|
32824
|
+
start,
|
|
32825
|
+
end,
|
|
32826
|
+
bpPerPixel,
|
|
32827
|
+
visibilityWindow: this.visibilityWindow,
|
|
32828
|
+
windowFunction
|
|
32829
|
+
});
|
|
32830
|
+
if (this.normalize && this.featureSource.normalizationFactor) {
|
|
32831
|
+
const scaleFactor = this.featureSource.normalizationFactor;
|
|
32832
|
+
for (let f of features) {
|
|
32833
|
+
f.value *= scaleFactor;
|
|
32433
32834
|
}
|
|
32434
|
-
|
|
32435
|
-
|
|
32436
|
-
|
|
32437
|
-
|
|
32438
|
-
|
|
32439
|
-
tiles.push({position: binaryParser.getLong(), size: binaryParser.getInt()});
|
|
32835
|
+
}
|
|
32836
|
+
if (this.scaleFactor) {
|
|
32837
|
+
const scaleFactor = this.scaleFactor;
|
|
32838
|
+
for (let f of features) {
|
|
32839
|
+
f.value *= scaleFactor;
|
|
32440
32840
|
}
|
|
32841
|
+
}
|
|
32441
32842
|
|
|
32442
|
-
|
|
32443
|
-
|
|
32444
|
-
|
|
32445
|
-
|
|
32446
|
-
|
|
32447
|
-
|
|
32448
|
-
|
|
32843
|
+
// Summarize features to current resolution. This needs to be done here, rather than in the "draw" function,
|
|
32844
|
+
// for group autoscale to work.
|
|
32845
|
+
if (this.summarize && ("mean" === windowFunction || "min" === windowFunction || "max" === windowFunction)) {
|
|
32846
|
+
return summarizeData(features, start, bpPerPixel, windowFunction)
|
|
32847
|
+
} else {
|
|
32848
|
+
return features
|
|
32849
|
+
}
|
|
32850
|
+
}
|
|
32449
32851
|
|
|
32450
|
-
|
|
32451
|
-
|
|
32852
|
+
menuItemList() {
|
|
32853
|
+
const items = [];
|
|
32854
|
+
|
|
32855
|
+
if (this.flipAxis !== undefined) {
|
|
32856
|
+
items.push('<hr>');
|
|
32857
|
+
|
|
32858
|
+
function click() {
|
|
32859
|
+
this.flipAxis = !this.flipAxis;
|
|
32860
|
+
this.trackView.repaintViews();
|
|
32861
|
+
}
|
|
32862
|
+
|
|
32863
|
+
items.push({label: 'Flip y-axis', click});
|
|
32452
32864
|
}
|
|
32865
|
+
|
|
32866
|
+
if(this.featureSource.windowFunctions) {
|
|
32867
|
+
items.push(...this.wigSummarizationItems());
|
|
32868
|
+
}
|
|
32869
|
+
|
|
32870
|
+
items.push(...this.numericDataMenuItems());
|
|
32871
|
+
|
|
32872
|
+
return items
|
|
32453
32873
|
}
|
|
32454
32874
|
|
|
32455
|
-
|
|
32875
|
+
wigSummarizationItems() {
|
|
32456
32876
|
|
|
32457
|
-
const
|
|
32458
|
-
const rootGroup = this.groupCache["/"];
|
|
32459
|
-
if (rootGroup) {
|
|
32460
|
-
return rootGroup
|
|
32461
|
-
} else {
|
|
32877
|
+
const windowFunctions = this.featureSource.windowFunctions;
|
|
32462
32878
|
|
|
32463
|
-
|
|
32464
|
-
|
|
32465
|
-
|
|
32879
|
+
const menuItems = [];
|
|
32880
|
+
menuItems.push('<hr/>');
|
|
32881
|
+
menuItems.push("<div>Windowing function</div>");
|
|
32882
|
+
for (const wf of windowFunctions) {
|
|
32883
|
+
const object = $$1(createCheckbox(wf, this.windowFunction === wf));
|
|
32466
32884
|
|
|
32467
|
-
|
|
32468
|
-
|
|
32469
|
-
this.
|
|
32885
|
+
function clickHandler() {
|
|
32886
|
+
this.windowFunction = wf;
|
|
32887
|
+
this.trackView.updateViews();
|
|
32470
32888
|
}
|
|
32471
32889
|
|
|
32472
|
-
|
|
32473
|
-
|
|
32474
|
-
group.totalCount = Number(totalCountString);
|
|
32475
|
-
}
|
|
32890
|
+
menuItems.push({object, click: clickHandler});
|
|
32891
|
+
}
|
|
32476
32892
|
|
|
32477
|
-
|
|
32478
|
-
|
|
32479
|
-
if (names) {
|
|
32480
|
-
names.split(",").forEach(function (chr) {
|
|
32481
|
-
const canonicalName = genome.getChromosomeName(chr);
|
|
32482
|
-
chrAliasTable[canonicalName] = chr;
|
|
32483
|
-
});
|
|
32484
|
-
}
|
|
32485
|
-
this.chrAliasTable = chrAliasTable;
|
|
32893
|
+
return menuItems
|
|
32894
|
+
}
|
|
32486
32895
|
|
|
32487
|
-
|
|
32488
|
-
|
|
32896
|
+
|
|
32897
|
+
async getHeader() {
|
|
32898
|
+
|
|
32899
|
+
if (typeof this.featureSource.getHeader === "function") {
|
|
32900
|
+
this.header = await this.featureSource.getHeader();
|
|
32489
32901
|
}
|
|
32902
|
+
return this.header
|
|
32490
32903
|
}
|
|
32491
32904
|
|
|
32492
|
-
|
|
32905
|
+
// TODO: refactor to igvUtils.js
|
|
32906
|
+
getScaleFactor(min, max, height, logScale) {
|
|
32907
|
+
const minValue = (logScale === true) ? ((min < 0) ? -Math.log10(Math.abs(min) + 1) : Math.log10(Math.abs(min) + 1)) : min;
|
|
32908
|
+
const maxValue = (logScale === true) ? Math.log10(Math.abs(max) + 1) : max;
|
|
32909
|
+
const scale = height / (maxValue - minValue);
|
|
32910
|
+
return scale
|
|
32911
|
+
}
|
|
32493
32912
|
|
|
32494
|
-
|
|
32495
|
-
|
|
32496
|
-
|
|
32497
|
-
} else {
|
|
32913
|
+
computeYPixelValue(yValue, yScaleFactor) {
|
|
32914
|
+
return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
|
|
32915
|
+
}
|
|
32498
32916
|
|
|
32499
|
-
|
|
32500
|
-
|
|
32501
|
-
|
|
32502
|
-
|
|
32503
|
-
|
|
32917
|
+
computeYPixelValueInLogScale(yValue, yScaleFactor) {
|
|
32918
|
+
let maxValue = this.dataRange.max;
|
|
32919
|
+
let minValue = this.dataRange.min;
|
|
32920
|
+
minValue = (minValue < 0) ? -Math.log10(Math.abs(minValue) + 1) : Math.log10(Math.abs(minValue) + 1);
|
|
32921
|
+
maxValue = (maxValue < 0) ? -Math.log10(Math.abs(maxValue) + 1) : Math.log10(Math.abs(maxValue) + 1);
|
|
32922
|
+
|
|
32923
|
+
yValue = (yValue < 0) ? -Math.log10(Math.abs(yValue) +1) : Math.log10(yValue + 1);
|
|
32924
|
+
return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
|
|
32925
|
+
}
|
|
32504
32926
|
|
|
32505
|
-
|
|
32506
|
-
|
|
32507
|
-
|
|
32508
|
-
|
|
32927
|
+
draw(options) {
|
|
32928
|
+
|
|
32929
|
+
const features = options.features;
|
|
32930
|
+
const ctx = options.context;
|
|
32931
|
+
const bpPerPixel = options.bpPerPixel;
|
|
32932
|
+
const bpStart = options.bpStart;
|
|
32933
|
+
const pixelWidth = options.pixelWidth;
|
|
32934
|
+
const pixelHeight = options.pixelHeight - 1;
|
|
32935
|
+
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
32936
|
+
|
|
32937
|
+
const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, pixelHeight, this.logScale);
|
|
32938
|
+
const yScale = (yValue) => this.logScale
|
|
32939
|
+
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
|
|
32940
|
+
: this.computeYPixelValue(yValue, scaleFactor);
|
|
32941
|
+
|
|
32942
|
+
if (features && features.length > 0) {
|
|
32943
|
+
|
|
32944
|
+
if (this.dataRange.min === undefined) this.dataRange.min = 0;
|
|
32945
|
+
|
|
32946
|
+
// Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
|
|
32947
|
+
// nothing to paint.
|
|
32948
|
+
if (this.dataRange.max > this.dataRange.min) {
|
|
32949
|
+
|
|
32950
|
+
let lastPixelEnd = -1;
|
|
32951
|
+
let lastY;
|
|
32952
|
+
const y0 = yScale(0);
|
|
32953
|
+
|
|
32954
|
+
for (let f of features) {
|
|
32955
|
+
|
|
32956
|
+
if (f.end < bpStart) continue
|
|
32957
|
+
if (f.start > bpEnd) break
|
|
32958
|
+
|
|
32959
|
+
const x = (f.start - bpStart) / bpPerPixel;
|
|
32960
|
+
if (isNaN(x)) continue
|
|
32961
|
+
|
|
32962
|
+
let y = yScale(f.value);
|
|
32963
|
+
|
|
32964
|
+
const rectEnd = (f.end - bpStart) / bpPerPixel;
|
|
32965
|
+
const width = rectEnd - x;
|
|
32966
|
+
|
|
32967
|
+
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
32968
|
+
|
|
32969
|
+
if (this.graphType === "line") {
|
|
32970
|
+
if (lastY !== undefined) {
|
|
32971
|
+
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
|
|
32972
|
+
"fillStyle": color,
|
|
32973
|
+
"strokeStyle": color
|
|
32974
|
+
});
|
|
32975
|
+
}
|
|
32976
|
+
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
32977
|
+
} else if (this.graphType === "points") {
|
|
32978
|
+
const pointSize = this.config.pointSize || 3;
|
|
32979
|
+
const px = x + width / 2;
|
|
32980
|
+
IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
|
|
32981
|
+
|
|
32982
|
+
if (f.value > this.dataRange.max) {
|
|
32983
|
+
IGVGraphics.fillCircle(ctx, px, pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
32984
|
+
} else if (f.value < this.dataRange.min) {
|
|
32985
|
+
IGVGraphics.fillCircle(ctx, px, pixelHeight - pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
32986
|
+
}
|
|
32987
|
+
|
|
32988
|
+
} else {
|
|
32989
|
+
// Default graph type (bar)
|
|
32990
|
+
const height = Math.min(pixelHeight, y - y0);
|
|
32991
|
+
IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
|
|
32992
|
+
if (f.value > this.dataRange.max) {
|
|
32993
|
+
IGVGraphics.fillRect(ctx, x, 0, width, 3, {fillStyle: this.overflowColor});
|
|
32994
|
+
} else if (f.value < this.dataRange.min) {
|
|
32995
|
+
IGVGraphics.fillRect(ctx, x, pixelHeight - 2, width, 3, {fillStyle: this.overflowColor});
|
|
32996
|
+
}
|
|
32997
|
+
|
|
32998
|
+
}
|
|
32999
|
+
lastPixelEnd = x + width;
|
|
33000
|
+
lastY = y;
|
|
32509
33001
|
}
|
|
32510
|
-
}));
|
|
32511
33002
|
|
|
32512
|
-
|
|
32513
|
-
|
|
33003
|
+
// If the track includes negative values draw a baseline
|
|
33004
|
+
if (this.dataRange.min < 0) {
|
|
33005
|
+
let maxValue = this.dataRange.max;
|
|
33006
|
+
let minValue = this.dataRange.min;
|
|
33007
|
+
minValue = (this.logScale === true) ? ((minValue < 0) ? -Math.log10(Math.abs(minValue) + 1) : Math.log10(Math.abs(minValue) + 1)) : minValue;
|
|
33008
|
+
maxValue = (this.logScale === true) ? ((maxValue < 0) ? -Math.log10(Math.abs(maxValue) + 1) : Math.log10(Math.abs(maxValue) + 1)) : maxValue;
|
|
33009
|
+
const ratio = maxValue / (maxValue - minValue);
|
|
33010
|
+
const basepx = this.flipAxis ? (1 - ratio) * pixelHeight : ratio * pixelHeight;
|
|
33011
|
+
IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {strokeStyle: this.baselineColor});
|
|
33012
|
+
}
|
|
32514
33013
|
}
|
|
33014
|
+
}
|
|
32515
33015
|
|
|
32516
|
-
|
|
32517
|
-
|
|
32518
|
-
let
|
|
32519
|
-
|
|
32520
|
-
|
|
32521
|
-
|
|
32522
|
-
|
|
33016
|
+
// Draw guidelines
|
|
33017
|
+
if (this.config.hasOwnProperty('guideLines')) {
|
|
33018
|
+
for (let line of this.config.guideLines) {
|
|
33019
|
+
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
|
|
33020
|
+
let y = yScale(line.y);
|
|
33021
|
+
let props = {
|
|
33022
|
+
'strokeStyle': line['color'],
|
|
33023
|
+
'strokeWidth': 2
|
|
33024
|
+
};
|
|
33025
|
+
if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);
|
|
33026
|
+
else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
|
|
33027
|
+
}
|
|
32523
33028
|
}
|
|
32524
|
-
this.groupCache[name] = group;
|
|
32525
|
-
return group
|
|
32526
33029
|
}
|
|
32527
33030
|
}
|
|
32528
33031
|
|
|
32529
|
-
|
|
33032
|
+
popupData(clickState, features) {
|
|
32530
33033
|
|
|
32531
|
-
|
|
32532
|
-
return a.position - b.position
|
|
32533
|
-
});
|
|
33034
|
+
if (features === undefined) features = this.clickedFeatures(clickState);
|
|
32534
33035
|
|
|
32535
|
-
|
|
32536
|
-
return idx.size > 0
|
|
32537
|
-
});
|
|
33036
|
+
if (features && features.length > 0) {
|
|
32538
33037
|
|
|
32539
|
-
|
|
32540
|
-
|
|
32541
|
-
}
|
|
33038
|
+
const genomicLocation = clickState.genomicLocation;
|
|
33039
|
+
const popupData = [];
|
|
32542
33040
|
|
|
32543
|
-
|
|
33041
|
+
// Sort features based on distance from click
|
|
33042
|
+
features.sort(function (a, b) {
|
|
33043
|
+
const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
|
|
33044
|
+
const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
|
|
33045
|
+
return distA - distB
|
|
33046
|
+
});
|
|
32544
33047
|
|
|
32545
|
-
|
|
33048
|
+
// Display closest 10
|
|
33049
|
+
const displayFeatures = features.length > 10 ? features.slice(0, 10) : features;
|
|
32546
33050
|
|
|
32547
|
-
|
|
33051
|
+
// Resort in ascending order
|
|
33052
|
+
displayFeatures.sort(function (a, b) {
|
|
33053
|
+
return a.start - b.start
|
|
33054
|
+
});
|
|
32548
33055
|
|
|
32549
|
-
|
|
32550
|
-
|
|
32551
|
-
|
|
32552
|
-
|
|
33056
|
+
for (let selectedFeature of displayFeatures) {
|
|
33057
|
+
if (selectedFeature) {
|
|
33058
|
+
if (popupData.length > 0) {
|
|
33059
|
+
popupData.push('<hr/>');
|
|
33060
|
+
}
|
|
33061
|
+
let posString = (selectedFeature.end - selectedFeature.start) === 1 ?
|
|
33062
|
+
numberFormatter$1(Math.floor(selectedFeature.start) + 1)
|
|
33063
|
+
: numberFormatter$1(Math.floor(selectedFeature.start) + 1) + "-" + numberFormatter$1(Math.floor(selectedFeature.end));
|
|
33064
|
+
popupData.push({name: "Position:", value: posString});
|
|
33065
|
+
popupData.push({
|
|
33066
|
+
name: "Value: ",
|
|
33067
|
+
value: numberFormatter$1(selectedFeature.value.toFixed(4))
|
|
33068
|
+
});
|
|
32553
33069
|
}
|
|
32554
|
-
}));
|
|
32555
|
-
|
|
32556
|
-
const tileData = this.compressed ? inflate_1$3(data).buffer : data;
|
|
32557
|
-
|
|
32558
|
-
const binaryParser = new BinaryParser$1(new DataView(tileData));
|
|
32559
|
-
const type = binaryParser.getString();
|
|
32560
|
-
let tile;
|
|
32561
|
-
switch (type) {
|
|
32562
|
-
case "fixedStep":
|
|
32563
|
-
tile = createFixedStep(binaryParser, nTracks);
|
|
32564
|
-
break
|
|
32565
|
-
case "variableStep":
|
|
32566
|
-
tile = createVariableStep(binaryParser, nTracks);
|
|
32567
|
-
break
|
|
32568
|
-
case "bed":
|
|
32569
|
-
case "bedWithName":
|
|
32570
|
-
tile = createBed(binaryParser, nTracks, type);
|
|
32571
|
-
break
|
|
32572
|
-
default:
|
|
32573
|
-
throw "Unknown tile type: " + type
|
|
32574
33070
|
}
|
|
32575
|
-
|
|
33071
|
+
if (displayFeatures.length < features.length) {
|
|
33072
|
+
popupData.push("<hr/>...");
|
|
33073
|
+
}
|
|
32576
33074
|
|
|
33075
|
+
return popupData
|
|
33076
|
+
|
|
33077
|
+
} else {
|
|
33078
|
+
return []
|
|
32577
33079
|
}
|
|
32578
|
-
return tiles
|
|
32579
33080
|
}
|
|
32580
33081
|
|
|
32581
|
-
|
|
33082
|
+
get supportsWholeGenome() {
|
|
33083
|
+
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
33084
|
+
}
|
|
32582
33085
|
|
|
32583
|
-
|
|
32584
|
-
|
|
32585
|
-
|
|
32586
|
-
|
|
32587
|
-
|
|
32588
|
-
}));
|
|
33086
|
+
/**
|
|
33087
|
+
* Return color for feature.
|
|
33088
|
+
* @param feature
|
|
33089
|
+
* @returns {string}
|
|
33090
|
+
*/
|
|
32589
33091
|
|
|
32590
|
-
|
|
32591
|
-
|
|
32592
|
-
|
|
32593
|
-
|
|
33092
|
+
getColorForFeature(f) {
|
|
33093
|
+
let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$2;
|
|
33094
|
+
return (typeof c === "function") ? c(f.value) : c
|
|
33095
|
+
}
|
|
32594
33096
|
|
|
32595
|
-
|
|
32596
|
-
|
|
32597
|
-
|
|
32598
|
-
|
|
32599
|
-
|
|
32600
|
-
|
|
32601
|
-
|
|
32602
|
-
|
|
32603
|
-
|
|
32604
|
-
|
|
33097
|
+
/**
|
|
33098
|
+
* Called when the track is removed. Do any needed cleanup here
|
|
33099
|
+
*/
|
|
33100
|
+
dispose() {
|
|
33101
|
+
this.trackView = undefined;
|
|
33102
|
+
}
|
|
33103
|
+
|
|
33104
|
+
}
|
|
33105
|
+
|
|
33106
|
+
/**
|
|
33107
|
+
* Summarize wig data in bins of size "bpPerPixel" with the given window function.
|
|
33108
|
+
*
|
|
33109
|
+
* @param features wig (numeric) data -- features cannot overlap, and are in ascending order by start position
|
|
33110
|
+
* @param startBP bp start position for computing binned data
|
|
33111
|
+
* @param bpPerPixel bp per pixel (bin)
|
|
33112
|
+
* @param windowFunction mean, min, or max
|
|
33113
|
+
* @returns {*|*[]}
|
|
33114
|
+
*/
|
|
33115
|
+
function summarizeData(features, startBP, bpPerPixel, windowFunction = "mean") {
|
|
33116
|
+
|
|
33117
|
+
if (bpPerPixel <= 1 || !features || features.length === 0) {
|
|
33118
|
+
return features
|
|
33119
|
+
}
|
|
33120
|
+
|
|
33121
|
+
// Assume features are sorted by position. Wig features cannot overlap. Note, UCSC "reductionLevel" == bpPerPixel
|
|
33122
|
+
const chr = features[0].chr;
|
|
33123
|
+
const binSize = bpPerPixel;
|
|
33124
|
+
const summaryFeatures = [];
|
|
33125
|
+
|
|
33126
|
+
const finishBin = (bin) => {
|
|
33127
|
+
const start = startBP + bin.bin * binSize;
|
|
33128
|
+
const end = start + binSize;
|
|
33129
|
+
let value;
|
|
33130
|
+
switch (windowFunction) {
|
|
33131
|
+
case "mean":
|
|
33132
|
+
value = bin.sumData / bin.count;
|
|
33133
|
+
break
|
|
33134
|
+
case "max":
|
|
33135
|
+
value = bin.max;
|
|
33136
|
+
break
|
|
33137
|
+
case "min":
|
|
33138
|
+
value = bin.min;
|
|
33139
|
+
break
|
|
32605
33140
|
default:
|
|
32606
|
-
throw
|
|
33141
|
+
throw Error(`Unknown window function: ${windowFunction}`)
|
|
33142
|
+
}
|
|
33143
|
+
const description = `${windowFunction} of ${bin.count} values`;
|
|
33144
|
+
summaryFeatures.push({chr, start, end, value, description});
|
|
33145
|
+
};
|
|
33146
|
+
|
|
33147
|
+
let currentBinData;
|
|
33148
|
+
for (let f of features) {
|
|
33149
|
+
|
|
33150
|
+
// Loop through bins this feature overlaps, updating the weighted sum for each bin or min/max,
|
|
33151
|
+
// depending on window function
|
|
33152
|
+
let startBin = Math.floor((f.start - startBP) / binSize);
|
|
33153
|
+
const endBin = Math.floor((f.end - startBP) / binSize);
|
|
33154
|
+
|
|
33155
|
+
if (currentBinData && startBin === currentBinData.bin) {
|
|
33156
|
+
currentBinData.add(f);
|
|
33157
|
+
startBin++;
|
|
32607
33158
|
}
|
|
32608
|
-
}
|
|
32609
33159
|
|
|
32610
|
-
|
|
33160
|
+
if (!currentBinData || endBin > currentBinData.bin) {
|
|
32611
33161
|
|
|
32612
|
-
|
|
32613
|
-
|
|
32614
|
-
|
|
32615
|
-
const span = binaryParser.getFloat();
|
|
33162
|
+
if(currentBinData) {
|
|
33163
|
+
finishBin(currentBinData);
|
|
33164
|
+
}
|
|
32616
33165
|
|
|
32617
|
-
|
|
32618
|
-
|
|
32619
|
-
|
|
32620
|
-
|
|
32621
|
-
|
|
32622
|
-
|
|
32623
|
-
|
|
33166
|
+
// Feature stretches across multiple bins.
|
|
33167
|
+
if (endBin > startBin) {
|
|
33168
|
+
const end = startBP + endBin * binSize;
|
|
33169
|
+
summaryFeatures.push({chr, start: f.start, end, value: f.value});
|
|
33170
|
+
}
|
|
33171
|
+
|
|
33172
|
+
currentBinData = new SummaryBinData(endBin, f);
|
|
32624
33173
|
}
|
|
32625
|
-
|
|
33174
|
+
|
|
33175
|
+
}
|
|
33176
|
+
if(currentBinData) {
|
|
33177
|
+
finishBin(currentBinData);
|
|
32626
33178
|
}
|
|
32627
33179
|
|
|
32628
|
-
|
|
32629
|
-
|
|
32630
|
-
|
|
32631
|
-
|
|
32632
|
-
|
|
32633
|
-
|
|
32634
|
-
|
|
33180
|
+
// Consolidate
|
|
33181
|
+
const c = [];
|
|
33182
|
+
let lastFeature = summaryFeatures[0];
|
|
33183
|
+
for (let f of summaryFeatures) {
|
|
33184
|
+
if (lastFeature.value === f.value && f.start <= lastFeature.end) {
|
|
33185
|
+
lastFeature.end = f.end;
|
|
33186
|
+
} else {
|
|
33187
|
+
c.push(lastFeature);
|
|
33188
|
+
lastFeature = f;
|
|
33189
|
+
}
|
|
32635
33190
|
}
|
|
32636
|
-
|
|
33191
|
+
c.push(lastFeature);
|
|
32637
33192
|
|
|
32638
|
-
|
|
33193
|
+
return c
|
|
32639
33194
|
|
|
32640
|
-
|
|
32641
|
-
const span = binaryParser.getFloat();
|
|
32642
|
-
const nPositions = binaryParser.getInt();
|
|
32643
|
-
const start = [];
|
|
33195
|
+
}
|
|
32644
33196
|
|
|
32645
|
-
|
|
32646
|
-
|
|
32647
|
-
|
|
33197
|
+
class SummaryBinData {
|
|
33198
|
+
constructor(bin, feature) {
|
|
33199
|
+
this.bin = bin;
|
|
33200
|
+
this.sumData = feature.value;
|
|
33201
|
+
this.count = 1;
|
|
33202
|
+
this.min = feature.value;
|
|
33203
|
+
this.max = feature.value;
|
|
32648
33204
|
}
|
|
32649
|
-
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
32650
33205
|
|
|
32651
|
-
|
|
32652
|
-
|
|
32653
|
-
|
|
32654
|
-
|
|
32655
|
-
|
|
32656
|
-
while (np-- > 0) {
|
|
32657
|
-
dtrack.push(binaryParser.getFloat());
|
|
32658
|
-
}
|
|
32659
|
-
data.push(dtrack);
|
|
33206
|
+
add(feature) {
|
|
33207
|
+
this.sumData += feature.value;
|
|
33208
|
+
this.max = Math.max(feature.value, this.max);
|
|
33209
|
+
this.min = Math.min(feature.value, this.min);
|
|
33210
|
+
this.count++;
|
|
32660
33211
|
}
|
|
32661
33212
|
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
tileStart: tileStart,
|
|
32665
|
-
span: span,
|
|
32666
|
-
start: start,
|
|
32667
|
-
data: data,
|
|
32668
|
-
nTracks: nTracks,
|
|
32669
|
-
nPositions: nPositions
|
|
33213
|
+
get mean() {
|
|
33214
|
+
return this.sumData / this.count
|
|
32670
33215
|
}
|
|
32671
33216
|
}
|
|
32672
33217
|
|
|
32673
|
-
|
|
33218
|
+
const DEFAULT_MAX_WG_COUNT = 10000;
|
|
32674
33219
|
|
|
32675
|
-
|
|
33220
|
+
/**
|
|
33221
|
+
* feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
|
|
33222
|
+
*
|
|
33223
|
+
* @param config
|
|
33224
|
+
* @constructor
|
|
33225
|
+
*/
|
|
33226
|
+
class TextFeatureSource extends BaseFeatureSource {
|
|
32676
33227
|
|
|
32677
|
-
|
|
32678
|
-
const start = [];
|
|
32679
|
-
while (n-- > 0) {
|
|
32680
|
-
start.push(binaryParser.getInt());
|
|
32681
|
-
}
|
|
33228
|
+
constructor(config, genome) {
|
|
32682
33229
|
|
|
32683
|
-
|
|
32684
|
-
const end = [];
|
|
32685
|
-
while (n-- > 0) {
|
|
32686
|
-
end.push(binaryParser.getInt());
|
|
32687
|
-
}
|
|
33230
|
+
super(genome);
|
|
32688
33231
|
|
|
32689
|
-
|
|
32690
|
-
|
|
32691
|
-
|
|
32692
|
-
|
|
32693
|
-
|
|
32694
|
-
const dtrack = [];
|
|
32695
|
-
while (np-- > 0) {
|
|
32696
|
-
dtrack.push(binaryParser.getFloat());
|
|
32697
|
-
}
|
|
32698
|
-
data.push(dtrack);
|
|
32699
|
-
}
|
|
33232
|
+
this.config = config || {};
|
|
33233
|
+
this.genome = genome;
|
|
33234
|
+
this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
|
|
33235
|
+
this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
|
|
33236
|
+
this.windowFunctions = ["mean", "min", "max", "none"];
|
|
32700
33237
|
|
|
32701
|
-
|
|
32702
|
-
|
|
32703
|
-
|
|
32704
|
-
|
|
32705
|
-
|
|
33238
|
+
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
33239
|
+
|
|
33240
|
+
this.queryable = config.indexURL || config.queryable === true; // False by default, unless explicitly set
|
|
33241
|
+
if (config.reader) {
|
|
33242
|
+
// Explicit reader implementation
|
|
33243
|
+
this.reader = config.reader;
|
|
33244
|
+
this.queryable = config.queryable !== false;
|
|
33245
|
+
} else if (config.sourceType === "ga4gh") {
|
|
33246
|
+
throw Error("Unsupported source type 'ga4gh'")
|
|
33247
|
+
} else if ((config.type === "eqtl" || config.type === "qtl") && config.sourceType === "gtex-ws") {
|
|
33248
|
+
this.reader = new GtexReader(config);
|
|
33249
|
+
this.queryable = true;
|
|
33250
|
+
} else if ("htsget" === config.sourceType) {
|
|
33251
|
+
this.reader = new HtsgetVariantReader(config, genome);
|
|
33252
|
+
this.queryable = true;
|
|
33253
|
+
} else if (config.sourceType === 'ucscservice') {
|
|
33254
|
+
this.reader = new UCSCServiceReader(config.source);
|
|
33255
|
+
this.queryable = true;
|
|
33256
|
+
} else if (config.sourceType === 'custom') {
|
|
33257
|
+
this.reader = new CustomServiceReader(config.source);
|
|
33258
|
+
this.queryable = false !== config.source.queryable;
|
|
33259
|
+
} else if ('service' === config.sourceType) {
|
|
33260
|
+
this.reader = new FeatureFileReader(config, genome);
|
|
33261
|
+
this.queryable = true;
|
|
33262
|
+
} else {
|
|
33263
|
+
// File of some type (i.e. not a webservice)
|
|
33264
|
+
this.reader = new FeatureFileReader(config, genome);
|
|
33265
|
+
if (config.queryable !== undefined) {
|
|
33266
|
+
this.queryable = config.queryable;
|
|
33267
|
+
} else if (queryableFormats.has(config.format) || this.reader.indexed) {
|
|
33268
|
+
this.queryable = true;
|
|
33269
|
+
} else ;
|
|
32706
33270
|
}
|
|
32707
|
-
}
|
|
32708
33271
|
|
|
32709
|
-
|
|
32710
|
-
|
|
32711
|
-
start: start,
|
|
32712
|
-
end: end,
|
|
32713
|
-
data: data,
|
|
32714
|
-
nTracks: nTracks,
|
|
32715
|
-
nPositions: nPositions
|
|
32716
|
-
}
|
|
32717
|
-
}
|
|
33272
|
+
// Flag indicating if features loaded by this source can be searched for by name or attribute, true by default
|
|
33273
|
+
this.searchable = config.searchable !== false;
|
|
32718
33274
|
|
|
32719
|
-
|
|
33275
|
+
}
|
|
32720
33276
|
|
|
32721
|
-
|
|
32722
|
-
|
|
32723
|
-
|
|
32724
|
-
const t = tiles[i];
|
|
32725
|
-
if (t.position > current.position + current.size) {
|
|
32726
|
-
consolidated.push(current);
|
|
32727
|
-
current = t;
|
|
32728
|
-
} else {
|
|
32729
|
-
current.size = t.position + t.size - current.position;
|
|
33277
|
+
async defaultVisibilityWindow() {
|
|
33278
|
+
if (this.reader && typeof this.reader.defaultVisibilityWindow === 'function') {
|
|
33279
|
+
return this.reader.defaultVisibilityWindow()
|
|
32730
33280
|
}
|
|
32731
33281
|
}
|
|
32732
|
-
consolidated.push(current);
|
|
32733
|
-
return consolidated
|
|
32734
|
-
}
|
|
32735
|
-
|
|
32736
|
-
/*
|
|
32737
|
-
* The MIT License (MIT)
|
|
32738
|
-
*
|
|
32739
|
-
* Copyright (c) 2016 University of California San Diego
|
|
32740
|
-
* Author: Jim Robinson
|
|
32741
|
-
*
|
|
32742
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32743
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
32744
|
-
* in the Software without restriction, including without limitation the rights
|
|
32745
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32746
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
32747
|
-
* furnished to do so, subject to the following conditions:
|
|
32748
|
-
*
|
|
32749
|
-
* The above copyright notice and this permission notice shall be included in
|
|
32750
|
-
* all copies or substantial portions of the Software.
|
|
32751
|
-
*
|
|
32752
|
-
*
|
|
32753
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32754
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32755
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32756
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32757
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32758
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32759
|
-
* THE SOFTWARE.
|
|
32760
|
-
*/
|
|
32761
|
-
|
|
32762
|
-
class TDFSource extends BaseFeatureSource {
|
|
32763
33282
|
|
|
32764
|
-
|
|
32765
|
-
|
|
32766
|
-
|
|
32767
|
-
|
|
32768
|
-
|
|
32769
|
-
|
|
33283
|
+
async trackType() {
|
|
33284
|
+
const header = await this.getHeader();
|
|
33285
|
+
if (header) {
|
|
33286
|
+
return header.type
|
|
33287
|
+
} else {
|
|
33288
|
+
return undefined // Convention for unknown or unspecified
|
|
33289
|
+
}
|
|
32770
33290
|
}
|
|
32771
33291
|
|
|
32772
|
-
async
|
|
33292
|
+
async getHeader() {
|
|
33293
|
+
if (!this.header) {
|
|
32773
33294
|
|
|
32774
|
-
|
|
32775
|
-
|
|
32776
|
-
|
|
32777
|
-
|
|
32778
|
-
|
|
32779
|
-
|
|
32780
|
-
const len = genome.getChromosome(c).bpLength;
|
|
32781
|
-
bpPerPixel = len / 1000;
|
|
32782
|
-
const chrFeatures = await this._getFeatures(c, 0, len, bpPerPixel, windowFunction);
|
|
32783
|
-
if (chrFeatures) {
|
|
32784
|
-
for (let f of chrFeatures) {
|
|
32785
|
-
const wg = Object.assign({}, f);
|
|
32786
|
-
wg.chr = "all";
|
|
32787
|
-
wg.start = genome.getGenomeCoordinate(f.chr, f.start);
|
|
32788
|
-
wg.end = genome.getGenomeCoordinate(f.chr, f.end);
|
|
32789
|
-
wg._f = f;
|
|
32790
|
-
wgFeatures.push(wg);
|
|
32791
|
-
}
|
|
33295
|
+
if (this.reader && typeof this.reader.readHeader === "function") {
|
|
33296
|
+
const header = await this.reader.readHeader();
|
|
33297
|
+
if (header) {
|
|
33298
|
+
this.header = header;
|
|
33299
|
+
if (header.format) {
|
|
33300
|
+
this.config.format = header.format;
|
|
32792
33301
|
}
|
|
33302
|
+
} else {
|
|
33303
|
+
this.header = {};
|
|
32793
33304
|
}
|
|
33305
|
+
} else {
|
|
33306
|
+
this.header = {};
|
|
32794
33307
|
}
|
|
32795
|
-
return wgFeatures
|
|
32796
|
-
|
|
32797
|
-
} else {
|
|
32798
|
-
return this._getFeatures(chr, start, end, bpPerPixel, windowFunction)
|
|
32799
33308
|
}
|
|
33309
|
+
return this.header
|
|
32800
33310
|
}
|
|
32801
|
-
async _getFeatures(chr, start, end, bpPerPixel, windowFunction) {
|
|
32802
|
-
const genomicInterval = new GenomicInterval(chr, start, end);
|
|
32803
|
-
const genome = this.genome;
|
|
32804
33311
|
|
|
33312
|
+
/**
|
|
33313
|
+
* Required function for all data source objects. Fetches features for the
|
|
33314
|
+
* range requested.
|
|
33315
|
+
*
|
|
33316
|
+
* This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
|
|
33317
|
+
* some not.
|
|
33318
|
+
*
|
|
33319
|
+
* @param chr
|
|
33320
|
+
* @param start
|
|
33321
|
+
* @param end
|
|
33322
|
+
* @param bpPerPixel
|
|
33323
|
+
*/
|
|
33324
|
+
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow, windowFunction}) {
|
|
32805
33325
|
|
|
32806
|
-
|
|
32807
|
-
this.rootGroup = await this.reader.readRootGroup();
|
|
32808
|
-
if (!this.normalizationFactor) {
|
|
32809
|
-
const totalCount = this.rootGroup.totalCount;
|
|
32810
|
-
if (totalCount) {
|
|
32811
|
-
this.normalizationFactor = 1.0e6 / totalCount;
|
|
32812
|
-
}
|
|
32813
|
-
}
|
|
32814
|
-
}
|
|
33326
|
+
const isWholeGenome = ("all" === chr.toLowerCase());
|
|
32815
33327
|
|
|
32816
|
-
|
|
32817
|
-
|
|
32818
|
-
let queryChr = this.reader.chrAliasTable[chr];
|
|
32819
|
-
let maxZoom = this.reader.maxZoom;
|
|
32820
|
-
if (queryChr === undefined) queryChr = chr;
|
|
32821
|
-
if (maxZoom === undefined) maxZoom = -1;
|
|
33328
|
+
start = start || 0;
|
|
33329
|
+
end = end || Number.MAX_SAFE_INTEGER;
|
|
32822
33330
|
|
|
32823
|
-
|
|
32824
|
-
|
|
32825
|
-
|
|
32826
|
-
|
|
33331
|
+
// Various conditions that can require a feature load
|
|
33332
|
+
// * view is "whole genome" but no features are loaded
|
|
33333
|
+
// * cache is disabled
|
|
33334
|
+
// * cache does not contain requested range
|
|
33335
|
+
// const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))
|
|
33336
|
+
if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) ||
|
|
33337
|
+
this.config.disableCache ||
|
|
33338
|
+
!this.featureCache ||
|
|
33339
|
+
!this.featureCache.containsRange(new GenomicInterval(chr, start, end))) {
|
|
33340
|
+
await this.loadFeatures(chr, start, end, visibilityWindow);
|
|
32827
33341
|
}
|
|
32828
33342
|
|
|
32829
|
-
|
|
32830
|
-
|
|
32831
|
-
|
|
32832
|
-
|
|
32833
|
-
|
|
32834
|
-
|
|
32835
|
-
|
|
32836
|
-
|
|
32837
|
-
|
|
32838
|
-
|
|
32839
|
-
|
|
32840
|
-
|
|
32841
|
-
decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
|
|
32842
|
-
break
|
|
32843
|
-
case "fixedStep":
|
|
32844
|
-
decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
|
|
32845
|
-
break
|
|
32846
|
-
default:
|
|
32847
|
-
throw ("Unknown tile type: " + tile.type)
|
|
33343
|
+
if (isWholeGenome) {
|
|
33344
|
+
if (!this.wgFeatures) {
|
|
33345
|
+
if (this.supportsWholeGenome()) {
|
|
33346
|
+
if("wig" === this.config.type) {
|
|
33347
|
+
const allWgFeatures = await computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, 1000000);
|
|
33348
|
+
this.wgFeatures = summarizeData(allWgFeatures, 0, bpPerPixel, windowFunction);
|
|
33349
|
+
} else {
|
|
33350
|
+
this.wgFeatures = await computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, this.maxWGCount);
|
|
33351
|
+
}
|
|
33352
|
+
} else {
|
|
33353
|
+
this.wgFeatures = [];
|
|
33354
|
+
}
|
|
32848
33355
|
}
|
|
33356
|
+
return this.wgFeatures
|
|
33357
|
+
} else {
|
|
33358
|
+
return this.featureCache.queryFeatures(chr, start, end)
|
|
32849
33359
|
}
|
|
32850
|
-
features.sort(function (a, b) {
|
|
32851
|
-
return a.start - b.start
|
|
32852
|
-
});
|
|
32853
|
-
|
|
32854
|
-
return features
|
|
32855
33360
|
}
|
|
32856
33361
|
|
|
32857
|
-
|
|
32858
|
-
return
|
|
33362
|
+
async findFeatures(fn) {
|
|
33363
|
+
return this.featureCache ? this.featureCache.findFeatures(fn) : []
|
|
32859
33364
|
}
|
|
32860
33365
|
|
|
32861
|
-
|
|
32862
|
-
return this.
|
|
33366
|
+
supportsWholeGenome() {
|
|
33367
|
+
return !this.queryable // queryable (indexed, web services) sources don't support whole genome view
|
|
32863
33368
|
}
|
|
32864
|
-
}
|
|
32865
33369
|
|
|
32866
|
-
|
|
32867
|
-
|
|
32868
|
-
|
|
32869
|
-
|
|
32870
|
-
|
|
32871
|
-
|
|
32872
|
-
|
|
32873
|
-
const s = starts[i];
|
|
32874
|
-
const e = ends[i];
|
|
32875
|
-
if (e < bpStart) continue
|
|
32876
|
-
if (s > bpEnd) break
|
|
32877
|
-
features.push({
|
|
32878
|
-
chr: chr,
|
|
32879
|
-
start: s,
|
|
32880
|
-
end: e,
|
|
32881
|
-
value: data[i]
|
|
32882
|
-
});
|
|
33370
|
+
// TODO -- experimental, will only work for non-indexed sources
|
|
33371
|
+
getAllFeatures() {
|
|
33372
|
+
if (this.queryable || !this.featureCache) { // queryable sources don't support all features
|
|
33373
|
+
return []
|
|
33374
|
+
} else {
|
|
33375
|
+
return this.featureCache.getAllFeatures()
|
|
33376
|
+
}
|
|
32883
33377
|
}
|
|
32884
|
-
}
|
|
32885
33378
|
|
|
32886
|
-
function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
32887
33379
|
|
|
32888
|
-
|
|
32889
|
-
const starts = tile.start;
|
|
32890
|
-
const span = tile.span;
|
|
32891
|
-
const data = tile.data[0]; // Single track for now
|
|
32892
|
-
for (let i = 0; i < nPositions; i++) {
|
|
32893
|
-
const s = starts[i];
|
|
32894
|
-
const e = s + span;
|
|
32895
|
-
if (e < bpStart) continue
|
|
32896
|
-
if (s > bpEnd) break
|
|
32897
|
-
features.push({
|
|
32898
|
-
chr: chr,
|
|
32899
|
-
start: s,
|
|
32900
|
-
end: e,
|
|
32901
|
-
value: data[i]
|
|
32902
|
-
});
|
|
32903
|
-
}
|
|
32904
|
-
}
|
|
33380
|
+
async loadFeatures(chr, start, end, visibilityWindow) {
|
|
32905
33381
|
|
|
32906
|
-
|
|
33382
|
+
await this.getHeader();
|
|
32907
33383
|
|
|
32908
|
-
|
|
32909
|
-
|
|
32910
|
-
|
|
32911
|
-
const data = tile.data[0]; // Single track for now
|
|
33384
|
+
const reader = this.reader;
|
|
33385
|
+
let intervalStart = start;
|
|
33386
|
+
let intervalEnd = end;
|
|
32912
33387
|
|
|
32913
|
-
|
|
32914
|
-
|
|
32915
|
-
if (
|
|
32916
|
-
|
|
32917
|
-
|
|
32918
|
-
|
|
32919
|
-
|
|
32920
|
-
|
|
32921
|
-
|
|
32922
|
-
|
|
32923
|
-
|
|
33388
|
+
// chr aliasing
|
|
33389
|
+
let queryChr = chr;
|
|
33390
|
+
if (!this.chrAliasManager && this.reader && this.reader.sequenceNames) {
|
|
33391
|
+
this.chrAliasManager = new ChromAliasManager(this.reader.sequenceNames, this.genome);
|
|
33392
|
+
}
|
|
33393
|
+
if (this.chrAliasManager) {
|
|
33394
|
+
queryChr = await this.chrAliasManager.getAliasName(chr);
|
|
33395
|
+
}
|
|
33396
|
+
|
|
33397
|
+
// Use visibility window to potentially expand query interval.
|
|
33398
|
+
// This can save re-queries as we zoom out. Visibility window <= 0 is a special case
|
|
33399
|
+
// indicating whole chromosome should be read at once.
|
|
33400
|
+
if ((!visibilityWindow || visibilityWindow <= 0) && this.config.expandQuery !== false) {
|
|
33401
|
+
// Whole chromosome
|
|
33402
|
+
const chromosome = this.genome ? this.genome.getChromosome(queryChr) : undefined;
|
|
33403
|
+
intervalStart = 0;
|
|
33404
|
+
intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
|
|
33405
|
+
} else if (visibilityWindow > (end - start) && this.config.expandQuery !== false) {
|
|
33406
|
+
let expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
|
|
33407
|
+
if(this.config.minQuerySize && expansionWindow < this.config.minQuerySize) {
|
|
33408
|
+
expansionWindow = this.config.minQuerySize;
|
|
32924
33409
|
}
|
|
33410
|
+
intervalStart = Math.max(0, (start + end - expansionWindow) / 2);
|
|
33411
|
+
intervalEnd = intervalStart + expansionWindow;
|
|
32925
33412
|
}
|
|
32926
|
-
s = e;
|
|
32927
|
-
}
|
|
32928
|
-
}
|
|
32929
33413
|
|
|
33414
|
+
let features = await reader.readFeatures(queryChr, intervalStart, intervalEnd);
|
|
33415
|
+
if (this.queryable === undefined) {
|
|
33416
|
+
this.queryable = reader.indexed;
|
|
33417
|
+
}
|
|
32930
33418
|
|
|
32931
|
-
|
|
33419
|
+
const genomicInterval = this.queryable ?
|
|
33420
|
+
new GenomicInterval(chr, intervalStart, intervalEnd) :
|
|
33421
|
+
undefined;
|
|
32932
33422
|
|
|
32933
|
-
|
|
33423
|
+
if (features) {
|
|
32934
33424
|
|
|
32935
|
-
|
|
32936
|
-
|
|
32937
|
-
|
|
33425
|
+
// Assign overlapping features to rows
|
|
33426
|
+
if (this.config.format !== "wig" && this.config.type !== "junctions") {
|
|
33427
|
+
const maxRows = this.config.maxRows || Number.MAX_SAFE_INTEGER;
|
|
33428
|
+
packFeatures(features, maxRows);
|
|
33429
|
+
}
|
|
32938
33430
|
|
|
32939
|
-
|
|
33431
|
+
// Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
|
|
33432
|
+
this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval);
|
|
32940
33433
|
|
|
32941
|
-
|
|
33434
|
+
// If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
33435
|
+
if (this.searchable) {
|
|
33436
|
+
this.addFeaturesToDB(features, this.config);
|
|
33437
|
+
}
|
|
33438
|
+
} else {
|
|
33439
|
+
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
33440
|
+
}
|
|
33441
|
+
}
|
|
33442
|
+
|
|
33443
|
+
addFeaturesToDB(featureList, config) {
|
|
33444
|
+
if (!this.featureMap) {
|
|
33445
|
+
this.featureMap = new Map();
|
|
33446
|
+
}
|
|
33447
|
+
const searchableFields = config.searchableFields || ["name", "transcript_id", "gene_id", "gene_name", "id"];
|
|
33448
|
+
for (let feature of featureList) {
|
|
33449
|
+
for (let field of searchableFields) {
|
|
33450
|
+
let key;
|
|
33451
|
+
if (typeof feature.getAttributeValue === 'function') {
|
|
33452
|
+
key = feature.getAttributeValue(field);
|
|
33453
|
+
}
|
|
33454
|
+
if (key) {
|
|
33455
|
+
key = key.replaceAll(' ', '+').toUpperCase();
|
|
33456
|
+
// If feature is already present keep largest one
|
|
33457
|
+
if (this.featureMap.has(key)) {
|
|
33458
|
+
const f2 = this.featureMap.get(key);
|
|
33459
|
+
if (feature.end - feature.start < f2.end - f2.start) {
|
|
33460
|
+
continue
|
|
33461
|
+
}
|
|
33462
|
+
}
|
|
33463
|
+
this.featureMap.set(key, feature);
|
|
33464
|
+
}
|
|
33465
|
+
}
|
|
33466
|
+
}
|
|
33467
|
+
}
|
|
33468
|
+
|
|
33469
|
+
search(term) {
|
|
33470
|
+
if (this.featureMap) {
|
|
33471
|
+
return this.featureMap.get(term.toUpperCase())
|
|
33472
|
+
}
|
|
33473
|
+
|
|
33474
|
+
}
|
|
32942
33475
|
}
|
|
32943
33476
|
|
|
32944
33477
|
/*
|
|
@@ -34230,7 +34763,7 @@ function renderFusionJuncSpan(feature, bpStart, xScale, pixelHeight, ctx) {
|
|
|
34230
34763
|
}
|
|
34231
34764
|
}
|
|
34232
34765
|
|
|
34233
|
-
const DEFAULT_COLOR$
|
|
34766
|
+
const DEFAULT_COLOR$1 = 'rgb(0, 0, 150)';
|
|
34234
34767
|
|
|
34235
34768
|
|
|
34236
34769
|
class FeatureTrack extends TrackBase {
|
|
@@ -34416,7 +34949,7 @@ class FeatureTrack extends TrackBase {
|
|
|
34416
34949
|
}
|
|
34417
34950
|
|
|
34418
34951
|
|
|
34419
|
-
if (!this.
|
|
34952
|
+
if (!this.isMergedTrack) {
|
|
34420
34953
|
IGVGraphics.fillRect(context, 0, options.pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
|
|
34421
34954
|
}
|
|
34422
34955
|
|
|
@@ -34727,7 +35260,7 @@ class FeatureTrack extends TrackBase {
|
|
|
34727
35260
|
|
|
34728
35261
|
// If no explicit setting use the default
|
|
34729
35262
|
if (!color) {
|
|
34730
|
-
color = DEFAULT_COLOR$
|
|
35263
|
+
color = DEFAULT_COLOR$1; // Track default
|
|
34731
35264
|
}
|
|
34732
35265
|
|
|
34733
35266
|
if (feature.alpha && feature.alpha !== 1) {
|
|
@@ -37854,7 +38387,6 @@ function indentLevel(str) {
|
|
|
37854
38387
|
}
|
|
37855
38388
|
|
|
37856
38389
|
const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes.json";
|
|
37857
|
-
const BACKUP_GENOMES_URL = "https://s3.amazonaws.com/igv.org.genomes/genomes.json";
|
|
37858
38390
|
|
|
37859
38391
|
const GenomeUtils = {
|
|
37860
38392
|
|
|
@@ -37866,21 +38398,9 @@ const GenomeUtils = {
|
|
|
37866
38398
|
|
|
37867
38399
|
// Get default genomes
|
|
37868
38400
|
if (config.loadDefaultGenomes !== false) {
|
|
37869
|
-
|
|
37870
|
-
|
|
37871
|
-
|
|
37872
|
-
processJson(jsonArray);
|
|
37873
|
-
} catch (e) {
|
|
37874
|
-
console.error(e);
|
|
37875
|
-
try {
|
|
37876
|
-
const url = BACKUP_GENOMES_URL;
|
|
37877
|
-
const jsonArray = await igvxhr.loadJson(url, {});
|
|
37878
|
-
processJson(jsonArray);
|
|
37879
|
-
} catch (e) {
|
|
37880
|
-
console.error(e);
|
|
37881
|
-
console.warn("Errors loading default genome definitions.");
|
|
37882
|
-
}
|
|
37883
|
-
}
|
|
38401
|
+
const url = DEFAULT_GENOMES_URL;
|
|
38402
|
+
const jsonArray = await igvxhr.loadJson(url, {timeout: 5000});
|
|
38403
|
+
processJson(jsonArray);
|
|
37884
38404
|
}
|
|
37885
38405
|
|
|
37886
38406
|
// Add user-defined genomes
|
|
@@ -37945,7 +38465,7 @@ const GenomeUtils = {
|
|
|
37945
38465
|
}
|
|
37946
38466
|
}
|
|
37947
38467
|
|
|
37948
|
-
if(!reference) {
|
|
38468
|
+
if (!reference) {
|
|
37949
38469
|
alert.present(new Error(`Unknown genome id: ${genomeID}`), undefined);
|
|
37950
38470
|
}
|
|
37951
38471
|
}
|
|
@@ -41343,301 +41863,242 @@ const hideAllMenuPopups = parent => {
|
|
|
41343
41863
|
* THE SOFTWARE.
|
|
41344
41864
|
*/
|
|
41345
41865
|
|
|
41346
|
-
class NavbarButton {
|
|
41347
|
-
|
|
41348
|
-
constructor(browser, parent, title, buttonLabel, imageSVG, imageHoverSVG, initialButtonState) {
|
|
41349
|
-
|
|
41350
|
-
this.browser = browser;
|
|
41351
|
-
|
|
41352
|
-
this.button = div({class: 'igv-navbar-text-button'});
|
|
41353
|
-
parent.appendChild(this.button);
|
|
41354
|
-
|
|
41355
|
-
if (Array.isArray(title)) {
|
|
41356
|
-
this.textContent = title[ 0 ];
|
|
41357
|
-
this.title = title[ 1 ];
|
|
41358
|
-
} else {
|
|
41359
|
-
this.textContent = this.title = title;
|
|
41360
|
-
}
|
|
41361
|
-
|
|
41362
|
-
this.buttonLabel = buttonLabel;
|
|
41363
|
-
|
|
41364
|
-
this.imageDictionary =
|
|
41365
|
-
{
|
|
41366
|
-
image: `url("data:image/svg+xml,${ encodeURIComponent(imageSVG) }")`,
|
|
41367
|
-
imageHover: `url("data:image/svg+xml,${ encodeURIComponent(imageHoverSVG) }")`,
|
|
41368
|
-
};
|
|
41369
|
-
|
|
41370
|
-
this.responsiveKey = 'text';
|
|
41371
|
-
|
|
41372
|
-
this.configureButton(this.textContent, this.title);
|
|
41373
|
-
|
|
41374
|
-
this.setState(initialButtonState);
|
|
41375
|
-
|
|
41376
|
-
browser.on('navbar-resize', navbarButtonCSSClass => {
|
|
41377
|
-
this.navbarResizeHandler(navbarButtonCSSClass);
|
|
41378
|
-
});
|
|
41379
|
-
|
|
41380
|
-
}
|
|
41381
|
-
|
|
41382
|
-
navbarResizeHandler(navbarButtonCSSClass) {
|
|
41383
|
-
const key = 'igv-navbar-icon-button' === navbarButtonCSSClass ? 'image' : 'text';
|
|
41384
|
-
if (key !== this.responsiveKey) {
|
|
41385
|
-
this.responsiveKey = key;
|
|
41386
|
-
this.configureButton(this.textContent, this.title);
|
|
41387
|
-
this.setState(undefined);
|
|
41388
|
-
}
|
|
41389
|
-
}
|
|
41390
|
-
|
|
41391
|
-
configureButton(textContent, title) {
|
|
41392
|
-
|
|
41393
|
-
this.groupElement = undefined;
|
|
41394
|
-
this.button.title = title;
|
|
41395
|
-
this.button.innerHTML = '';
|
|
41396
|
-
this.button.style.backgroundImage = 'none';
|
|
41397
|
-
this.button.classList.remove('igv-navbar-icon-button');
|
|
41398
|
-
this.button.classList.remove('igv-navbar-text-button');
|
|
41399
|
-
|
|
41400
|
-
'text' === this.responsiveKey ? this.configureTextButton(textContent) : this.configureIconButton();
|
|
41401
|
-
|
|
41402
|
-
}
|
|
41403
|
-
|
|
41404
|
-
configureTextButton(textContent) {
|
|
41405
|
-
|
|
41406
|
-
this.button.classList.add('igv-navbar-text-button');
|
|
41407
|
-
|
|
41408
|
-
const tempDiv = document.createElement('div');
|
|
41409
|
-
tempDiv.innerHTML = this.buttonLabel;
|
|
41410
|
-
const svgRoot = tempDiv.firstChild;
|
|
41411
|
-
this.button.appendChild(svgRoot);
|
|
41412
|
-
|
|
41413
|
-
this.groupElement = svgRoot.querySelector('#igv-navbar-button-group');
|
|
41414
|
-
|
|
41415
|
-
const tspanElement = svgRoot.querySelector('#igv-navbar-button-label');
|
|
41416
|
-
tspanElement.textContent = textContent;
|
|
41417
|
-
}
|
|
41418
|
-
|
|
41419
|
-
configureIconButton() {
|
|
41420
|
-
this.button.classList.add('igv-navbar-icon-button');
|
|
41421
|
-
}
|
|
41422
|
-
|
|
41423
|
-
setState(doHover) {
|
|
41424
|
-
|
|
41425
|
-
if (undefined !== doHover) {
|
|
41426
|
-
this.doHover = doHover;
|
|
41427
|
-
}
|
|
41428
|
-
|
|
41429
|
-
'text' === this.responsiveKey ? this.setTextButtonState(this.doHover) : this.setIconButtonState(this.doHover);
|
|
41430
|
-
|
|
41431
|
-
}
|
|
41432
|
-
|
|
41433
|
-
setTextButtonState(doHover) {
|
|
41434
|
-
this.groupElement.classList.remove(...this.groupElement.classList);
|
|
41435
|
-
const className = true === doHover ? 'igv-navbar-text-button-svg-hover' : 'igv-navbar-text-button-svg-inactive';
|
|
41436
|
-
this.groupElement.classList.add(className);
|
|
41437
|
-
}
|
|
41438
|
-
|
|
41439
|
-
setIconButtonState(doHover) {
|
|
41440
|
-
this.button.style.backgroundImage = true === doHover ? this.imageDictionary.imageHover : this.imageDictionary.image;
|
|
41441
|
-
}
|
|
41442
|
-
|
|
41443
|
-
show() {
|
|
41444
|
-
// this.button.style.display = 'block'
|
|
41445
|
-
this.button.style.display = 'flex';
|
|
41446
|
-
}
|
|
41447
|
-
|
|
41448
|
-
hide() {
|
|
41449
|
-
this.button.style.display = 'none';
|
|
41450
|
-
}
|
|
41451
|
-
|
|
41452
|
-
setVisibility(isVisible) {
|
|
41453
|
-
if (true === isVisible) {
|
|
41454
|
-
this.show();
|
|
41455
|
-
} else {
|
|
41456
|
-
this.hide();
|
|
41457
|
-
}
|
|
41458
|
-
}
|
|
41459
|
-
|
|
41460
|
-
static currentNavbarButtonClass(browser) {
|
|
41461
|
-
const el = browser.$navigation.get(0).querySelector('.igv-navbar-text-button');
|
|
41462
|
-
return el ? 'igv-navbar-text-button' : 'igv-navbar-icon-button'
|
|
41463
|
-
}
|
|
41464
|
-
}
|
|
41465
|
-
|
|
41466
|
-
const overlayTrackImage =
|
|
41467
|
-
`<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41468
|
-
<title>Overlay Tracks</title>
|
|
41469
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41470
|
-
<g id="Overlay-Tracks">
|
|
41471
|
-
<rect id="backdrop" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41472
|
-
<g id="layer-group" transform="translate(3, 127)">
|
|
41473
|
-
<rect id="a" stroke="#737373" stroke-width="24" fill="#A1A1A1" x="12" y="12" width="332" height="139"></rect>
|
|
41474
|
-
<rect id="a---hold-out" fill="#A1A1A1" x="9" y="25" width="324" height="115"></rect>
|
|
41475
|
-
<rect id="b" stroke="#737373" stroke-width="24" fill="#C9C9C9" x="81" y="103" width="474" height="139"></rect>
|
|
41476
|
-
<rect id="c" stroke="#737373" stroke-width="24" fill="#ECECEC" x="238" y="214" width="372" height="139"></rect>
|
|
41477
|
-
<rect id="c---hold-out" fill="#ECECEC" x="250" y="226" width="372" height="115"></rect>
|
|
41478
|
-
</g>
|
|
41479
|
-
<rect id="over-border" stroke="#737373" stroke-width="12" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41480
|
-
</g>
|
|
41481
|
-
</g>
|
|
41482
|
-
</svg>`;
|
|
41483
|
-
|
|
41484
|
-
const overlayTrackImageHover =
|
|
41485
|
-
`<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41486
|
-
<title>Overlay Tracks Hover</title>
|
|
41487
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41488
|
-
<g id="Overlay-Tracks-Hover">
|
|
41489
|
-
<rect id="backdrop-copy" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41490
|
-
<g id="layer-group" transform="translate(3, 127)">
|
|
41491
|
-
<rect id="a" stroke="#FFFFFF" stroke-width="24" fill="#A1A1A1" x="12" y="12" width="332" height="139"></rect>
|
|
41492
|
-
<rect id="a---hold-out" fill="#A1A1A1" x="9" y="25" width="324" height="115"></rect>
|
|
41493
|
-
<rect id="b" stroke="#FFFFFF" stroke-width="24" fill="#C9C9C9" x="81" y="103" width="474" height="139"></rect>
|
|
41494
|
-
<rect id="c" stroke="#FFFFFF" stroke-width="24" fill="#ECECEC" x="238" y="214" width="372" height="139"></rect>
|
|
41495
|
-
<rect id="c---hold-out" fill="#ECECEC" x="250" y="226" width="372" height="115"></rect>
|
|
41496
|
-
</g>
|
|
41497
|
-
<rect id="over-border-copy" stroke="#737373" stroke-width="12" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41498
|
-
</g>
|
|
41499
|
-
</g>
|
|
41500
|
-
</svg>`;
|
|
41501
|
-
|
|
41502
|
-
const buttonLabel =
|
|
41503
|
-
`<svg width="80px" height="18px" viewBox="0 0 80 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41504
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41505
|
-
<g id="igv-navbar-button-group">
|
|
41506
|
-
<rect id="Rectangle" x="0.5" y="0.5" width="79" height="17" rx="6"></rect>
|
|
41507
|
-
<text id="igv-text-button-label" x="50%" y="50%" dy=".1em" font-family="Helvetica" font-size="12" font-weight="normal" letter-spacing="-0.372">
|
|
41508
|
-
<tspan id="igv-navbar-button-label"></tspan>
|
|
41509
|
-
</text>
|
|
41510
|
-
</g>
|
|
41511
|
-
</g>
|
|
41512
|
-
</svg>`;
|
|
41513
|
-
|
|
41514
|
-
const sampleNameButtonLabel =
|
|
41515
|
-
`<svg width="90px" height="20px" viewBox="0 0 90 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41516
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41517
|
-
<g id="igv-navbar-button-group">
|
|
41518
|
-
<rect id="Rectangle" x="0.5" y="0.5" width="89" height="18" rx="6"></rect>
|
|
41519
|
-
<text id="igv-text-button-label" x="50%" y="50%" dy=".1em" font-family="Helvetica" font-size="12" font-weight="normal" letter-spacing="-0.372">
|
|
41520
|
-
<tspan id="igv-navbar-button-label"></tspan>
|
|
41521
|
-
</text>
|
|
41522
|
-
</g>
|
|
41523
|
-
</g>
|
|
41524
|
-
</svg>`;
|
|
41525
|
-
|
|
41526
|
-
|
|
41527
|
-
|
|
41528
|
-
|
|
41529
|
-
|
|
41530
|
-
|
|
41531
|
-
|
|
41532
|
-
|
|
41533
|
-
|
|
41534
|
-
|
|
41535
|
-
|
|
41536
|
-
|
|
41537
|
-
|
|
41538
|
-
|
|
41539
|
-
|
|
41540
|
-
|
|
41541
|
-
|
|
41542
|
-
|
|
41543
|
-
|
|
41544
|
-
|
|
41545
|
-
|
|
41546
|
-
|
|
41547
|
-
|
|
41548
|
-
|
|
41549
|
-
|
|
41550
|
-
|
|
41551
|
-
};
|
|
41552
|
-
|
|
41553
|
-
// tick
|
|
41554
|
-
IGVGraphics.strokeLine(ctx, xTickStart, shim * height, xTickEnd, shim * height, properties);
|
|
41555
|
-
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), xTickStart + 4, shim * height + 12, properties);
|
|
41556
|
-
|
|
41557
|
-
const y = (1.0 - shim) * height;
|
|
41558
|
-
|
|
41559
|
-
// tick
|
|
41560
|
-
IGVGraphics.strokeLine(ctx, xTickStart, y, xTickEnd, y, properties);
|
|
41561
|
-
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), xTickStart + 4, y - 4, properties);
|
|
41562
|
-
|
|
41563
|
-
// vertical axis
|
|
41564
|
-
IGVGraphics.strokeLine(ctx, xTickEnd, shim * height, xTickEnd, y, properties);
|
|
41565
|
-
|
|
41566
|
-
function prettyPrint(number) {
|
|
41567
|
-
|
|
41568
|
-
if (number === 0) {
|
|
41569
|
-
return "0"
|
|
41570
|
-
} else if (Math.abs(number) >= 10) {
|
|
41571
|
-
return number.toFixed()
|
|
41572
|
-
} else if (Math.abs(number) >= 1) {
|
|
41573
|
-
return number.toFixed(1)
|
|
41574
|
-
} else if (Math.abs(number) >= 0.1) {
|
|
41575
|
-
return number.toFixed(2)
|
|
41576
|
-
} else {
|
|
41577
|
-
return number.toExponential(1)
|
|
41578
|
-
}
|
|
41579
|
-
}
|
|
41580
|
-
}
|
|
41581
|
-
|
|
41582
|
-
/*
|
|
41583
|
-
* The MIT License (MIT)
|
|
41584
|
-
*
|
|
41585
|
-
* Copyright (c) 2016-2017 The Regents of the University of California
|
|
41586
|
-
* Author: Jim Robinson
|
|
41587
|
-
*
|
|
41588
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
41589
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
41590
|
-
* in the Software without restriction, including without limitation the rights
|
|
41591
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
41592
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
41593
|
-
* furnished to do so, subject to the following conditions:
|
|
41594
|
-
*
|
|
41595
|
-
* The above copyright notice and this permission notice shall be included in
|
|
41596
|
-
* all copies or substantial portions of the Software.
|
|
41597
|
-
*
|
|
41598
|
-
*
|
|
41599
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
41600
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
41601
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
41602
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
41603
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
41604
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
41605
|
-
* THE SOFTWARE.
|
|
41606
|
-
*/
|
|
41607
|
-
|
|
41866
|
+
class NavbarButton {
|
|
41867
|
+
|
|
41868
|
+
constructor(browser, parent, title, buttonLabel, imageSVG, imageHoverSVG, initialButtonState) {
|
|
41869
|
+
|
|
41870
|
+
this.browser = browser;
|
|
41871
|
+
|
|
41872
|
+
this.button = div({class: 'igv-navbar-text-button'});
|
|
41873
|
+
parent.appendChild(this.button);
|
|
41874
|
+
|
|
41875
|
+
if (Array.isArray(title)) {
|
|
41876
|
+
this.textContent = title[ 0 ];
|
|
41877
|
+
this.title = title[ 1 ];
|
|
41878
|
+
} else {
|
|
41879
|
+
this.textContent = this.title = title;
|
|
41880
|
+
}
|
|
41881
|
+
|
|
41882
|
+
this.buttonLabel = buttonLabel;
|
|
41883
|
+
|
|
41884
|
+
this.imageDictionary =
|
|
41885
|
+
{
|
|
41886
|
+
image: `url("data:image/svg+xml,${ encodeURIComponent(imageSVG) }")`,
|
|
41887
|
+
imageHover: `url("data:image/svg+xml,${ encodeURIComponent(imageHoverSVG) }")`,
|
|
41888
|
+
};
|
|
41889
|
+
|
|
41890
|
+
this.responsiveKey = 'text';
|
|
41891
|
+
|
|
41892
|
+
this.configureButton(this.textContent, this.title);
|
|
41893
|
+
|
|
41894
|
+
this.setState(initialButtonState);
|
|
41895
|
+
|
|
41896
|
+
browser.on('navbar-resize', navbarButtonCSSClass => {
|
|
41897
|
+
this.navbarResizeHandler(navbarButtonCSSClass);
|
|
41898
|
+
});
|
|
41899
|
+
|
|
41900
|
+
}
|
|
41901
|
+
|
|
41902
|
+
navbarResizeHandler(navbarButtonCSSClass) {
|
|
41903
|
+
const key = 'igv-navbar-icon-button' === navbarButtonCSSClass ? 'image' : 'text';
|
|
41904
|
+
if (key !== this.responsiveKey) {
|
|
41905
|
+
this.responsiveKey = key;
|
|
41906
|
+
this.configureButton(this.textContent, this.title);
|
|
41907
|
+
this.setState(undefined);
|
|
41908
|
+
}
|
|
41909
|
+
}
|
|
41910
|
+
|
|
41911
|
+
configureButton(textContent, title) {
|
|
41912
|
+
|
|
41913
|
+
this.groupElement = undefined;
|
|
41914
|
+
this.button.title = title;
|
|
41915
|
+
this.button.innerHTML = '';
|
|
41916
|
+
this.button.style.backgroundImage = 'none';
|
|
41917
|
+
this.button.classList.remove('igv-navbar-icon-button');
|
|
41918
|
+
this.button.classList.remove('igv-navbar-text-button');
|
|
41919
|
+
|
|
41920
|
+
'text' === this.responsiveKey ? this.configureTextButton(textContent) : this.configureIconButton();
|
|
41921
|
+
|
|
41922
|
+
}
|
|
41923
|
+
|
|
41924
|
+
configureTextButton(textContent) {
|
|
41925
|
+
|
|
41926
|
+
this.button.classList.add('igv-navbar-text-button');
|
|
41927
|
+
|
|
41928
|
+
const tempDiv = document.createElement('div');
|
|
41929
|
+
tempDiv.innerHTML = this.buttonLabel;
|
|
41930
|
+
const svgRoot = tempDiv.firstChild;
|
|
41931
|
+
this.button.appendChild(svgRoot);
|
|
41932
|
+
|
|
41933
|
+
this.groupElement = svgRoot.querySelector('#igv-navbar-button-group');
|
|
41934
|
+
|
|
41935
|
+
const tspanElement = svgRoot.querySelector('#igv-navbar-button-label');
|
|
41936
|
+
tspanElement.textContent = textContent;
|
|
41937
|
+
}
|
|
41938
|
+
|
|
41939
|
+
configureIconButton() {
|
|
41940
|
+
this.button.classList.add('igv-navbar-icon-button');
|
|
41941
|
+
}
|
|
41942
|
+
|
|
41943
|
+
setState(doHover) {
|
|
41944
|
+
|
|
41945
|
+
if (undefined !== doHover) {
|
|
41946
|
+
this.doHover = doHover;
|
|
41947
|
+
}
|
|
41948
|
+
|
|
41949
|
+
'text' === this.responsiveKey ? this.setTextButtonState(this.doHover) : this.setIconButtonState(this.doHover);
|
|
41950
|
+
|
|
41951
|
+
}
|
|
41952
|
+
|
|
41953
|
+
setTextButtonState(doHover) {
|
|
41954
|
+
this.groupElement.classList.remove(...this.groupElement.classList);
|
|
41955
|
+
const className = true === doHover ? 'igv-navbar-text-button-svg-hover' : 'igv-navbar-text-button-svg-inactive';
|
|
41956
|
+
this.groupElement.classList.add(className);
|
|
41957
|
+
}
|
|
41958
|
+
|
|
41959
|
+
setIconButtonState(doHover) {
|
|
41960
|
+
this.button.style.backgroundImage = true === doHover ? this.imageDictionary.imageHover : this.imageDictionary.image;
|
|
41961
|
+
}
|
|
41962
|
+
|
|
41963
|
+
show() {
|
|
41964
|
+
// this.button.style.display = 'block'
|
|
41965
|
+
this.button.style.display = 'flex';
|
|
41966
|
+
}
|
|
41967
|
+
|
|
41968
|
+
hide() {
|
|
41969
|
+
this.button.style.display = 'none';
|
|
41970
|
+
}
|
|
41971
|
+
|
|
41972
|
+
setVisibility(isVisible) {
|
|
41973
|
+
if (true === isVisible) {
|
|
41974
|
+
this.show();
|
|
41975
|
+
} else {
|
|
41976
|
+
this.hide();
|
|
41977
|
+
}
|
|
41978
|
+
}
|
|
41979
|
+
|
|
41980
|
+
static currentNavbarButtonClass(browser) {
|
|
41981
|
+
const el = browser.$navigation.get(0).querySelector('.igv-navbar-text-button');
|
|
41982
|
+
return el ? 'igv-navbar-text-button' : 'igv-navbar-icon-button'
|
|
41983
|
+
}
|
|
41984
|
+
}
|
|
41985
|
+
|
|
41986
|
+
const overlayTrackImage =
|
|
41987
|
+
`<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
41988
|
+
<title>Overlay Tracks</title>
|
|
41989
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
41990
|
+
<g id="Overlay-Tracks">
|
|
41991
|
+
<rect id="backdrop" stroke="#737373" stroke-width="12" fill="#FFFFFF" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
41992
|
+
<g id="layer-group" transform="translate(3, 127)">
|
|
41993
|
+
<rect id="a" stroke="#737373" stroke-width="24" fill="#A1A1A1" x="12" y="12" width="332" height="139"></rect>
|
|
41994
|
+
<rect id="a---hold-out" fill="#A1A1A1" x="9" y="25" width="324" height="115"></rect>
|
|
41995
|
+
<rect id="b" stroke="#737373" stroke-width="24" fill="#C9C9C9" x="81" y="103" width="474" height="139"></rect>
|
|
41996
|
+
<rect id="c" stroke="#737373" stroke-width="24" fill="#ECECEC" x="238" y="214" width="372" height="139"></rect>
|
|
41997
|
+
<rect id="c---hold-out" fill="#ECECEC" x="250" y="226" width="372" height="115"></rect>
|
|
41998
|
+
</g>
|
|
41999
|
+
<rect id="over-border" stroke="#737373" stroke-width="12" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
42000
|
+
</g>
|
|
42001
|
+
</g>
|
|
42002
|
+
</svg>`;
|
|
42003
|
+
|
|
42004
|
+
const overlayTrackImageHover =
|
|
42005
|
+
`<svg width="625px" height="625px" viewBox="0 0 625 625" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
42006
|
+
<title>Overlay Tracks Hover</title>
|
|
42007
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
42008
|
+
<g id="Overlay-Tracks-Hover">
|
|
42009
|
+
<rect id="backdrop-copy" stroke="#737373" stroke-width="12" fill="#737373" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
42010
|
+
<g id="layer-group" transform="translate(3, 127)">
|
|
42011
|
+
<rect id="a" stroke="#FFFFFF" stroke-width="24" fill="#A1A1A1" x="12" y="12" width="332" height="139"></rect>
|
|
42012
|
+
<rect id="a---hold-out" fill="#A1A1A1" x="9" y="25" width="324" height="115"></rect>
|
|
42013
|
+
<rect id="b" stroke="#FFFFFF" stroke-width="24" fill="#C9C9C9" x="81" y="103" width="474" height="139"></rect>
|
|
42014
|
+
<rect id="c" stroke="#FFFFFF" stroke-width="24" fill="#ECECEC" x="238" y="214" width="372" height="139"></rect>
|
|
42015
|
+
<rect id="c---hold-out" fill="#ECECEC" x="250" y="226" width="372" height="115"></rect>
|
|
42016
|
+
</g>
|
|
42017
|
+
<rect id="over-border-copy" stroke="#737373" stroke-width="12" x="6" y="6" width="613" height="613" rx="135"></rect>
|
|
42018
|
+
</g>
|
|
42019
|
+
</g>
|
|
42020
|
+
</svg>`;
|
|
42021
|
+
|
|
42022
|
+
const buttonLabel =
|
|
42023
|
+
`<svg width="80px" height="18px" viewBox="0 0 80 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
42024
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
42025
|
+
<g id="igv-navbar-button-group">
|
|
42026
|
+
<rect id="Rectangle" x="0.5" y="0.5" width="79" height="17" rx="6"></rect>
|
|
42027
|
+
<text id="igv-text-button-label" x="50%" y="50%" dy=".1em" font-family="Helvetica" font-size="12" font-weight="normal" letter-spacing="-0.372">
|
|
42028
|
+
<tspan id="igv-navbar-button-label"></tspan>
|
|
42029
|
+
</text>
|
|
42030
|
+
</g>
|
|
42031
|
+
</g>
|
|
42032
|
+
</svg>`;
|
|
42033
|
+
|
|
42034
|
+
const sampleNameButtonLabel =
|
|
42035
|
+
`<svg width="90px" height="20px" viewBox="0 0 90 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
42036
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
42037
|
+
<g id="igv-navbar-button-group">
|
|
42038
|
+
<rect id="Rectangle" x="0.5" y="0.5" width="89" height="18" rx="6"></rect>
|
|
42039
|
+
<text id="igv-text-button-label" x="50%" y="50%" dy=".1em" font-family="Helvetica" font-size="12" font-weight="normal" letter-spacing="-0.372">
|
|
42040
|
+
<tspan id="igv-navbar-button-label"></tspan>
|
|
42041
|
+
</text>
|
|
42042
|
+
</g>
|
|
42043
|
+
</g>
|
|
42044
|
+
</svg>`;
|
|
42045
|
+
|
|
42046
|
+
/*
|
|
42047
|
+
* The MIT License (MIT)
|
|
42048
|
+
*
|
|
42049
|
+
* Copyright (c) 2016-2017 The Regents of the University of California
|
|
42050
|
+
* Author: Jim Robinson
|
|
42051
|
+
*
|
|
42052
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
42053
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
42054
|
+
* in the Software without restriction, including without limitation the rights
|
|
42055
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
42056
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
42057
|
+
* furnished to do so, subject to the following conditions:
|
|
42058
|
+
*
|
|
42059
|
+
* The above copyright notice and this permission notice shall be included in
|
|
42060
|
+
* all copies or substantial portions of the Software.
|
|
42061
|
+
*
|
|
42062
|
+
*
|
|
42063
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
42064
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
42065
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
42066
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
42067
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
42068
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
42069
|
+
* THE SOFTWARE.
|
|
42070
|
+
*/
|
|
41608
42071
|
|
|
41609
42072
|
/**
|
|
41610
|
-
* Represents 2 or more
|
|
42073
|
+
* Represents 2 or more tracks overlaid on a common viewport.
|
|
41611
42074
|
*/
|
|
41612
42075
|
class MergedTrack extends TrackBase {
|
|
41613
42076
|
|
|
41614
42077
|
static defaults = {
|
|
42078
|
+
autoscale: undefined,
|
|
41615
42079
|
alpha: 0.5,
|
|
41616
42080
|
height: 50
|
|
41617
42081
|
}
|
|
41618
42082
|
|
|
41619
|
-
constructor(config, browser) {
|
|
42083
|
+
constructor(config, browser, tracks) {
|
|
41620
42084
|
super(config, browser);
|
|
41621
42085
|
this.type = "merged";
|
|
41622
|
-
this.featureType = "numeric";
|
|
41623
42086
|
this.paintAxis = paintAxis;
|
|
41624
42087
|
this.graphType = config.graphType;
|
|
41625
|
-
|
|
41626
|
-
|
|
41627
|
-
|
|
41628
|
-
|
|
41629
|
-
throw Error("Error: no tracks defined for merged track" + config)
|
|
42088
|
+
if (tracks) {
|
|
42089
|
+
this.tracks = tracks; // Dynamic creation, actual track objects (not configurations)
|
|
42090
|
+
} else {
|
|
42091
|
+
this.tracks = [];
|
|
41630
42092
|
}
|
|
41631
|
-
super.init(config);
|
|
41632
42093
|
}
|
|
41633
42094
|
|
|
42095
|
+
|
|
41634
42096
|
async postInit() {
|
|
41635
42097
|
|
|
41636
|
-
this.tracks = [];
|
|
41637
42098
|
if (this.config.tracks) {
|
|
41638
|
-
//
|
|
42099
|
+
// Track configurations, this indicates a configured merged track as opposed to dynamic merge through the UI
|
|
42100
|
+
// Actual track objects need to be created.
|
|
41639
42101
|
for (let tconf of this.config.tracks) {
|
|
41640
|
-
tconf.isMergedTrack = true;
|
|
41641
42102
|
const t = await this.browser.createTrack(tconf);
|
|
41642
42103
|
if (t) {
|
|
41643
42104
|
this.tracks.push(t);
|
|
@@ -41648,24 +42109,27 @@ class MergedTrack extends TrackBase {
|
|
|
41648
42109
|
await t.postInit();
|
|
41649
42110
|
}
|
|
41650
42111
|
}
|
|
41651
|
-
//
|
|
41652
|
-
|
|
41653
|
-
|
|
41654
|
-
|
|
41655
|
-
this.dataRange = {
|
|
41656
|
-
min: this.config.min || 0,
|
|
41657
|
-
max: this.config.max
|
|
41658
|
-
};
|
|
41659
|
-
} else {
|
|
41660
|
-
this.autoscale = !this.tracks.every(t => t.config.autoscale || t.config.max !== undefined);
|
|
42112
|
+
// Default to autoscale unless scale if range or autoscale is not otherwise defined
|
|
42113
|
+
const allTracksSpecified = this.config.tracks.every(config => config.autoscale !== undefined || config.max !== undefined);
|
|
42114
|
+
if (!allTracksSpecified) {
|
|
42115
|
+
this.config.autoscale = this.config.max === undefined;
|
|
41661
42116
|
}
|
|
41662
|
-
} else {
|
|
41663
|
-
// Dynamic merged track
|
|
41664
|
-
this.tracks = this.config._tracks;
|
|
41665
|
-
this.autoscale = false;
|
|
41666
|
-
delete this.config._tracks;
|
|
41667
42117
|
}
|
|
41668
42118
|
|
|
42119
|
+
// Mark constitutive tracks as merged.
|
|
42120
|
+
for (let t of this.tracks) t.isMergedTrack = true;
|
|
42121
|
+
|
|
42122
|
+
// Explicit settings -- these will override any individual track settings
|
|
42123
|
+
if(this.config.autoscale) {
|
|
42124
|
+
this.autoscale = this.config.autoscale;
|
|
42125
|
+
} else if (this.config.max !== undefined) {
|
|
42126
|
+
this.setDataRange ({
|
|
42127
|
+
min: this.config.min || 0,
|
|
42128
|
+
max: this.config.max
|
|
42129
|
+
});
|
|
42130
|
+
}
|
|
42131
|
+
|
|
42132
|
+
|
|
41669
42133
|
if (this.config.flipAxis !== undefined) {
|
|
41670
42134
|
for (let t of this.tracks) t.flipAxis = this.config.flipAxis;
|
|
41671
42135
|
}
|
|
@@ -41675,25 +42139,28 @@ class MergedTrack extends TrackBase {
|
|
|
41675
42139
|
}
|
|
41676
42140
|
|
|
41677
42141
|
this.resolutionAware = this.tracks.some(t => t.resolutionAware);
|
|
41678
|
-
|
|
41679
42142
|
}
|
|
41680
42143
|
|
|
41681
42144
|
set flipAxis(b) {
|
|
41682
42145
|
this.config.flipAxis = b;
|
|
41683
|
-
for (let t of this.tracks)
|
|
42146
|
+
for (let t of numericTracks(this.tracks)) {
|
|
42147
|
+
t.flipAxis = b;
|
|
42148
|
+
}
|
|
41684
42149
|
}
|
|
41685
42150
|
|
|
41686
42151
|
get flipAxis() {
|
|
41687
|
-
return this.tracks.every(t => t.flipAxis)
|
|
42152
|
+
return numericTracks(this.tracks).every(t => t.flipAxis)
|
|
41688
42153
|
}
|
|
41689
42154
|
|
|
41690
42155
|
set logScale(b) {
|
|
41691
42156
|
this.config.logScale = b;
|
|
41692
|
-
for (let t of this.tracks)
|
|
42157
|
+
for (let t of numericTracks(this.tracks)) {
|
|
42158
|
+
t.logScale = b;
|
|
42159
|
+
}
|
|
41693
42160
|
}
|
|
41694
42161
|
|
|
41695
42162
|
get logScale() {
|
|
41696
|
-
return this.tracks.every(t => t.logScale)
|
|
42163
|
+
return numericTracks(this.tracks).every(t => t.logScale)
|
|
41697
42164
|
}
|
|
41698
42165
|
|
|
41699
42166
|
get height() {
|
|
@@ -41711,49 +42178,76 @@ class MergedTrack extends TrackBase {
|
|
|
41711
42178
|
}
|
|
41712
42179
|
}
|
|
41713
42180
|
|
|
41714
|
-
|
|
41715
|
-
|
|
41716
|
-
if
|
|
41717
|
-
|
|
42181
|
+
set autoscale(b) {
|
|
42182
|
+
this._autoscale = b;
|
|
42183
|
+
if(b === false && this.tracks) {
|
|
42184
|
+
for(let t of this.tracks) t.autoscale = false;
|
|
41718
42185
|
}
|
|
42186
|
+
}
|
|
41719
42187
|
|
|
41720
|
-
|
|
41721
|
-
|
|
41722
|
-
|
|
41723
|
-
}
|
|
42188
|
+
get autoscale() {
|
|
42189
|
+
return this._autoscale
|
|
42190
|
+
}
|
|
41724
42191
|
|
|
41725
|
-
|
|
41726
|
-
|
|
41727
|
-
|
|
41728
|
-
|
|
42192
|
+
/**
|
|
42193
|
+
* Set the data range of all constitutive numeric tracks. This method is called from the menu item, i.e. an explicit
|
|
42194
|
+
* setting, so it should disable autoscale as well.
|
|
42195
|
+
*
|
|
42196
|
+
* @param min
|
|
42197
|
+
* @param max
|
|
42198
|
+
*/
|
|
41729
42199
|
|
|
41730
|
-
|
|
41731
|
-
|
|
41732
|
-
|
|
42200
|
+
setDataRange({min, max}) {
|
|
42201
|
+
this.autoscale = false;
|
|
42202
|
+
for (const track of numericTracks(this.tracks)) {
|
|
42203
|
+
track.dataRange = {min, max};
|
|
42204
|
+
track.autoscale = false;
|
|
42205
|
+
track.autoscaleGroup = false;
|
|
41733
42206
|
}
|
|
42207
|
+
}
|
|
41734
42208
|
|
|
41735
|
-
|
|
42209
|
+
set dataRange({min, max}) {
|
|
42210
|
+
for (const track of numericTracks(this.tracks)) {
|
|
42211
|
+
track.dataRange = {min, max};
|
|
42212
|
+
}
|
|
41736
42213
|
}
|
|
41737
42214
|
|
|
41738
|
-
|
|
41739
|
-
|
|
41740
|
-
|
|
42215
|
+
/**
|
|
42216
|
+
* Return a DataRang {min, max} if all constitutive numeric tracks have identical range. A numeric track is defined
|
|
42217
|
+
* as a track with a data range. Otherwise return undefined.
|
|
42218
|
+
*
|
|
42219
|
+
* @returns {{min: any, max: any}|undefined}
|
|
42220
|
+
*/
|
|
42221
|
+
get dataRange() {
|
|
42222
|
+
if(this.tracks) {
|
|
42223
|
+
const num = numericTracks(this.tracks);
|
|
42224
|
+
if (num.length > 0) {
|
|
42225
|
+
const firstRange = num[0].dataRange;
|
|
42226
|
+
if (num.every(t => t.dataRange && t.dataRange.min === firstRange.min && t.dataRange.max === firstRange.max)) {
|
|
42227
|
+
return firstRange
|
|
42228
|
+
}
|
|
42229
|
+
}
|
|
41741
42230
|
}
|
|
42231
|
+
return undefined
|
|
41742
42232
|
}
|
|
41743
42233
|
|
|
42234
|
+
|
|
41744
42235
|
menuItemList() {
|
|
41745
42236
|
const items = [];
|
|
41746
|
-
if (this.
|
|
41747
|
-
items.push({
|
|
41748
|
-
label: "Flip y-axis",
|
|
41749
|
-
click: function flipYAxisHandler() {
|
|
41750
|
-
this.flipAxis = !this.flipAxis;
|
|
41751
|
-
this.trackView.repaintViews();
|
|
41752
|
-
}
|
|
41753
|
-
});
|
|
41754
|
-
}
|
|
42237
|
+
if (numericTracks(this.tracks).length > 0) {
|
|
41755
42238
|
|
|
41756
|
-
|
|
42239
|
+
if (this.flipAxis !== undefined) {
|
|
42240
|
+
items.push({
|
|
42241
|
+
label: "Flip y-axis",
|
|
42242
|
+
click: function flipYAxisHandler() {
|
|
42243
|
+
this.flipAxis = !this.flipAxis;
|
|
42244
|
+
this.trackView.repaintViews();
|
|
42245
|
+
}
|
|
42246
|
+
});
|
|
42247
|
+
}
|
|
42248
|
+
|
|
42249
|
+
items.push(...this.numericDataMenuItems());
|
|
42250
|
+
}
|
|
41757
42251
|
|
|
41758
42252
|
items.push('<hr/>');
|
|
41759
42253
|
items.push(this.overlayTrackAlphaAdjustmentMenuItem());
|
|
@@ -41770,7 +42264,14 @@ class MergedTrack extends TrackBase {
|
|
|
41770
42264
|
|
|
41771
42265
|
const promises = this.tracks.map((t) => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel));
|
|
41772
42266
|
const featureArrays = await Promise.all(promises);
|
|
41773
|
-
|
|
42267
|
+
|
|
42268
|
+
if (featureArrays.every((arr) => arr.length === 0)){
|
|
42269
|
+
return new MergedFeatureCollection([], [])
|
|
42270
|
+
}
|
|
42271
|
+
else {
|
|
42272
|
+
const trackNames = this.tracks.map((t) => t.name);
|
|
42273
|
+
return new MergedFeatureCollection(featureArrays, trackNames)
|
|
42274
|
+
}
|
|
41774
42275
|
}
|
|
41775
42276
|
|
|
41776
42277
|
draw(options) {
|
|
@@ -41782,11 +42283,6 @@ class MergedTrack extends TrackBase {
|
|
|
41782
42283
|
trackOptions.features = mergedFeatures.featureArrays[i];
|
|
41783
42284
|
trackOptions.alpha = this.alpha;
|
|
41784
42285
|
|
|
41785
|
-
if (this.dataRange) {
|
|
41786
|
-
// Single data scale for all tracks
|
|
41787
|
-
this.tracks[i].dataRange = this.dataRange;
|
|
41788
|
-
}
|
|
41789
|
-
|
|
41790
42286
|
if (this.graphType) {
|
|
41791
42287
|
this.tracks[i].graphType = this.graphType;
|
|
41792
42288
|
}
|
|
@@ -41880,6 +42376,7 @@ class MergedTrack extends TrackBase {
|
|
|
41880
42376
|
let scaleChange;
|
|
41881
42377
|
|
|
41882
42378
|
if (this.autoscale) {
|
|
42379
|
+
// Overrides any specific track scale settings
|
|
41883
42380
|
scaleChange = true;
|
|
41884
42381
|
let allFeatures = [];
|
|
41885
42382
|
for (let visibleViewport of visibleViewports) {
|
|
@@ -41894,7 +42391,12 @@ class MergedTrack extends TrackBase {
|
|
|
41894
42391
|
allFeatures.push({value: mergedFeatureCollection.getMin(start, end)});
|
|
41895
42392
|
}
|
|
41896
42393
|
}
|
|
41897
|
-
|
|
42394
|
+
const dataRange = doAutoscale(allFeatures);
|
|
42395
|
+
for (const track of numericTracks(this.tracks)) {
|
|
42396
|
+
// Do not use this.dataRange, as that has side effects
|
|
42397
|
+
track.dataRange = dataRange;
|
|
42398
|
+
}
|
|
42399
|
+
|
|
41898
42400
|
}
|
|
41899
42401
|
} else {
|
|
41900
42402
|
// Individual track scaling
|
|
@@ -41974,6 +42476,7 @@ class MergedTrack extends TrackBase {
|
|
|
41974
42476
|
if (groupAutoscale) {
|
|
41975
42477
|
track.autoscaleGroup = name;
|
|
41976
42478
|
}
|
|
42479
|
+
track.isMergedTrack = false;
|
|
41977
42480
|
browser.addTrack(track.config, track);
|
|
41978
42481
|
}
|
|
41979
42482
|
browser.updateViews();
|
|
@@ -41987,25 +42490,30 @@ class MergedTrack extends TrackBase {
|
|
|
41987
42490
|
|
|
41988
42491
|
class MergedFeatureCollection {
|
|
41989
42492
|
|
|
41990
|
-
constructor(featureArrays) {
|
|
42493
|
+
constructor(featureArrays,trackNames) {
|
|
41991
42494
|
this.featureArrays = featureArrays;
|
|
42495
|
+
//trackNames is needed for the popup data to populate track names
|
|
42496
|
+
//preserving the order of the actual tracks
|
|
42497
|
+
this.trackNames = trackNames;
|
|
41992
42498
|
}
|
|
41993
42499
|
|
|
41994
42500
|
getMax(start, end) {
|
|
41995
42501
|
let max = -Number.MAX_VALUE;
|
|
41996
42502
|
|
|
41997
42503
|
for (let a of this.featureArrays) {
|
|
41998
|
-
|
|
41999
|
-
|
|
42000
|
-
|
|
42001
|
-
|
|
42002
|
-
|
|
42003
|
-
|
|
42004
|
-
|
|
42005
|
-
|
|
42006
|
-
|
|
42504
|
+
if (Array.isArray(a)) {
|
|
42505
|
+
for (let f of a) {
|
|
42506
|
+
if (typeof f.value === 'undefined' || Number.isNaN(f.value)) {
|
|
42507
|
+
continue
|
|
42508
|
+
}
|
|
42509
|
+
if (f.end < start) {
|
|
42510
|
+
continue
|
|
42511
|
+
}
|
|
42512
|
+
if (f.start > end) {
|
|
42513
|
+
break
|
|
42514
|
+
}
|
|
42515
|
+
max = Math.max(max, f.value);
|
|
42007
42516
|
}
|
|
42008
|
-
max = Math.max(max, f.value);
|
|
42009
42517
|
}
|
|
42010
42518
|
}
|
|
42011
42519
|
|
|
@@ -42016,15 +42524,17 @@ class MergedFeatureCollection {
|
|
|
42016
42524
|
getMin(start, end) {
|
|
42017
42525
|
let min = 0;
|
|
42018
42526
|
for (let a of this.featureArrays) {
|
|
42019
|
-
|
|
42020
|
-
|
|
42021
|
-
if (f.
|
|
42022
|
-
|
|
42023
|
-
|
|
42024
|
-
|
|
42025
|
-
|
|
42527
|
+
if (Array.isArray(a)) {
|
|
42528
|
+
for (let f of a) {
|
|
42529
|
+
if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
|
|
42530
|
+
if (f.end < start) {
|
|
42531
|
+
continue
|
|
42532
|
+
}
|
|
42533
|
+
if (f.start > end) {
|
|
42534
|
+
break
|
|
42535
|
+
}
|
|
42536
|
+
min = Math.min(min, f.value);
|
|
42026
42537
|
}
|
|
42027
|
-
min = Math.min(min, f.value);
|
|
42028
42538
|
}
|
|
42029
42539
|
}
|
|
42030
42540
|
}
|
|
@@ -42032,6 +42542,16 @@ class MergedFeatureCollection {
|
|
|
42032
42542
|
}
|
|
42033
42543
|
}
|
|
42034
42544
|
|
|
42545
|
+
/**
|
|
42546
|
+
* Heuristic for finding numeric tracks.
|
|
42547
|
+
*
|
|
42548
|
+
* @param tracks
|
|
42549
|
+
* @returns {*}
|
|
42550
|
+
*/
|
|
42551
|
+
const numericTracks = (tracks) => {
|
|
42552
|
+
return tracks ? tracks.filter(track => undefined !== track.dataRange || undefined !== track.autoscale || undefined !== track.autoscaleGroup) : []
|
|
42553
|
+
};
|
|
42554
|
+
|
|
42035
42555
|
class OverlayTrackButton extends NavbarButton {
|
|
42036
42556
|
constructor(browser, parent) {
|
|
42037
42557
|
|
|
@@ -42077,13 +42597,13 @@ function trackOverlayClickHandler(e) {
|
|
|
42077
42597
|
{
|
|
42078
42598
|
name: 'Overlay',
|
|
42079
42599
|
type: 'merged',
|
|
42600
|
+
autoscale: false,
|
|
42080
42601
|
alpha: 0.5, //fudge * (1.0/tracks.length),
|
|
42081
42602
|
height: Math.max(...tracks.map(({ height }) => height)),
|
|
42082
42603
|
order: Math.min(...tracks.map(({ order }) => order)),
|
|
42083
|
-
_tracks: flattenedTracks
|
|
42084
42604
|
};
|
|
42085
42605
|
|
|
42086
|
-
const mergedTrack = new MergedTrack(config, this.browser);
|
|
42606
|
+
const mergedTrack = new MergedTrack(config, this.browser, flattenedTracks);
|
|
42087
42607
|
|
|
42088
42608
|
for (const track of tracks) {
|
|
42089
42609
|
this.browser.removeTrack(track);
|
|
@@ -42303,27 +42823,6 @@ class TrackView {
|
|
|
42303
42823
|
}
|
|
42304
42824
|
}
|
|
42305
42825
|
|
|
42306
|
-
get dataRange() {
|
|
42307
|
-
return this.track.dataRange ? this.track.dataRange : undefined
|
|
42308
|
-
}
|
|
42309
|
-
|
|
42310
|
-
set dataRange({ min, max }) {
|
|
42311
|
-
|
|
42312
|
-
this.track.dataRange = { min, max };
|
|
42313
|
-
|
|
42314
|
-
this.track.autoscale = false;
|
|
42315
|
-
this.track.autoscaleGroup = undefined;
|
|
42316
|
-
|
|
42317
|
-
const list = this.browser.trackViews.filter(({track}) => track.autoscaleGroup);
|
|
42318
|
-
if (1 === list.length) {
|
|
42319
|
-
list[0].track.autoscale = false;
|
|
42320
|
-
list[0].track.autoscaleGroup = undefined;
|
|
42321
|
-
list[0].repaintViews();
|
|
42322
|
-
}
|
|
42323
|
-
|
|
42324
|
-
this.repaintViews();
|
|
42325
|
-
|
|
42326
|
-
}
|
|
42327
42826
|
|
|
42328
42827
|
presentColorPicker(key) {
|
|
42329
42828
|
|
|
@@ -43139,462 +43638,6 @@ function renderSVGAxis(context, track, axisCanvas, deltaX, deltaY) {
|
|
|
43139
43638
|
|
|
43140
43639
|
}
|
|
43141
43640
|
|
|
43142
|
-
const DEFAULT_COLOR$1 = 'rgb(150, 150, 150)';
|
|
43143
|
-
|
|
43144
|
-
|
|
43145
|
-
class WigTrack extends TrackBase {
|
|
43146
|
-
|
|
43147
|
-
static defaults = {
|
|
43148
|
-
height: 50,
|
|
43149
|
-
flipAxis: false,
|
|
43150
|
-
logScale: false,
|
|
43151
|
-
windowFunction: 'mean',
|
|
43152
|
-
graphType: 'bar',
|
|
43153
|
-
normalize: undefined,
|
|
43154
|
-
scaleFactor: undefined,
|
|
43155
|
-
overflowColor: `rgb(255, 32, 255)`,
|
|
43156
|
-
baselineColor: 'lightGray',
|
|
43157
|
-
summarize: true
|
|
43158
|
-
}
|
|
43159
|
-
|
|
43160
|
-
constructor(config, browser) {
|
|
43161
|
-
super(config, browser);
|
|
43162
|
-
}
|
|
43163
|
-
|
|
43164
|
-
init(config) {
|
|
43165
|
-
|
|
43166
|
-
super.init(config);
|
|
43167
|
-
|
|
43168
|
-
this.type = "wig";
|
|
43169
|
-
this.featureType = 'numeric';
|
|
43170
|
-
this.resolutionAware = true;
|
|
43171
|
-
this.paintAxis = paintAxis;
|
|
43172
|
-
|
|
43173
|
-
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
43174
|
-
if (config.featureSource) {
|
|
43175
|
-
this.featureSource = config.featureSource;
|
|
43176
|
-
delete config.featureSource;
|
|
43177
|
-
} else if ("bigwig" === format) {
|
|
43178
|
-
this.featureSource = new BWSource(config, this.browser.genome);
|
|
43179
|
-
} else if ("tdf" === format) {
|
|
43180
|
-
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
43181
|
-
} else {
|
|
43182
|
-
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
43183
|
-
}
|
|
43184
|
-
|
|
43185
|
-
|
|
43186
|
-
// Override autoscale default
|
|
43187
|
-
if (config.max === undefined || config.autoscale === true) {
|
|
43188
|
-
this.autoscale = true;
|
|
43189
|
-
} else {
|
|
43190
|
-
this.dataRange = {
|
|
43191
|
-
min: config.min || 0,
|
|
43192
|
-
max: config.max
|
|
43193
|
-
};
|
|
43194
|
-
}
|
|
43195
|
-
}
|
|
43196
|
-
|
|
43197
|
-
async postInit() {
|
|
43198
|
-
const header = await this.getHeader();
|
|
43199
|
-
if (this.disposed) return // This track was removed during async load
|
|
43200
|
-
if (header) this.setTrackProperties(header);
|
|
43201
|
-
}
|
|
43202
|
-
|
|
43203
|
-
async getFeatures(chr, start, end, bpPerPixel) {
|
|
43204
|
-
|
|
43205
|
-
const windowFunction = this.windowFunction;
|
|
43206
|
-
|
|
43207
|
-
const features = await this.featureSource.getFeatures({
|
|
43208
|
-
chr,
|
|
43209
|
-
start,
|
|
43210
|
-
end,
|
|
43211
|
-
bpPerPixel,
|
|
43212
|
-
visibilityWindow: this.visibilityWindow,
|
|
43213
|
-
windowFunction
|
|
43214
|
-
});
|
|
43215
|
-
if (this.normalize && this.featureSource.normalizationFactor) {
|
|
43216
|
-
const scaleFactor = this.featureSource.normalizationFactor;
|
|
43217
|
-
for (let f of features) {
|
|
43218
|
-
f.value *= scaleFactor;
|
|
43219
|
-
}
|
|
43220
|
-
}
|
|
43221
|
-
if (this.scaleFactor) {
|
|
43222
|
-
const scaleFactor = this.scaleFactor;
|
|
43223
|
-
for (let f of features) {
|
|
43224
|
-
f.value *= scaleFactor;
|
|
43225
|
-
}
|
|
43226
|
-
}
|
|
43227
|
-
|
|
43228
|
-
// Summarize features to current resolution. This needs to be done here, rather than in the "draw" function,
|
|
43229
|
-
// for group autoscale to work.
|
|
43230
|
-
if (this.summarize && ("mean" === windowFunction || "min" === windowFunction || "max" === windowFunction)) {
|
|
43231
|
-
return summarizeData(features, start, bpPerPixel, windowFunction)
|
|
43232
|
-
} else {
|
|
43233
|
-
return features
|
|
43234
|
-
}
|
|
43235
|
-
}
|
|
43236
|
-
|
|
43237
|
-
menuItemList() {
|
|
43238
|
-
const items = [];
|
|
43239
|
-
|
|
43240
|
-
if (this.flipAxis !== undefined) {
|
|
43241
|
-
items.push('<hr>');
|
|
43242
|
-
|
|
43243
|
-
function click() {
|
|
43244
|
-
this.flipAxis = !this.flipAxis;
|
|
43245
|
-
this.trackView.repaintViews();
|
|
43246
|
-
}
|
|
43247
|
-
|
|
43248
|
-
items.push({label: 'Flip y-axis', click});
|
|
43249
|
-
}
|
|
43250
|
-
|
|
43251
|
-
if(this.featureSource.windowFunctions) {
|
|
43252
|
-
items.push(...this.wigSummarizationItems());
|
|
43253
|
-
}
|
|
43254
|
-
|
|
43255
|
-
items.push(...this.numericDataMenuItems());
|
|
43256
|
-
|
|
43257
|
-
return items
|
|
43258
|
-
}
|
|
43259
|
-
|
|
43260
|
-
wigSummarizationItems() {
|
|
43261
|
-
|
|
43262
|
-
const windowFunctions = this.featureSource.windowFunctions;
|
|
43263
|
-
|
|
43264
|
-
const menuItems = [];
|
|
43265
|
-
menuItems.push('<hr/>');
|
|
43266
|
-
menuItems.push("<div>Windowing function</div>");
|
|
43267
|
-
for (const wf of windowFunctions) {
|
|
43268
|
-
const object = $$1(createCheckbox(wf, this.windowFunction === wf));
|
|
43269
|
-
|
|
43270
|
-
function clickHandler() {
|
|
43271
|
-
this.windowFunction = wf;
|
|
43272
|
-
this.trackView.updateViews();
|
|
43273
|
-
}
|
|
43274
|
-
|
|
43275
|
-
menuItems.push({object, click: clickHandler});
|
|
43276
|
-
}
|
|
43277
|
-
|
|
43278
|
-
return menuItems
|
|
43279
|
-
}
|
|
43280
|
-
|
|
43281
|
-
|
|
43282
|
-
async getHeader() {
|
|
43283
|
-
|
|
43284
|
-
if (typeof this.featureSource.getHeader === "function") {
|
|
43285
|
-
this.header = await this.featureSource.getHeader();
|
|
43286
|
-
}
|
|
43287
|
-
return this.header
|
|
43288
|
-
}
|
|
43289
|
-
|
|
43290
|
-
// TODO: refactor to igvUtils.js
|
|
43291
|
-
getScaleFactor(min, max, height, logScale) {
|
|
43292
|
-
const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
|
|
43293
|
-
return scale
|
|
43294
|
-
}
|
|
43295
|
-
|
|
43296
|
-
computeYPixelValue(yValue, yScaleFactor) {
|
|
43297
|
-
return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
|
|
43298
|
-
}
|
|
43299
|
-
|
|
43300
|
-
computeYPixelValueInLogScale(yValue, yScaleFactor) {
|
|
43301
|
-
let maxValue = this.dataRange.max;
|
|
43302
|
-
let minValue = this.dataRange.min;
|
|
43303
|
-
if (maxValue <= 0) return 0 // TODO:
|
|
43304
|
-
if (minValue <= -1) minValue = 0;
|
|
43305
|
-
minValue = (minValue <= 0) ? 0 : Math.log10(minValue + 1);
|
|
43306
|
-
maxValue = Math.log10(maxValue + 1);
|
|
43307
|
-
yValue = Math.log10(yValue + 1);
|
|
43308
|
-
return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
|
|
43309
|
-
}
|
|
43310
|
-
|
|
43311
|
-
draw(options) {
|
|
43312
|
-
|
|
43313
|
-
const features = options.features;
|
|
43314
|
-
const ctx = options.context;
|
|
43315
|
-
const bpPerPixel = options.bpPerPixel;
|
|
43316
|
-
const bpStart = options.bpStart;
|
|
43317
|
-
const pixelWidth = options.pixelWidth;
|
|
43318
|
-
const pixelHeight = options.pixelHeight;
|
|
43319
|
-
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
43320
|
-
this.color || DEFAULT_COLOR$1;
|
|
43321
|
-
const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, options.pixelHeight, this.logScale);
|
|
43322
|
-
const yScale = (yValue) => this.logScale
|
|
43323
|
-
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
|
|
43324
|
-
: this.computeYPixelValue(yValue, scaleFactor);
|
|
43325
|
-
|
|
43326
|
-
if (features && features.length > 0) {
|
|
43327
|
-
|
|
43328
|
-
if (this.dataRange.min === undefined) this.dataRange.min = 0;
|
|
43329
|
-
|
|
43330
|
-
// Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
|
|
43331
|
-
// nothing to paint.
|
|
43332
|
-
if (this.dataRange.max > this.dataRange.min) {
|
|
43333
|
-
|
|
43334
|
-
let lastPixelEnd = -1;
|
|
43335
|
-
let lastY;
|
|
43336
|
-
const y0 = yScale(0);
|
|
43337
|
-
|
|
43338
|
-
for (let f of features) {
|
|
43339
|
-
|
|
43340
|
-
if (f.end < bpStart) continue
|
|
43341
|
-
if (f.start > bpEnd) break
|
|
43342
|
-
|
|
43343
|
-
const x = (f.start - bpStart) / bpPerPixel;
|
|
43344
|
-
if (isNaN(x)) continue
|
|
43345
|
-
|
|
43346
|
-
let y = yScale(f.value);
|
|
43347
|
-
|
|
43348
|
-
const rectEnd = (f.end - bpStart) / bpPerPixel;
|
|
43349
|
-
const width = rectEnd - x;
|
|
43350
|
-
|
|
43351
|
-
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
43352
|
-
|
|
43353
|
-
if (this.graphType === "line") {
|
|
43354
|
-
if (lastY !== undefined) {
|
|
43355
|
-
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
|
|
43356
|
-
"fillStyle": color,
|
|
43357
|
-
"strokeStyle": color
|
|
43358
|
-
});
|
|
43359
|
-
}
|
|
43360
|
-
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
43361
|
-
} else if (this.graphType === "points") {
|
|
43362
|
-
const pointSize = this.config.pointSize || 3;
|
|
43363
|
-
const px = x + width / 2;
|
|
43364
|
-
IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
|
|
43365
|
-
|
|
43366
|
-
if (f.value > this.dataRange.max) {
|
|
43367
|
-
IGVGraphics.fillCircle(ctx, px, pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
43368
|
-
} else if (f.value < this.dataRange.min) {
|
|
43369
|
-
IGVGraphics.fillCircle(ctx, px, pixelHeight - pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
43370
|
-
}
|
|
43371
|
-
|
|
43372
|
-
} else {
|
|
43373
|
-
// Default graph type (bar)
|
|
43374
|
-
const height = Math.min(pixelHeight, y - y0);
|
|
43375
|
-
IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
|
|
43376
|
-
if (f.value > this.dataRange.max) {
|
|
43377
|
-
IGVGraphics.fillRect(ctx, x, 0, width, 3, {fillStyle: this.overflowColor});
|
|
43378
|
-
} else if (f.value < this.dataRange.min) {
|
|
43379
|
-
IGVGraphics.fillRect(ctx, x, pixelHeight - 3, width, 3, {fillStyle: this.overflowColor});
|
|
43380
|
-
}
|
|
43381
|
-
|
|
43382
|
-
}
|
|
43383
|
-
lastPixelEnd = x + width;
|
|
43384
|
-
lastY = y;
|
|
43385
|
-
}
|
|
43386
|
-
|
|
43387
|
-
// If the track includes negative values draw a baseline
|
|
43388
|
-
if (this.dataRange.min < 0) {
|
|
43389
|
-
const ratio = this.dataRange.max / (this.dataRange.max - this.dataRange.min);
|
|
43390
|
-
const basepx = this.flipAxis ? (1 - ratio) * options.pixelHeight : ratio * options.pixelHeight;
|
|
43391
|
-
IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {strokeStyle: this.baselineColor});
|
|
43392
|
-
}
|
|
43393
|
-
}
|
|
43394
|
-
}
|
|
43395
|
-
|
|
43396
|
-
// Draw guidelines
|
|
43397
|
-
if (this.config.hasOwnProperty('guideLines')) {
|
|
43398
|
-
for (let line of this.config.guideLines) {
|
|
43399
|
-
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
|
|
43400
|
-
let y = yScale(line.y);
|
|
43401
|
-
let props = {
|
|
43402
|
-
'strokeStyle': line['color'],
|
|
43403
|
-
'strokeWidth': 2
|
|
43404
|
-
};
|
|
43405
|
-
if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);
|
|
43406
|
-
else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
|
|
43407
|
-
}
|
|
43408
|
-
}
|
|
43409
|
-
}
|
|
43410
|
-
}
|
|
43411
|
-
|
|
43412
|
-
popupData(clickState, features) {
|
|
43413
|
-
|
|
43414
|
-
if (features === undefined) features = this.clickedFeatures(clickState);
|
|
43415
|
-
|
|
43416
|
-
if (features && features.length > 0) {
|
|
43417
|
-
|
|
43418
|
-
const genomicLocation = clickState.genomicLocation;
|
|
43419
|
-
const popupData = [];
|
|
43420
|
-
|
|
43421
|
-
// Sort features based on distance from click
|
|
43422
|
-
features.sort(function (a, b) {
|
|
43423
|
-
const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
|
|
43424
|
-
const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
|
|
43425
|
-
return distA - distB
|
|
43426
|
-
});
|
|
43427
|
-
|
|
43428
|
-
// Display closest 10
|
|
43429
|
-
const displayFeatures = features.length > 10 ? features.slice(0, 10) : features;
|
|
43430
|
-
|
|
43431
|
-
// Resort in ascending order
|
|
43432
|
-
displayFeatures.sort(function (a, b) {
|
|
43433
|
-
return a.start - b.start
|
|
43434
|
-
});
|
|
43435
|
-
|
|
43436
|
-
for (let selectedFeature of displayFeatures) {
|
|
43437
|
-
if (selectedFeature) {
|
|
43438
|
-
if (popupData.length > 0) {
|
|
43439
|
-
popupData.push('<hr/>');
|
|
43440
|
-
}
|
|
43441
|
-
let posString = (selectedFeature.end - selectedFeature.start) === 1 ?
|
|
43442
|
-
numberFormatter$1(Math.floor(selectedFeature.start) + 1)
|
|
43443
|
-
: numberFormatter$1(Math.floor(selectedFeature.start) + 1) + "-" + numberFormatter$1(Math.floor(selectedFeature.end));
|
|
43444
|
-
popupData.push({name: "Position:", value: posString});
|
|
43445
|
-
popupData.push({
|
|
43446
|
-
name: "Value: ",
|
|
43447
|
-
value: numberFormatter$1(selectedFeature.value.toFixed(4))
|
|
43448
|
-
});
|
|
43449
|
-
}
|
|
43450
|
-
}
|
|
43451
|
-
if (displayFeatures.length < features.length) {
|
|
43452
|
-
popupData.push("<hr/>...");
|
|
43453
|
-
}
|
|
43454
|
-
|
|
43455
|
-
return popupData
|
|
43456
|
-
|
|
43457
|
-
} else {
|
|
43458
|
-
return []
|
|
43459
|
-
}
|
|
43460
|
-
}
|
|
43461
|
-
|
|
43462
|
-
get supportsWholeGenome() {
|
|
43463
|
-
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
43464
|
-
}
|
|
43465
|
-
|
|
43466
|
-
/**
|
|
43467
|
-
* Return color for feature.
|
|
43468
|
-
* @param feature
|
|
43469
|
-
* @returns {string}
|
|
43470
|
-
*/
|
|
43471
|
-
|
|
43472
|
-
getColorForFeature(f) {
|
|
43473
|
-
let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$1;
|
|
43474
|
-
return (typeof c === "function") ? c(f.value) : c
|
|
43475
|
-
}
|
|
43476
|
-
|
|
43477
|
-
/**
|
|
43478
|
-
* Called when the track is removed. Do any needed cleanup here
|
|
43479
|
-
*/
|
|
43480
|
-
dispose() {
|
|
43481
|
-
this.trackView = undefined;
|
|
43482
|
-
}
|
|
43483
|
-
|
|
43484
|
-
}
|
|
43485
|
-
|
|
43486
|
-
/**
|
|
43487
|
-
* Summarize wig data in bins of size "bpPerPixel" with the given window function.
|
|
43488
|
-
*
|
|
43489
|
-
* @param features wig (numeric) data -- features cannot overlap, and are in ascending order by start position
|
|
43490
|
-
* @param startBP bp start position for computing binned data
|
|
43491
|
-
* @param bpPerPixel bp per pixel (bin)
|
|
43492
|
-
* @param windowFunction mean, min, or max
|
|
43493
|
-
* @returns {*|*[]}
|
|
43494
|
-
*/
|
|
43495
|
-
function summarizeData(features, startBP, bpPerPixel, windowFunction = "mean") {
|
|
43496
|
-
|
|
43497
|
-
if (bpPerPixel <= 1 || !features || features.length === 0) {
|
|
43498
|
-
return features
|
|
43499
|
-
}
|
|
43500
|
-
|
|
43501
|
-
// Assume features are sorted by position. Wig features cannot overlap. Note, UCSC "reductionLevel" == bpPerPixel
|
|
43502
|
-
const chr = features[0].chr;
|
|
43503
|
-
const binSize = bpPerPixel;
|
|
43504
|
-
const summaryFeatures = [];
|
|
43505
|
-
|
|
43506
|
-
const finishBin = (bin) => {
|
|
43507
|
-
const start = startBP + bin.bin * binSize;
|
|
43508
|
-
const end = start + binSize;
|
|
43509
|
-
let value;
|
|
43510
|
-
switch (windowFunction) {
|
|
43511
|
-
case "mean":
|
|
43512
|
-
value = bin.sumData / bin.count;
|
|
43513
|
-
break
|
|
43514
|
-
case "max":
|
|
43515
|
-
value = bin.max;
|
|
43516
|
-
break
|
|
43517
|
-
case "min":
|
|
43518
|
-
value = bin.min;
|
|
43519
|
-
break
|
|
43520
|
-
default:
|
|
43521
|
-
throw Error(`Unknown window function: ${windowFunction}`)
|
|
43522
|
-
}
|
|
43523
|
-
const description = `${windowFunction} of ${bin.count} values`;
|
|
43524
|
-
summaryFeatures.push({chr, start, end, value, description});
|
|
43525
|
-
};
|
|
43526
|
-
|
|
43527
|
-
let currentBinData;
|
|
43528
|
-
for (let f of features) {
|
|
43529
|
-
|
|
43530
|
-
// Loop through bins this feature overlaps, updating the weighted sum for each bin or min/max,
|
|
43531
|
-
// depending on window function
|
|
43532
|
-
let startBin = Math.floor((f.start - startBP) / binSize);
|
|
43533
|
-
const endBin = Math.floor((f.end - startBP) / binSize);
|
|
43534
|
-
|
|
43535
|
-
if (currentBinData && startBin === currentBinData.bin) {
|
|
43536
|
-
currentBinData.add(f);
|
|
43537
|
-
startBin++;
|
|
43538
|
-
}
|
|
43539
|
-
|
|
43540
|
-
if (!currentBinData || endBin > currentBinData.bin) {
|
|
43541
|
-
|
|
43542
|
-
if(currentBinData) {
|
|
43543
|
-
finishBin(currentBinData);
|
|
43544
|
-
}
|
|
43545
|
-
|
|
43546
|
-
// Feature stretches across multiple bins.
|
|
43547
|
-
if (endBin > startBin) {
|
|
43548
|
-
const end = startBP + endBin * binSize;
|
|
43549
|
-
summaryFeatures.push({chr, start: f.start, end, value: f.value});
|
|
43550
|
-
}
|
|
43551
|
-
|
|
43552
|
-
currentBinData = new SummaryBinData(endBin, f);
|
|
43553
|
-
}
|
|
43554
|
-
|
|
43555
|
-
}
|
|
43556
|
-
if(currentBinData) {
|
|
43557
|
-
finishBin(currentBinData);
|
|
43558
|
-
}
|
|
43559
|
-
|
|
43560
|
-
// Consolidate
|
|
43561
|
-
const c = [];
|
|
43562
|
-
let lastFeature = summaryFeatures[0];
|
|
43563
|
-
for (let f of summaryFeatures) {
|
|
43564
|
-
if (lastFeature.value === f.value && f.start <= lastFeature.end) {
|
|
43565
|
-
lastFeature.end = f.end;
|
|
43566
|
-
} else {
|
|
43567
|
-
c.push(lastFeature);
|
|
43568
|
-
lastFeature = f;
|
|
43569
|
-
}
|
|
43570
|
-
}
|
|
43571
|
-
c.push(lastFeature);
|
|
43572
|
-
|
|
43573
|
-
return c
|
|
43574
|
-
|
|
43575
|
-
}
|
|
43576
|
-
|
|
43577
|
-
class SummaryBinData {
|
|
43578
|
-
constructor(bin, feature) {
|
|
43579
|
-
this.bin = bin;
|
|
43580
|
-
this.sumData = feature.value;
|
|
43581
|
-
this.count = 1;
|
|
43582
|
-
this.min = feature.value;
|
|
43583
|
-
this.max = feature.value;
|
|
43584
|
-
}
|
|
43585
|
-
|
|
43586
|
-
add(feature) {
|
|
43587
|
-
this.sumData += feature.value;
|
|
43588
|
-
this.max = Math.max(feature.value, this.max);
|
|
43589
|
-
this.min = Math.min(feature.value, this.min);
|
|
43590
|
-
this.count++;
|
|
43591
|
-
}
|
|
43592
|
-
|
|
43593
|
-
get mean() {
|
|
43594
|
-
return this.sumData / this.count
|
|
43595
|
-
}
|
|
43596
|
-
}
|
|
43597
|
-
|
|
43598
43641
|
/**
|
|
43599
43642
|
*
|
|
43600
43643
|
* @param cs - object containing
|
|
@@ -48509,7 +48552,7 @@ class AlignmentContainer {
|
|
|
48509
48552
|
allAlignments() {
|
|
48510
48553
|
if (this.alignments) {
|
|
48511
48554
|
return this.alignments
|
|
48512
|
-
} else {
|
|
48555
|
+
} else if (this.packedGroups) {
|
|
48513
48556
|
const all = Array.from(this.packedGroups.values()).flatMap(group => group.rows.flatMap(row => row.alignments));
|
|
48514
48557
|
if (this.#unpacked && this.#unpacked.length > 0) {
|
|
48515
48558
|
for (let a of this.#unpacked) {
|
|
@@ -48517,6 +48560,8 @@ class AlignmentContainer {
|
|
|
48517
48560
|
}
|
|
48518
48561
|
}
|
|
48519
48562
|
return all
|
|
48563
|
+
} else {
|
|
48564
|
+
return []
|
|
48520
48565
|
}
|
|
48521
48566
|
}
|
|
48522
48567
|
|
|
@@ -48525,9 +48570,10 @@ class AlignmentContainer {
|
|
|
48525
48570
|
}
|
|
48526
48571
|
|
|
48527
48572
|
sortRows(options) {
|
|
48528
|
-
|
|
48529
|
-
|
|
48530
|
-
|
|
48573
|
+
if(this.packedGroups) {
|
|
48574
|
+
for (let group of this.packedGroups.values()) {
|
|
48575
|
+
group.sortRows(options, this);
|
|
48576
|
+
}
|
|
48531
48577
|
}
|
|
48532
48578
|
}
|
|
48533
48579
|
}
|
|
@@ -49772,46 +49818,41 @@ const BamUtils = {
|
|
|
49772
49818
|
},
|
|
49773
49819
|
|
|
49774
49820
|
/**
|
|
49821
|
+
* @param ba - UInt8Array bytes to decode
|
|
49775
49822
|
*
|
|
49776
|
-
* @
|
|
49777
|
-
* @param genome optional igv genome object
|
|
49778
|
-
* @returns {{ magicNumer: number, size: number, chrNames: Array, chrToIndex: ({}|*), chrAliasTable: ({}|*) }}
|
|
49823
|
+
* @returns {{size: *, chrNames: *[], magicNumber: *, chrToIndex: {}}}
|
|
49779
49824
|
*/
|
|
49780
|
-
decodeBamHeader: function (ba
|
|
49825
|
+
decodeBamHeader: function (ba) {
|
|
49781
49826
|
|
|
49782
|
-
var magic, samHeaderLen, samHeader, chrToIndex, chrNames;
|
|
49783
49827
|
|
|
49784
|
-
magic = readInt(ba, 0);
|
|
49828
|
+
const magic = readInt(ba, 0);
|
|
49785
49829
|
if (magic !== BAM1_MAGIC_NUMBER) {
|
|
49786
49830
|
throw new Error('BAM header errror: bad magic number. This could be caused by either a corrupt or missing file.')
|
|
49787
49831
|
}
|
|
49788
49832
|
|
|
49789
|
-
samHeaderLen = readInt(ba, 4);
|
|
49790
|
-
samHeader = '';
|
|
49791
|
-
|
|
49833
|
+
const samHeaderLen = readInt(ba, 4);
|
|
49834
|
+
let samHeader = '';
|
|
49792
49835
|
for (var i = 0; i < samHeaderLen; ++i) {
|
|
49793
49836
|
samHeader += String.fromCharCode(ba[i + 8]);
|
|
49794
49837
|
}
|
|
49795
49838
|
|
|
49796
|
-
|
|
49797
|
-
|
|
49839
|
+
const nRef = readInt(ba, samHeaderLen + 8);
|
|
49840
|
+
let p = samHeaderLen + 12;
|
|
49798
49841
|
|
|
49799
|
-
chrToIndex = {};
|
|
49800
|
-
chrNames = [];
|
|
49842
|
+
const chrToIndex = {};
|
|
49843
|
+
const chrNames = [];
|
|
49801
49844
|
|
|
49802
49845
|
for (i = 0; i < nRef; ++i) {
|
|
49803
|
-
|
|
49804
|
-
|
|
49805
|
-
for (var j = 0; j <
|
|
49846
|
+
const len = readInt(ba, p);
|
|
49847
|
+
let name = '';
|
|
49848
|
+
for (var j = 0; j < len - 1; ++j) {
|
|
49806
49849
|
name += String.fromCharCode(ba[p + 4 + j]);
|
|
49807
49850
|
}
|
|
49808
|
-
readInt(ba, p + lName + 4);
|
|
49809
|
-
//dlog(name + ': ' + lRef);
|
|
49810
49851
|
|
|
49811
49852
|
chrToIndex[name] = i;
|
|
49812
49853
|
chrNames[i] = name;
|
|
49813
49854
|
|
|
49814
|
-
p = p + 8 +
|
|
49855
|
+
p = p + 8 + len;
|
|
49815
49856
|
}
|
|
49816
49857
|
|
|
49817
49858
|
return {
|
|
@@ -50801,8 +50842,8 @@ class HtsgetBamReader extends HtsgetReader {
|
|
|
50801
50842
|
const ba = unbgzf(compressedData.buffer);
|
|
50802
50843
|
this.header = BamUtils.decodeBamHeader(ba, this.genome);
|
|
50803
50844
|
this.chrAliasTable = new Map();
|
|
50804
|
-
for (let
|
|
50805
|
-
this.chrAliasTable.set(
|
|
50845
|
+
for (let name of this.header.chrNames) {
|
|
50846
|
+
this.chrAliasTable.set(name, this.genome.getChromosomeName(name));
|
|
50806
50847
|
}
|
|
50807
50848
|
}
|
|
50808
50849
|
|
|
@@ -50812,6 +50853,7 @@ class HtsgetBamReader extends HtsgetReader {
|
|
|
50812
50853
|
|
|
50813
50854
|
// BAM decoding
|
|
50814
50855
|
const ba = unbgzf(compressedData.buffer);
|
|
50856
|
+
this.header = BamUtils.decodeBamHeader(ba, this.genome);
|
|
50815
50857
|
|
|
50816
50858
|
const chrIdx = this.header.chrToIndex[chr];
|
|
50817
50859
|
const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
|
|
@@ -57321,23 +57363,24 @@ class AlignmentTrack extends TrackBase {
|
|
|
57321
57363
|
const y = clickState.y;
|
|
57322
57364
|
const offsetY = y - this.top;
|
|
57323
57365
|
const genomicLocation = clickState.genomicLocation;
|
|
57324
|
-
|
|
57325
|
-
|
|
57326
|
-
|
|
57327
|
-
|
|
57328
|
-
|
|
57329
|
-
|
|
57330
|
-
|
|
57331
|
-
|
|
57332
|
-
|
|
57333
|
-
|
|
57334
|
-
|
|
57335
|
-
|
|
57336
|
-
|
|
57337
|
-
|
|
57338
|
-
|
|
57339
|
-
|
|
57340
|
-
|
|
57366
|
+
|
|
57367
|
+
if(features.packedGroups) {
|
|
57368
|
+
let minGroupY = Number.MAX_VALUE;
|
|
57369
|
+
for (let group of features.packedGroups.values()) {
|
|
57370
|
+
minGroupY = Math.min(minGroupY, group.pixelTop);
|
|
57371
|
+
if (offsetY > group.pixelTop && offsetY <= group.pixelBottom) {
|
|
57372
|
+
|
|
57373
|
+
const alignmentRowHeight = this.displayMode === "SQUISHED" ?
|
|
57374
|
+
this.squishedRowHeight :
|
|
57375
|
+
this.alignmentRowHeight;
|
|
57376
|
+
|
|
57377
|
+
let packedAlignmentsIndex = Math.floor((offsetY - group.pixelTop) / alignmentRowHeight);
|
|
57378
|
+
|
|
57379
|
+
if (packedAlignmentsIndex >= 0 && packedAlignmentsIndex < group.length) {
|
|
57380
|
+
const alignmentRow = group.rows[packedAlignmentsIndex];
|
|
57381
|
+
const clicked = alignmentRow.alignments.filter(alignment => alignment.containsLocation(genomicLocation, this.showSoftClips));
|
|
57382
|
+
if (clicked.length > 0) return clicked[0]
|
|
57383
|
+
}
|
|
57341
57384
|
}
|
|
57342
57385
|
}
|
|
57343
57386
|
}
|
|
@@ -57656,6 +57699,7 @@ class CoverageTrack {
|
|
|
57656
57699
|
|
|
57657
57700
|
|
|
57658
57701
|
constructor(config, parent) {
|
|
57702
|
+
this.featureType = 'numeric';
|
|
57659
57703
|
this.parent = parent;
|
|
57660
57704
|
this.featureSource = parent.featureSource;
|
|
57661
57705
|
|
|
@@ -66134,12 +66178,12 @@ class CNVPytorTrack extends TrackBase {
|
|
|
66134
66178
|
if (this.signals.includes(signal_name)) {
|
|
66135
66179
|
let tconf = {};
|
|
66136
66180
|
tconf.type = "wig";
|
|
66137
|
-
tconf.isMergedTrack = true;
|
|
66138
66181
|
tconf.features = wig;
|
|
66139
66182
|
tconf.name = signal_name;
|
|
66140
66183
|
tconf.color = this.signal_colors.filter(x => x.singal_name === signal_name).map(x => x.color);
|
|
66141
66184
|
const t = await this.browser.createTrack(tconf);
|
|
66142
66185
|
if (t) {
|
|
66186
|
+
t.isMergedTrack = true;
|
|
66143
66187
|
t.autoscale = false; // Scaling done from merged track
|
|
66144
66188
|
this.tracks.push(t);
|
|
66145
66189
|
} else {
|
|
@@ -66318,12 +66362,12 @@ class CNVPytorTrack extends TrackBase {
|
|
|
66318
66362
|
if (this.signals.includes(signal_name)) {
|
|
66319
66363
|
let tconf = {};
|
|
66320
66364
|
tconf.type = "wig";
|
|
66321
|
-
tconf.isMergedTrack = true;
|
|
66322
66365
|
tconf.features = wig;
|
|
66323
66366
|
tconf.name = signal_name;
|
|
66324
66367
|
tconf.color = this.signal_colors.filter(x => x.singal_name === signal_name).map(x => x.color);
|
|
66325
66368
|
const t = await this.browser.createTrack(tconf);
|
|
66326
66369
|
if (t) {
|
|
66370
|
+
t.isMergedTrack = true;
|
|
66327
66371
|
t.autoscale = false; // Scaling done from merged track
|
|
66328
66372
|
this.tracks.push(t);
|
|
66329
66373
|
} else {
|
|
@@ -66716,23 +66760,26 @@ class VariantTrack extends TrackBase {
|
|
|
66716
66760
|
this.header = await this.getHeader();
|
|
66717
66761
|
|
|
66718
66762
|
// Set colorBy, if not explicitly set default to allele frequency, if available, otherwise default to none (undefined)
|
|
66719
|
-
|
|
66720
|
-
|
|
66721
|
-
|
|
66722
|
-
|
|
66723
|
-
this.
|
|
66724
|
-
|
|
66763
|
+
if(this.header.INFO) {
|
|
66764
|
+
const infoFields = new Set(Object.keys(this.header.INFO));
|
|
66765
|
+
if (this.config.colorBy) {
|
|
66766
|
+
this.colorBy = this.config.colorBy;
|
|
66767
|
+
} else if (!this.config.color && infoFields.has('AF')) {
|
|
66768
|
+
this.colorBy = 'AF';
|
|
66769
|
+
}
|
|
66725
66770
|
|
|
66726
|
-
|
|
66727
|
-
|
|
66728
|
-
|
|
66729
|
-
|
|
66730
|
-
|
|
66731
|
-
|
|
66732
|
-
|
|
66733
|
-
|
|
66734
|
-
|
|
66771
|
+
// Configure menu items based on info available
|
|
66772
|
+
if (infoFields.has('AF')) {
|
|
66773
|
+
this._colorByItems.set('AF', 'Allele frequency');
|
|
66774
|
+
}
|
|
66775
|
+
if (infoFields.has('VT')) {
|
|
66776
|
+
this._colorByItems.set('VT', 'Variant Type');
|
|
66777
|
+
}
|
|
66778
|
+
if (infoFields.has('SVTYPE')) {
|
|
66779
|
+
this._colorByItems.set('SVTYPE', 'SV Type');
|
|
66780
|
+
}
|
|
66735
66781
|
}
|
|
66782
|
+
|
|
66736
66783
|
if (this.config.colorBy && !this._colorByItems.has(this.config.colorBy)) {
|
|
66737
66784
|
this._colorByItems.set(this.config.colorBy, this.config.colorBy);
|
|
66738
66785
|
}
|
|
@@ -69577,11 +69624,15 @@ someMotifValues.forEach(motif => {
|
|
|
69577
69624
|
JUNCTION_MOTIF_PALETTE.getColor(motif);
|
|
69578
69625
|
});
|
|
69579
69626
|
|
|
69580
|
-
// rendering context with values that only need to be computed once per render, rather than for each splice junction
|
|
69581
|
-
const junctionRenderingContext = {};
|
|
69582
69627
|
|
|
69583
69628
|
class SpliceJunctionTrack extends TrackBase {
|
|
69584
69629
|
|
|
69630
|
+
static defaults = {
|
|
69631
|
+
margin: 10,
|
|
69632
|
+
colorByNumReadsThreshold: 5,
|
|
69633
|
+
height: 100
|
|
69634
|
+
}
|
|
69635
|
+
|
|
69585
69636
|
constructor(config, browser) {
|
|
69586
69637
|
super(config, browser);
|
|
69587
69638
|
}
|
|
@@ -69602,16 +69653,6 @@ class SpliceJunctionTrack extends TrackBase {
|
|
|
69602
69653
|
FeatureSource(config, this.browser.genome);
|
|
69603
69654
|
}
|
|
69604
69655
|
|
|
69605
|
-
this.margin = config.margin === undefined ? 10 : config.margin;
|
|
69606
|
-
|
|
69607
|
-
if (!this.height) {
|
|
69608
|
-
this.height = 100;
|
|
69609
|
-
}
|
|
69610
|
-
|
|
69611
|
-
//set defaults
|
|
69612
|
-
if (config.colorByNumReadsThreshold === undefined) {
|
|
69613
|
-
config.colorByNumReadsThreshold = 5;
|
|
69614
|
-
}
|
|
69615
69656
|
}
|
|
69616
69657
|
|
|
69617
69658
|
async postInit() {
|
|
@@ -69666,13 +69707,16 @@ class SpliceJunctionTrack extends TrackBase {
|
|
|
69666
69707
|
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
69667
69708
|
|
|
69668
69709
|
|
|
69669
|
-
if (!this.
|
|
69710
|
+
if (!this.isMergedTrack) {
|
|
69670
69711
|
IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"});
|
|
69671
69712
|
}
|
|
69672
69713
|
|
|
69673
69714
|
if (featureList) {
|
|
69674
69715
|
|
|
69675
69716
|
|
|
69717
|
+
// rendering context with values that only need to be computed once per render, rather than for each splice junction
|
|
69718
|
+
const junctionRenderingContext = {};
|
|
69719
|
+
|
|
69676
69720
|
junctionRenderingContext.referenceFrame = options.viewport.referenceFrame;
|
|
69677
69721
|
junctionRenderingContext.referenceFrameStart = junctionRenderingContext.referenceFrame.start;
|
|
69678
69722
|
junctionRenderingContext.referenceFrameEnd = junctionRenderingContext.referenceFrameStart +
|
|
@@ -69685,7 +69729,7 @@ class SpliceJunctionTrack extends TrackBase {
|
|
|
69685
69729
|
for (let feature of featureList) {
|
|
69686
69730
|
if (feature.end < bpStart) continue
|
|
69687
69731
|
if (feature.start > bpEnd) break
|
|
69688
|
-
this.renderJunction(feature, bpStart, bpPerPixel, pixelHeight, ctx);
|
|
69732
|
+
this.renderJunction(feature, bpStart, bpPerPixel, pixelHeight, ctx, junctionRenderingContext);
|
|
69689
69733
|
}
|
|
69690
69734
|
|
|
69691
69735
|
} else {
|
|
@@ -69702,7 +69746,7 @@ class SpliceJunctionTrack extends TrackBase {
|
|
|
69702
69746
|
* @param pixelHeight pixel height of the current canvas
|
|
69703
69747
|
* @param ctx the canvas 2d context
|
|
69704
69748
|
*/
|
|
69705
|
-
renderJunction(feature, bpStart, xScale, pixelHeight, ctx) {
|
|
69749
|
+
renderJunction(feature, bpStart, xScale, pixelHeight, ctx, junctionRenderingContext) {
|
|
69706
69750
|
// cache whether this junction is rendered or filtered out. Use later to exclude non-rendered junctions from click detection.
|
|
69707
69751
|
feature.isVisible = false;
|
|
69708
69752
|
|
|
@@ -69786,7 +69830,7 @@ class SpliceJunctionTrack extends TrackBase {
|
|
|
69786
69830
|
}
|
|
69787
69831
|
|
|
69788
69832
|
const py = this.margin;
|
|
69789
|
-
const rowHeight =
|
|
69833
|
+
const rowHeight = pixelHeight;
|
|
69790
69834
|
|
|
69791
69835
|
const cy = py + 0.5 * rowHeight;
|
|
69792
69836
|
let topY = py;
|
|
@@ -70471,7 +70515,7 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
|
|
|
70471
70515
|
})
|
|
70472
70516
|
}
|
|
70473
70517
|
|
|
70474
|
-
const _version = "3.0.
|
|
70518
|
+
const _version = "3.0.3";
|
|
70475
70519
|
function version() {
|
|
70476
70520
|
return _version
|
|
70477
70521
|
}
|
|
@@ -71942,16 +71986,17 @@ class ROIMenu {
|
|
|
71942
71986
|
|
|
71943
71987
|
}
|
|
71944
71988
|
|
|
71945
|
-
async present(feature,
|
|
71946
|
-
const menuItems = this.menuItems(feature,
|
|
71989
|
+
async present(feature, roiSet, event, roiManager, columnContainer, regionElement) {
|
|
71990
|
+
const menuItems = this.menuItems(feature, roiSet, event, roiManager, columnContainer, regionElement);
|
|
71947
71991
|
this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
|
|
71948
71992
|
}
|
|
71949
71993
|
|
|
71950
|
-
menuItems(feature,
|
|
71994
|
+
menuItems(feature, roiSet, event, roiManager, columnContainer, regionElement) {
|
|
71995
|
+
const items = feature.name ? [`<b>${feature.name}</b><br/>`] : [];
|
|
71996
|
+
if ('name' in roiSet) items.push(`<b>ROI Set: ${roiSet.name}</b>`);
|
|
71997
|
+
if (items.length > 0) items.push(`<hr/>`);
|
|
71951
71998
|
|
|
71952
|
-
|
|
71953
|
-
|
|
71954
|
-
if (isUserDefined) {
|
|
71999
|
+
if (roiSet.isUserDefined) {
|
|
71955
72000
|
items.push(
|
|
71956
72001
|
{
|
|
71957
72002
|
label: 'Set description ...',
|
|
@@ -72015,7 +72060,7 @@ class ROIMenu {
|
|
|
72015
72060
|
}
|
|
72016
72061
|
|
|
72017
72062
|
|
|
72018
|
-
if (isUserDefined) {
|
|
72063
|
+
if (roiSet.isUserDefined) {
|
|
72019
72064
|
items.push(
|
|
72020
72065
|
'<hr/>',
|
|
72021
72066
|
{
|
|
@@ -72300,13 +72345,17 @@ class ROIManager {
|
|
|
72300
72345
|
const [rectA, rectB] = tracks
|
|
72301
72346
|
.map(track => track.trackView.viewports[0].$viewport.get(0))
|
|
72302
72347
|
.map(element => getElementVerticalDimension(element));
|
|
72303
|
-
|
|
72348
|
+
|
|
72349
|
+
//Covers cases in which ruler and/or ideogram are hidden
|
|
72350
|
+
const heightA = rectA ? rectA.height : 0;
|
|
72351
|
+
const heightB = rectB ? rectB.height : 0;
|
|
72352
|
+
|
|
72304
72353
|
const elements = browser.columnContainer.querySelectorAll('.igv-roi-region');
|
|
72305
72354
|
|
|
72306
72355
|
const fudge = -0.5;
|
|
72307
72356
|
if (elements) {
|
|
72308
72357
|
for (const element of elements) {
|
|
72309
|
-
element.style.marginTop = `${
|
|
72358
|
+
element.style.marginTop = `${heightA + heightB + fudge}px`;
|
|
72310
72359
|
}
|
|
72311
72360
|
|
|
72312
72361
|
}
|
|
@@ -72474,7 +72523,6 @@ class ROIManager {
|
|
|
72474
72523
|
if (features) {
|
|
72475
72524
|
|
|
72476
72525
|
for (let feature of features) {
|
|
72477
|
-
|
|
72478
72526
|
const regionKey = createRegionKey(chr, feature.start, feature.end);
|
|
72479
72527
|
|
|
72480
72528
|
const {
|
|
@@ -72519,8 +72567,7 @@ class ROIManager {
|
|
|
72519
72567
|
event.stopPropagation();
|
|
72520
72568
|
|
|
72521
72569
|
translateMouseCoordinates(event, columnContainer);
|
|
72522
|
-
|
|
72523
|
-
this.roiMenu.present(feature, isUserDefined, event, this, columnContainer, regionElement);
|
|
72570
|
+
this.roiMenu.present(feature, roiSet, event, this, columnContainer, regionElement);
|
|
72524
72571
|
});
|
|
72525
72572
|
|
|
72526
72573
|
|
|
@@ -72922,7 +72969,7 @@ class ChromAliasBB {
|
|
|
72922
72969
|
}
|
|
72923
72970
|
|
|
72924
72971
|
/**
|
|
72925
|
-
* Return the canonical chromosome name for the alias. If none found return the alias.
|
|
72972
|
+
* Return the cached canonical chromosome name for the alias. If none found return the alias.
|
|
72926
72973
|
*
|
|
72927
72974
|
* Note this will only work if a "search" for ths chromosome has been performed previously.
|
|
72928
72975
|
*
|
|
@@ -73689,7 +73736,7 @@ function generateGenomeID(config) {
|
|
|
73689
73736
|
}
|
|
73690
73737
|
}
|
|
73691
73738
|
|
|
73692
|
-
var igvCss = '.igv-ui-dropdown {\n cursor: default;\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n background-color: white;\n}\n.igv-ui-dropdown > div {\n overflow-y: auto;\n overflow-x: hidden;\n background-color: white;\n}\n.igv-ui-dropdown > div > div {\n padding: 4px;\n width: 100%;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: 1px;\n background-color: white;\n}\n.igv-ui-dropdown > div > div:last-child {\n border-bottom-color: transparent;\n border-bottom-width: 0;\n}\n.igv-ui-dropdown > div > div:hover {\n cursor: pointer;\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white;\n}\n.igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-width: 0;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px;\n}\n.igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-popover > div:last-child {\n user-select: text;\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white;\n border-bottom-width: 0;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.igv-ui-popover > div:last-child > div {\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.igv-ui-popover > div:last-child > div > span {\n font-weight: bolder;\n}\n.igv-ui-popover > div:last-child hr {\n width: 100%;\n}\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px;\n}\n.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll;\n}\n.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset;\n}\n.igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f;\n}\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px;\n}\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray;\n}\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px;\n}\n.igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f;\n}\n.igv-ui-colorpicker-menu-close-button i.fa:hover,\n.igv-ui-colorpicker-menu-close-button i.fa:focus,\n.igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f;\n}\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: fit-content;\n padding-bottom: 16px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input > div {\n width: fit-content;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input[type=range] {\n width: 70%;\n -webkit-appearance: none;\n background: linear-gradient(90deg, white, black);\n outline: none;\n margin: 0;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input output {\n display: block;\n height: 100%;\n width: 20%;\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n padding-top: 16px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div {\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-container > div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd;\n}\n.igv-ui-generic-container > div:first-child > div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px;\n}\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n}\n.igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-ui-panel, .igv-ui-panel-row, .igv-ui-panel-column {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column;\n}\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row;\n}\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-ui-table {\n background-color: white;\n}\n\n.igv-ui-table thead {\n position: sticky;\n top: 0;\n}\n\n.igv-ui-table th {\n text-align: left;\n}\n\n.igv-ui-table td {\n padding-right: 20px;\n}\n\n.igv-ui-table tr:hover {\n background-color: lightblue;\n}\n\n.igv-ui-center-fixed {\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n}\n\n.igv-navbar {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n box-sizing: border-box;\n width: 100%;\n color: #444;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n line-height: 32px;\n padding-left: 8px;\n padding-right: 8px;\n margin-top: 2px;\n margin-bottom: 6px;\n height: 32px;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: #f3f3f3;\n}\n.igv-navbar .igv-navbar-left-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 32px;\n line-height: 32px;\n}\n.igv-navbar .igv-navbar-left-container .igv-logo {\n width: 34px;\n height: 32px;\n margin-right: 8px;\n}\n.igv-navbar .igv-navbar-left-container .igv-current-genome {\n height: 32px;\n margin-left: 4px;\n margin-right: 4px;\n user-select: none;\n line-height: 32px;\n vertical-align: middle;\n text-align: center;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 100%;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n height: 100%;\n width: 125px;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container select {\n display: block;\n cursor: pointer;\n width: 100px;\n height: 75%;\n outline: none;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n margin-left: 8px;\n height: 22px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 240px;\n height: 22px;\n line-height: 22px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container input.igv-search-input {\n cursor: text;\n width: 85%;\n height: 22px;\n line-height: 22px;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n text-align: left;\n padding-left: 8px;\n margin-right: 8px;\n outline: none;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: white;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container .igv-search-icon-container {\n cursor: pointer;\n height: 16px;\n width: 16px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-windowsize-panel-container {\n margin-left: 4px;\n user-select: none;\n}\n.igv-navbar .igv-navbar-right-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container {\n position: relative;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-hidden {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n height: 100%;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget {\n color: #737373;\n font-size: 18px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:first-child {\n height: 20px;\n width: 20px;\n margin-left: unset;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:last-child {\n height: 20px;\n width: 20px;\n margin-left: 4px;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n display: block;\n height: fit-content;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n display: block;\n width: 125px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 {\n color: #737373;\n font-size: 18px;\n height: 32px;\n line-height: 32px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:first-child {\n height: 20px;\n width: 20px;\n margin-left: unset;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:last-child {\n height: 20px;\n width: 20px;\n margin-left: 4px;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:nth-child(even) {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n display: none;\n}\n\n.igv-navbar-button {\n display: block;\n box-sizing: unset;\n padding-left: 6px;\n padding-right: 6px;\n height: 18px;\n text-transform: capitalize;\n user-select: none;\n line-height: 18px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 11px;\n font-weight: 200;\n color: #737373;\n background-color: #f3f3f3;\n border-color: #737373;\n border-style: solid;\n border-width: thin;\n border-radius: 6px;\n}\n\n.igv-navbar-button:hover {\n cursor: pointer;\n}\n\n.igv-navbar-button-clicked {\n color: white;\n background-color: #737373;\n}\n\n.igv-navbar-icon-button {\n cursor: pointer;\n position: relative;\n width: 24px;\n height: 24px;\n margin-left: 4px;\n margin-right: 4px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-icon-button > div:first-child {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: -18px;\n width: 24px;\n height: 24px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-icon-button > div:last-child {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 18px;\n width: 24px;\n height: 24px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.igv-navbar-text-button {\n cursor: pointer;\n position: relative;\n margin-left: 2px;\n margin-right: 2px;\n border: none;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n.igv-navbar-text-button > div:nth-child(2) {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 0;\n width: 38px;\n height: 18px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-text-button > div:nth-child(3) {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 42px;\n width: 38px;\n height: 18px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n#igv-text-button-label {\n text-anchor: middle;\n dominant-baseline: middle;\n}\n\n.igv-navbar-text-button-svg-inactive rect {\n stroke: #737373;\n fill: white;\n}\n.igv-navbar-text-button-svg-inactive text {\n fill: #737373;\n}\n.igv-navbar-text-button-svg-inactive tspan {\n dominant-baseline: middle;\n}\n\n.igv-navbar-text-button-svg-hover rect {\n stroke: #737373;\n fill: #737373;\n}\n.igv-navbar-text-button-svg-hover text {\n fill: white;\n}\n.igv-navbar-text-button-svg-hover tspan {\n dominant-baseline: middle;\n}\n\n#igv-save-svg-group rect {\n stroke: #737373;\n fill: white;\n}\n#igv-save-svg-group text {\n fill: #737373;\n}\n\n#igv-save-svg-group:hover rect {\n stroke: #737373;\n fill: #737373;\n}\n#igv-save-svg-group:hover text {\n fill: white;\n}\n\n#igv-save-png-group rect {\n stroke: #737373;\n fill: white;\n}\n#igv-save-png-group text {\n fill: #737373;\n}\n\n#igv-save-png-group:hover rect {\n stroke: #737373;\n fill: #737373;\n}\n#igv-save-png-group:hover text {\n fill: white;\n}\n\n.igv-zoom-in-notice-container {\n z-index: 256;\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translate(-50%, 0%);\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n background-color: white;\n}\n.igv-zoom-in-notice-container > div {\n padding-left: 4px;\n padding-right: 4px;\n padding-top: 2px;\n padding-bottom: 2px;\n width: 100%;\n height: 100%;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #3f3f3f;\n}\n\n.igv-zoom-in-notice {\n position: absolute;\n top: 10px;\n left: 50%;\n}\n.igv-zoom-in-notice div {\n position: relative;\n left: -50%;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #3f3f3f;\n background-color: rgba(255, 255, 255, 0.51);\n z-index: 64;\n}\n\n.igv-container-spinner {\n position: absolute;\n top: 90%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 1024;\n width: 24px;\n height: 24px;\n pointer-events: none;\n color: #737373;\n}\n\n.igv-multi-locus-close-button {\n position: absolute;\n top: 2px;\n right: 0;\n padding-left: 2px;\n padding-right: 2px;\n width: 12px;\n height: 12px;\n color: #666666;\n background-color: white;\n z-index: 1000;\n}\n.igv-multi-locus-close-button > svg {\n vertical-align: top;\n}\n\n.igv-multi-locus-close-button:hover {\n cursor: pointer;\n color: #434343;\n}\n\n.igv-multi-locus-ruler-label {\n z-index: 64;\n position: absolute;\n top: 2px;\n left: 0;\n width: 100%;\n height: 12px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-multi-locus-ruler-label > div {\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n color: rgb(16, 16, 16);\n background-color: white;\n}\n.igv-multi-locus-ruler-label > div {\n cursor: pointer;\n}\n\n.igv-multi-locus-ruler-label-square-dot {\n z-index: 64;\n position: absolute;\n left: 50%;\n top: 5%;\n transform: translate(-50%, 0%);\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-multi-locus-ruler-label-square-dot > div:first-child {\n width: 14px;\n height: 14px;\n}\n.igv-multi-locus-ruler-label-square-dot > div:last-child {\n margin-left: 16px;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: rgb(16, 16, 16);\n}\n\n.igv-ruler-sweeper {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 26px;\n bottom: 0;\n left: 0;\n width: 0;\n z-index: 99999;\n background-color: rgba(68, 134, 247, 0.25);\n}\n\n.igv-ruler-tooltip {\n pointer-events: none;\n z-index: 128;\n position: absolute;\n top: 0;\n left: 0;\n width: 1px;\n height: 32px;\n background-color: transparent;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ruler-tooltip > div {\n pointer-events: none;\n width: 128px;\n height: auto;\n padding: 1px;\n color: #373737;\n font-size: 10px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n background-color: white;\n border-style: solid;\n border-width: thin;\n border-color: #373737;\n}\n\n.igv-track-label {\n position: absolute;\n left: 8px;\n top: 8px;\n width: auto;\n height: auto;\n max-width: 50%;\n padding-left: 4px;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n text-align: center;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-color: #444;\n border-radius: 2px;\n border-style: solid;\n border-width: thin;\n background-color: white;\n z-index: 128;\n cursor: pointer;\n}\n\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n background-color: #e8e8e8;\n}\n\n.igv-track-label-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-top: 4px;\n}\n\n.igv-center-line {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n z-index: 8;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-left-style: dashed;\n border-left-width: thin;\n border-right-style: dashed;\n border-right-width: thin;\n}\n\n.igv-center-line-wide {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(127, 127, 127, 0.51);\n}\n\n.igv-center-line-thin {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(0, 0, 0, 0);\n}\n\n.igv-cursor-guide-horizontal {\n display: none;\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n left: 0;\n right: 0;\n top: 50%;\n height: 1px;\n z-index: 32;\n margin-left: 50px;\n margin-right: 54px;\n border-top-style: dotted;\n border-top-width: thin;\n border-top-color: rgba(127, 127, 127, 0.76);\n}\n\n.igv-cursor-guide-vertical {\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n z-index: 32;\n border-left-style: dotted;\n border-left-width: thin;\n border-left-color: rgba(127, 127, 127, 0.76);\n display: none;\n}\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent;\n}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\n\n.igv-generic-dialog-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-generic-dialog-container .igv-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n font-size: 16px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-generic-container {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd;\n}\n.igv-generic-container div:first-child i {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px;\n}\n\n.igv-menu-popup {\n position: absolute;\n top: 0;\n left: 0;\n width: max-content;\n z-index: 512;\n cursor: pointer;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background: white;\n border-radius: 4px;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-end;\n text-align: left;\n}\n.igv-menu-popup > div:not(:first-child) {\n width: 100%;\n}\n.igv-menu-popup > div:not(:first-child) > div {\n background: white;\n}\n.igv-menu-popup > div:not(:first-child) > div.context-menu {\n padding-left: 4px;\n padding-right: 4px;\n}\n.igv-menu-popup > div:not(:first-child) > div:last-child {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-menu-popup > div:not(:first-child) > div:hover {\n background: #efefef;\n}\n\n.igv-menu-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 1px;\n padding-top: 1px;\n}\n\n.igv-menu-popup-header {\n position: relative;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-menu-popup-header div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-menu-popup-header div:hover {\n cursor: pointer;\n color: #444;\n}\n\n.igv-menu-popup-check-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 20px;\n margin-right: 4px;\n background-color: transparent;\n}\n.igv-menu-popup-check-container div {\n padding-top: 2px;\n padding-left: 8px;\n}\n.igv-menu-popup-check-container div:first-child {\n position: relative;\n width: 12px;\n height: 12px;\n}\n.igv-menu-popup-check-container div:first-child svg {\n position: absolute;\n width: 12px;\n height: 12px;\n}\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent;\n}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\n\n.igv-loading-spinner-container {\n z-index: 1024;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 32px;\n height: 32px;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-loading-spinner-container > div {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n border: 4px solid rgba(128, 128, 128, 0.5);\n border-top-color: rgb(255, 255, 255);\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n}\n\n@keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n@-webkit-keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n.igv-roi-menu {\n position: absolute;\n z-index: 512;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background-color: white;\n width: 192px;\n border-radius: 4px;\n border-color: #7F7F7F;\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: stretch;\n}\n.igv-roi-menu > div:first-child {\n height: 24px;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-roi-menu > div:first-child > div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-roi-menu > div:first-child > div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-menu > div:last-child {\n background-color: white;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: 0;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n text-align: start;\n vertical-align: middle;\n}\n.igv-roi-menu > div:last-child > div {\n height: 24px;\n padding-left: 4px;\n border-bottom-style: solid;\n border-bottom-width: thin;\n border-bottom-color: #7f7f7f;\n}\n.igv-roi-menu > div:last-child > div:not(:first-child):hover {\n cursor: pointer;\n background-color: rgba(127, 127, 127, 0.1);\n}\n.igv-roi-menu > div:last-child div:first-child {\n font-style: italic;\n text-align: center;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.igv-roi-menu > div:last-child > div:last-child {\n border-bottom-width: 0;\n border-bottom-color: transparent;\n}\n\n.igv-roi-placeholder {\n font-style: normal;\n color: rgba(75, 75, 75, 0.6);\n}\n\n.igv-roi-table {\n position: absolute;\n z-index: 1024;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n resize: both;\n overflow: hidden;\n width: min-content;\n max-width: 1600px;\n border-color: #7f7f7f;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n background-color: white;\n cursor: default;\n}\n.igv-roi-table > div {\n height: 24px;\n font-size: 14px;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n}\n.igv-roi-table > div:first-child {\n border-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-top-width: 0;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-roi-table > div:first-child > div:first-child {\n text-align: center;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n margin-left: 4px;\n margin-right: 4px;\n width: calc(100% - 4px - 12px);\n}\n.igv-roi-table > div:first-child > div:last-child {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7f7f7f;\n}\n.igv-roi-table > div:first-child > div:last-child > svg {\n display: block;\n}\n.igv-roi-table > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-table > .igv-roi-table-description {\n padding: 4px;\n margin-left: 4px;\n word-break: break-all;\n overflow-y: auto;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-goto-explainer {\n margin-top: 5px;\n margin-left: 4px;\n color: #7F7F7F;\n font-style: italic;\n height: 24px;\n border-top: solid lightgray;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-column-titles {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n padding-right: 16px;\n background-color: white;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: #7f7f7f;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n overflow: auto;\n height: 360px;\n flex: 1 1 auto;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: transparent;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row-hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n.igv-roi-table > div:last-child {\n min-height: 32px;\n height: 32px;\n line-height: 32px;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: transparent;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-width: 0;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n\n.igv-roi-table-row-selected {\n background-color: rgba(0, 0, 0, 0.125);\n}\n\n.igv-roi-table-button {\n cursor: pointer;\n height: 20px;\n user-select: none;\n line-height: 20px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 13px;\n font-weight: 400;\n color: black;\n padding-left: 6px;\n padding-right: 6px;\n background-color: rgb(239, 239, 239);\n border-color: black;\n border-style: solid;\n border-width: thin;\n border-radius: 3px;\n}\n\n.igv-roi-table-button:hover {\n font-weight: 400;\n background-color: rgba(0, 0, 0, 0.13);\n}\n\n.igv-roi-region {\n z-index: 64;\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n overflow: visible;\n margin-top: 66px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-region > div {\n position: relative;\n width: 100%;\n height: 8px;\n pointer-events: auto;\n}\n\n.igv-roi-menu-row {\n height: 24px;\n padding-left: 8px;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n background-color: white;\n}\n\n.igv-roi-menu-row-edit-description {\n width: -webkit-fill-available;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n background-color: white;\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-menu-row-edit-description > label {\n margin-left: 2px;\n margin-bottom: 0;\n display: block;\n width: -webkit-fill-available;\n}\n.igv-roi-menu-row-edit-description > input {\n display: block;\n margin-left: 2px;\n margin-right: 2px;\n margin-bottom: 1px;\n width: -webkit-fill-available;\n}\n\n.igv-container {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n padding-top: 4px;\n user-select: none;\n -webkit-user-select: none;\n -ms-user-select: none;\n min-height: 160px;\n}\n\n.igv-viewport {\n position: relative;\n margin-top: 5px;\n line-height: 1;\n overflow-x: hidden;\n overflow-y: hidden;\n}\n\n.igv-viewport-content {\n position: relative;\n width: 100%;\n}\n.igv-viewport-content > canvas {\n position: relative;\n display: block;\n}\n\n.igv-column-container {\n position: relative;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n width: 100%;\n}\n\n.igv-column-shim {\n width: 1px;\n margin-left: 2px;\n margin-right: 2px;\n background-color: #545453;\n}\n\n.igv-axis-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 50px;\n}\n.igv-axis-column > div {\n position: relative;\n margin-top: 5px;\n width: 100%;\n}\n.igv-axis-column > div > div {\n z-index: 512;\n position: absolute;\n top: 8px;\n left: 8px;\n width: fit-content;\n height: fit-content;\n background-color: transparent;\n display: grid;\n align-items: start;\n justify-items: center;\n}\n.igv-axis-column > div > div > input {\n display: block;\n margin: unset;\n cursor: pointer;\n}\n\n.igv-column {\n position: relative;\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-sample-info-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-sample-name-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-scrollbar-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 14px;\n}\n.igv-scrollbar-column > div {\n position: relative;\n margin-top: 5px;\n width: 14px;\n}\n.igv-scrollbar-column > div > div {\n cursor: pointer;\n position: absolute;\n top: 0;\n left: 2px;\n width: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: #c4c4c4;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.igv-scrollbar-column > div > div:hover {\n background-color: #c4c4c4;\n}\n\n.igv-track-drag-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 12px;\n background-color: white;\n}\n.igv-track-drag-column > .igv-track-drag-handle {\n z-index: 512;\n position: relative;\n cursor: pointer;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n.igv-track-drag-column .igv-track-drag-handle-color {\n background-color: #c4c4c4;\n}\n.igv-track-drag-column .igv-track-drag-handle-hover-color {\n background-color: #787878;\n}\n.igv-track-drag-column .igv-track-drag-handle-selected-color {\n background-color: #0963fa;\n}\n.igv-track-drag-column > .igv-track-drag-shim {\n position: relative;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n}\n\n.igv-gear-menu-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 28px;\n}\n.igv-gear-menu-column > div {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n margin-top: 5px;\n width: 100%;\n background: white;\n}\n.igv-gear-menu-column > div > div {\n position: relative;\n margin-top: 4px;\n width: 16px;\n height: 16px;\n color: #7F7F7F;\n}\n.igv-gear-menu-column > div > div:hover {\n cursor: pointer;\n color: #444;\n}\n\n/*# sourceMappingURL=igv.css.map */\n';
|
|
73739
|
+
var igvCss = '.igv-ui-dropdown {\n cursor: default;\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n background-color: white;\n}\n.igv-ui-dropdown > div {\n overflow-y: auto;\n overflow-x: hidden;\n background-color: white;\n}\n.igv-ui-dropdown > div > div {\n padding: 4px;\n width: 100%;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: 1px;\n background-color: white;\n}\n.igv-ui-dropdown > div > div:last-child {\n border-bottom-color: transparent;\n border-bottom-width: 0;\n}\n.igv-ui-dropdown > div > div:hover {\n cursor: pointer;\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.igv-ui-popover {\n cursor: default;\n position: absolute;\n z-index: 2048;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: 1px;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n background-color: white;\n}\n.igv-ui-popover > div:first-child {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-width: 0;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-popover > div:first-child > div:first-child {\n margin-left: 4px;\n}\n.igv-ui-popover > div:first-child > div:last-child {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-popover > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-popover > div:last-child {\n user-select: text;\n overflow-y: auto;\n overflow-x: hidden;\n max-height: 400px;\n max-width: 800px;\n background-color: white;\n border-bottom-width: 0;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.igv-ui-popover > div:last-child > div {\n margin-left: 4px;\n margin-right: 4px;\n min-width: 220px;\n overflow-x: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.igv-ui-popover > div:last-child > div > span {\n font-weight: bolder;\n}\n.igv-ui-popover > div:last-child hr {\n width: 100%;\n}\n\n.igv-ui-alert-dialog-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n top: 50%;\n left: 50%;\n width: 400px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n outline: none;\n font-family: \"Open Sans\", sans-serif;\n font-size: 15px;\n font-weight: 400;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-ui-alert-dialog-container > div:first-child {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-alert-dialog-container > div:first-child div:first-child {\n padding-left: 8px;\n}\n.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n -webkit-user-select: text;\n -moz-user-select: text;\n -ms-user-select: text;\n user-select: text;\n color: #373737;\n width: 100%;\n height: calc(100% - 24px - 64px);\n overflow-y: scroll;\n}\n.igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n margin: 16px;\n width: auto;\n height: auto;\n overflow-wrap: break-word;\n word-break: break-word;\n background-color: white;\n border: unset;\n}\n.igv-ui-alert-dialog-container > div:last-child {\n width: 100%;\n margin-bottom: 10px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-ui-alert-dialog-container > div:last-child div {\n margin: unset;\n width: 40px;\n height: 30px;\n line-height: 30px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n border-color: #2B81AF;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-ui-alert-dialog-container > div:last-child div:hover {\n cursor: pointer;\n border-color: #25597f;\n background-color: #25597f;\n}\n\n.igv-ui-color-swatch {\n position: relative;\n box-sizing: content-box;\n display: flex;\n flex-flow: row;\n flex-wrap: wrap;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-style: solid;\n border-width: 2px;\n border-color: white;\n border-radius: 4px;\n}\n\n.igv-ui-color-swatch:hover {\n border-color: dimgray;\n}\n\n.igv-ui-colorpicker-menu-close-button {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 32px;\n margin-top: 4px;\n margin-bottom: 4px;\n padding-right: 8px;\n}\n.igv-ui-colorpicker-menu-close-button i.fa {\n display: block;\n margin-left: 4px;\n margin-right: 4px;\n color: #5f5f5f;\n}\n.igv-ui-colorpicker-menu-close-button i.fa:hover,\n.igv-ui-colorpicker-menu-close-button i.fa:focus,\n.igv-ui-colorpicker-menu-close-button i.fa:active {\n cursor: pointer;\n color: #0f0f0f;\n}\n\n.igv-ui-generic-dialog-container {\n box-sizing: content-box;\n position: fixed;\n top: 0;\n left: 0;\n width: 300px;\n height: fit-content;\n padding-bottom: 16px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input > div {\n width: fit-content;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input[type=range] {\n width: 70%;\n -webkit-appearance: none;\n background: linear-gradient(90deg, white, black);\n outline: none;\n margin: 0;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-input output {\n display: block;\n height: 100%;\n width: 20%;\n font-size: 16px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n padding-top: 16px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div {\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel > div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n\n.igv-ui-generic-container {\n box-sizing: content-box;\n position: absolute;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ui-generic-container > div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd;\n}\n.igv-ui-generic-container > div:first-child > div {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px;\n}\n\n.igv-ui-dialog {\n z-index: 2048;\n position: fixed;\n width: fit-content;\n height: fit-content;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n background-color: white;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n}\n.igv-ui-dialog .igv-ui-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-ui-dialog .igv-ui-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-ui-dialog .igv-ui-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-ui-dialog .igv-ui-dialog-one-liner {\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin: 8px;\n overflow-wrap: break-word;\n background-color: white;\n font-weight: bold;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel {\n width: 100%;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n margin: 16px;\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n background-color: #5ea4e0;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n background-color: #c4c4c4;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n.igv-ui-dialog .igv-ui-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-ui-dialog .igv-ui-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-ui-dialog .igv-ui-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-ui-panel, .igv-ui-panel-row, .igv-ui-panel-column {\n z-index: 2048;\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-ui-panel-column {\n display: flex;\n flex-direction: column;\n}\n\n.igv-ui-panel-row {\n display: flex;\n flex-direction: row;\n}\n\n.igv-ui-textbox {\n background-color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n}\n\n.igv-ui-table {\n background-color: white;\n}\n\n.igv-ui-table thead {\n position: sticky;\n top: 0;\n}\n\n.igv-ui-table th {\n text-align: left;\n}\n\n.igv-ui-table td {\n padding-right: 20px;\n}\n\n.igv-ui-table tr:hover {\n background-color: lightblue;\n}\n\n.igv-ui-center-fixed {\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n}\n\n.igv-navbar {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n box-sizing: border-box;\n width: 100%;\n color: #444;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n line-height: 32px;\n padding-left: 8px;\n padding-right: 8px;\n margin-top: 2px;\n margin-bottom: 6px;\n height: 32px;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: #f3f3f3;\n}\n.igv-navbar .igv-navbar-left-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 32px;\n line-height: 32px;\n}\n.igv-navbar .igv-navbar-left-container .igv-logo {\n width: 34px;\n height: 32px;\n margin-right: 8px;\n}\n.igv-navbar .igv-navbar-left-container .igv-current-genome {\n height: 32px;\n margin-left: 4px;\n margin-right: 4px;\n user-select: none;\n line-height: 32px;\n vertical-align: middle;\n text-align: center;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n height: 100%;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n height: 100%;\n width: 125px;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container select {\n display: block;\n cursor: pointer;\n width: 100px;\n height: 75%;\n outline: none;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n margin-left: 8px;\n height: 22px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 240px;\n height: 22px;\n line-height: 22px;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container input.igv-search-input {\n cursor: text;\n width: 85%;\n height: 22px;\n line-height: 22px;\n font-size: 12px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n text-align: left;\n padding-left: 8px;\n margin-right: 8px;\n outline: none;\n border-style: solid;\n border-radius: 3px;\n border-width: thin;\n border-color: #bfbfbf;\n background-color: white;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container .igv-search-icon-container {\n cursor: pointer;\n height: 16px;\n width: 16px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-windowsize-panel-container {\n margin-left: 4px;\n user-select: none;\n}\n.igv-navbar .igv-navbar-right-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container {\n position: relative;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-hidden {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n height: 100%;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget {\n color: #737373;\n font-size: 18px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:first-child {\n height: 20px;\n width: 20px;\n margin-left: unset;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:last-child {\n height: 20px;\n width: 20px;\n margin-left: 4px;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n display: block;\n height: fit-content;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n display: block;\n width: 125px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 {\n color: #737373;\n font-size: 18px;\n height: 32px;\n line-height: 32px;\n margin-left: 8px;\n user-select: none;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n cursor: pointer;\n margin-left: unset;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:first-child {\n height: 20px;\n width: 20px;\n margin-left: unset;\n margin-right: 4px;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:last-child {\n height: 20px;\n width: 20px;\n margin-left: 4px;\n margin-right: unset;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:nth-child(even) {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n width: 0;\n height: 0;\n display: none;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n display: block;\n}\n.igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n display: none;\n}\n\n.igv-navbar-button {\n display: block;\n box-sizing: unset;\n padding-left: 6px;\n padding-right: 6px;\n height: 18px;\n text-transform: capitalize;\n user-select: none;\n line-height: 18px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 11px;\n font-weight: 200;\n color: #737373;\n background-color: #f3f3f3;\n border-color: #737373;\n border-style: solid;\n border-width: thin;\n border-radius: 6px;\n}\n\n.igv-navbar-button:hover {\n cursor: pointer;\n}\n\n.igv-navbar-button-clicked {\n color: white;\n background-color: #737373;\n}\n\n.igv-navbar-icon-button {\n cursor: pointer;\n position: relative;\n width: 24px;\n height: 24px;\n margin-left: 4px;\n margin-right: 4px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-icon-button > div:first-child {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: -18px;\n width: 24px;\n height: 24px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-icon-button > div:last-child {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 18px;\n width: 24px;\n height: 24px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.igv-navbar-text-button {\n cursor: pointer;\n position: relative;\n margin-left: 2px;\n margin-right: 2px;\n border: none;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n.igv-navbar-text-button > div:nth-child(2) {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 0;\n width: 38px;\n height: 18px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n.igv-navbar-text-button > div:nth-child(3) {\n z-index: 512;\n position: absolute;\n top: 36px;\n left: 42px;\n width: 38px;\n height: 18px;\n border: none;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n#igv-text-button-label {\n text-anchor: middle;\n dominant-baseline: middle;\n}\n\n.igv-navbar-text-button-svg-inactive rect {\n stroke: #737373;\n fill: white;\n}\n.igv-navbar-text-button-svg-inactive text {\n fill: #737373;\n}\n.igv-navbar-text-button-svg-inactive tspan {\n dominant-baseline: middle;\n}\n\n.igv-navbar-text-button-svg-hover rect {\n stroke: #737373;\n fill: #737373;\n}\n.igv-navbar-text-button-svg-hover text {\n fill: white;\n}\n.igv-navbar-text-button-svg-hover tspan {\n dominant-baseline: middle;\n}\n\n#igv-save-svg-group rect {\n stroke: #737373;\n fill: white;\n}\n#igv-save-svg-group text {\n fill: #737373;\n}\n\n#igv-save-svg-group:hover rect {\n stroke: #737373;\n fill: #737373;\n}\n#igv-save-svg-group:hover text {\n fill: white;\n}\n\n#igv-save-png-group rect {\n stroke: #737373;\n fill: white;\n}\n#igv-save-png-group text {\n fill: #737373;\n}\n\n#igv-save-png-group:hover rect {\n stroke: #737373;\n fill: #737373;\n}\n#igv-save-png-group:hover text {\n fill: white;\n}\n\n.igv-zoom-in-notice-container {\n z-index: 256;\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translate(-50%, 0%);\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n background-color: white;\n}\n.igv-zoom-in-notice-container > div {\n padding-left: 4px;\n padding-right: 4px;\n padding-top: 2px;\n padding-bottom: 2px;\n width: 100%;\n height: 100%;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: #3f3f3f;\n}\n\n.igv-zoom-in-notice {\n position: absolute;\n top: 10px;\n left: 50%;\n}\n.igv-zoom-in-notice div {\n position: relative;\n left: -50%;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #3f3f3f;\n background-color: rgba(255, 255, 255, 0.51);\n z-index: 64;\n}\n\n.igv-container-spinner {\n position: absolute;\n top: 90%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 1024;\n width: 24px;\n height: 24px;\n pointer-events: none;\n color: #737373;\n}\n\n.igv-multi-locus-close-button {\n position: absolute;\n top: 2px;\n right: 0;\n padding-left: 2px;\n padding-right: 2px;\n width: 12px;\n height: 12px;\n color: #666666;\n background-color: white;\n z-index: 1000;\n}\n.igv-multi-locus-close-button > svg {\n vertical-align: top;\n}\n\n.igv-multi-locus-close-button:hover {\n cursor: pointer;\n color: #434343;\n}\n\n.igv-multi-locus-ruler-label {\n z-index: 64;\n position: absolute;\n top: 2px;\n left: 0;\n width: 100%;\n height: 12px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-multi-locus-ruler-label > div {\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n color: rgb(16, 16, 16);\n background-color: white;\n}\n.igv-multi-locus-ruler-label > div {\n cursor: pointer;\n}\n\n.igv-multi-locus-ruler-label-square-dot {\n z-index: 64;\n position: absolute;\n left: 50%;\n top: 5%;\n transform: translate(-50%, 0%);\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-multi-locus-ruler-label-square-dot > div:first-child {\n width: 14px;\n height: 14px;\n}\n.igv-multi-locus-ruler-label-square-dot > div:last-child {\n margin-left: 16px;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n color: rgb(16, 16, 16);\n}\n\n.igv-ruler-sweeper {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 26px;\n bottom: 0;\n left: 0;\n width: 0;\n z-index: 99999;\n background-color: rgba(68, 134, 247, 0.25);\n}\n\n.igv-ruler-tooltip {\n pointer-events: none;\n z-index: 128;\n position: absolute;\n top: 0;\n left: 0;\n width: 1px;\n height: 32px;\n background-color: transparent;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-ruler-tooltip > div {\n pointer-events: none;\n width: 128px;\n height: auto;\n padding: 1px;\n color: #373737;\n font-size: 10px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n background-color: white;\n border-style: solid;\n border-width: thin;\n border-color: #373737;\n}\n\n.igv-track-label {\n position: absolute;\n left: 8px;\n top: 8px;\n width: auto;\n height: auto;\n max-width: 50%;\n padding-left: 4px;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n text-align: center;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-color: #444;\n border-radius: 2px;\n border-style: solid;\n border-width: thin;\n background-color: white;\n z-index: 128;\n cursor: pointer;\n}\n\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n background-color: #e8e8e8;\n}\n\n.igv-track-label-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-top: 4px;\n}\n\n.igv-center-line {\n display: none;\n pointer-events: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n z-index: 8;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n border-left-style: dashed;\n border-left-width: thin;\n border-right-style: dashed;\n border-right-width: thin;\n}\n\n.igv-center-line-wide {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(127, 127, 127, 0.51);\n}\n\n.igv-center-line-thin {\n background-color: rgba(0, 0, 0, 0);\n border-left-color: rgba(127, 127, 127, 0.51);\n border-right-color: rgba(0, 0, 0, 0);\n}\n\n.igv-cursor-guide-horizontal {\n display: none;\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n left: 0;\n right: 0;\n top: 50%;\n height: 1px;\n z-index: 32;\n margin-left: 50px;\n margin-right: 54px;\n border-top-style: dotted;\n border-top-width: thin;\n border-top-color: rgba(127, 127, 127, 0.76);\n}\n\n.igv-cursor-guide-vertical {\n pointer-events: none;\n user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n z-index: 32;\n border-left-style: dotted;\n border-left-width: thin;\n border-left-color: rgba(127, 127, 127, 0.76);\n display: none;\n}\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent;\n}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\n\n.igv-generic-dialog-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 300px;\n height: 200px;\n border-color: #7F7F7F;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n z-index: 2048;\n background-color: white;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header div {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-generic-dialog-container .igv-generic-dialog-one-liner {\n color: #373737;\n width: 95%;\n height: 24px;\n line-height: 24px;\n text-align: left;\n margin-top: 8px;\n padding-left: 8px;\n overflow-wrap: break-word;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input {\n margin-top: 8px;\n width: 95%;\n height: 24px;\n color: #373737;\n line-height: 24px;\n padding-left: 8px;\n background-color: white;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input div {\n width: 30%;\n height: 100%;\n font-size: 16px;\n text-align: right;\n padding-right: 8px;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-label-input input {\n width: 50%;\n font-size: 16px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input {\n margin-top: 8px;\n width: calc(100% - 16px);\n height: 24px;\n color: #373737;\n line-height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n display: block;\n height: 100%;\n width: 100%;\n padding-left: 4px;\n font-family: \"Open Sans\", sans-serif;\n font-weight: 400;\n color: #373737;\n text-align: left;\n outline: none;\n border-style: solid;\n border-width: thin;\n border-color: #7F7F7F;\n background-color: white;\n}\n.igv-generic-dialog-container .igv-generic-dialog-input input {\n font-size: 16px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel {\n width: 100%;\n height: 28px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div {\n margin-top: 32px;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: 14px;\n font-weight: 400;\n width: 75px;\n height: 28px;\n line-height: 28px;\n text-align: center;\n border-color: transparent;\n border-style: solid;\n border-width: thin;\n border-radius: 2px;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child {\n margin-left: 32px;\n margin-right: 0;\n background-color: #5ea4e0;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child {\n margin-left: 0;\n margin-right: 32px;\n background-color: #c4c4c4;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n cursor: pointer;\n background-color: #3b5c7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n cursor: pointer;\n background-color: #7f7f7f;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok {\n width: 100%;\n height: 36px;\n margin-top: 32px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok div {\n width: 98px;\n height: 36px;\n line-height: 36px;\n text-align: center;\n color: white;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n border-color: white;\n border-style: solid;\n border-width: thin;\n border-radius: 4px;\n background-color: #2B81AF;\n}\n.igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n cursor: pointer;\n background-color: #25597f;\n}\n\n.igv-generic-container {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2048;\n background-color: white;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-generic-container div:first-child {\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n height: 24px;\n width: 100%;\n background-color: #dddddd;\n}\n.igv-generic-container div:first-child i {\n display: block;\n color: #5f5f5f;\n cursor: pointer;\n width: 14px;\n height: 14px;\n margin-right: 8px;\n margin-bottom: 4px;\n}\n\n.igv-menu-popup {\n position: absolute;\n top: 0;\n left: 0;\n width: max-content;\n max-width: 400px;\n z-index: 512;\n cursor: pointer;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background: white;\n border-radius: 4px;\n border-color: #7F7F7F;\n border-style: solid;\n border-width: thin;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-end;\n text-align: left;\n}\n.igv-menu-popup > div:not(:first-child) {\n width: 100%;\n}\n.igv-menu-popup > div:not(:first-child) > div {\n background: white;\n}\n.igv-menu-popup > div:not(:first-child) > div.context-menu {\n padding-left: 4px;\n padding-right: 4px;\n}\n.igv-menu-popup > div:not(:first-child) > div:last-child {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-menu-popup > div:not(:first-child) > div:hover {\n background: #efefef;\n}\n\n.igv-menu-popup-shim {\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 1px;\n padding-top: 1px;\n}\n\n.igv-menu-popup-header {\n position: relative;\n width: 100%;\n height: 24px;\n cursor: move;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-menu-popup-header div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-menu-popup-header div:hover {\n cursor: pointer;\n color: #444;\n}\n\n.igv-menu-popup-check-container {\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n width: 100%;\n height: 20px;\n margin-right: 4px;\n background-color: transparent;\n}\n.igv-menu-popup-check-container div {\n padding-top: 2px;\n padding-left: 8px;\n}\n.igv-menu-popup-check-container div:first-child {\n position: relative;\n width: 12px;\n height: 12px;\n}\n.igv-menu-popup-check-container div:first-child svg {\n position: absolute;\n width: 12px;\n height: 12px;\n}\n\n.igv-user-feedback {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 512px;\n height: 360px;\n z-index: 2048;\n background-color: white;\n border-color: #a2a2a2;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: medium;\n font-weight: 400;\n color: #444;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n}\n.igv-user-feedback div:first-child {\n position: relative;\n height: 24px;\n width: 100%;\n background-color: white;\n border-bottom-color: #a2a2a2;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-user-feedback div:first-child div {\n position: absolute;\n top: 2px;\n width: 16px;\n height: 16px;\n background-color: transparent;\n}\n.igv-user-feedback div:first-child div:first-child {\n left: 8px;\n}\n.igv-user-feedback div:first-child div:last-child {\n cursor: pointer;\n right: 8px;\n}\n.igv-user-feedback div:last-child {\n width: 100%;\n height: calc(100% - 24px);\n border-width: 0;\n}\n.igv-user-feedback div:last-child div {\n width: auto;\n height: auto;\n margin: 8px;\n}\n\n.igv-loading-spinner-container {\n z-index: 1024;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 32px;\n height: 32px;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: center;\n align-items: center;\n}\n.igv-loading-spinner-container > div {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n border: 4px solid rgba(128, 128, 128, 0.5);\n border-top-color: rgb(255, 255, 255);\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n}\n\n@keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n@-webkit-keyframes spin {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n.igv-roi-menu {\n position: absolute;\n z-index: 512;\n font-family: \"Open Sans\", sans-serif;\n font-size: small;\n font-weight: 400;\n color: #4b4b4b;\n background-color: white;\n width: 192px;\n border-radius: 4px;\n border-color: #7F7F7F;\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: stretch;\n}\n.igv-roi-menu > div:first-child {\n height: 24px;\n border-top-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-color: #7F7F7F;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: flex-end;\n align-items: center;\n}\n.igv-roi-menu > div:first-child > div {\n margin-right: 4px;\n height: 12px;\n width: 12px;\n color: #7F7F7F;\n}\n.igv-roi-menu > div:first-child > div:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-menu > div:last-child {\n background-color: white;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-color: transparent;\n border-bottom-style: solid;\n border-bottom-width: 0;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n text-align: start;\n vertical-align: middle;\n}\n.igv-roi-menu > div:last-child > div {\n height: 24px;\n padding-left: 4px;\n border-bottom-style: solid;\n border-bottom-width: thin;\n border-bottom-color: #7f7f7f;\n}\n.igv-roi-menu > div:last-child > div:not(:first-child):hover {\n cursor: pointer;\n background-color: rgba(127, 127, 127, 0.1);\n}\n.igv-roi-menu > div:last-child div:first-child {\n font-style: italic;\n text-align: center;\n padding-right: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.igv-roi-menu > div:last-child > div:last-child {\n border-bottom-width: 0;\n border-bottom-color: transparent;\n}\n\n.igv-roi-placeholder {\n font-style: normal;\n color: rgba(75, 75, 75, 0.6);\n}\n\n.igv-roi-table {\n position: absolute;\n z-index: 1024;\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n resize: both;\n overflow: hidden;\n width: min-content;\n max-width: 1600px;\n border-color: #7f7f7f;\n border-radius: 4px;\n border-style: solid;\n border-width: thin;\n font-family: \"Open Sans\", sans-serif;\n font-size: 12px;\n font-weight: 400;\n background-color: white;\n cursor: default;\n}\n.igv-roi-table > div {\n height: 24px;\n font-size: 14px;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n}\n.igv-roi-table > div:first-child {\n border-color: transparent;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-top-width: 0;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n background-color: #eee;\n cursor: move;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n align-items: center;\n}\n.igv-roi-table > div:first-child > div:first-child {\n text-align: center;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n margin-left: 4px;\n margin-right: 4px;\n width: calc(100% - 4px - 12px);\n}\n.igv-roi-table > div:first-child > div:last-child {\n margin-right: 4px;\n margin-bottom: 2px;\n height: 12px;\n width: 12px;\n color: #7f7f7f;\n}\n.igv-roi-table > div:first-child > div:last-child > svg {\n display: block;\n}\n.igv-roi-table > div:first-child > div:last-child:hover {\n cursor: pointer;\n color: #444;\n}\n.igv-roi-table > .igv-roi-table-description {\n padding: 4px;\n margin-left: 4px;\n word-break: break-all;\n overflow-y: auto;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-goto-explainer {\n margin-top: 5px;\n margin-left: 4px;\n color: #7F7F7F;\n font-style: italic;\n height: 24px;\n border-top: solid lightgray;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-column-titles {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n padding-right: 16px;\n background-color: white;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: #7f7f7f;\n border-bottom-style: solid;\n border-bottom-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: #7f7f7f;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-column-titles > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container {\n display: flex;\n flex-flow: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n overflow: auto;\n height: 360px;\n flex: 1 1 auto;\n background-color: transparent;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row {\n height: 24px;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div {\n font-size: 14px;\n vertical-align: middle;\n line-height: 24px;\n text-align: left;\n margin-left: 4px;\n height: 24px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right-color: transparent;\n border-right-style: solid;\n border-right-width: thin;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row > div:last-child {\n border-right: unset;\n}\n.igv-roi-table > .igv-roi-table-row-container > .igv-roi-table-row-hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n.igv-roi-table > div:last-child {\n min-height: 32px;\n height: 32px;\n line-height: 32px;\n border-top-color: #7f7f7f;\n border-top-style: solid;\n border-top-width: thin;\n border-bottom-color: transparent;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n border-bottom-width: 0;\n background-color: #eee;\n display: flex;\n flex-flow: row;\n flex-wrap: nowrap;\n justify-content: space-around;\n align-items: center;\n}\n\n.igv-roi-table-row-selected {\n background-color: rgba(0, 0, 0, 0.125);\n}\n\n.igv-roi-table-button {\n cursor: pointer;\n height: 20px;\n user-select: none;\n line-height: 20px;\n text-align: center;\n vertical-align: middle;\n font-family: \"Open Sans\", sans-serif;\n font-size: 13px;\n font-weight: 400;\n color: black;\n padding-left: 6px;\n padding-right: 6px;\n background-color: rgb(239, 239, 239);\n border-color: black;\n border-style: solid;\n border-width: thin;\n border-radius: 3px;\n}\n\n.igv-roi-table-button:hover {\n font-weight: 400;\n background-color: rgba(0, 0, 0, 0.13);\n}\n\n.igv-roi-region {\n z-index: 64;\n position: absolute;\n top: 0;\n bottom: 0;\n pointer-events: none;\n overflow: visible;\n margin-top: 66px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n}\n.igv-roi-region > div {\n position: relative;\n width: 100%;\n height: 8px;\n pointer-events: auto;\n}\n\n.igv-roi-menu-row {\n height: 24px;\n padding-left: 8px;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n line-height: 24px;\n background-color: white;\n}\n\n.igv-roi-menu-row-edit-description {\n width: -webkit-fill-available;\n font-size: small;\n text-align: start;\n vertical-align: middle;\n background-color: white;\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: stretch;\n align-items: stretch;\n}\n.igv-roi-menu-row-edit-description > label {\n margin-left: 2px;\n margin-bottom: 0;\n display: block;\n width: -webkit-fill-available;\n}\n.igv-roi-menu-row-edit-description > input {\n display: block;\n margin-left: 2px;\n margin-right: 2px;\n margin-bottom: 1px;\n width: -webkit-fill-available;\n}\n\n.igv-container {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n padding-top: 4px;\n user-select: none;\n -webkit-user-select: none;\n -ms-user-select: none;\n min-height: 160px;\n}\n\n.igv-viewport {\n position: relative;\n margin-top: 5px;\n line-height: 1;\n overflow-x: hidden;\n overflow-y: hidden;\n}\n\n.igv-viewport-content {\n position: relative;\n width: 100%;\n}\n.igv-viewport-content > canvas {\n position: relative;\n display: block;\n}\n\n.igv-column-container {\n position: relative;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: stretch;\n width: 100%;\n}\n\n.igv-column-shim {\n width: 1px;\n margin-left: 2px;\n margin-right: 2px;\n background-color: #545453;\n}\n\n.igv-axis-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 50px;\n}\n.igv-axis-column > div {\n position: relative;\n margin-top: 5px;\n width: 100%;\n}\n.igv-axis-column > div > div {\n z-index: 512;\n position: absolute;\n top: 8px;\n left: 8px;\n width: fit-content;\n height: fit-content;\n background-color: transparent;\n display: grid;\n align-items: start;\n justify-items: center;\n}\n.igv-axis-column > div > div > input {\n display: block;\n margin: unset;\n cursor: pointer;\n}\n\n.igv-column {\n position: relative;\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-sample-info-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-sample-name-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n}\n\n.igv-scrollbar-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 14px;\n}\n.igv-scrollbar-column > div {\n position: relative;\n margin-top: 5px;\n width: 14px;\n}\n.igv-scrollbar-column > div > div {\n cursor: pointer;\n position: absolute;\n top: 0;\n left: 2px;\n width: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: #c4c4c4;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.igv-scrollbar-column > div > div:hover {\n background-color: #c4c4c4;\n}\n\n.igv-track-drag-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 12px;\n background-color: white;\n}\n.igv-track-drag-column > .igv-track-drag-handle {\n z-index: 512;\n position: relative;\n cursor: pointer;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n.igv-track-drag-column .igv-track-drag-handle-color {\n background-color: #c4c4c4;\n}\n.igv-track-drag-column .igv-track-drag-handle-hover-color {\n background-color: #787878;\n}\n.igv-track-drag-column .igv-track-drag-handle-selected-color {\n background-color: #0963fa;\n}\n.igv-track-drag-column > .igv-track-drag-shim {\n position: relative;\n margin-top: 5px;\n width: 100%;\n border-style: solid;\n border-width: 0;\n}\n\n.igv-gear-menu-column {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: flex-start;\n box-sizing: border-box;\n height: 100%;\n width: 28px;\n}\n.igv-gear-menu-column > div {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n margin-top: 5px;\n width: 100%;\n background: white;\n}\n.igv-gear-menu-column > div > div {\n position: relative;\n margin-top: 4px;\n width: 16px;\n height: 16px;\n color: #7F7F7F;\n}\n.igv-gear-menu-column > div > div:hover {\n cursor: pointer;\n color: #444;\n}\n\n/*# sourceMappingURL=igv.css.map */\n';
|
|
73693
73740
|
|
|
73694
73741
|
/**
|
|
73695
73742
|
* Manages XQTL selections.
|
|
@@ -74572,6 +74619,10 @@ class Browser {
|
|
|
74572
74619
|
*/
|
|
74573
74620
|
async loadGenome(idOrConfig) {
|
|
74574
74621
|
|
|
74622
|
+
if(idOrConfig.genarkAccession) {
|
|
74623
|
+
idOrConfig.url = convertToHubURL(idOrConfig.genarkAccession);
|
|
74624
|
+
}
|
|
74625
|
+
|
|
74575
74626
|
// Translate the generic "url" field, used by clients such as igv-webapp
|
|
74576
74627
|
if (idOrConfig.url) {
|
|
74577
74628
|
if (isString$2(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt")) {
|