igv 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/igv.esm.js +1360 -1348
- package/dist/igv.esm.min.js +10 -10
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +1360 -1348
- package/dist/igv.min.js +10 -10
- package/dist/igv.min.js.map +1 -1
- package/package.json +1 -1
package/dist/igv.js
CHANGED
|
@@ -25222,9 +25222,7 @@
|
|
|
25222
25222
|
} else {
|
|
25223
25223
|
combinedFeatures = this.combineFeaturesByType(features);
|
|
25224
25224
|
}
|
|
25225
|
-
|
|
25226
|
-
return a.start - b.start
|
|
25227
|
-
});
|
|
25225
|
+
|
|
25228
25226
|
this.numberExons(combinedFeatures, genomicInterval);
|
|
25229
25227
|
this.nameFeatures(combinedFeatures);
|
|
25230
25228
|
return combinedFeatures
|
|
@@ -29781,20 +29779,30 @@
|
|
|
29781
29779
|
await this.readHeader();
|
|
29782
29780
|
}
|
|
29783
29781
|
|
|
29782
|
+
let allFeatures;
|
|
29784
29783
|
const index = await this.getIndex();
|
|
29785
29784
|
if (index) {
|
|
29786
29785
|
this.indexed = true;
|
|
29787
|
-
|
|
29786
|
+
allFeatures = await this.loadFeaturesWithIndex(chr, start, end);
|
|
29788
29787
|
} else if (this.dataURI) {
|
|
29789
29788
|
this.indexed = false;
|
|
29790
|
-
|
|
29789
|
+
allFeatures = await this.loadFeaturesFromDataURI();
|
|
29791
29790
|
} else if ("service" === this.config.sourceType) {
|
|
29792
|
-
|
|
29791
|
+
allFeatures = await this.loadFeaturesFromService(chr, start, end);
|
|
29793
29792
|
} else {
|
|
29794
29793
|
this.indexed = false;
|
|
29795
|
-
|
|
29794
|
+
allFeatures = await this.loadFeaturesNoIndex();
|
|
29796
29795
|
}
|
|
29797
29796
|
|
|
29797
|
+
allFeatures.sort(function (a, b) {
|
|
29798
|
+
if (a.chr === b.chr) {
|
|
29799
|
+
return a.start - b.start
|
|
29800
|
+
} else {
|
|
29801
|
+
return a.chr.localeCompare(b.chr)
|
|
29802
|
+
}
|
|
29803
|
+
});
|
|
29804
|
+
|
|
29805
|
+
return allFeatures
|
|
29798
29806
|
}
|
|
29799
29807
|
|
|
29800
29808
|
async readHeader() {
|
|
@@ -29966,9 +29974,6 @@
|
|
|
29966
29974
|
await this._parse(allFeatures, dataWrapper, chr, end, start);
|
|
29967
29975
|
|
|
29968
29976
|
}
|
|
29969
|
-
allFeatures.sort(function (a, b) {
|
|
29970
|
-
return a.start - b.start
|
|
29971
|
-
});
|
|
29972
29977
|
|
|
29973
29978
|
return allFeatures
|
|
29974
29979
|
}
|
|
@@ -29998,8 +30003,13 @@
|
|
|
29998
30003
|
|
|
29999
30004
|
let features = await this.parser.parseFeatures(dataWrapper);
|
|
30000
30005
|
|
|
30001
|
-
|
|
30002
|
-
|
|
30006
|
+
features.sort(function (a, b) {
|
|
30007
|
+
if (a.chr === b.chr) {
|
|
30008
|
+
return a.start - b.start
|
|
30009
|
+
} else {
|
|
30010
|
+
return a.chr.localeCompare(b.chr)
|
|
30011
|
+
}
|
|
30012
|
+
});
|
|
30003
30013
|
|
|
30004
30014
|
// Filter features not in requested range.
|
|
30005
30015
|
if (undefined === chr) {
|
|
@@ -30008,26 +30018,21 @@
|
|
|
30008
30018
|
let inInterval = false;
|
|
30009
30019
|
for (let i = 0; i < features.length; i++) {
|
|
30010
30020
|
const f = features[i];
|
|
30011
|
-
if (f.chr
|
|
30012
|
-
if (
|
|
30013
|
-
|
|
30014
|
-
|
|
30015
|
-
break //adjacent chr to the right
|
|
30021
|
+
if (f.chr === chr) {
|
|
30022
|
+
if (f.start > end) {
|
|
30023
|
+
allFeatures.push(f); // First feature beyond interval
|
|
30024
|
+
break
|
|
30016
30025
|
}
|
|
30017
|
-
|
|
30018
|
-
|
|
30019
|
-
|
|
30020
|
-
|
|
30021
|
-
|
|
30022
|
-
|
|
30023
|
-
|
|
30024
|
-
if (!inInterval) {
|
|
30025
|
-
inInterval = true;
|
|
30026
|
-
if (i > 0) {
|
|
30027
|
-
allFeatures.push(features[i - 1]);
|
|
30026
|
+
if (f.end >= start && f.start <= end) {
|
|
30027
|
+
// All this to grab first feature before start of interval. Needed for some track renderers, like line plot
|
|
30028
|
+
if (!inInterval) {
|
|
30029
|
+
inInterval = true;
|
|
30030
|
+
if (i > 0) {
|
|
30031
|
+
allFeatures.push(features[i - 1]);
|
|
30032
|
+
}
|
|
30028
30033
|
}
|
|
30034
|
+
allFeatures.push(f);
|
|
30029
30035
|
}
|
|
30030
|
-
allFeatures.push(f);
|
|
30031
30036
|
}
|
|
30032
30037
|
}
|
|
30033
30038
|
}
|
|
@@ -30750,286 +30755,686 @@
|
|
|
30750
30755
|
|
|
30751
30756
|
}
|
|
30752
30757
|
|
|
30753
|
-
|
|
30754
|
-
|
|
30755
|
-
/**
|
|
30756
|
-
* feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
|
|
30758
|
+
/*
|
|
30759
|
+
* The MIT License (MIT)
|
|
30757
30760
|
*
|
|
30758
|
-
*
|
|
30759
|
-
*
|
|
30761
|
+
* Copyright (c) 2016 University of California San Diego
|
|
30762
|
+
* Author: Jim Robinson
|
|
30763
|
+
*
|
|
30764
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
30765
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
30766
|
+
* in the Software without restriction, including without limitation the rights
|
|
30767
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
30768
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
30769
|
+
* furnished to do so, subject to the following conditions:
|
|
30770
|
+
*
|
|
30771
|
+
* The above copyright notice and this permission notice shall be included in
|
|
30772
|
+
* all copies or substantial portions of the Software.
|
|
30773
|
+
*
|
|
30774
|
+
*
|
|
30775
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30776
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30777
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30778
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30779
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30780
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
30781
|
+
* THE SOFTWARE.
|
|
30760
30782
|
*/
|
|
30761
|
-
class TextFeatureSource extends BaseFeatureSource {
|
|
30762
30783
|
|
|
30763
|
-
|
|
30784
|
+
const GZIP_FLAG = 0x1;
|
|
30764
30785
|
|
|
30765
|
-
|
|
30786
|
+
class TDFReader {
|
|
30766
30787
|
|
|
30767
|
-
|
|
30788
|
+
constructor(config, genome) {
|
|
30789
|
+
this.config = config;
|
|
30768
30790
|
this.genome = genome;
|
|
30769
|
-
this.
|
|
30770
|
-
this.
|
|
30771
|
-
this.
|
|
30791
|
+
this.path = config.url;
|
|
30792
|
+
this.groupCache = {};
|
|
30793
|
+
this.datasetCache = {};
|
|
30794
|
+
}
|
|
30772
30795
|
|
|
30773
|
-
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
30774
30796
|
|
|
30775
|
-
|
|
30776
|
-
|
|
30777
|
-
|
|
30778
|
-
this
|
|
30779
|
-
this.queryable = config.queryable !== false;
|
|
30780
|
-
} else if (config.sourceType === "ga4gh") {
|
|
30781
|
-
throw Error("Unsupported source type 'ga4gh'")
|
|
30782
|
-
} else if ((config.type === "eqtl" || config.type === "qtl") && config.sourceType === "gtex-ws") {
|
|
30783
|
-
this.reader = new GtexReader(config);
|
|
30784
|
-
this.queryable = true;
|
|
30785
|
-
} else if ("htsget" === config.sourceType) {
|
|
30786
|
-
this.reader = new HtsgetVariantReader(config, genome);
|
|
30787
|
-
this.queryable = true;
|
|
30788
|
-
} else if (config.sourceType === 'ucscservice') {
|
|
30789
|
-
this.reader = new UCSCServiceReader(config.source);
|
|
30790
|
-
this.queryable = true;
|
|
30791
|
-
} else if (config.sourceType === 'custom') {
|
|
30792
|
-
this.reader = new CustomServiceReader(config.source);
|
|
30793
|
-
this.queryable = false !== config.source.queryable;
|
|
30794
|
-
} else if ('service' === config.sourceType) {
|
|
30795
|
-
this.reader = new FeatureFileReader(config, genome);
|
|
30796
|
-
this.queryable = true;
|
|
30797
|
-
} else {
|
|
30798
|
-
// File of some type (i.e. not a webservice)
|
|
30799
|
-
this.reader = new FeatureFileReader(config, genome);
|
|
30800
|
-
if (config.queryable !== undefined) {
|
|
30801
|
-
this.queryable = config.queryable;
|
|
30802
|
-
} else if (queryableFormats.has(config.format) || this.reader.indexed) {
|
|
30803
|
-
this.queryable = true;
|
|
30804
|
-
} else ;
|
|
30797
|
+
async readHeader() {
|
|
30798
|
+
|
|
30799
|
+
if (this.magic !== undefined) {
|
|
30800
|
+
return this // Already read
|
|
30805
30801
|
}
|
|
30806
30802
|
|
|
30807
|
-
|
|
30808
|
-
|
|
30803
|
+
let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {range: {start: 0, size: 64000}}));
|
|
30804
|
+
let binaryParser = new BinaryParser$1(new DataView(data));
|
|
30805
|
+
this.magic = binaryParser.getInt();
|
|
30806
|
+
this.version = binaryParser.getInt();
|
|
30807
|
+
this.indexPos = binaryParser.getLong();
|
|
30808
|
+
this.indexSize = binaryParser.getInt();
|
|
30809
|
+
binaryParser.getInt();
|
|
30809
30810
|
|
|
30810
|
-
}
|
|
30811
30811
|
|
|
30812
|
-
|
|
30813
|
-
|
|
30814
|
-
|
|
30812
|
+
if (this.version >= 2) {
|
|
30813
|
+
let nWindowFunctions = binaryParser.getInt();
|
|
30814
|
+
this.windowFunctions = [];
|
|
30815
|
+
while (nWindowFunctions-- > 0) {
|
|
30816
|
+
this.windowFunctions.push(binaryParser.getString());
|
|
30817
|
+
}
|
|
30815
30818
|
}
|
|
30816
|
-
}
|
|
30817
30819
|
|
|
30818
|
-
|
|
30819
|
-
|
|
30820
|
-
if (header) {
|
|
30821
|
-
return header.type
|
|
30822
|
-
} else {
|
|
30823
|
-
return undefined // Convention for unknown or unspecified
|
|
30824
|
-
}
|
|
30825
|
-
}
|
|
30820
|
+
this.trackType = binaryParser.getString();
|
|
30821
|
+
this.trackLine = binaryParser.getString();
|
|
30826
30822
|
|
|
30827
|
-
|
|
30828
|
-
|
|
30823
|
+
let nTracks = binaryParser.getInt();
|
|
30824
|
+
this.trackNames = [];
|
|
30825
|
+
while (nTracks-- > 0) {
|
|
30826
|
+
this.trackNames.push(binaryParser.getString());
|
|
30827
|
+
}
|
|
30828
|
+
this.genomeID = binaryParser.getString();
|
|
30829
|
+
this.flags = binaryParser.getInt();
|
|
30830
|
+
this.compressed = (this.flags & GZIP_FLAG) !== 0;
|
|
30829
30831
|
|
|
30830
|
-
|
|
30831
|
-
|
|
30832
|
-
|
|
30833
|
-
|
|
30834
|
-
|
|
30835
|
-
this.config.format = header.format;
|
|
30836
|
-
}
|
|
30837
|
-
} else {
|
|
30838
|
-
this.header = {};
|
|
30839
|
-
}
|
|
30840
|
-
} else {
|
|
30841
|
-
this.header = {};
|
|
30832
|
+
// Now read index
|
|
30833
|
+
data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30834
|
+
range: {
|
|
30835
|
+
start: this.indexPos,
|
|
30836
|
+
size: this.indexSize
|
|
30842
30837
|
}
|
|
30838
|
+
}));
|
|
30839
|
+
binaryParser = new BinaryParser$1(new DataView(data));
|
|
30840
|
+
this.datasetIndex = {};
|
|
30841
|
+
let nEntries = binaryParser.getInt();
|
|
30842
|
+
while (nEntries-- > 0) {
|
|
30843
|
+
const name = binaryParser.getString();
|
|
30844
|
+
const pos = binaryParser.getLong();
|
|
30845
|
+
const size = binaryParser.getInt();
|
|
30846
|
+
this.datasetIndex[name] = {position: pos, size: size};
|
|
30843
30847
|
}
|
|
30844
|
-
|
|
30848
|
+
|
|
30849
|
+
this.groupIndex = {};
|
|
30850
|
+
nEntries = binaryParser.getInt();
|
|
30851
|
+
while (nEntries-- > 0) {
|
|
30852
|
+
const name = binaryParser.getString();
|
|
30853
|
+
const pos = binaryParser.getLong();
|
|
30854
|
+
const size = binaryParser.getInt();
|
|
30855
|
+
this.groupIndex[name] = {position: pos, size: size};
|
|
30856
|
+
}
|
|
30857
|
+
|
|
30858
|
+
return this
|
|
30845
30859
|
}
|
|
30846
30860
|
|
|
30847
|
-
|
|
30848
|
-
* Required function for all data source objects. Fetches features for the
|
|
30849
|
-
* range requested.
|
|
30850
|
-
*
|
|
30851
|
-
* This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
|
|
30852
|
-
* some not.
|
|
30853
|
-
*
|
|
30854
|
-
* @param chr
|
|
30855
|
-
* @param start
|
|
30856
|
-
* @param end
|
|
30857
|
-
* @param bpPerPixel
|
|
30858
|
-
*/
|
|
30859
|
-
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow}) {
|
|
30861
|
+
async readDataset(chr, windowFunction, zoom) {
|
|
30860
30862
|
|
|
30861
|
-
const
|
|
30863
|
+
const key = chr + "_" + windowFunction + "_" + zoom;
|
|
30862
30864
|
|
|
30863
|
-
|
|
30864
|
-
|
|
30865
|
+
if (this.datasetCache[key]) {
|
|
30866
|
+
return this.datasetCache[key]
|
|
30865
30867
|
|
|
30866
|
-
|
|
30867
|
-
|
|
30868
|
-
|
|
30869
|
-
|
|
30870
|
-
// const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))
|
|
30871
|
-
if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) ||
|
|
30872
|
-
this.config.disableCache ||
|
|
30873
|
-
!this.featureCache ||
|
|
30874
|
-
!this.featureCache.containsRange(new GenomicInterval(chr, start, end))) {
|
|
30875
|
-
await this.loadFeatures(chr, start, end, visibilityWindow);
|
|
30876
|
-
}
|
|
30868
|
+
} else {
|
|
30869
|
+
await this.readHeader();
|
|
30870
|
+
const wf = (this.version < 2) ? "" : "/" + windowFunction;
|
|
30871
|
+
const zoomString = (chr.toLowerCase() === "all" || zoom === undefined) ? "0" : zoom.toString();
|
|
30877
30872
|
|
|
30878
|
-
|
|
30879
|
-
if (
|
|
30880
|
-
|
|
30881
|
-
|
|
30882
|
-
|
|
30883
|
-
|
|
30873
|
+
let dsName;
|
|
30874
|
+
if (windowFunction === "raw") {
|
|
30875
|
+
dsName = "/" + chr + "/raw";
|
|
30876
|
+
} else {
|
|
30877
|
+
dsName = "/" + chr + "/z" + zoomString + wf;
|
|
30878
|
+
}
|
|
30879
|
+
const indexEntry = this.datasetIndex[dsName];
|
|
30880
|
+
|
|
30881
|
+
if (indexEntry === undefined) {
|
|
30882
|
+
return undefined
|
|
30883
|
+
}
|
|
30884
|
+
|
|
30885
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30886
|
+
range: {
|
|
30887
|
+
start: indexEntry.position,
|
|
30888
|
+
size: indexEntry.size
|
|
30884
30889
|
}
|
|
30890
|
+
}));
|
|
30891
|
+
|
|
30892
|
+
if (!data) {
|
|
30893
|
+
return undefined
|
|
30885
30894
|
}
|
|
30886
|
-
return this.wgFeatures
|
|
30887
|
-
} else {
|
|
30888
|
-
return this.featureCache.queryFeatures(chr, start, end)
|
|
30889
|
-
}
|
|
30890
|
-
}
|
|
30891
30895
|
|
|
30892
|
-
|
|
30893
|
-
|
|
30894
|
-
|
|
30896
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
30897
|
+
let nAttributes = binaryParser.getInt();
|
|
30898
|
+
const attributes = {};
|
|
30899
|
+
while (nAttributes-- > 0) {
|
|
30900
|
+
attributes[binaryParser.getString()] = binaryParser.getString();
|
|
30901
|
+
}
|
|
30902
|
+
const dataType = binaryParser.getString();
|
|
30903
|
+
const tileWidth = binaryParser.getFloat();
|
|
30904
|
+
let nTiles = binaryParser.getInt();
|
|
30905
|
+
const tiles = [];
|
|
30906
|
+
while (nTiles-- > 0) {
|
|
30907
|
+
tiles.push({position: binaryParser.getLong(), size: binaryParser.getInt()});
|
|
30908
|
+
}
|
|
30895
30909
|
|
|
30896
|
-
|
|
30897
|
-
|
|
30898
|
-
|
|
30910
|
+
const dataset = {
|
|
30911
|
+
name: dsName,
|
|
30912
|
+
attributes: attributes,
|
|
30913
|
+
dataType: dataType,
|
|
30914
|
+
tileWidth: tileWidth,
|
|
30915
|
+
tiles: tiles
|
|
30916
|
+
};
|
|
30899
30917
|
|
|
30900
|
-
|
|
30901
|
-
|
|
30902
|
-
if (this.queryable || !this.featureCache) { // queryable sources don't support all features
|
|
30903
|
-
return []
|
|
30904
|
-
} else {
|
|
30905
|
-
return this.featureCache.getAllFeatures()
|
|
30918
|
+
this.datasetCache[key] = dataset;
|
|
30919
|
+
return dataset
|
|
30906
30920
|
}
|
|
30907
30921
|
}
|
|
30908
30922
|
|
|
30923
|
+
async readRootGroup() {
|
|
30909
30924
|
|
|
30910
|
-
|
|
30925
|
+
const genome = this.genome;
|
|
30926
|
+
const rootGroup = this.groupCache["/"];
|
|
30927
|
+
if (rootGroup) {
|
|
30928
|
+
return rootGroup
|
|
30929
|
+
} else {
|
|
30911
30930
|
|
|
30912
|
-
|
|
30931
|
+
const group = await this.readGroup("/");
|
|
30932
|
+
const names = group["chromosomes"];
|
|
30933
|
+
const maxZoomString = group["maxZoom"];
|
|
30913
30934
|
|
|
30914
|
-
|
|
30915
|
-
|
|
30916
|
-
|
|
30935
|
+
// Now parse out interesting attributes.
|
|
30936
|
+
if (maxZoomString) {
|
|
30937
|
+
this.maxZoom = Number(maxZoomString);
|
|
30938
|
+
}
|
|
30917
30939
|
|
|
30918
|
-
|
|
30919
|
-
|
|
30920
|
-
|
|
30921
|
-
|
|
30922
|
-
}
|
|
30923
|
-
if (this.chrAliasManager) {
|
|
30924
|
-
queryChr = await this.chrAliasManager.getAliasName(chr);
|
|
30925
|
-
}
|
|
30940
|
+
const totalCountString = group["totalCount"];
|
|
30941
|
+
if (totalCountString) {
|
|
30942
|
+
group.totalCount = Number(totalCountString);
|
|
30943
|
+
}
|
|
30926
30944
|
|
|
30927
|
-
|
|
30928
|
-
|
|
30929
|
-
|
|
30930
|
-
|
|
30931
|
-
|
|
30932
|
-
|
|
30933
|
-
|
|
30934
|
-
intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
|
|
30935
|
-
} else if (visibilityWindow > (end - start) && this.config.expandQuery !== false) {
|
|
30936
|
-
let expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
|
|
30937
|
-
if(this.config.minQuerySize && expansionWindow < this.config.minQuerySize) {
|
|
30938
|
-
expansionWindow = this.config.minQuerySize;
|
|
30945
|
+
// Chromosome names
|
|
30946
|
+
const chrAliasTable = {};
|
|
30947
|
+
if (names) {
|
|
30948
|
+
names.split(",").forEach(function (chr) {
|
|
30949
|
+
const canonicalName = genome.getChromosomeName(chr);
|
|
30950
|
+
chrAliasTable[canonicalName] = chr;
|
|
30951
|
+
});
|
|
30939
30952
|
}
|
|
30940
|
-
|
|
30941
|
-
intervalEnd = intervalStart + expansionWindow;
|
|
30942
|
-
}
|
|
30953
|
+
this.chrAliasTable = chrAliasTable;
|
|
30943
30954
|
|
|
30944
|
-
|
|
30945
|
-
|
|
30946
|
-
this.queryable = reader.indexed;
|
|
30955
|
+
this.groupCache["/"] = group;
|
|
30956
|
+
return group
|
|
30947
30957
|
}
|
|
30958
|
+
}
|
|
30948
30959
|
|
|
30949
|
-
|
|
30950
|
-
new GenomicInterval(chr, intervalStart, intervalEnd) :
|
|
30951
|
-
undefined;
|
|
30960
|
+
async readGroup(name) {
|
|
30952
30961
|
|
|
30953
|
-
|
|
30962
|
+
const group = this.groupCache[name];
|
|
30963
|
+
if (group) {
|
|
30964
|
+
return group
|
|
30965
|
+
} else {
|
|
30954
30966
|
|
|
30955
|
-
|
|
30956
|
-
|
|
30957
|
-
|
|
30958
|
-
|
|
30967
|
+
await this.readHeader();
|
|
30968
|
+
const indexEntry = this.groupIndex[name];
|
|
30969
|
+
if (indexEntry === undefined) {
|
|
30970
|
+
return undefined
|
|
30959
30971
|
}
|
|
30960
30972
|
|
|
30961
|
-
|
|
30962
|
-
|
|
30973
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
30974
|
+
range: {
|
|
30975
|
+
start: indexEntry.position,
|
|
30976
|
+
size: indexEntry.size
|
|
30977
|
+
}
|
|
30978
|
+
}));
|
|
30963
30979
|
|
|
30964
|
-
|
|
30965
|
-
|
|
30966
|
-
this.addFeaturesToDB(features, this.config);
|
|
30980
|
+
if (!data) {
|
|
30981
|
+
return undefined
|
|
30967
30982
|
}
|
|
30968
|
-
|
|
30969
|
-
|
|
30983
|
+
|
|
30984
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
30985
|
+
const group = {name: name};
|
|
30986
|
+
let nAttributes = binaryParser.getInt();
|
|
30987
|
+
while (nAttributes-- > 0) {
|
|
30988
|
+
const key = binaryParser.getString();
|
|
30989
|
+
const value = binaryParser.getString();
|
|
30990
|
+
group[key] = value;
|
|
30991
|
+
}
|
|
30992
|
+
this.groupCache[name] = group;
|
|
30993
|
+
return group
|
|
30970
30994
|
}
|
|
30971
30995
|
}
|
|
30972
30996
|
|
|
30973
|
-
|
|
30974
|
-
|
|
30975
|
-
|
|
30997
|
+
async readTiles(tileIndeces, nTracks) {
|
|
30998
|
+
|
|
30999
|
+
tileIndeces.sort(function (a, b) {
|
|
31000
|
+
return a.position - b.position
|
|
31001
|
+
});
|
|
31002
|
+
|
|
31003
|
+
tileIndeces = tileIndeces.filter(function (idx) {
|
|
31004
|
+
return idx.size > 0
|
|
31005
|
+
});
|
|
31006
|
+
|
|
31007
|
+
if (tileIndeces.length === 0) {
|
|
31008
|
+
return []
|
|
30976
31009
|
}
|
|
30977
|
-
|
|
30978
|
-
|
|
30979
|
-
|
|
30980
|
-
|
|
30981
|
-
|
|
30982
|
-
|
|
30983
|
-
|
|
30984
|
-
|
|
30985
|
-
|
|
30986
|
-
// If feature is already present keep largest one
|
|
30987
|
-
if (this.featureMap.has(key)) {
|
|
30988
|
-
const f2 = this.featureMap.get(key);
|
|
30989
|
-
if (feature.end - feature.start < f2.end - f2.start) {
|
|
30990
|
-
continue
|
|
30991
|
-
}
|
|
30992
|
-
}
|
|
30993
|
-
this.featureMap.set(key, feature);
|
|
31010
|
+
|
|
31011
|
+
const tiles = [];
|
|
31012
|
+
|
|
31013
|
+
for (let indexEntry of tileIndeces) {
|
|
31014
|
+
|
|
31015
|
+
const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
31016
|
+
range: {
|
|
31017
|
+
start: indexEntry.position,
|
|
31018
|
+
size: indexEntry.size
|
|
30994
31019
|
}
|
|
31020
|
+
}));
|
|
31021
|
+
|
|
31022
|
+
let tileData;
|
|
31023
|
+
try {
|
|
31024
|
+
tileData = this.compressed ? inflate_1$3(data).buffer : data;
|
|
31025
|
+
} catch (e) {
|
|
31026
|
+
console.error(e);
|
|
31027
|
+
continue
|
|
31028
|
+
}
|
|
31029
|
+
|
|
31030
|
+
const binaryParser = new BinaryParser$1(new DataView(tileData));
|
|
31031
|
+
const type = binaryParser.getString();
|
|
31032
|
+
let tile;
|
|
31033
|
+
switch (type) {
|
|
31034
|
+
case "fixedStep":
|
|
31035
|
+
tile = createFixedStep(binaryParser, nTracks);
|
|
31036
|
+
break
|
|
31037
|
+
case "variableStep":
|
|
31038
|
+
tile = createVariableStep(binaryParser, nTracks);
|
|
31039
|
+
break
|
|
31040
|
+
case "bed":
|
|
31041
|
+
case "bedWithName":
|
|
31042
|
+
tile = createBed(binaryParser, nTracks, type);
|
|
31043
|
+
break
|
|
31044
|
+
default:
|
|
31045
|
+
throw "Unknown tile type: " + type
|
|
30995
31046
|
}
|
|
31047
|
+
tiles.push(tile);
|
|
30996
31048
|
}
|
|
31049
|
+
return tiles
|
|
30997
31050
|
}
|
|
30998
31051
|
|
|
30999
|
-
|
|
31000
|
-
|
|
31001
|
-
|
|
31052
|
+
async readTile(indexEntry, nTracks) {
|
|
31053
|
+
|
|
31054
|
+
let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
|
|
31055
|
+
range: {
|
|
31056
|
+
start: indexEntry.position,
|
|
31057
|
+
size: indexEntry.size
|
|
31058
|
+
}
|
|
31059
|
+
}));
|
|
31060
|
+
|
|
31061
|
+
if (this.compressed) {
|
|
31062
|
+
const plain = inflate_1$3(data);
|
|
31063
|
+
data = plain.buffer;
|
|
31002
31064
|
}
|
|
31003
31065
|
|
|
31066
|
+
const binaryParser = new BinaryParser$1(new DataView(data));
|
|
31067
|
+
const type = binaryParser.getString();
|
|
31068
|
+
switch (type) {
|
|
31069
|
+
case "fixedStep":
|
|
31070
|
+
return createFixedStep(binaryParser, nTracks)
|
|
31071
|
+
case "variableStep":
|
|
31072
|
+
return createVariableStep(binaryParser, nTracks)
|
|
31073
|
+
case "bed":
|
|
31074
|
+
case "bedWithName":
|
|
31075
|
+
return createBed(binaryParser, nTracks, type)
|
|
31076
|
+
default:
|
|
31077
|
+
throw "Unknown tile type: " + type
|
|
31078
|
+
}
|
|
31004
31079
|
}
|
|
31080
|
+
|
|
31005
31081
|
}
|
|
31006
31082
|
|
|
31007
|
-
|
|
31008
|
-
|
|
31009
|
-
|
|
31010
|
-
|
|
31011
|
-
*
|
|
31012
|
-
* Both maps are needed by IGV
|
|
31013
|
-
*
|
|
31014
|
-
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
31015
|
-
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
31016
|
-
* leaving only the mapps.
|
|
31017
|
-
*/
|
|
31018
|
-
class ChromTree {
|
|
31083
|
+
function createFixedStep(binaryParser, nTracks) {
|
|
31084
|
+
const nPositions = binaryParser.getInt();
|
|
31085
|
+
const start = binaryParser.getInt();
|
|
31086
|
+
const span = binaryParser.getFloat();
|
|
31019
31087
|
|
|
31020
|
-
|
|
31021
|
-
|
|
31022
|
-
|
|
31023
|
-
|
|
31024
|
-
|
|
31088
|
+
const data = [];
|
|
31089
|
+
let nt = nTracks;
|
|
31090
|
+
while (nt-- > 0) {
|
|
31091
|
+
let np = nPositions;
|
|
31092
|
+
const dtrack = [];
|
|
31093
|
+
while (np-- > 0) {
|
|
31094
|
+
dtrack.push(binaryParser.getFloat());
|
|
31095
|
+
}
|
|
31096
|
+
data.push(dtrack);
|
|
31025
31097
|
}
|
|
31026
31098
|
|
|
31027
|
-
|
|
31028
|
-
|
|
31029
|
-
|
|
31030
|
-
|
|
31031
|
-
|
|
31032
|
-
|
|
31099
|
+
return {
|
|
31100
|
+
type: "fixedStep",
|
|
31101
|
+
start: start,
|
|
31102
|
+
span: span,
|
|
31103
|
+
data: data,
|
|
31104
|
+
nTracks: nTracks,
|
|
31105
|
+
nPositions: nPositions
|
|
31106
|
+
}
|
|
31107
|
+
}
|
|
31108
|
+
|
|
31109
|
+
function createVariableStep(binaryParser, nTracks) {
|
|
31110
|
+
|
|
31111
|
+
const tileStart = binaryParser.getInt();
|
|
31112
|
+
const span = binaryParser.getFloat();
|
|
31113
|
+
const nPositions = binaryParser.getInt();
|
|
31114
|
+
const start = [];
|
|
31115
|
+
|
|
31116
|
+
let np = nPositions;
|
|
31117
|
+
while (np-- > 0) {
|
|
31118
|
+
start.push(binaryParser.getInt());
|
|
31119
|
+
}
|
|
31120
|
+
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
31121
|
+
|
|
31122
|
+
const data = [];
|
|
31123
|
+
let nt = nTracks;
|
|
31124
|
+
while (nt-- > 0) {
|
|
31125
|
+
np = nPositions;
|
|
31126
|
+
const dtrack = [];
|
|
31127
|
+
while (np-- > 0) {
|
|
31128
|
+
dtrack.push(binaryParser.getFloat());
|
|
31129
|
+
}
|
|
31130
|
+
data.push(dtrack);
|
|
31131
|
+
}
|
|
31132
|
+
|
|
31133
|
+
return {
|
|
31134
|
+
type: "variableStep",
|
|
31135
|
+
tileStart: tileStart,
|
|
31136
|
+
span: span,
|
|
31137
|
+
start: start,
|
|
31138
|
+
data: data,
|
|
31139
|
+
nTracks: nTracks,
|
|
31140
|
+
nPositions: nPositions
|
|
31141
|
+
}
|
|
31142
|
+
}
|
|
31143
|
+
|
|
31144
|
+
function createBed(binaryParser, nTracks, type) {
|
|
31145
|
+
|
|
31146
|
+
const nPositions = binaryParser.getInt();
|
|
31147
|
+
|
|
31148
|
+
let n = nPositions;
|
|
31149
|
+
const start = [];
|
|
31150
|
+
while (n-- > 0) {
|
|
31151
|
+
start.push(binaryParser.getInt());
|
|
31152
|
+
}
|
|
31153
|
+
|
|
31154
|
+
n = nPositions;
|
|
31155
|
+
const end = [];
|
|
31156
|
+
while (n-- > 0) {
|
|
31157
|
+
end.push(binaryParser.getInt());
|
|
31158
|
+
}
|
|
31159
|
+
|
|
31160
|
+
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
31161
|
+
const data = [];
|
|
31162
|
+
let nt = nTracks;
|
|
31163
|
+
while (nt-- > 0) {
|
|
31164
|
+
let np = nPositions;
|
|
31165
|
+
const dtrack = [];
|
|
31166
|
+
while (np-- > 0) {
|
|
31167
|
+
dtrack.push(binaryParser.getFloat());
|
|
31168
|
+
}
|
|
31169
|
+
data.push(dtrack);
|
|
31170
|
+
}
|
|
31171
|
+
|
|
31172
|
+
if (type === "bedWithName") {
|
|
31173
|
+
n = nPositions;
|
|
31174
|
+
const name = [];
|
|
31175
|
+
while (n-- > 0) {
|
|
31176
|
+
name.push(binaryParser.getString());
|
|
31177
|
+
}
|
|
31178
|
+
}
|
|
31179
|
+
|
|
31180
|
+
return {
|
|
31181
|
+
type: type,
|
|
31182
|
+
start: start,
|
|
31183
|
+
end: end,
|
|
31184
|
+
data: data,
|
|
31185
|
+
nTracks: nTracks,
|
|
31186
|
+
nPositions: nPositions
|
|
31187
|
+
}
|
|
31188
|
+
}
|
|
31189
|
+
|
|
31190
|
+
/*
|
|
31191
|
+
* The MIT License (MIT)
|
|
31192
|
+
*
|
|
31193
|
+
* Copyright (c) 2016 University of California San Diego
|
|
31194
|
+
* Author: Jim Robinson
|
|
31195
|
+
*
|
|
31196
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
31197
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
31198
|
+
* in the Software without restriction, including without limitation the rights
|
|
31199
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31200
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
31201
|
+
* furnished to do so, subject to the following conditions:
|
|
31202
|
+
*
|
|
31203
|
+
* The above copyright notice and this permission notice shall be included in
|
|
31204
|
+
* all copies or substantial portions of the Software.
|
|
31205
|
+
*
|
|
31206
|
+
*
|
|
31207
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
31208
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31209
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
31210
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31211
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31212
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
31213
|
+
* THE SOFTWARE.
|
|
31214
|
+
*/
|
|
31215
|
+
|
|
31216
|
+
class TDFSource extends BaseFeatureSource {
|
|
31217
|
+
|
|
31218
|
+
#wgValues = {}
|
|
31219
|
+
searchable = false
|
|
31220
|
+
|
|
31221
|
+
|
|
31222
|
+
constructor(config, genome) {
|
|
31223
|
+
super(genome);
|
|
31224
|
+
this.genome = genome;
|
|
31225
|
+
this.reader = new TDFReader(config, genome);
|
|
31226
|
+
this.queryable = true;
|
|
31227
|
+
}
|
|
31228
|
+
|
|
31229
|
+
async getFeatures({chr, start, end, bpPerPixel, windowFunction = "mean"}) {
|
|
31230
|
+
|
|
31231
|
+
if (chr.toLowerCase() === "all") {
|
|
31232
|
+
return this.getWGValues(windowFunction, bpPerPixel)
|
|
31233
|
+
} else {
|
|
31234
|
+
return this._getFeatures(chr, start, end, bpPerPixel, windowFunction)
|
|
31235
|
+
}
|
|
31236
|
+
}
|
|
31237
|
+
async _getFeatures(chr, start, end, bpPerPixel, windowFunction) {
|
|
31238
|
+
const genomicInterval = new GenomicInterval(chr, start, end);
|
|
31239
|
+
const genome = this.genome;
|
|
31240
|
+
|
|
31241
|
+
|
|
31242
|
+
if (!this.rootGroup) {
|
|
31243
|
+
this.rootGroup = await this.reader.readRootGroup();
|
|
31244
|
+
if (!this.normalizationFactor) {
|
|
31245
|
+
const totalCount = this.rootGroup.totalCount;
|
|
31246
|
+
if (totalCount) {
|
|
31247
|
+
this.normalizationFactor = 1.0e6 / totalCount;
|
|
31248
|
+
}
|
|
31249
|
+
}
|
|
31250
|
+
}
|
|
31251
|
+
|
|
31252
|
+
genomicInterval.bpPerPixel = bpPerPixel;
|
|
31253
|
+
const zoom = zoomLevelForScale$1(chr, bpPerPixel, genome);
|
|
31254
|
+
let queryChr = this.reader.chrAliasTable[chr];
|
|
31255
|
+
let maxZoom = this.reader.maxZoom;
|
|
31256
|
+
if (queryChr === undefined) queryChr = chr;
|
|
31257
|
+
if (maxZoom === undefined) maxZoom = -1;
|
|
31258
|
+
|
|
31259
|
+
const wf = zoom > maxZoom ? "raw" : windowFunction;
|
|
31260
|
+
const dataset = await this.reader.readDataset(queryChr, wf, zoom);
|
|
31261
|
+
if (dataset == null) {
|
|
31262
|
+
return []
|
|
31263
|
+
}
|
|
31264
|
+
|
|
31265
|
+
const tileWidth = dataset.tileWidth;
|
|
31266
|
+
const startTile = Math.floor(start / tileWidth);
|
|
31267
|
+
const endTile = Math.floor(end / tileWidth);
|
|
31268
|
+
const NTRACKS = 1; // TODO read this
|
|
31269
|
+
const tiles = await this.reader.readTiles(dataset.tiles.slice(startTile, endTile + 1), NTRACKS);
|
|
31270
|
+
const features = [];
|
|
31271
|
+
for (let tile of tiles) {
|
|
31272
|
+
switch (tile.type) {
|
|
31273
|
+
case "bed":
|
|
31274
|
+
decodeBedTile(tile, chr, start, end, bpPerPixel, features);
|
|
31275
|
+
break
|
|
31276
|
+
case "variableStep":
|
|
31277
|
+
decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
|
|
31278
|
+
break
|
|
31279
|
+
case "fixedStep":
|
|
31280
|
+
decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
|
|
31281
|
+
break
|
|
31282
|
+
default:
|
|
31283
|
+
throw ("Unknown tile type: " + tile.type)
|
|
31284
|
+
}
|
|
31285
|
+
}
|
|
31286
|
+
features.sort(function (a, b) {
|
|
31287
|
+
return a.start - b.start
|
|
31288
|
+
});
|
|
31289
|
+
|
|
31290
|
+
return features
|
|
31291
|
+
}
|
|
31292
|
+
|
|
31293
|
+
get supportsWholeGenome() {
|
|
31294
|
+
return true
|
|
31295
|
+
}
|
|
31296
|
+
|
|
31297
|
+
get windowFunctions() {
|
|
31298
|
+
return this.reader.windowFunctions
|
|
31299
|
+
}
|
|
31300
|
+
|
|
31301
|
+
async getWGValues(windowFunction, bpPerPixel) {
|
|
31302
|
+
|
|
31303
|
+
const cached = this.#wgValues[windowFunction];
|
|
31304
|
+
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
31305
|
+
return cached.values
|
|
31306
|
+
} else {
|
|
31307
|
+
const wgFeatures = [];
|
|
31308
|
+
const genome = this.genome;
|
|
31309
|
+
const chrNames = this.genome.wgChromosomeNames;
|
|
31310
|
+
if (chrNames) {
|
|
31311
|
+
for (let c of genome.wgChromosomeNames) {
|
|
31312
|
+
const len = genome.getChromosome(c).bpLength;
|
|
31313
|
+
bpPerPixel = len / 1000;
|
|
31314
|
+
const chrFeatures = await this._getFeatures(c, 0, len, bpPerPixel, windowFunction);
|
|
31315
|
+
if (chrFeatures) {
|
|
31316
|
+
for (let f of chrFeatures) {
|
|
31317
|
+
const wg = Object.assign({}, f);
|
|
31318
|
+
wg.chr = "all";
|
|
31319
|
+
wg.start = genome.getGenomeCoordinate(f.chr, f.start);
|
|
31320
|
+
wg.end = genome.getGenomeCoordinate(f.chr, f.end);
|
|
31321
|
+
wg._f = f;
|
|
31322
|
+
wgFeatures.push(wg);
|
|
31323
|
+
}
|
|
31324
|
+
}
|
|
31325
|
+
}
|
|
31326
|
+
}
|
|
31327
|
+
this.#wgValues[windowFunction] = {values: wgFeatures, bpPerPixel};
|
|
31328
|
+
return wgFeatures
|
|
31329
|
+
}
|
|
31330
|
+
}
|
|
31331
|
+
|
|
31332
|
+
}
|
|
31333
|
+
|
|
31334
|
+
function decodeBedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31335
|
+
|
|
31336
|
+
const nPositions = tile.nPositions;
|
|
31337
|
+
const starts = tile.start;
|
|
31338
|
+
const ends = tile.end;
|
|
31339
|
+
const data = tile.data[0]; // Single track for now
|
|
31340
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31341
|
+
const s = starts[i];
|
|
31342
|
+
const e = ends[i];
|
|
31343
|
+
if (e < bpStart) continue
|
|
31344
|
+
if (s > bpEnd) break
|
|
31345
|
+
features.push({
|
|
31346
|
+
chr: chr,
|
|
31347
|
+
start: s,
|
|
31348
|
+
end: e,
|
|
31349
|
+
value: data[i]
|
|
31350
|
+
});
|
|
31351
|
+
}
|
|
31352
|
+
}
|
|
31353
|
+
|
|
31354
|
+
function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31355
|
+
|
|
31356
|
+
const nPositions = tile.nPositions;
|
|
31357
|
+
const starts = tile.start;
|
|
31358
|
+
const span = tile.span;
|
|
31359
|
+
const data = tile.data[0]; // Single track for now
|
|
31360
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31361
|
+
const s = starts[i];
|
|
31362
|
+
const e = s + span;
|
|
31363
|
+
if (e < bpStart) continue
|
|
31364
|
+
if (s > bpEnd) break
|
|
31365
|
+
features.push({
|
|
31366
|
+
chr: chr,
|
|
31367
|
+
start: s,
|
|
31368
|
+
end: e,
|
|
31369
|
+
value: data[i]
|
|
31370
|
+
});
|
|
31371
|
+
}
|
|
31372
|
+
}
|
|
31373
|
+
|
|
31374
|
+
function decodeFixedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
31375
|
+
|
|
31376
|
+
const nPositions = tile.nPositions;
|
|
31377
|
+
let s = tile.start;
|
|
31378
|
+
const span = tile.span;
|
|
31379
|
+
const data = tile.data[0]; // Single track for now
|
|
31380
|
+
|
|
31381
|
+
for (let i = 0; i < nPositions; i++) {
|
|
31382
|
+
const e = s + span;
|
|
31383
|
+
if (s > bpEnd) break
|
|
31384
|
+
if (e >= bpStart) {
|
|
31385
|
+
if (!Number.isNaN(data[i])) {
|
|
31386
|
+
features.push({
|
|
31387
|
+
chr: chr,
|
|
31388
|
+
start: s,
|
|
31389
|
+
end: e,
|
|
31390
|
+
value: data[i]
|
|
31391
|
+
});
|
|
31392
|
+
}
|
|
31393
|
+
}
|
|
31394
|
+
s = e;
|
|
31395
|
+
}
|
|
31396
|
+
}
|
|
31397
|
+
|
|
31398
|
+
|
|
31399
|
+
var log2 = Math.log(2);
|
|
31400
|
+
|
|
31401
|
+
function zoomLevelForScale$1(chr, bpPerPixel, genome) {
|
|
31402
|
+
|
|
31403
|
+
// Convert bpPerPixel to IGV "zoom" level. This is a bit convoluted, TDF is computed zoom levels assuming
|
|
31404
|
+
// display in a 700 pixel window. The fully zoomed out view of a chromosome is zoom level "0".
|
|
31405
|
+
// Zoom level 1 is magnified 2X, and so forth
|
|
31406
|
+
|
|
31407
|
+
var chrSize = genome.getChromosome(chr).bpLength;
|
|
31408
|
+
|
|
31409
|
+
return Math.ceil(Math.log(Math.max(0, (chrSize / (bpPerPixel * 700)))) / log2)
|
|
31410
|
+
}
|
|
31411
|
+
|
|
31412
|
+
/**
|
|
31413
|
+
* A ChromTree parses a UCSC bigbed/bigwig "chromosomeTree" section of the header to produce 2 maps,
|
|
31414
|
+
* (1) ID -> chromosome names, and its
|
|
31415
|
+
* (2) chromsome name -> ID
|
|
31416
|
+
*
|
|
31417
|
+
* Both maps are needed by IGV
|
|
31418
|
+
*
|
|
31419
|
+
* The chromosome tree is a B+ index, but is located continguously in memory in the header section of the file. This
|
|
31420
|
+
* makes it feasible to parse the whole tree with data from a single fetch. In the end the tree is discarded
|
|
31421
|
+
* leaving only the mapps.
|
|
31422
|
+
*/
|
|
31423
|
+
class ChromTree {
|
|
31424
|
+
|
|
31425
|
+
constructor(header, nameToID, valueToKey, sumLengths) {
|
|
31426
|
+
this.header = header;
|
|
31427
|
+
this.nameToId = nameToID;
|
|
31428
|
+
this.idToName = valueToKey;
|
|
31429
|
+
this.sumLengths = sumLengths;
|
|
31430
|
+
}
|
|
31431
|
+
|
|
31432
|
+
static parseTree(binaryParser, startOffset, genome = false) {
|
|
31433
|
+
{
|
|
31434
|
+
const magic = binaryParser.getInt();
|
|
31435
|
+
const blockSize = binaryParser.getInt();
|
|
31436
|
+
const keySize = binaryParser.getInt();
|
|
31437
|
+
const valSize = binaryParser.getInt();
|
|
31033
31438
|
const itemCount = binaryParser.getLong();
|
|
31034
31439
|
const reserved = binaryParser.getLong();
|
|
31035
31440
|
|
|
@@ -31575,7 +31980,7 @@
|
|
|
31575
31980
|
if (this.type === "bigwig") {
|
|
31576
31981
|
// Select a biwig "zoom level" appropriate for the current resolution.
|
|
31577
31982
|
const zoomLevelHeaders = await this.getZoomHeaders();
|
|
31578
|
-
let zoomLevelHeader = bpPerPixel ? zoomLevelForScale
|
|
31983
|
+
let zoomLevelHeader = bpPerPixel ? zoomLevelForScale(bpPerPixel, zoomLevelHeaders) : undefined;
|
|
31579
31984
|
if (zoomLevelHeader) {
|
|
31580
31985
|
treeOffset = zoomLevelHeader.indexOffset;
|
|
31581
31986
|
decodeFunction = decodeZoomData;
|
|
@@ -32031,7 +32436,7 @@
|
|
|
32031
32436
|
}
|
|
32032
32437
|
}
|
|
32033
32438
|
|
|
32034
|
-
function zoomLevelForScale
|
|
32439
|
+
function zoomLevelForScale(bpPerPixel, zoomLevelHeaders) {
|
|
32035
32440
|
let level;
|
|
32036
32441
|
for (let i = 0; i < zoomLevelHeaders.length; i++) {
|
|
32037
32442
|
const zl = zoomLevelHeaders[i];
|
|
@@ -32219,7 +32624,7 @@
|
|
|
32219
32624
|
class BWSource extends BaseFeatureSource {
|
|
32220
32625
|
|
|
32221
32626
|
queryable = true
|
|
32222
|
-
wgValues = {}
|
|
32627
|
+
#wgValues = {}
|
|
32223
32628
|
windowFunctions = ["mean", "min", "max"]
|
|
32224
32629
|
|
|
32225
32630
|
constructor(config, genome) {
|
|
@@ -32231,12 +32636,15 @@
|
|
|
32231
32636
|
|
|
32232
32637
|
async getFeatures({chr, start, end, bpPerPixel, windowFunction}) {
|
|
32233
32638
|
|
|
32234
|
-
await
|
|
32639
|
+
await this.reader.loadHeader();
|
|
32235
32640
|
const isBigWig = this.reader.type === "bigwig";
|
|
32236
32641
|
|
|
32237
|
-
|
|
32238
|
-
|
|
32239
|
-
await this.
|
|
32642
|
+
let features;
|
|
32643
|
+
if ("all" === chr.toLowerCase()) {
|
|
32644
|
+
features = isBigWig ? await this.getWGValues(windowFunction, bpPerPixel) : [];
|
|
32645
|
+
} else {
|
|
32646
|
+
features = await this.reader.readFeatures(chr, start, chr, end, bpPerPixel, windowFunction);
|
|
32647
|
+
}
|
|
32240
32648
|
|
|
32241
32649
|
if (!isBigWig) {
|
|
32242
32650
|
pack(features);
|
|
@@ -32252,26 +32660,25 @@
|
|
|
32252
32660
|
if (this.reader.type === "bigwig") {
|
|
32253
32661
|
return -1
|
|
32254
32662
|
} else {
|
|
32255
|
-
return this.reader.featureDensity ?
|
|
32663
|
+
return this.reader.featureDensity ? Math.floor(10000 / this.reader.featureDensity) : -1
|
|
32256
32664
|
}
|
|
32257
32665
|
|
|
32258
32666
|
}
|
|
32259
32667
|
|
|
32260
|
-
async getWGValues(windowFunction) {
|
|
32668
|
+
async getWGValues(windowFunction, bpPerPixel) {
|
|
32261
32669
|
|
|
32262
|
-
const numberOfBins = 1000; // This doesn't need to be precise
|
|
32263
32670
|
const genome = this.genome;
|
|
32264
|
-
|
|
32265
|
-
if (
|
|
32266
|
-
return
|
|
32671
|
+
const cached = this.#wgValues[windowFunction];
|
|
32672
|
+
if (cached && cached.bpPerPixel > 0.8 * bpPerPixel && cached.bpPerPixel < 1.2 * bpPerPixel) {
|
|
32673
|
+
return cached.values
|
|
32267
32674
|
} else {
|
|
32268
32675
|
|
|
32269
|
-
const bpPerPixel = genome.getGenomeLength() / numberOfBins;
|
|
32270
32676
|
const features = await this.reader.readWGFeatures(bpPerPixel, windowFunction);
|
|
32271
32677
|
let wgValues = [];
|
|
32272
32678
|
for (let f of features) {
|
|
32273
32679
|
const chr = f.chr;
|
|
32274
32680
|
const offset = genome.getCumulativeOffset(chr);
|
|
32681
|
+
if (undefined === offset) continue
|
|
32275
32682
|
const wgFeature = Object.assign({}, f);
|
|
32276
32683
|
wgFeature.chr = "all";
|
|
32277
32684
|
wgFeature.start = offset + f.start;
|
|
@@ -32280,7 +32687,7 @@
|
|
|
32280
32687
|
wgValues.push(wgFeature);
|
|
32281
32688
|
}
|
|
32282
32689
|
wgValues.sort((a, b) => a.start - b.start);
|
|
32283
|
-
this
|
|
32690
|
+
this.#wgValues[windowFunction] = {values: wgValues, bpPerPixel};
|
|
32284
32691
|
return wgValues
|
|
32285
32692
|
}
|
|
32286
32693
|
}
|
|
@@ -32302,658 +32709,780 @@
|
|
|
32302
32709
|
}
|
|
32303
32710
|
}
|
|
32304
32711
|
|
|
32305
|
-
|
|
32306
|
-
|
|
32307
|
-
|
|
32308
|
-
|
|
32309
|
-
* Author: Jim Robinson
|
|
32310
|
-
*
|
|
32311
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32312
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
32313
|
-
* in the Software without restriction, including without limitation the rights
|
|
32314
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32315
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
32316
|
-
* furnished to do so, subject to the following conditions:
|
|
32317
|
-
*
|
|
32318
|
-
* The above copyright notice and this permission notice shall be included in
|
|
32319
|
-
* all copies or substantial portions of the Software.
|
|
32320
|
-
*
|
|
32321
|
-
*
|
|
32322
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32323
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32324
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32325
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32326
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32327
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32328
|
-
* THE SOFTWARE.
|
|
32329
|
-
*/
|
|
32330
|
-
|
|
32331
|
-
const GZIP_FLAG = 0x1;
|
|
32712
|
+
const shim = .01;
|
|
32713
|
+
const colorStripWidth = 4;
|
|
32714
|
+
const axesXOffset = colorStripWidth + 1;
|
|
32715
|
+
function paintAxis(ctx, width, height, colorOrUndefined) {
|
|
32332
32716
|
|
|
32333
|
-
|
|
32717
|
+
if (undefined === this.dataRange || undefined === this.dataRange.max || undefined === this.dataRange.min) {
|
|
32718
|
+
return
|
|
32719
|
+
}
|
|
32334
32720
|
|
|
32335
|
-
|
|
32336
|
-
|
|
32337
|
-
|
|
32338
|
-
this.path = config.url;
|
|
32339
|
-
this.groupCache = {};
|
|
32340
|
-
this.datasetCache = {};
|
|
32721
|
+
IGVGraphics.fillRect(ctx, 0, 0, width, height, { fillStyle: 'white' });
|
|
32722
|
+
if (colorOrUndefined) {
|
|
32723
|
+
IGVGraphics.fillRect(ctx, width - colorStripWidth - 2, 0, colorStripWidth, height, { fillStyle: colorOrUndefined });
|
|
32341
32724
|
}
|
|
32342
32725
|
|
|
32726
|
+
const flipAxis = (undefined === this.flipAxis) ? false : this.flipAxis;
|
|
32343
32727
|
|
|
32344
|
-
|
|
32728
|
+
const xTickStart = 0.95 * width - 8 - axesXOffset;
|
|
32729
|
+
const xTickEnd = 0.95 * width - axesXOffset;
|
|
32345
32730
|
|
|
32346
|
-
|
|
32347
|
-
|
|
32348
|
-
|
|
32731
|
+
const properties =
|
|
32732
|
+
{
|
|
32733
|
+
font: 'normal 10px Arial',
|
|
32734
|
+
textAlign: 'right',
|
|
32735
|
+
fillStyle: 'black',
|
|
32736
|
+
strokeStyle: 'black',
|
|
32737
|
+
};
|
|
32349
32738
|
|
|
32350
|
-
|
|
32351
|
-
|
|
32352
|
-
|
|
32353
|
-
this.version = binaryParser.getInt();
|
|
32354
|
-
this.indexPos = binaryParser.getLong();
|
|
32355
|
-
this.indexSize = binaryParser.getInt();
|
|
32356
|
-
binaryParser.getInt();
|
|
32739
|
+
// tick
|
|
32740
|
+
IGVGraphics.strokeLine(ctx, xTickStart, shim * height, xTickEnd, shim * height, properties);
|
|
32741
|
+
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), xTickStart + 4, shim * height + 12, properties);
|
|
32357
32742
|
|
|
32743
|
+
const y = (1.0 - shim) * height;
|
|
32358
32744
|
|
|
32359
|
-
|
|
32360
|
-
|
|
32361
|
-
|
|
32362
|
-
while (nWindowFunctions-- > 0) {
|
|
32363
|
-
this.windowFunctions.push(binaryParser.getString());
|
|
32364
|
-
}
|
|
32365
|
-
}
|
|
32745
|
+
// tick
|
|
32746
|
+
IGVGraphics.strokeLine(ctx, xTickStart, y, xTickEnd, y, properties);
|
|
32747
|
+
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), xTickStart + 4, y - 4, properties);
|
|
32366
32748
|
|
|
32367
|
-
|
|
32368
|
-
|
|
32749
|
+
// vertical axis
|
|
32750
|
+
IGVGraphics.strokeLine(ctx, xTickEnd, shim * height, xTickEnd, y, properties);
|
|
32369
32751
|
|
|
32370
|
-
|
|
32371
|
-
this.trackNames = [];
|
|
32372
|
-
while (nTracks-- > 0) {
|
|
32373
|
-
this.trackNames.push(binaryParser.getString());
|
|
32374
|
-
}
|
|
32375
|
-
this.genomeID = binaryParser.getString();
|
|
32376
|
-
this.flags = binaryParser.getInt();
|
|
32377
|
-
this.compressed = (this.flags & GZIP_FLAG) !== 0;
|
|
32752
|
+
function prettyPrint(number) {
|
|
32378
32753
|
|
|
32379
|
-
|
|
32380
|
-
|
|
32381
|
-
|
|
32382
|
-
|
|
32383
|
-
|
|
32384
|
-
|
|
32385
|
-
}))
|
|
32386
|
-
|
|
32387
|
-
|
|
32388
|
-
|
|
32389
|
-
while (nEntries-- > 0) {
|
|
32390
|
-
const name = binaryParser.getString();
|
|
32391
|
-
const pos = binaryParser.getLong();
|
|
32392
|
-
const size = binaryParser.getInt();
|
|
32393
|
-
this.datasetIndex[name] = {position: pos, size: size};
|
|
32754
|
+
if (number === 0) {
|
|
32755
|
+
return "0"
|
|
32756
|
+
} else if (Math.abs(number) >= 10) {
|
|
32757
|
+
return number.toFixed()
|
|
32758
|
+
} else if (Math.abs(number) >= 1) {
|
|
32759
|
+
return number.toFixed(1)
|
|
32760
|
+
} else if (Math.abs(number) >= 0.1) {
|
|
32761
|
+
return number.toFixed(2)
|
|
32762
|
+
} else {
|
|
32763
|
+
return number.toExponential(1)
|
|
32394
32764
|
}
|
|
32765
|
+
}
|
|
32766
|
+
}
|
|
32395
32767
|
|
|
32396
|
-
|
|
32397
|
-
nEntries = binaryParser.getInt();
|
|
32398
|
-
while (nEntries-- > 0) {
|
|
32399
|
-
const name = binaryParser.getString();
|
|
32400
|
-
const pos = binaryParser.getLong();
|
|
32401
|
-
const size = binaryParser.getInt();
|
|
32402
|
-
this.groupIndex[name] = {position: pos, size: size};
|
|
32403
|
-
}
|
|
32768
|
+
const DEFAULT_COLOR$2 = 'rgb(150, 150, 150)';
|
|
32404
32769
|
|
|
32405
|
-
|
|
32770
|
+
|
|
32771
|
+
class WigTrack extends TrackBase {
|
|
32772
|
+
|
|
32773
|
+
static defaults = {
|
|
32774
|
+
height: 50,
|
|
32775
|
+
flipAxis: false,
|
|
32776
|
+
logScale: false,
|
|
32777
|
+
windowFunction: 'mean',
|
|
32778
|
+
graphType: 'bar',
|
|
32779
|
+
normalize: undefined,
|
|
32780
|
+
scaleFactor: undefined,
|
|
32781
|
+
overflowColor: `rgb(255, 32, 255)`,
|
|
32782
|
+
baselineColor: 'lightGray',
|
|
32783
|
+
summarize: true
|
|
32406
32784
|
}
|
|
32407
32785
|
|
|
32408
|
-
|
|
32786
|
+
constructor(config, browser) {
|
|
32787
|
+
super(config, browser);
|
|
32788
|
+
}
|
|
32409
32789
|
|
|
32410
|
-
|
|
32790
|
+
init(config) {
|
|
32791
|
+
|
|
32792
|
+
super.init(config);
|
|
32793
|
+
|
|
32794
|
+
this.type = "wig";
|
|
32795
|
+
this.featureType = 'numeric';
|
|
32796
|
+
this.resolutionAware = true;
|
|
32797
|
+
this.paintAxis = paintAxis;
|
|
32798
|
+
|
|
32799
|
+
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
32800
|
+
if (config.featureSource) {
|
|
32801
|
+
this.featureSource = config.featureSource;
|
|
32802
|
+
delete config.featureSource;
|
|
32803
|
+
} else if ("bigwig" === format) {
|
|
32804
|
+
this.featureSource = new BWSource(config, this.browser.genome);
|
|
32805
|
+
} else if ("tdf" === format) {
|
|
32806
|
+
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
32807
|
+
} else {
|
|
32808
|
+
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
32809
|
+
}
|
|
32411
32810
|
|
|
32412
|
-
if (this.datasetCache[key]) {
|
|
32413
|
-
return this.datasetCache[key]
|
|
32414
32811
|
|
|
32812
|
+
// Override autoscale default
|
|
32813
|
+
if (config.max === undefined || config.autoscale === true) {
|
|
32814
|
+
this.autoscale = true;
|
|
32415
32815
|
} else {
|
|
32416
|
-
|
|
32417
|
-
|
|
32418
|
-
|
|
32816
|
+
this.dataRange = {
|
|
32817
|
+
min: config.min || 0,
|
|
32818
|
+
max: config.max
|
|
32819
|
+
};
|
|
32820
|
+
}
|
|
32821
|
+
}
|
|
32419
32822
|
|
|
32420
|
-
|
|
32421
|
-
|
|
32422
|
-
|
|
32423
|
-
|
|
32424
|
-
|
|
32425
|
-
}
|
|
32426
|
-
const indexEntry = this.datasetIndex[dsName];
|
|
32823
|
+
async postInit() {
|
|
32824
|
+
const header = await this.getHeader();
|
|
32825
|
+
if (this.disposed) return // This track was removed during async load
|
|
32826
|
+
if (header) this.setTrackProperties(header);
|
|
32827
|
+
}
|
|
32427
32828
|
|
|
32428
|
-
|
|
32429
|
-
return undefined
|
|
32430
|
-
}
|
|
32829
|
+
async getFeatures(chr, start, end, bpPerPixel) {
|
|
32431
32830
|
|
|
32432
|
-
|
|
32433
|
-
range: {
|
|
32434
|
-
start: indexEntry.position,
|
|
32435
|
-
size: indexEntry.size
|
|
32436
|
-
}
|
|
32437
|
-
}));
|
|
32831
|
+
const windowFunction = this.windowFunction;
|
|
32438
32832
|
|
|
32439
|
-
|
|
32440
|
-
|
|
32833
|
+
const features = await this.featureSource.getFeatures({
|
|
32834
|
+
chr,
|
|
32835
|
+
start,
|
|
32836
|
+
end,
|
|
32837
|
+
bpPerPixel,
|
|
32838
|
+
visibilityWindow: this.visibilityWindow,
|
|
32839
|
+
windowFunction
|
|
32840
|
+
});
|
|
32841
|
+
if (this.normalize && this.featureSource.normalizationFactor) {
|
|
32842
|
+
const scaleFactor = this.featureSource.normalizationFactor;
|
|
32843
|
+
for (let f of features) {
|
|
32844
|
+
f.value *= scaleFactor;
|
|
32441
32845
|
}
|
|
32442
|
-
|
|
32443
|
-
|
|
32444
|
-
|
|
32445
|
-
|
|
32446
|
-
|
|
32447
|
-
attributes[binaryParser.getString()] = binaryParser.getString();
|
|
32846
|
+
}
|
|
32847
|
+
if (this.scaleFactor) {
|
|
32848
|
+
const scaleFactor = this.scaleFactor;
|
|
32849
|
+
for (let f of features) {
|
|
32850
|
+
f.value *= scaleFactor;
|
|
32448
32851
|
}
|
|
32449
|
-
|
|
32450
|
-
|
|
32451
|
-
|
|
32452
|
-
|
|
32453
|
-
|
|
32454
|
-
|
|
32852
|
+
}
|
|
32853
|
+
|
|
32854
|
+
// Summarize features to current resolution. This needs to be done here, rather than in the "draw" function,
|
|
32855
|
+
// for group autoscale to work.
|
|
32856
|
+
if (this.summarize && ("mean" === windowFunction || "min" === windowFunction || "max" === windowFunction)) {
|
|
32857
|
+
return summarizeData(features, start, bpPerPixel, windowFunction)
|
|
32858
|
+
} else {
|
|
32859
|
+
return features
|
|
32860
|
+
}
|
|
32861
|
+
}
|
|
32862
|
+
|
|
32863
|
+
menuItemList() {
|
|
32864
|
+
const items = [];
|
|
32865
|
+
|
|
32866
|
+
if (this.flipAxis !== undefined) {
|
|
32867
|
+
items.push('<hr>');
|
|
32868
|
+
|
|
32869
|
+
function click() {
|
|
32870
|
+
this.flipAxis = !this.flipAxis;
|
|
32871
|
+
this.trackView.repaintViews();
|
|
32455
32872
|
}
|
|
32456
32873
|
|
|
32457
|
-
|
|
32458
|
-
|
|
32459
|
-
attributes: attributes,
|
|
32460
|
-
dataType: dataType,
|
|
32461
|
-
tileWidth: tileWidth,
|
|
32462
|
-
tiles: tiles
|
|
32463
|
-
};
|
|
32874
|
+
items.push({label: 'Flip y-axis', click});
|
|
32875
|
+
}
|
|
32464
32876
|
|
|
32465
|
-
|
|
32466
|
-
|
|
32877
|
+
if(this.featureSource.windowFunctions) {
|
|
32878
|
+
items.push(...this.wigSummarizationItems());
|
|
32467
32879
|
}
|
|
32880
|
+
|
|
32881
|
+
items.push(...this.numericDataMenuItems());
|
|
32882
|
+
|
|
32883
|
+
return items
|
|
32468
32884
|
}
|
|
32469
32885
|
|
|
32470
|
-
|
|
32886
|
+
wigSummarizationItems() {
|
|
32471
32887
|
|
|
32472
|
-
const
|
|
32473
|
-
const rootGroup = this.groupCache["/"];
|
|
32474
|
-
if (rootGroup) {
|
|
32475
|
-
return rootGroup
|
|
32476
|
-
} else {
|
|
32888
|
+
const windowFunctions = this.featureSource.windowFunctions;
|
|
32477
32889
|
|
|
32478
|
-
|
|
32479
|
-
|
|
32480
|
-
|
|
32890
|
+
const menuItems = [];
|
|
32891
|
+
menuItems.push('<hr/>');
|
|
32892
|
+
menuItems.push("<div>Windowing function</div>");
|
|
32893
|
+
for (const wf of windowFunctions) {
|
|
32894
|
+
const object = $$1(createCheckbox(wf, this.windowFunction === wf));
|
|
32481
32895
|
|
|
32482
|
-
|
|
32483
|
-
|
|
32484
|
-
this.
|
|
32896
|
+
function clickHandler() {
|
|
32897
|
+
this.windowFunction = wf;
|
|
32898
|
+
this.trackView.updateViews();
|
|
32485
32899
|
}
|
|
32486
32900
|
|
|
32487
|
-
|
|
32488
|
-
|
|
32489
|
-
group.totalCount = Number(totalCountString);
|
|
32490
|
-
}
|
|
32901
|
+
menuItems.push({object, click: clickHandler});
|
|
32902
|
+
}
|
|
32491
32903
|
|
|
32492
|
-
|
|
32493
|
-
|
|
32494
|
-
if (names) {
|
|
32495
|
-
names.split(",").forEach(function (chr) {
|
|
32496
|
-
const canonicalName = genome.getChromosomeName(chr);
|
|
32497
|
-
chrAliasTable[canonicalName] = chr;
|
|
32498
|
-
});
|
|
32499
|
-
}
|
|
32500
|
-
this.chrAliasTable = chrAliasTable;
|
|
32904
|
+
return menuItems
|
|
32905
|
+
}
|
|
32501
32906
|
|
|
32502
|
-
|
|
32503
|
-
|
|
32907
|
+
|
|
32908
|
+
async getHeader() {
|
|
32909
|
+
|
|
32910
|
+
if (typeof this.featureSource.getHeader === "function") {
|
|
32911
|
+
this.header = await this.featureSource.getHeader();
|
|
32504
32912
|
}
|
|
32913
|
+
return this.header
|
|
32505
32914
|
}
|
|
32506
32915
|
|
|
32507
|
-
|
|
32916
|
+
// TODO: refactor to igvUtils.js
|
|
32917
|
+
getScaleFactor(min, max, height, logScale) {
|
|
32918
|
+
const minValue = (logScale === true) ? ((min < 0) ? -Math.log10(Math.abs(min) + 1) : Math.log10(Math.abs(min) + 1)) : min;
|
|
32919
|
+
const maxValue = (logScale === true) ? Math.log10(Math.abs(max) + 1) : max;
|
|
32920
|
+
const scale = height / (maxValue - minValue);
|
|
32921
|
+
return scale
|
|
32922
|
+
}
|
|
32508
32923
|
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
} else {
|
|
32924
|
+
computeYPixelValue(yValue, yScaleFactor) {
|
|
32925
|
+
return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
|
|
32926
|
+
}
|
|
32513
32927
|
|
|
32514
|
-
|
|
32515
|
-
|
|
32516
|
-
|
|
32517
|
-
|
|
32518
|
-
|
|
32928
|
+
computeYPixelValueInLogScale(yValue, yScaleFactor) {
|
|
32929
|
+
let maxValue = this.dataRange.max;
|
|
32930
|
+
let minValue = this.dataRange.min;
|
|
32931
|
+
minValue = (minValue < 0) ? -Math.log10(Math.abs(minValue) + 1) : Math.log10(Math.abs(minValue) + 1);
|
|
32932
|
+
maxValue = (maxValue < 0) ? -Math.log10(Math.abs(maxValue) + 1) : Math.log10(Math.abs(maxValue) + 1);
|
|
32933
|
+
|
|
32934
|
+
yValue = (yValue < 0) ? -Math.log10(Math.abs(yValue) +1) : Math.log10(yValue + 1);
|
|
32935
|
+
return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
|
|
32936
|
+
}
|
|
32519
32937
|
|
|
32520
|
-
|
|
32521
|
-
|
|
32522
|
-
|
|
32523
|
-
|
|
32938
|
+
draw(options) {
|
|
32939
|
+
|
|
32940
|
+
const features = options.features;
|
|
32941
|
+
const ctx = options.context;
|
|
32942
|
+
const bpPerPixel = options.bpPerPixel;
|
|
32943
|
+
const bpStart = options.bpStart;
|
|
32944
|
+
const pixelWidth = options.pixelWidth;
|
|
32945
|
+
const pixelHeight = options.pixelHeight - 1;
|
|
32946
|
+
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
32947
|
+
|
|
32948
|
+
const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, pixelHeight, this.logScale);
|
|
32949
|
+
const yScale = (yValue) => this.logScale
|
|
32950
|
+
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
|
|
32951
|
+
: this.computeYPixelValue(yValue, scaleFactor);
|
|
32952
|
+
|
|
32953
|
+
if (features && features.length > 0) {
|
|
32954
|
+
|
|
32955
|
+
if (this.dataRange.min === undefined) this.dataRange.min = 0;
|
|
32956
|
+
|
|
32957
|
+
// Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
|
|
32958
|
+
// nothing to paint.
|
|
32959
|
+
if (this.dataRange.max > this.dataRange.min) {
|
|
32960
|
+
|
|
32961
|
+
let lastPixelEnd = -1;
|
|
32962
|
+
let lastY;
|
|
32963
|
+
const y0 = yScale(0);
|
|
32964
|
+
|
|
32965
|
+
for (let f of features) {
|
|
32966
|
+
|
|
32967
|
+
if (f.end < bpStart) continue
|
|
32968
|
+
if (f.start > bpEnd) break
|
|
32969
|
+
|
|
32970
|
+
const x = (f.start - bpStart) / bpPerPixel;
|
|
32971
|
+
if (isNaN(x)) continue
|
|
32972
|
+
|
|
32973
|
+
let y = yScale(f.value);
|
|
32974
|
+
|
|
32975
|
+
const rectEnd = (f.end - bpStart) / bpPerPixel;
|
|
32976
|
+
const width = rectEnd - x;
|
|
32977
|
+
|
|
32978
|
+
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
32979
|
+
|
|
32980
|
+
if (this.graphType === "line") {
|
|
32981
|
+
if (lastY !== undefined) {
|
|
32982
|
+
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
|
|
32983
|
+
"fillStyle": color,
|
|
32984
|
+
"strokeStyle": color
|
|
32985
|
+
});
|
|
32986
|
+
}
|
|
32987
|
+
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
32988
|
+
} else if (this.graphType === "points") {
|
|
32989
|
+
const pointSize = this.config.pointSize || 3;
|
|
32990
|
+
const px = x + width / 2;
|
|
32991
|
+
IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
|
|
32992
|
+
|
|
32993
|
+
if (f.value > this.dataRange.max) {
|
|
32994
|
+
IGVGraphics.fillCircle(ctx, px, pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
32995
|
+
} else if (f.value < this.dataRange.min) {
|
|
32996
|
+
IGVGraphics.fillCircle(ctx, px, pixelHeight - pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
32997
|
+
}
|
|
32998
|
+
|
|
32999
|
+
} else {
|
|
33000
|
+
// Default graph type (bar)
|
|
33001
|
+
const height = Math.min(pixelHeight, y - y0);
|
|
33002
|
+
IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
|
|
33003
|
+
if (f.value > this.dataRange.max) {
|
|
33004
|
+
IGVGraphics.fillRect(ctx, x, 0, width, 3, {fillStyle: this.overflowColor});
|
|
33005
|
+
} else if (f.value < this.dataRange.min) {
|
|
33006
|
+
IGVGraphics.fillRect(ctx, x, pixelHeight - 2, width, 3, {fillStyle: this.overflowColor});
|
|
33007
|
+
}
|
|
33008
|
+
|
|
33009
|
+
}
|
|
33010
|
+
lastPixelEnd = x + width;
|
|
33011
|
+
lastY = y;
|
|
32524
33012
|
}
|
|
32525
|
-
}));
|
|
32526
33013
|
|
|
32527
|
-
|
|
32528
|
-
|
|
33014
|
+
// If the track includes negative values draw a baseline
|
|
33015
|
+
if (this.dataRange.min < 0) {
|
|
33016
|
+
let maxValue = this.dataRange.max;
|
|
33017
|
+
let minValue = this.dataRange.min;
|
|
33018
|
+
minValue = (this.logScale === true) ? ((minValue < 0) ? -Math.log10(Math.abs(minValue) + 1) : Math.log10(Math.abs(minValue) + 1)) : minValue;
|
|
33019
|
+
maxValue = (this.logScale === true) ? ((maxValue < 0) ? -Math.log10(Math.abs(maxValue) + 1) : Math.log10(Math.abs(maxValue) + 1)) : maxValue;
|
|
33020
|
+
const ratio = maxValue / (maxValue - minValue);
|
|
33021
|
+
const basepx = this.flipAxis ? (1 - ratio) * pixelHeight : ratio * pixelHeight;
|
|
33022
|
+
IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {strokeStyle: this.baselineColor});
|
|
33023
|
+
}
|
|
32529
33024
|
}
|
|
33025
|
+
}
|
|
32530
33026
|
|
|
32531
|
-
|
|
32532
|
-
|
|
32533
|
-
let
|
|
32534
|
-
|
|
32535
|
-
|
|
32536
|
-
|
|
32537
|
-
|
|
33027
|
+
// Draw guidelines
|
|
33028
|
+
if (this.config.hasOwnProperty('guideLines')) {
|
|
33029
|
+
for (let line of this.config.guideLines) {
|
|
33030
|
+
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
|
|
33031
|
+
let y = yScale(line.y);
|
|
33032
|
+
let props = {
|
|
33033
|
+
'strokeStyle': line['color'],
|
|
33034
|
+
'strokeWidth': 2
|
|
33035
|
+
};
|
|
33036
|
+
if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);
|
|
33037
|
+
else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
|
|
33038
|
+
}
|
|
32538
33039
|
}
|
|
32539
|
-
this.groupCache[name] = group;
|
|
32540
|
-
return group
|
|
32541
33040
|
}
|
|
32542
33041
|
}
|
|
32543
33042
|
|
|
32544
|
-
|
|
33043
|
+
popupData(clickState, features) {
|
|
32545
33044
|
|
|
32546
|
-
|
|
32547
|
-
return a.position - b.position
|
|
32548
|
-
});
|
|
33045
|
+
if (features === undefined) features = this.clickedFeatures(clickState);
|
|
32549
33046
|
|
|
32550
|
-
|
|
32551
|
-
return idx.size > 0
|
|
32552
|
-
});
|
|
33047
|
+
if (features && features.length > 0) {
|
|
32553
33048
|
|
|
32554
|
-
|
|
32555
|
-
|
|
32556
|
-
}
|
|
33049
|
+
const genomicLocation = clickState.genomicLocation;
|
|
33050
|
+
const popupData = [];
|
|
32557
33051
|
|
|
32558
|
-
|
|
33052
|
+
// Sort features based on distance from click
|
|
33053
|
+
features.sort(function (a, b) {
|
|
33054
|
+
const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
|
|
33055
|
+
const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
|
|
33056
|
+
return distA - distB
|
|
33057
|
+
});
|
|
32559
33058
|
|
|
32560
|
-
|
|
33059
|
+
// Display closest 10
|
|
33060
|
+
const displayFeatures = features.length > 10 ? features.slice(0, 10) : features;
|
|
32561
33061
|
|
|
32562
|
-
|
|
33062
|
+
// Resort in ascending order
|
|
33063
|
+
displayFeatures.sort(function (a, b) {
|
|
33064
|
+
return a.start - b.start
|
|
33065
|
+
});
|
|
32563
33066
|
|
|
32564
|
-
|
|
32565
|
-
|
|
32566
|
-
|
|
32567
|
-
|
|
33067
|
+
for (let selectedFeature of displayFeatures) {
|
|
33068
|
+
if (selectedFeature) {
|
|
33069
|
+
if (popupData.length > 0) {
|
|
33070
|
+
popupData.push('<hr/>');
|
|
33071
|
+
}
|
|
33072
|
+
let posString = (selectedFeature.end - selectedFeature.start) === 1 ?
|
|
33073
|
+
numberFormatter$1(Math.floor(selectedFeature.start) + 1)
|
|
33074
|
+
: numberFormatter$1(Math.floor(selectedFeature.start) + 1) + "-" + numberFormatter$1(Math.floor(selectedFeature.end));
|
|
33075
|
+
popupData.push({name: "Position:", value: posString});
|
|
33076
|
+
popupData.push({
|
|
33077
|
+
name: "Value: ",
|
|
33078
|
+
value: numberFormatter$1(selectedFeature.value.toFixed(4))
|
|
33079
|
+
});
|
|
32568
33080
|
}
|
|
32569
|
-
}));
|
|
32570
|
-
|
|
32571
|
-
const tileData = this.compressed ? inflate_1$3(data).buffer : data;
|
|
32572
|
-
|
|
32573
|
-
const binaryParser = new BinaryParser$1(new DataView(tileData));
|
|
32574
|
-
const type = binaryParser.getString();
|
|
32575
|
-
let tile;
|
|
32576
|
-
switch (type) {
|
|
32577
|
-
case "fixedStep":
|
|
32578
|
-
tile = createFixedStep(binaryParser, nTracks);
|
|
32579
|
-
break
|
|
32580
|
-
case "variableStep":
|
|
32581
|
-
tile = createVariableStep(binaryParser, nTracks);
|
|
32582
|
-
break
|
|
32583
|
-
case "bed":
|
|
32584
|
-
case "bedWithName":
|
|
32585
|
-
tile = createBed(binaryParser, nTracks, type);
|
|
32586
|
-
break
|
|
32587
|
-
default:
|
|
32588
|
-
throw "Unknown tile type: " + type
|
|
32589
33081
|
}
|
|
32590
|
-
|
|
33082
|
+
if (displayFeatures.length < features.length) {
|
|
33083
|
+
popupData.push("<hr/>...");
|
|
33084
|
+
}
|
|
33085
|
+
|
|
33086
|
+
return popupData
|
|
32591
33087
|
|
|
33088
|
+
} else {
|
|
33089
|
+
return []
|
|
32592
33090
|
}
|
|
32593
|
-
return tiles
|
|
32594
33091
|
}
|
|
32595
33092
|
|
|
32596
|
-
|
|
33093
|
+
get supportsWholeGenome() {
|
|
33094
|
+
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
33095
|
+
}
|
|
32597
33096
|
|
|
32598
|
-
|
|
32599
|
-
|
|
32600
|
-
|
|
32601
|
-
|
|
32602
|
-
|
|
32603
|
-
}));
|
|
33097
|
+
/**
|
|
33098
|
+
* Return color for feature.
|
|
33099
|
+
* @param feature
|
|
33100
|
+
* @returns {string}
|
|
33101
|
+
*/
|
|
32604
33102
|
|
|
32605
|
-
|
|
32606
|
-
|
|
32607
|
-
|
|
32608
|
-
|
|
33103
|
+
getColorForFeature(f) {
|
|
33104
|
+
let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$2;
|
|
33105
|
+
return (typeof c === "function") ? c(f.value) : c
|
|
33106
|
+
}
|
|
32609
33107
|
|
|
32610
|
-
|
|
32611
|
-
|
|
32612
|
-
|
|
32613
|
-
|
|
32614
|
-
|
|
32615
|
-
case "variableStep":
|
|
32616
|
-
return createVariableStep(binaryParser, nTracks)
|
|
32617
|
-
case "bed":
|
|
32618
|
-
case "bedWithName":
|
|
32619
|
-
return createBed(binaryParser, nTracks, type)
|
|
32620
|
-
default:
|
|
32621
|
-
throw "Unknown tile type: " + type
|
|
32622
|
-
}
|
|
33108
|
+
/**
|
|
33109
|
+
* Called when the track is removed. Do any needed cleanup here
|
|
33110
|
+
*/
|
|
33111
|
+
dispose() {
|
|
33112
|
+
this.trackView = undefined;
|
|
32623
33113
|
}
|
|
32624
33114
|
|
|
32625
33115
|
}
|
|
32626
33116
|
|
|
32627
|
-
|
|
32628
|
-
|
|
32629
|
-
|
|
32630
|
-
|
|
33117
|
+
/**
|
|
33118
|
+
* Summarize wig data in bins of size "bpPerPixel" with the given window function.
|
|
33119
|
+
*
|
|
33120
|
+
* @param features wig (numeric) data -- features cannot overlap, and are in ascending order by start position
|
|
33121
|
+
* @param startBP bp start position for computing binned data
|
|
33122
|
+
* @param bpPerPixel bp per pixel (bin)
|
|
33123
|
+
* @param windowFunction mean, min, or max
|
|
33124
|
+
* @returns {*|*[]}
|
|
33125
|
+
*/
|
|
33126
|
+
function summarizeData(features, startBP, bpPerPixel, windowFunction = "mean") {
|
|
32631
33127
|
|
|
32632
|
-
|
|
32633
|
-
|
|
32634
|
-
while (nt-- > 0) {
|
|
32635
|
-
let np = nPositions;
|
|
32636
|
-
const dtrack = [];
|
|
32637
|
-
while (np-- > 0) {
|
|
32638
|
-
dtrack.push(binaryParser.getFloat());
|
|
32639
|
-
}
|
|
32640
|
-
data.push(dtrack);
|
|
33128
|
+
if (bpPerPixel <= 1 || !features || features.length === 0) {
|
|
33129
|
+
return features
|
|
32641
33130
|
}
|
|
32642
33131
|
|
|
32643
|
-
|
|
32644
|
-
|
|
32645
|
-
|
|
32646
|
-
|
|
32647
|
-
data: data,
|
|
32648
|
-
nTracks: nTracks,
|
|
32649
|
-
nPositions: nPositions
|
|
32650
|
-
}
|
|
32651
|
-
}
|
|
33132
|
+
// Assume features are sorted by position. Wig features cannot overlap. Note, UCSC "reductionLevel" == bpPerPixel
|
|
33133
|
+
const chr = features[0].chr;
|
|
33134
|
+
const binSize = bpPerPixel;
|
|
33135
|
+
const summaryFeatures = [];
|
|
32652
33136
|
|
|
32653
|
-
|
|
33137
|
+
const finishBin = (bin) => {
|
|
33138
|
+
const start = startBP + bin.bin * binSize;
|
|
33139
|
+
const end = start + binSize;
|
|
33140
|
+
let value;
|
|
33141
|
+
switch (windowFunction) {
|
|
33142
|
+
case "mean":
|
|
33143
|
+
value = bin.sumData / bin.count;
|
|
33144
|
+
break
|
|
33145
|
+
case "max":
|
|
33146
|
+
value = bin.max;
|
|
33147
|
+
break
|
|
33148
|
+
case "min":
|
|
33149
|
+
value = bin.min;
|
|
33150
|
+
break
|
|
33151
|
+
default:
|
|
33152
|
+
throw Error(`Unknown window function: ${windowFunction}`)
|
|
33153
|
+
}
|
|
33154
|
+
const description = `${windowFunction} of ${bin.count} values`;
|
|
33155
|
+
summaryFeatures.push({chr, start, end, value, description});
|
|
33156
|
+
};
|
|
32654
33157
|
|
|
32655
|
-
|
|
32656
|
-
|
|
32657
|
-
const nPositions = binaryParser.getInt();
|
|
32658
|
-
const start = [];
|
|
33158
|
+
let currentBinData;
|
|
33159
|
+
for (let f of features) {
|
|
32659
33160
|
|
|
32660
|
-
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
binaryParser.getInt(); // # of samples, ignored but should === nTracks
|
|
33161
|
+
// Loop through bins this feature overlaps, updating the weighted sum for each bin or min/max,
|
|
33162
|
+
// depending on window function
|
|
33163
|
+
let startBin = Math.floor((f.start - startBP) / binSize);
|
|
33164
|
+
const endBin = Math.floor((f.end - startBP) / binSize);
|
|
32665
33165
|
|
|
32666
|
-
|
|
32667
|
-
|
|
32668
|
-
|
|
32669
|
-
np = nPositions;
|
|
32670
|
-
const dtrack = [];
|
|
32671
|
-
while (np-- > 0) {
|
|
32672
|
-
dtrack.push(binaryParser.getFloat());
|
|
33166
|
+
if (currentBinData && startBin === currentBinData.bin) {
|
|
33167
|
+
currentBinData.add(f);
|
|
33168
|
+
startBin++;
|
|
32673
33169
|
}
|
|
32674
|
-
data.push(dtrack);
|
|
32675
|
-
}
|
|
32676
33170
|
|
|
32677
|
-
|
|
32678
|
-
|
|
32679
|
-
|
|
32680
|
-
|
|
32681
|
-
|
|
32682
|
-
data: data,
|
|
32683
|
-
nTracks: nTracks,
|
|
32684
|
-
nPositions: nPositions
|
|
32685
|
-
}
|
|
32686
|
-
}
|
|
33171
|
+
if (!currentBinData || endBin > currentBinData.bin) {
|
|
33172
|
+
|
|
33173
|
+
if(currentBinData) {
|
|
33174
|
+
finishBin(currentBinData);
|
|
33175
|
+
}
|
|
32687
33176
|
|
|
32688
|
-
|
|
33177
|
+
// Feature stretches across multiple bins.
|
|
33178
|
+
if (endBin > startBin) {
|
|
33179
|
+
const end = startBP + endBin * binSize;
|
|
33180
|
+
summaryFeatures.push({chr, start: f.start, end, value: f.value});
|
|
33181
|
+
}
|
|
32689
33182
|
|
|
32690
|
-
|
|
33183
|
+
currentBinData = new SummaryBinData(endBin, f);
|
|
33184
|
+
}
|
|
32691
33185
|
|
|
32692
|
-
let n = nPositions;
|
|
32693
|
-
const start = [];
|
|
32694
|
-
while (n-- > 0) {
|
|
32695
|
-
start.push(binaryParser.getInt());
|
|
32696
33186
|
}
|
|
32697
|
-
|
|
32698
|
-
|
|
32699
|
-
const end = [];
|
|
32700
|
-
while (n-- > 0) {
|
|
32701
|
-
end.push(binaryParser.getInt());
|
|
33187
|
+
if(currentBinData) {
|
|
33188
|
+
finishBin(currentBinData);
|
|
32702
33189
|
}
|
|
32703
33190
|
|
|
32704
|
-
|
|
32705
|
-
const
|
|
32706
|
-
let
|
|
32707
|
-
|
|
32708
|
-
|
|
32709
|
-
|
|
32710
|
-
|
|
32711
|
-
|
|
33191
|
+
// Consolidate
|
|
33192
|
+
const c = [];
|
|
33193
|
+
let lastFeature = summaryFeatures[0];
|
|
33194
|
+
for (let f of summaryFeatures) {
|
|
33195
|
+
if (lastFeature.value === f.value && f.start <= lastFeature.end) {
|
|
33196
|
+
lastFeature.end = f.end;
|
|
33197
|
+
} else {
|
|
33198
|
+
c.push(lastFeature);
|
|
33199
|
+
lastFeature = f;
|
|
32712
33200
|
}
|
|
32713
|
-
data.push(dtrack);
|
|
32714
33201
|
}
|
|
33202
|
+
c.push(lastFeature);
|
|
32715
33203
|
|
|
32716
|
-
|
|
32717
|
-
n = nPositions;
|
|
32718
|
-
const name = [];
|
|
32719
|
-
while (n-- > 0) {
|
|
32720
|
-
name.push(binaryParser.getString());
|
|
32721
|
-
}
|
|
32722
|
-
}
|
|
33204
|
+
return c
|
|
32723
33205
|
|
|
32724
|
-
return {
|
|
32725
|
-
type: type,
|
|
32726
|
-
start: start,
|
|
32727
|
-
end: end,
|
|
32728
|
-
data: data,
|
|
32729
|
-
nTracks: nTracks,
|
|
32730
|
-
nPositions: nPositions
|
|
32731
|
-
}
|
|
32732
33206
|
}
|
|
32733
33207
|
|
|
32734
|
-
|
|
33208
|
+
class SummaryBinData {
|
|
33209
|
+
constructor(bin, feature) {
|
|
33210
|
+
this.bin = bin;
|
|
33211
|
+
this.sumData = feature.value;
|
|
33212
|
+
this.count = 1;
|
|
33213
|
+
this.min = feature.value;
|
|
33214
|
+
this.max = feature.value;
|
|
33215
|
+
}
|
|
33216
|
+
|
|
33217
|
+
add(feature) {
|
|
33218
|
+
this.sumData += feature.value;
|
|
33219
|
+
this.max = Math.max(feature.value, this.max);
|
|
33220
|
+
this.min = Math.min(feature.value, this.min);
|
|
33221
|
+
this.count++;
|
|
33222
|
+
}
|
|
32735
33223
|
|
|
32736
|
-
|
|
32737
|
-
|
|
32738
|
-
for (let i = 1; i < tiles.length; i++) {
|
|
32739
|
-
const t = tiles[i];
|
|
32740
|
-
if (t.position > current.position + current.size) {
|
|
32741
|
-
consolidated.push(current);
|
|
32742
|
-
current = t;
|
|
32743
|
-
} else {
|
|
32744
|
-
current.size = t.position + t.size - current.position;
|
|
32745
|
-
}
|
|
33224
|
+
get mean() {
|
|
33225
|
+
return this.sumData / this.count
|
|
32746
33226
|
}
|
|
32747
|
-
consolidated.push(current);
|
|
32748
|
-
return consolidated
|
|
32749
33227
|
}
|
|
32750
33228
|
|
|
32751
|
-
|
|
32752
|
-
|
|
32753
|
-
|
|
32754
|
-
*
|
|
32755
|
-
* Author: Jim Robinson
|
|
32756
|
-
*
|
|
32757
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32758
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
32759
|
-
* in the Software without restriction, including without limitation the rights
|
|
32760
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32761
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
32762
|
-
* furnished to do so, subject to the following conditions:
|
|
32763
|
-
*
|
|
32764
|
-
* The above copyright notice and this permission notice shall be included in
|
|
32765
|
-
* all copies or substantial portions of the Software.
|
|
32766
|
-
*
|
|
33229
|
+
const DEFAULT_MAX_WG_COUNT = 10000;
|
|
33230
|
+
|
|
33231
|
+
/**
|
|
33232
|
+
* feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
|
|
32767
33233
|
*
|
|
32768
|
-
*
|
|
32769
|
-
*
|
|
32770
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32771
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32772
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32773
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
32774
|
-
* THE SOFTWARE.
|
|
33234
|
+
* @param config
|
|
33235
|
+
* @constructor
|
|
32775
33236
|
*/
|
|
33237
|
+
class TextFeatureSource extends BaseFeatureSource {
|
|
32776
33238
|
|
|
32777
|
-
class TDFSource extends BaseFeatureSource {
|
|
32778
|
-
|
|
32779
|
-
searchable = false
|
|
32780
33239
|
constructor(config, genome) {
|
|
33240
|
+
|
|
32781
33241
|
super(genome);
|
|
33242
|
+
|
|
33243
|
+
this.config = config || {};
|
|
32782
33244
|
this.genome = genome;
|
|
32783
|
-
this.
|
|
32784
|
-
this.
|
|
32785
|
-
|
|
33245
|
+
this.sourceType = (config.sourceType === undefined ? "file" : config.sourceType);
|
|
33246
|
+
this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
|
|
33247
|
+
this.windowFunctions = ["mean", "min", "max", "none"];
|
|
32786
33248
|
|
|
32787
|
-
|
|
33249
|
+
const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);
|
|
32788
33250
|
|
|
32789
|
-
|
|
32790
|
-
|
|
32791
|
-
|
|
32792
|
-
|
|
32793
|
-
|
|
32794
|
-
|
|
32795
|
-
|
|
32796
|
-
|
|
32797
|
-
|
|
32798
|
-
|
|
32799
|
-
|
|
32800
|
-
|
|
32801
|
-
|
|
32802
|
-
|
|
32803
|
-
|
|
32804
|
-
|
|
32805
|
-
|
|
32806
|
-
|
|
32807
|
-
|
|
32808
|
-
|
|
32809
|
-
|
|
32810
|
-
|
|
33251
|
+
this.queryable = config.indexURL || config.queryable === true; // False by default, unless explicitly set
|
|
33252
|
+
if (config.reader) {
|
|
33253
|
+
// Explicit reader implementation
|
|
33254
|
+
this.reader = config.reader;
|
|
33255
|
+
this.queryable = config.queryable !== false;
|
|
33256
|
+
} else if (config.sourceType === "ga4gh") {
|
|
33257
|
+
throw Error("Unsupported source type 'ga4gh'")
|
|
33258
|
+
} else if ((config.type === "eqtl" || config.type === "qtl") && config.sourceType === "gtex-ws") {
|
|
33259
|
+
this.reader = new GtexReader(config);
|
|
33260
|
+
this.queryable = true;
|
|
33261
|
+
} else if ("htsget" === config.sourceType) {
|
|
33262
|
+
this.reader = new HtsgetVariantReader(config, genome);
|
|
33263
|
+
this.queryable = true;
|
|
33264
|
+
} else if (config.sourceType === 'ucscservice') {
|
|
33265
|
+
this.reader = new UCSCServiceReader(config.source);
|
|
33266
|
+
this.queryable = true;
|
|
33267
|
+
} else if (config.sourceType === 'custom') {
|
|
33268
|
+
this.reader = new CustomServiceReader(config.source);
|
|
33269
|
+
this.queryable = false !== config.source.queryable;
|
|
33270
|
+
} else if ('service' === config.sourceType) {
|
|
33271
|
+
this.reader = new FeatureFileReader(config, genome);
|
|
33272
|
+
this.queryable = true;
|
|
33273
|
+
} else {
|
|
33274
|
+
// File of some type (i.e. not a webservice)
|
|
33275
|
+
this.reader = new FeatureFileReader(config, genome);
|
|
33276
|
+
if (config.queryable !== undefined) {
|
|
33277
|
+
this.queryable = config.queryable;
|
|
33278
|
+
} else if (queryableFormats.has(config.format) || this.reader.indexed) {
|
|
33279
|
+
this.queryable = true;
|
|
33280
|
+
} else ;
|
|
33281
|
+
}
|
|
33282
|
+
|
|
33283
|
+
// Flag indicating if features loaded by this source can be searched for by name or attribute, true by default
|
|
33284
|
+
this.searchable = config.searchable !== false;
|
|
33285
|
+
|
|
33286
|
+
}
|
|
33287
|
+
|
|
33288
|
+
async defaultVisibilityWindow() {
|
|
33289
|
+
if (this.reader && typeof this.reader.defaultVisibilityWindow === 'function') {
|
|
33290
|
+
return this.reader.defaultVisibilityWindow()
|
|
33291
|
+
}
|
|
33292
|
+
}
|
|
32811
33293
|
|
|
33294
|
+
async trackType() {
|
|
33295
|
+
const header = await this.getHeader();
|
|
33296
|
+
if (header) {
|
|
33297
|
+
return header.type
|
|
32812
33298
|
} else {
|
|
32813
|
-
return
|
|
33299
|
+
return undefined // Convention for unknown or unspecified
|
|
32814
33300
|
}
|
|
32815
33301
|
}
|
|
32816
|
-
async _getFeatures(chr, start, end, bpPerPixel, windowFunction) {
|
|
32817
|
-
const genomicInterval = new GenomicInterval(chr, start, end);
|
|
32818
|
-
const genome = this.genome;
|
|
32819
33302
|
|
|
33303
|
+
async getHeader() {
|
|
33304
|
+
if (!this.header) {
|
|
32820
33305
|
|
|
32821
|
-
|
|
32822
|
-
|
|
32823
|
-
|
|
32824
|
-
|
|
32825
|
-
|
|
32826
|
-
|
|
33306
|
+
if (this.reader && typeof this.reader.readHeader === "function") {
|
|
33307
|
+
const header = await this.reader.readHeader();
|
|
33308
|
+
if (header) {
|
|
33309
|
+
this.header = header;
|
|
33310
|
+
if (header.format) {
|
|
33311
|
+
this.config.format = header.format;
|
|
33312
|
+
}
|
|
33313
|
+
} else {
|
|
33314
|
+
this.header = {};
|
|
32827
33315
|
}
|
|
33316
|
+
} else {
|
|
33317
|
+
this.header = {};
|
|
32828
33318
|
}
|
|
32829
33319
|
}
|
|
33320
|
+
return this.header
|
|
33321
|
+
}
|
|
32830
33322
|
|
|
32831
|
-
|
|
32832
|
-
|
|
32833
|
-
|
|
32834
|
-
|
|
32835
|
-
|
|
32836
|
-
|
|
33323
|
+
/**
|
|
33324
|
+
* Required function for all data source objects. Fetches features for the
|
|
33325
|
+
* range requested.
|
|
33326
|
+
*
|
|
33327
|
+
* This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
|
|
33328
|
+
* some not.
|
|
33329
|
+
*
|
|
33330
|
+
* @param chr
|
|
33331
|
+
* @param start
|
|
33332
|
+
* @param end
|
|
33333
|
+
* @param bpPerPixel
|
|
33334
|
+
*/
|
|
33335
|
+
async getFeatures({chr, start, end, bpPerPixel, visibilityWindow, windowFunction}) {
|
|
32837
33336
|
|
|
32838
|
-
const
|
|
32839
|
-
|
|
32840
|
-
|
|
32841
|
-
|
|
33337
|
+
const isWholeGenome = ("all" === chr.toLowerCase());
|
|
33338
|
+
|
|
33339
|
+
start = start || 0;
|
|
33340
|
+
end = end || Number.MAX_SAFE_INTEGER;
|
|
33341
|
+
|
|
33342
|
+
// Various conditions that can require a feature load
|
|
33343
|
+
// * view is "whole genome" but no features are loaded
|
|
33344
|
+
// * cache is disabled
|
|
33345
|
+
// * cache does not contain requested range
|
|
33346
|
+
// const containsRange = this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))
|
|
33347
|
+
if ((isWholeGenome && !this.wgFeatures && this.supportsWholeGenome()) ||
|
|
33348
|
+
this.config.disableCache ||
|
|
33349
|
+
!this.featureCache ||
|
|
33350
|
+
!this.featureCache.containsRange(new GenomicInterval(chr, start, end))) {
|
|
33351
|
+
await this.loadFeatures(chr, start, end, visibilityWindow);
|
|
32842
33352
|
}
|
|
32843
33353
|
|
|
32844
|
-
|
|
32845
|
-
|
|
32846
|
-
|
|
32847
|
-
|
|
32848
|
-
|
|
32849
|
-
|
|
32850
|
-
|
|
32851
|
-
|
|
32852
|
-
|
|
32853
|
-
|
|
32854
|
-
|
|
32855
|
-
|
|
32856
|
-
decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
|
|
32857
|
-
break
|
|
32858
|
-
case "fixedStep":
|
|
32859
|
-
decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
|
|
32860
|
-
break
|
|
32861
|
-
default:
|
|
32862
|
-
throw ("Unknown tile type: " + tile.type)
|
|
33354
|
+
if (isWholeGenome) {
|
|
33355
|
+
if (!this.wgFeatures) {
|
|
33356
|
+
if (this.supportsWholeGenome()) {
|
|
33357
|
+
if("wig" === this.config.type) {
|
|
33358
|
+
const allWgFeatures = await computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, 1000000);
|
|
33359
|
+
this.wgFeatures = summarizeData(allWgFeatures, 0, bpPerPixel, windowFunction);
|
|
33360
|
+
} else {
|
|
33361
|
+
this.wgFeatures = await computeWGFeatures(this.featureCache.getAllFeatures(), this.genome, this.maxWGCount);
|
|
33362
|
+
}
|
|
33363
|
+
} else {
|
|
33364
|
+
this.wgFeatures = [];
|
|
33365
|
+
}
|
|
32863
33366
|
}
|
|
33367
|
+
return this.wgFeatures
|
|
33368
|
+
} else {
|
|
33369
|
+
return this.featureCache.queryFeatures(chr, start, end)
|
|
32864
33370
|
}
|
|
32865
|
-
features.sort(function (a, b) {
|
|
32866
|
-
return a.start - b.start
|
|
32867
|
-
});
|
|
32868
|
-
|
|
32869
|
-
return features
|
|
32870
33371
|
}
|
|
32871
33372
|
|
|
32872
|
-
|
|
32873
|
-
return
|
|
33373
|
+
async findFeatures(fn) {
|
|
33374
|
+
return this.featureCache ? this.featureCache.findFeatures(fn) : []
|
|
32874
33375
|
}
|
|
32875
33376
|
|
|
32876
|
-
|
|
32877
|
-
return this.
|
|
33377
|
+
supportsWholeGenome() {
|
|
33378
|
+
return !this.queryable // queryable (indexed, web services) sources don't support whole genome view
|
|
32878
33379
|
}
|
|
32879
|
-
}
|
|
32880
|
-
|
|
32881
|
-
function decodeBedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
32882
33380
|
|
|
32883
|
-
|
|
32884
|
-
|
|
32885
|
-
|
|
32886
|
-
|
|
32887
|
-
|
|
32888
|
-
|
|
32889
|
-
|
|
32890
|
-
if (e < bpStart) continue
|
|
32891
|
-
if (s > bpEnd) break
|
|
32892
|
-
features.push({
|
|
32893
|
-
chr: chr,
|
|
32894
|
-
start: s,
|
|
32895
|
-
end: e,
|
|
32896
|
-
value: data[i]
|
|
32897
|
-
});
|
|
33381
|
+
// TODO -- experimental, will only work for non-indexed sources
|
|
33382
|
+
getAllFeatures() {
|
|
33383
|
+
if (this.queryable || !this.featureCache) { // queryable sources don't support all features
|
|
33384
|
+
return []
|
|
33385
|
+
} else {
|
|
33386
|
+
return this.featureCache.getAllFeatures()
|
|
33387
|
+
}
|
|
32898
33388
|
}
|
|
32899
|
-
}
|
|
32900
33389
|
|
|
32901
|
-
function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
|
|
32902
33390
|
|
|
32903
|
-
|
|
32904
|
-
const starts = tile.start;
|
|
32905
|
-
const span = tile.span;
|
|
32906
|
-
const data = tile.data[0]; // Single track for now
|
|
32907
|
-
for (let i = 0; i < nPositions; i++) {
|
|
32908
|
-
const s = starts[i];
|
|
32909
|
-
const e = s + span;
|
|
32910
|
-
if (e < bpStart) continue
|
|
32911
|
-
if (s > bpEnd) break
|
|
32912
|
-
features.push({
|
|
32913
|
-
chr: chr,
|
|
32914
|
-
start: s,
|
|
32915
|
-
end: e,
|
|
32916
|
-
value: data[i]
|
|
32917
|
-
});
|
|
32918
|
-
}
|
|
32919
|
-
}
|
|
33391
|
+
async loadFeatures(chr, start, end, visibilityWindow) {
|
|
32920
33392
|
|
|
32921
|
-
|
|
33393
|
+
await this.getHeader();
|
|
32922
33394
|
|
|
32923
|
-
|
|
32924
|
-
|
|
32925
|
-
|
|
32926
|
-
const data = tile.data[0]; // Single track for now
|
|
33395
|
+
const reader = this.reader;
|
|
33396
|
+
let intervalStart = start;
|
|
33397
|
+
let intervalEnd = end;
|
|
32927
33398
|
|
|
32928
|
-
|
|
32929
|
-
|
|
32930
|
-
if (
|
|
32931
|
-
|
|
32932
|
-
|
|
32933
|
-
|
|
32934
|
-
|
|
32935
|
-
|
|
32936
|
-
|
|
32937
|
-
|
|
32938
|
-
|
|
33399
|
+
// chr aliasing
|
|
33400
|
+
let queryChr = chr;
|
|
33401
|
+
if (!this.chrAliasManager && this.reader && this.reader.sequenceNames) {
|
|
33402
|
+
this.chrAliasManager = new ChromAliasManager(this.reader.sequenceNames, this.genome);
|
|
33403
|
+
}
|
|
33404
|
+
if (this.chrAliasManager) {
|
|
33405
|
+
queryChr = await this.chrAliasManager.getAliasName(chr);
|
|
33406
|
+
}
|
|
33407
|
+
|
|
33408
|
+
// Use visibility window to potentially expand query interval.
|
|
33409
|
+
// This can save re-queries as we zoom out. Visibility window <= 0 is a special case
|
|
33410
|
+
// indicating whole chromosome should be read at once.
|
|
33411
|
+
if ((!visibilityWindow || visibilityWindow <= 0) && this.config.expandQuery !== false) {
|
|
33412
|
+
// Whole chromosome
|
|
33413
|
+
const chromosome = this.genome ? this.genome.getChromosome(queryChr) : undefined;
|
|
33414
|
+
intervalStart = 0;
|
|
33415
|
+
intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
|
|
33416
|
+
} else if (visibilityWindow > (end - start) && this.config.expandQuery !== false) {
|
|
33417
|
+
let expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
|
|
33418
|
+
if(this.config.minQuerySize && expansionWindow < this.config.minQuerySize) {
|
|
33419
|
+
expansionWindow = this.config.minQuerySize;
|
|
32939
33420
|
}
|
|
33421
|
+
intervalStart = Math.max(0, (start + end - expansionWindow) / 2);
|
|
33422
|
+
intervalEnd = intervalStart + expansionWindow;
|
|
32940
33423
|
}
|
|
32941
|
-
s = e;
|
|
32942
|
-
}
|
|
32943
|
-
}
|
|
32944
33424
|
|
|
33425
|
+
let features = await reader.readFeatures(queryChr, intervalStart, intervalEnd);
|
|
33426
|
+
if (this.queryable === undefined) {
|
|
33427
|
+
this.queryable = reader.indexed;
|
|
33428
|
+
}
|
|
32945
33429
|
|
|
32946
|
-
|
|
33430
|
+
const genomicInterval = this.queryable ?
|
|
33431
|
+
new GenomicInterval(chr, intervalStart, intervalEnd) :
|
|
33432
|
+
undefined;
|
|
32947
33433
|
|
|
32948
|
-
|
|
33434
|
+
if (features) {
|
|
32949
33435
|
|
|
32950
|
-
|
|
32951
|
-
|
|
32952
|
-
|
|
33436
|
+
// Assign overlapping features to rows
|
|
33437
|
+
if (this.config.format !== "wig" && this.config.type !== "junctions") {
|
|
33438
|
+
const maxRows = this.config.maxRows || Number.MAX_SAFE_INTEGER;
|
|
33439
|
+
packFeatures(features, maxRows);
|
|
33440
|
+
}
|
|
32953
33441
|
|
|
32954
|
-
|
|
33442
|
+
// Note - replacing previous cache with new one. genomicInterval is optional (might be undefined => includes all features)
|
|
33443
|
+
this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval);
|
|
32955
33444
|
|
|
32956
|
-
|
|
33445
|
+
// If track is marked "searchable"< cache features by name -- use this with caution, memory intensive
|
|
33446
|
+
if (this.searchable) {
|
|
33447
|
+
this.addFeaturesToDB(features, this.config);
|
|
33448
|
+
}
|
|
33449
|
+
} else {
|
|
33450
|
+
this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
|
|
33451
|
+
}
|
|
33452
|
+
}
|
|
33453
|
+
|
|
33454
|
+
addFeaturesToDB(featureList, config) {
|
|
33455
|
+
if (!this.featureMap) {
|
|
33456
|
+
this.featureMap = new Map();
|
|
33457
|
+
}
|
|
33458
|
+
const searchableFields = config.searchableFields || ["name", "transcript_id", "gene_id", "gene_name", "id"];
|
|
33459
|
+
for (let feature of featureList) {
|
|
33460
|
+
for (let field of searchableFields) {
|
|
33461
|
+
let key;
|
|
33462
|
+
if (typeof feature.getAttributeValue === 'function') {
|
|
33463
|
+
key = feature.getAttributeValue(field);
|
|
33464
|
+
}
|
|
33465
|
+
if (key) {
|
|
33466
|
+
key = key.replaceAll(' ', '+').toUpperCase();
|
|
33467
|
+
// If feature is already present keep largest one
|
|
33468
|
+
if (this.featureMap.has(key)) {
|
|
33469
|
+
const f2 = this.featureMap.get(key);
|
|
33470
|
+
if (feature.end - feature.start < f2.end - f2.start) {
|
|
33471
|
+
continue
|
|
33472
|
+
}
|
|
33473
|
+
}
|
|
33474
|
+
this.featureMap.set(key, feature);
|
|
33475
|
+
}
|
|
33476
|
+
}
|
|
33477
|
+
}
|
|
33478
|
+
}
|
|
33479
|
+
|
|
33480
|
+
search(term) {
|
|
33481
|
+
if (this.featureMap) {
|
|
33482
|
+
return this.featureMap.get(term.toUpperCase())
|
|
33483
|
+
}
|
|
33484
|
+
|
|
33485
|
+
}
|
|
32957
33486
|
}
|
|
32958
33487
|
|
|
32959
33488
|
/*
|
|
@@ -33977,7 +34506,11 @@
|
|
|
33977
34506
|
if (name === undefined) name = feature.id || feature.ID;
|
|
33978
34507
|
if (!name || name === '.') return
|
|
33979
34508
|
|
|
33980
|
-
let
|
|
34509
|
+
let pixelXOffset = options.pixelXOffset || 0;
|
|
34510
|
+
const t1 = Math.max(featureX, -pixelXOffset);
|
|
34511
|
+
const t2 = Math.min(featureX1, -pixelXOffset + options.viewportWidth);
|
|
34512
|
+
let centerX = (t1 + t2) / 2;
|
|
34513
|
+
//let centerX = (featureX + featureX1) / 2
|
|
33981
34514
|
|
|
33982
34515
|
let transform;
|
|
33983
34516
|
if (this.displayMode === "COLLAPSED" && this.labelDisplayMode === "SLANT") {
|
|
@@ -34245,7 +34778,7 @@
|
|
|
34245
34778
|
}
|
|
34246
34779
|
}
|
|
34247
34780
|
|
|
34248
|
-
const DEFAULT_COLOR$
|
|
34781
|
+
const DEFAULT_COLOR$1 = 'rgb(0, 0, 150)';
|
|
34249
34782
|
|
|
34250
34783
|
|
|
34251
34784
|
class FeatureTrack extends TrackBase {
|
|
@@ -34256,7 +34789,6 @@
|
|
|
34256
34789
|
displayMode: "EXPANDED", // COLLAPSED | EXPANDED | SQUISHED
|
|
34257
34790
|
margin: 10,
|
|
34258
34791
|
featureHeight: 14,
|
|
34259
|
-
autoHeight: false,
|
|
34260
34792
|
useScore: false
|
|
34261
34793
|
}
|
|
34262
34794
|
|
|
@@ -34742,7 +35274,7 @@
|
|
|
34742
35274
|
|
|
34743
35275
|
// If no explicit setting use the default
|
|
34744
35276
|
if (!color) {
|
|
34745
|
-
color = DEFAULT_COLOR$
|
|
35277
|
+
color = DEFAULT_COLOR$1; // Track default
|
|
34746
35278
|
}
|
|
34747
35279
|
|
|
34748
35280
|
if (feature.alpha && feature.alpha !== 1) {
|
|
@@ -34788,7 +35320,8 @@
|
|
|
34788
35320
|
|
|
34789
35321
|
function onDragEnd() {
|
|
34790
35322
|
if (track.trackView && track.displayMode !== "SQUISHED") {
|
|
34791
|
-
|
|
35323
|
+
// Repaint views to adjust feature name if center is moved out of view
|
|
35324
|
+
track.trackView.repaintViews();
|
|
34792
35325
|
}
|
|
34793
35326
|
}
|
|
34794
35327
|
|
|
@@ -37869,7 +38402,6 @@
|
|
|
37869
38402
|
}
|
|
37870
38403
|
|
|
37871
38404
|
const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes.json";
|
|
37872
|
-
const BACKUP_GENOMES_URL = "https://s3.amazonaws.com/igv.org.genomes/genomes.json";
|
|
37873
38405
|
|
|
37874
38406
|
const GenomeUtils = {
|
|
37875
38407
|
|
|
@@ -37881,21 +38413,9 @@
|
|
|
37881
38413
|
|
|
37882
38414
|
// Get default genomes
|
|
37883
38415
|
if (config.loadDefaultGenomes !== false) {
|
|
37884
|
-
|
|
37885
|
-
|
|
37886
|
-
|
|
37887
|
-
processJson(jsonArray);
|
|
37888
|
-
} catch (e) {
|
|
37889
|
-
console.error(e);
|
|
37890
|
-
try {
|
|
37891
|
-
const url = BACKUP_GENOMES_URL;
|
|
37892
|
-
const jsonArray = await igvxhr.loadJson(url, {});
|
|
37893
|
-
processJson(jsonArray);
|
|
37894
|
-
} catch (e) {
|
|
37895
|
-
console.error(e);
|
|
37896
|
-
console.warn("Errors loading default genome definitions.");
|
|
37897
|
-
}
|
|
37898
|
-
}
|
|
38416
|
+
const url = DEFAULT_GENOMES_URL;
|
|
38417
|
+
const jsonArray = await igvxhr.loadJson(url, {timeout: 5000});
|
|
38418
|
+
processJson(jsonArray);
|
|
37899
38419
|
}
|
|
37900
38420
|
|
|
37901
38421
|
// Add user-defined genomes
|
|
@@ -37960,7 +38480,7 @@
|
|
|
37960
38480
|
}
|
|
37961
38481
|
}
|
|
37962
38482
|
|
|
37963
|
-
if(!reference) {
|
|
38483
|
+
if (!reference) {
|
|
37964
38484
|
alert.present(new Error(`Unknown genome id: ${genomeID}`), undefined);
|
|
37965
38485
|
}
|
|
37966
38486
|
}
|
|
@@ -41538,62 +42058,6 @@
|
|
|
41538
42058
|
</g>
|
|
41539
42059
|
</svg>`;
|
|
41540
42060
|
|
|
41541
|
-
const shim = .01;
|
|
41542
|
-
const colorStripWidth = 4;
|
|
41543
|
-
const axesXOffset = colorStripWidth + 1;
|
|
41544
|
-
function paintAxis(ctx, width, height, colorOrUndefined) {
|
|
41545
|
-
|
|
41546
|
-
if (undefined === this.dataRange || undefined === this.dataRange.max || undefined === this.dataRange.min) {
|
|
41547
|
-
return
|
|
41548
|
-
}
|
|
41549
|
-
|
|
41550
|
-
IGVGraphics.fillRect(ctx, 0, 0, width, height, { fillStyle: 'white' });
|
|
41551
|
-
if (colorOrUndefined) {
|
|
41552
|
-
IGVGraphics.fillRect(ctx, width - colorStripWidth - 2, 0, colorStripWidth, height, { fillStyle: colorOrUndefined });
|
|
41553
|
-
}
|
|
41554
|
-
|
|
41555
|
-
const flipAxis = (undefined === this.flipAxis) ? false : this.flipAxis;
|
|
41556
|
-
|
|
41557
|
-
const xTickStart = 0.95 * width - 8 - axesXOffset;
|
|
41558
|
-
const xTickEnd = 0.95 * width - axesXOffset;
|
|
41559
|
-
|
|
41560
|
-
const properties =
|
|
41561
|
-
{
|
|
41562
|
-
font: 'normal 10px Arial',
|
|
41563
|
-
textAlign: 'right',
|
|
41564
|
-
fillStyle: 'black',
|
|
41565
|
-
strokeStyle: 'black',
|
|
41566
|
-
};
|
|
41567
|
-
|
|
41568
|
-
// tick
|
|
41569
|
-
IGVGraphics.strokeLine(ctx, xTickStart, shim * height, xTickEnd, shim * height, properties);
|
|
41570
|
-
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), xTickStart + 4, shim * height + 12, properties);
|
|
41571
|
-
|
|
41572
|
-
const y = (1.0 - shim) * height;
|
|
41573
|
-
|
|
41574
|
-
// tick
|
|
41575
|
-
IGVGraphics.strokeLine(ctx, xTickStart, y, xTickEnd, y, properties);
|
|
41576
|
-
IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), xTickStart + 4, y - 4, properties);
|
|
41577
|
-
|
|
41578
|
-
// vertical axis
|
|
41579
|
-
IGVGraphics.strokeLine(ctx, xTickEnd, shim * height, xTickEnd, y, properties);
|
|
41580
|
-
|
|
41581
|
-
function prettyPrint(number) {
|
|
41582
|
-
|
|
41583
|
-
if (number === 0) {
|
|
41584
|
-
return "0"
|
|
41585
|
-
} else if (Math.abs(number) >= 10) {
|
|
41586
|
-
return number.toFixed()
|
|
41587
|
-
} else if (Math.abs(number) >= 1) {
|
|
41588
|
-
return number.toFixed(1)
|
|
41589
|
-
} else if (Math.abs(number) >= 0.1) {
|
|
41590
|
-
return number.toFixed(2)
|
|
41591
|
-
} else {
|
|
41592
|
-
return number.toExponential(1)
|
|
41593
|
-
}
|
|
41594
|
-
}
|
|
41595
|
-
}
|
|
41596
|
-
|
|
41597
42061
|
/*
|
|
41598
42062
|
* The MIT License (MIT)
|
|
41599
42063
|
*
|
|
@@ -42594,7 +43058,7 @@
|
|
|
42594
43058
|
const viewportsToRepaint = visibleViewports.filter(vp => vp.needsRepaint()).filter(viewport => viewport.checkZoomIn());
|
|
42595
43059
|
|
|
42596
43060
|
// Get viewports that require a data load
|
|
42597
|
-
const viewportsToReload =
|
|
43061
|
+
const viewportsToReload = visibleViewports.filter(viewport => viewport.needsReload());
|
|
42598
43062
|
|
|
42599
43063
|
// Trigger viewport to load features needed to cover current genomic range
|
|
42600
43064
|
// NOTE: these must be loaded synchronously, do not user Promise.all, not all file readers are thread safe
|
|
@@ -43189,462 +43653,6 @@
|
|
|
43189
43653
|
|
|
43190
43654
|
}
|
|
43191
43655
|
|
|
43192
|
-
const DEFAULT_COLOR$1 = 'rgb(150, 150, 150)';
|
|
43193
|
-
|
|
43194
|
-
|
|
43195
|
-
class WigTrack extends TrackBase {
|
|
43196
|
-
|
|
43197
|
-
static defaults = {
|
|
43198
|
-
height: 50,
|
|
43199
|
-
flipAxis: false,
|
|
43200
|
-
logScale: false,
|
|
43201
|
-
windowFunction: 'mean',
|
|
43202
|
-
graphType: 'bar',
|
|
43203
|
-
normalize: undefined,
|
|
43204
|
-
scaleFactor: undefined,
|
|
43205
|
-
overflowColor: `rgb(255, 32, 255)`,
|
|
43206
|
-
baselineColor: 'lightGray',
|
|
43207
|
-
summarize: true
|
|
43208
|
-
}
|
|
43209
|
-
|
|
43210
|
-
constructor(config, browser) {
|
|
43211
|
-
super(config, browser);
|
|
43212
|
-
}
|
|
43213
|
-
|
|
43214
|
-
init(config) {
|
|
43215
|
-
|
|
43216
|
-
super.init(config);
|
|
43217
|
-
|
|
43218
|
-
this.type = "wig";
|
|
43219
|
-
this.featureType = 'numeric';
|
|
43220
|
-
this.resolutionAware = true;
|
|
43221
|
-
this.paintAxis = paintAxis;
|
|
43222
|
-
|
|
43223
|
-
const format = config.format ? config.format.toLowerCase() : config.format;
|
|
43224
|
-
if (config.featureSource) {
|
|
43225
|
-
this.featureSource = config.featureSource;
|
|
43226
|
-
delete config.featureSource;
|
|
43227
|
-
} else if ("bigwig" === format) {
|
|
43228
|
-
this.featureSource = new BWSource(config, this.browser.genome);
|
|
43229
|
-
} else if ("tdf" === format) {
|
|
43230
|
-
this.featureSource = new TDFSource(config, this.browser.genome);
|
|
43231
|
-
} else {
|
|
43232
|
-
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
43233
|
-
}
|
|
43234
|
-
|
|
43235
|
-
|
|
43236
|
-
// Override autoscale default
|
|
43237
|
-
if (config.max === undefined || config.autoscale === true) {
|
|
43238
|
-
this.autoscale = true;
|
|
43239
|
-
} else {
|
|
43240
|
-
this.dataRange = {
|
|
43241
|
-
min: config.min || 0,
|
|
43242
|
-
max: config.max
|
|
43243
|
-
};
|
|
43244
|
-
}
|
|
43245
|
-
}
|
|
43246
|
-
|
|
43247
|
-
async postInit() {
|
|
43248
|
-
const header = await this.getHeader();
|
|
43249
|
-
if (this.disposed) return // This track was removed during async load
|
|
43250
|
-
if (header) this.setTrackProperties(header);
|
|
43251
|
-
}
|
|
43252
|
-
|
|
43253
|
-
async getFeatures(chr, start, end, bpPerPixel) {
|
|
43254
|
-
|
|
43255
|
-
const windowFunction = this.windowFunction;
|
|
43256
|
-
|
|
43257
|
-
const features = await this.featureSource.getFeatures({
|
|
43258
|
-
chr,
|
|
43259
|
-
start,
|
|
43260
|
-
end,
|
|
43261
|
-
bpPerPixel,
|
|
43262
|
-
visibilityWindow: this.visibilityWindow,
|
|
43263
|
-
windowFunction
|
|
43264
|
-
});
|
|
43265
|
-
if (this.normalize && this.featureSource.normalizationFactor) {
|
|
43266
|
-
const scaleFactor = this.featureSource.normalizationFactor;
|
|
43267
|
-
for (let f of features) {
|
|
43268
|
-
f.value *= scaleFactor;
|
|
43269
|
-
}
|
|
43270
|
-
}
|
|
43271
|
-
if (this.scaleFactor) {
|
|
43272
|
-
const scaleFactor = this.scaleFactor;
|
|
43273
|
-
for (let f of features) {
|
|
43274
|
-
f.value *= scaleFactor;
|
|
43275
|
-
}
|
|
43276
|
-
}
|
|
43277
|
-
|
|
43278
|
-
// Summarize features to current resolution. This needs to be done here, rather than in the "draw" function,
|
|
43279
|
-
// for group autoscale to work.
|
|
43280
|
-
if (this.summarize && ("mean" === windowFunction || "min" === windowFunction || "max" === windowFunction)) {
|
|
43281
|
-
return summarizeData(features, start, bpPerPixel, windowFunction)
|
|
43282
|
-
} else {
|
|
43283
|
-
return features
|
|
43284
|
-
}
|
|
43285
|
-
}
|
|
43286
|
-
|
|
43287
|
-
menuItemList() {
|
|
43288
|
-
const items = [];
|
|
43289
|
-
|
|
43290
|
-
if (this.flipAxis !== undefined) {
|
|
43291
|
-
items.push('<hr>');
|
|
43292
|
-
|
|
43293
|
-
function click() {
|
|
43294
|
-
this.flipAxis = !this.flipAxis;
|
|
43295
|
-
this.trackView.repaintViews();
|
|
43296
|
-
}
|
|
43297
|
-
|
|
43298
|
-
items.push({label: 'Flip y-axis', click});
|
|
43299
|
-
}
|
|
43300
|
-
|
|
43301
|
-
if(this.featureSource.windowFunctions) {
|
|
43302
|
-
items.push(...this.wigSummarizationItems());
|
|
43303
|
-
}
|
|
43304
|
-
|
|
43305
|
-
items.push(...this.numericDataMenuItems());
|
|
43306
|
-
|
|
43307
|
-
return items
|
|
43308
|
-
}
|
|
43309
|
-
|
|
43310
|
-
wigSummarizationItems() {
|
|
43311
|
-
|
|
43312
|
-
const windowFunctions = this.featureSource.windowFunctions;
|
|
43313
|
-
|
|
43314
|
-
const menuItems = [];
|
|
43315
|
-
menuItems.push('<hr/>');
|
|
43316
|
-
menuItems.push("<div>Windowing function</div>");
|
|
43317
|
-
for (const wf of windowFunctions) {
|
|
43318
|
-
const object = $$1(createCheckbox(wf, this.windowFunction === wf));
|
|
43319
|
-
|
|
43320
|
-
function clickHandler() {
|
|
43321
|
-
this.windowFunction = wf;
|
|
43322
|
-
this.trackView.updateViews();
|
|
43323
|
-
}
|
|
43324
|
-
|
|
43325
|
-
menuItems.push({object, click: clickHandler});
|
|
43326
|
-
}
|
|
43327
|
-
|
|
43328
|
-
return menuItems
|
|
43329
|
-
}
|
|
43330
|
-
|
|
43331
|
-
|
|
43332
|
-
async getHeader() {
|
|
43333
|
-
|
|
43334
|
-
if (typeof this.featureSource.getHeader === "function") {
|
|
43335
|
-
this.header = await this.featureSource.getHeader();
|
|
43336
|
-
}
|
|
43337
|
-
return this.header
|
|
43338
|
-
}
|
|
43339
|
-
|
|
43340
|
-
// TODO: refactor to igvUtils.js
|
|
43341
|
-
getScaleFactor(min, max, height, logScale) {
|
|
43342
|
-
const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
|
|
43343
|
-
return scale
|
|
43344
|
-
}
|
|
43345
|
-
|
|
43346
|
-
computeYPixelValue(yValue, yScaleFactor) {
|
|
43347
|
-
return (this.flipAxis ? (yValue - this.dataRange.min) : (this.dataRange.max - yValue)) * yScaleFactor
|
|
43348
|
-
}
|
|
43349
|
-
|
|
43350
|
-
computeYPixelValueInLogScale(yValue, yScaleFactor) {
|
|
43351
|
-
let maxValue = this.dataRange.max;
|
|
43352
|
-
let minValue = this.dataRange.min;
|
|
43353
|
-
if (maxValue <= 0) return 0 // TODO:
|
|
43354
|
-
if (minValue <= -1) minValue = 0;
|
|
43355
|
-
minValue = (minValue <= 0) ? 0 : Math.log10(minValue + 1);
|
|
43356
|
-
maxValue = Math.log10(maxValue + 1);
|
|
43357
|
-
yValue = Math.log10(yValue + 1);
|
|
43358
|
-
return ((this.flipAxis ? (yValue - minValue) : (maxValue - yValue)) * yScaleFactor)
|
|
43359
|
-
}
|
|
43360
|
-
|
|
43361
|
-
draw(options) {
|
|
43362
|
-
|
|
43363
|
-
const features = options.features;
|
|
43364
|
-
const ctx = options.context;
|
|
43365
|
-
const bpPerPixel = options.bpPerPixel;
|
|
43366
|
-
const bpStart = options.bpStart;
|
|
43367
|
-
const pixelWidth = options.pixelWidth;
|
|
43368
|
-
const pixelHeight = options.pixelHeight;
|
|
43369
|
-
const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
|
|
43370
|
-
this.color || DEFAULT_COLOR$1;
|
|
43371
|
-
const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, options.pixelHeight, this.logScale);
|
|
43372
|
-
const yScale = (yValue) => this.logScale
|
|
43373
|
-
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
|
|
43374
|
-
: this.computeYPixelValue(yValue, scaleFactor);
|
|
43375
|
-
|
|
43376
|
-
if (features && features.length > 0) {
|
|
43377
|
-
|
|
43378
|
-
if (this.dataRange.min === undefined) this.dataRange.min = 0;
|
|
43379
|
-
|
|
43380
|
-
// Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
|
|
43381
|
-
// nothing to paint.
|
|
43382
|
-
if (this.dataRange.max > this.dataRange.min) {
|
|
43383
|
-
|
|
43384
|
-
let lastPixelEnd = -1;
|
|
43385
|
-
let lastY;
|
|
43386
|
-
const y0 = yScale(0);
|
|
43387
|
-
|
|
43388
|
-
for (let f of features) {
|
|
43389
|
-
|
|
43390
|
-
if (f.end < bpStart) continue
|
|
43391
|
-
if (f.start > bpEnd) break
|
|
43392
|
-
|
|
43393
|
-
const x = (f.start - bpStart) / bpPerPixel;
|
|
43394
|
-
if (isNaN(x)) continue
|
|
43395
|
-
|
|
43396
|
-
let y = yScale(f.value);
|
|
43397
|
-
|
|
43398
|
-
const rectEnd = (f.end - bpStart) / bpPerPixel;
|
|
43399
|
-
const width = rectEnd - x;
|
|
43400
|
-
|
|
43401
|
-
const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
|
|
43402
|
-
|
|
43403
|
-
if (this.graphType === "line") {
|
|
43404
|
-
if (lastY !== undefined) {
|
|
43405
|
-
IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
|
|
43406
|
-
"fillStyle": color,
|
|
43407
|
-
"strokeStyle": color
|
|
43408
|
-
});
|
|
43409
|
-
}
|
|
43410
|
-
IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
|
|
43411
|
-
} else if (this.graphType === "points") {
|
|
43412
|
-
const pointSize = this.config.pointSize || 3;
|
|
43413
|
-
const px = x + width / 2;
|
|
43414
|
-
IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
|
|
43415
|
-
|
|
43416
|
-
if (f.value > this.dataRange.max) {
|
|
43417
|
-
IGVGraphics.fillCircle(ctx, px, pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
43418
|
-
} else if (f.value < this.dataRange.min) {
|
|
43419
|
-
IGVGraphics.fillCircle(ctx, px, pixelHeight - pointSize / 2, pointSize / 2, 3, {fillStyle: this.overflowColor});
|
|
43420
|
-
}
|
|
43421
|
-
|
|
43422
|
-
} else {
|
|
43423
|
-
// Default graph type (bar)
|
|
43424
|
-
const height = Math.min(pixelHeight, y - y0);
|
|
43425
|
-
IGVGraphics.fillRect(ctx, x, y0, width, height, {fillStyle: color});
|
|
43426
|
-
if (f.value > this.dataRange.max) {
|
|
43427
|
-
IGVGraphics.fillRect(ctx, x, 0, width, 3, {fillStyle: this.overflowColor});
|
|
43428
|
-
} else if (f.value < this.dataRange.min) {
|
|
43429
|
-
IGVGraphics.fillRect(ctx, x, pixelHeight - 3, width, 3, {fillStyle: this.overflowColor});
|
|
43430
|
-
}
|
|
43431
|
-
|
|
43432
|
-
}
|
|
43433
|
-
lastPixelEnd = x + width;
|
|
43434
|
-
lastY = y;
|
|
43435
|
-
}
|
|
43436
|
-
|
|
43437
|
-
// If the track includes negative values draw a baseline
|
|
43438
|
-
if (this.dataRange.min < 0) {
|
|
43439
|
-
const ratio = this.dataRange.max / (this.dataRange.max - this.dataRange.min);
|
|
43440
|
-
const basepx = this.flipAxis ? (1 - ratio) * options.pixelHeight : ratio * options.pixelHeight;
|
|
43441
|
-
IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {strokeStyle: this.baselineColor});
|
|
43442
|
-
}
|
|
43443
|
-
}
|
|
43444
|
-
}
|
|
43445
|
-
|
|
43446
|
-
// Draw guidelines
|
|
43447
|
-
if (this.config.hasOwnProperty('guideLines')) {
|
|
43448
|
-
for (let line of this.config.guideLines) {
|
|
43449
|
-
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
|
|
43450
|
-
let y = yScale(line.y);
|
|
43451
|
-
let props = {
|
|
43452
|
-
'strokeStyle': line['color'],
|
|
43453
|
-
'strokeWidth': 2
|
|
43454
|
-
};
|
|
43455
|
-
if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);
|
|
43456
|
-
else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
|
|
43457
|
-
}
|
|
43458
|
-
}
|
|
43459
|
-
}
|
|
43460
|
-
}
|
|
43461
|
-
|
|
43462
|
-
popupData(clickState, features) {
|
|
43463
|
-
|
|
43464
|
-
if (features === undefined) features = this.clickedFeatures(clickState);
|
|
43465
|
-
|
|
43466
|
-
if (features && features.length > 0) {
|
|
43467
|
-
|
|
43468
|
-
const genomicLocation = clickState.genomicLocation;
|
|
43469
|
-
const popupData = [];
|
|
43470
|
-
|
|
43471
|
-
// Sort features based on distance from click
|
|
43472
|
-
features.sort(function (a, b) {
|
|
43473
|
-
const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
|
|
43474
|
-
const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
|
|
43475
|
-
return distA - distB
|
|
43476
|
-
});
|
|
43477
|
-
|
|
43478
|
-
// Display closest 10
|
|
43479
|
-
const displayFeatures = features.length > 10 ? features.slice(0, 10) : features;
|
|
43480
|
-
|
|
43481
|
-
// Resort in ascending order
|
|
43482
|
-
displayFeatures.sort(function (a, b) {
|
|
43483
|
-
return a.start - b.start
|
|
43484
|
-
});
|
|
43485
|
-
|
|
43486
|
-
for (let selectedFeature of displayFeatures) {
|
|
43487
|
-
if (selectedFeature) {
|
|
43488
|
-
if (popupData.length > 0) {
|
|
43489
|
-
popupData.push('<hr/>');
|
|
43490
|
-
}
|
|
43491
|
-
let posString = (selectedFeature.end - selectedFeature.start) === 1 ?
|
|
43492
|
-
numberFormatter$1(Math.floor(selectedFeature.start) + 1)
|
|
43493
|
-
: numberFormatter$1(Math.floor(selectedFeature.start) + 1) + "-" + numberFormatter$1(Math.floor(selectedFeature.end));
|
|
43494
|
-
popupData.push({name: "Position:", value: posString});
|
|
43495
|
-
popupData.push({
|
|
43496
|
-
name: "Value: ",
|
|
43497
|
-
value: numberFormatter$1(selectedFeature.value.toFixed(4))
|
|
43498
|
-
});
|
|
43499
|
-
}
|
|
43500
|
-
}
|
|
43501
|
-
if (displayFeatures.length < features.length) {
|
|
43502
|
-
popupData.push("<hr/>...");
|
|
43503
|
-
}
|
|
43504
|
-
|
|
43505
|
-
return popupData
|
|
43506
|
-
|
|
43507
|
-
} else {
|
|
43508
|
-
return []
|
|
43509
|
-
}
|
|
43510
|
-
}
|
|
43511
|
-
|
|
43512
|
-
get supportsWholeGenome() {
|
|
43513
|
-
return !this.config.indexURL && this.config.supportsWholeGenome !== false
|
|
43514
|
-
}
|
|
43515
|
-
|
|
43516
|
-
/**
|
|
43517
|
-
* Return color for feature.
|
|
43518
|
-
* @param feature
|
|
43519
|
-
* @returns {string}
|
|
43520
|
-
*/
|
|
43521
|
-
|
|
43522
|
-
getColorForFeature(f) {
|
|
43523
|
-
let c = (f.value < 0 && this.altColor) ? this.altColor : this.color || DEFAULT_COLOR$1;
|
|
43524
|
-
return (typeof c === "function") ? c(f.value) : c
|
|
43525
|
-
}
|
|
43526
|
-
|
|
43527
|
-
/**
|
|
43528
|
-
* Called when the track is removed. Do any needed cleanup here
|
|
43529
|
-
*/
|
|
43530
|
-
dispose() {
|
|
43531
|
-
this.trackView = undefined;
|
|
43532
|
-
}
|
|
43533
|
-
|
|
43534
|
-
}
|
|
43535
|
-
|
|
43536
|
-
/**
|
|
43537
|
-
* Summarize wig data in bins of size "bpPerPixel" with the given window function.
|
|
43538
|
-
*
|
|
43539
|
-
* @param features wig (numeric) data -- features cannot overlap, and are in ascending order by start position
|
|
43540
|
-
* @param startBP bp start position for computing binned data
|
|
43541
|
-
* @param bpPerPixel bp per pixel (bin)
|
|
43542
|
-
* @param windowFunction mean, min, or max
|
|
43543
|
-
* @returns {*|*[]}
|
|
43544
|
-
*/
|
|
43545
|
-
function summarizeData(features, startBP, bpPerPixel, windowFunction = "mean") {
|
|
43546
|
-
|
|
43547
|
-
if (bpPerPixel <= 1 || !features || features.length === 0) {
|
|
43548
|
-
return features
|
|
43549
|
-
}
|
|
43550
|
-
|
|
43551
|
-
// Assume features are sorted by position. Wig features cannot overlap. Note, UCSC "reductionLevel" == bpPerPixel
|
|
43552
|
-
const chr = features[0].chr;
|
|
43553
|
-
const binSize = bpPerPixel;
|
|
43554
|
-
const summaryFeatures = [];
|
|
43555
|
-
|
|
43556
|
-
const finishBin = (bin) => {
|
|
43557
|
-
const start = startBP + bin.bin * binSize;
|
|
43558
|
-
const end = start + binSize;
|
|
43559
|
-
let value;
|
|
43560
|
-
switch (windowFunction) {
|
|
43561
|
-
case "mean":
|
|
43562
|
-
value = bin.sumData / bin.count;
|
|
43563
|
-
break
|
|
43564
|
-
case "max":
|
|
43565
|
-
value = bin.max;
|
|
43566
|
-
break
|
|
43567
|
-
case "min":
|
|
43568
|
-
value = bin.min;
|
|
43569
|
-
break
|
|
43570
|
-
default:
|
|
43571
|
-
throw Error(`Unknown window function: ${windowFunction}`)
|
|
43572
|
-
}
|
|
43573
|
-
const description = `${windowFunction} of ${bin.count} values`;
|
|
43574
|
-
summaryFeatures.push({chr, start, end, value, description});
|
|
43575
|
-
};
|
|
43576
|
-
|
|
43577
|
-
let currentBinData;
|
|
43578
|
-
for (let f of features) {
|
|
43579
|
-
|
|
43580
|
-
// Loop through bins this feature overlaps, updating the weighted sum for each bin or min/max,
|
|
43581
|
-
// depending on window function
|
|
43582
|
-
let startBin = Math.floor((f.start - startBP) / binSize);
|
|
43583
|
-
const endBin = Math.floor((f.end - startBP) / binSize);
|
|
43584
|
-
|
|
43585
|
-
if (currentBinData && startBin === currentBinData.bin) {
|
|
43586
|
-
currentBinData.add(f);
|
|
43587
|
-
startBin++;
|
|
43588
|
-
}
|
|
43589
|
-
|
|
43590
|
-
if (!currentBinData || endBin > currentBinData.bin) {
|
|
43591
|
-
|
|
43592
|
-
if(currentBinData) {
|
|
43593
|
-
finishBin(currentBinData);
|
|
43594
|
-
}
|
|
43595
|
-
|
|
43596
|
-
// Feature stretches across multiple bins.
|
|
43597
|
-
if (endBin > startBin) {
|
|
43598
|
-
const end = startBP + endBin * binSize;
|
|
43599
|
-
summaryFeatures.push({chr, start: f.start, end, value: f.value});
|
|
43600
|
-
}
|
|
43601
|
-
|
|
43602
|
-
currentBinData = new SummaryBinData(endBin, f);
|
|
43603
|
-
}
|
|
43604
|
-
|
|
43605
|
-
}
|
|
43606
|
-
if(currentBinData) {
|
|
43607
|
-
finishBin(currentBinData);
|
|
43608
|
-
}
|
|
43609
|
-
|
|
43610
|
-
// Consolidate
|
|
43611
|
-
const c = [];
|
|
43612
|
-
let lastFeature = summaryFeatures[0];
|
|
43613
|
-
for (let f of summaryFeatures) {
|
|
43614
|
-
if (lastFeature.value === f.value && f.start <= lastFeature.end) {
|
|
43615
|
-
lastFeature.end = f.end;
|
|
43616
|
-
} else {
|
|
43617
|
-
c.push(lastFeature);
|
|
43618
|
-
lastFeature = f;
|
|
43619
|
-
}
|
|
43620
|
-
}
|
|
43621
|
-
c.push(lastFeature);
|
|
43622
|
-
|
|
43623
|
-
return c
|
|
43624
|
-
|
|
43625
|
-
}
|
|
43626
|
-
|
|
43627
|
-
class SummaryBinData {
|
|
43628
|
-
constructor(bin, feature) {
|
|
43629
|
-
this.bin = bin;
|
|
43630
|
-
this.sumData = feature.value;
|
|
43631
|
-
this.count = 1;
|
|
43632
|
-
this.min = feature.value;
|
|
43633
|
-
this.max = feature.value;
|
|
43634
|
-
}
|
|
43635
|
-
|
|
43636
|
-
add(feature) {
|
|
43637
|
-
this.sumData += feature.value;
|
|
43638
|
-
this.max = Math.max(feature.value, this.max);
|
|
43639
|
-
this.min = Math.min(feature.value, this.min);
|
|
43640
|
-
this.count++;
|
|
43641
|
-
}
|
|
43642
|
-
|
|
43643
|
-
get mean() {
|
|
43644
|
-
return this.sumData / this.count
|
|
43645
|
-
}
|
|
43646
|
-
}
|
|
43647
|
-
|
|
43648
43656
|
/**
|
|
43649
43657
|
*
|
|
43650
43658
|
* @param cs - object containing
|
|
@@ -48559,7 +48567,7 @@
|
|
|
48559
48567
|
allAlignments() {
|
|
48560
48568
|
if (this.alignments) {
|
|
48561
48569
|
return this.alignments
|
|
48562
|
-
} else {
|
|
48570
|
+
} else if (this.packedGroups) {
|
|
48563
48571
|
const all = Array.from(this.packedGroups.values()).flatMap(group => group.rows.flatMap(row => row.alignments));
|
|
48564
48572
|
if (this.#unpacked && this.#unpacked.length > 0) {
|
|
48565
48573
|
for (let a of this.#unpacked) {
|
|
@@ -48567,6 +48575,8 @@
|
|
|
48567
48575
|
}
|
|
48568
48576
|
}
|
|
48569
48577
|
return all
|
|
48578
|
+
} else {
|
|
48579
|
+
return []
|
|
48570
48580
|
}
|
|
48571
48581
|
}
|
|
48572
48582
|
|
|
@@ -48575,9 +48585,10 @@
|
|
|
48575
48585
|
}
|
|
48576
48586
|
|
|
48577
48587
|
sortRows(options) {
|
|
48578
|
-
|
|
48579
|
-
|
|
48580
|
-
|
|
48588
|
+
if(this.packedGroups) {
|
|
48589
|
+
for (let group of this.packedGroups.values()) {
|
|
48590
|
+
group.sortRows(options, this);
|
|
48591
|
+
}
|
|
48581
48592
|
}
|
|
48582
48593
|
}
|
|
48583
48594
|
}
|
|
@@ -57367,23 +57378,24 @@
|
|
|
57367
57378
|
const y = clickState.y;
|
|
57368
57379
|
const offsetY = y - this.top;
|
|
57369
57380
|
const genomicLocation = clickState.genomicLocation;
|
|
57370
|
-
|
|
57371
|
-
|
|
57372
|
-
|
|
57373
|
-
|
|
57374
|
-
|
|
57375
|
-
|
|
57376
|
-
|
|
57377
|
-
|
|
57378
|
-
|
|
57379
|
-
|
|
57380
|
-
|
|
57381
|
-
|
|
57382
|
-
|
|
57383
|
-
|
|
57384
|
-
|
|
57385
|
-
|
|
57386
|
-
|
|
57381
|
+
|
|
57382
|
+
if(features.packedGroups) {
|
|
57383
|
+
let minGroupY = Number.MAX_VALUE;
|
|
57384
|
+
for (let group of features.packedGroups.values()) {
|
|
57385
|
+
minGroupY = Math.min(minGroupY, group.pixelTop);
|
|
57386
|
+
if (offsetY > group.pixelTop && offsetY <= group.pixelBottom) {
|
|
57387
|
+
|
|
57388
|
+
const alignmentRowHeight = this.displayMode === "SQUISHED" ?
|
|
57389
|
+
this.squishedRowHeight :
|
|
57390
|
+
this.alignmentRowHeight;
|
|
57391
|
+
|
|
57392
|
+
let packedAlignmentsIndex = Math.floor((offsetY - group.pixelTop) / alignmentRowHeight);
|
|
57393
|
+
|
|
57394
|
+
if (packedAlignmentsIndex >= 0 && packedAlignmentsIndex < group.length) {
|
|
57395
|
+
const alignmentRow = group.rows[packedAlignmentsIndex];
|
|
57396
|
+
const clicked = alignmentRow.alignments.filter(alignment => alignment.containsLocation(genomicLocation, this.showSoftClips));
|
|
57397
|
+
if (clicked.length > 0) return clicked[0]
|
|
57398
|
+
}
|
|
57387
57399
|
}
|
|
57388
57400
|
}
|
|
57389
57401
|
}
|
|
@@ -67611,8 +67623,6 @@
|
|
|
67611
67623
|
this.trackView.setTrackHeight(this.config.height || CNVPytorTrack.DEFAULT_TRACK_HEIGHT);
|
|
67612
67624
|
this.trackView.checkContentHeight();
|
|
67613
67625
|
this.trackView.updateViews();
|
|
67614
|
-
this.trackView.track.autoHeight = false;
|
|
67615
|
-
|
|
67616
67626
|
|
|
67617
67627
|
} finally {
|
|
67618
67628
|
this.trackView.stopSpinner();
|
|
@@ -68033,13 +68043,8 @@
|
|
|
68033
68043
|
this.divider = config.divider || "rgb(225,225,225)";
|
|
68034
68044
|
this.dotSize = config.dotSize || 2;
|
|
68035
68045
|
this.height = config.height || 100;
|
|
68036
|
-
this.autoHeight = false;
|
|
68037
68046
|
this.disableButtons = config.disableButtons;
|
|
68038
68047
|
|
|
68039
|
-
// Limit visibility window to 2 mb, gtex server gets flaky beyond that
|
|
68040
|
-
//this.visibilityWindow = config.visibilityWindow === undefined ?
|
|
68041
|
-
// 2000000 : config.visibilityWindow >= 0 ? Math.min(2000000, config.visibilityWindow) : 2000000
|
|
68042
|
-
|
|
68043
68048
|
this.featureSource = FeatureSource(config, this.browser.genome);
|
|
68044
68049
|
}
|
|
68045
68050
|
|
|
@@ -70518,7 +70523,7 @@
|
|
|
70518
70523
|
})
|
|
70519
70524
|
}
|
|
70520
70525
|
|
|
70521
|
-
const _version = "3.0.
|
|
70526
|
+
const _version = "3.0.4";
|
|
70522
70527
|
function version() {
|
|
70523
70528
|
return _version
|
|
70524
70529
|
}
|
|
@@ -71989,16 +71994,17 @@
|
|
|
71989
71994
|
|
|
71990
71995
|
}
|
|
71991
71996
|
|
|
71992
|
-
async present(feature,
|
|
71993
|
-
const menuItems = this.menuItems(feature,
|
|
71997
|
+
async present(feature, roiSet, event, roiManager, columnContainer, regionElement) {
|
|
71998
|
+
const menuItems = this.menuItems(feature, roiSet, event, roiManager, columnContainer, regionElement);
|
|
71994
71999
|
this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
|
|
71995
72000
|
}
|
|
71996
72001
|
|
|
71997
|
-
menuItems(feature,
|
|
72002
|
+
menuItems(feature, roiSet, event, roiManager, columnContainer, regionElement) {
|
|
72003
|
+
const items = feature.name ? [`<b>${feature.name}</b><br/>`] : [];
|
|
72004
|
+
if ('name' in roiSet) items.push(`<b>ROI Set: ${roiSet.name}</b>`);
|
|
72005
|
+
if (items.length > 0) items.push(`<hr/>`);
|
|
71998
72006
|
|
|
71999
|
-
|
|
72000
|
-
|
|
72001
|
-
if (isUserDefined) {
|
|
72007
|
+
if (roiSet.isUserDefined) {
|
|
72002
72008
|
items.push(
|
|
72003
72009
|
{
|
|
72004
72010
|
label: 'Set description ...',
|
|
@@ -72062,7 +72068,7 @@
|
|
|
72062
72068
|
}
|
|
72063
72069
|
|
|
72064
72070
|
|
|
72065
|
-
if (isUserDefined) {
|
|
72071
|
+
if (roiSet.isUserDefined) {
|
|
72066
72072
|
items.push(
|
|
72067
72073
|
'<hr/>',
|
|
72068
72074
|
{
|
|
@@ -72347,13 +72353,17 @@
|
|
|
72347
72353
|
const [rectA, rectB] = tracks
|
|
72348
72354
|
.map(track => track.trackView.viewports[0].$viewport.get(0))
|
|
72349
72355
|
.map(element => getElementVerticalDimension(element));
|
|
72350
|
-
|
|
72356
|
+
|
|
72357
|
+
//Covers cases in which ruler and/or ideogram are hidden
|
|
72358
|
+
const heightA = rectA ? rectA.height : 0;
|
|
72359
|
+
const heightB = rectB ? rectB.height : 0;
|
|
72360
|
+
|
|
72351
72361
|
const elements = browser.columnContainer.querySelectorAll('.igv-roi-region');
|
|
72352
72362
|
|
|
72353
72363
|
const fudge = -0.5;
|
|
72354
72364
|
if (elements) {
|
|
72355
72365
|
for (const element of elements) {
|
|
72356
|
-
element.style.marginTop = `${
|
|
72366
|
+
element.style.marginTop = `${heightA + heightB + fudge}px`;
|
|
72357
72367
|
}
|
|
72358
72368
|
|
|
72359
72369
|
}
|
|
@@ -72521,7 +72531,6 @@
|
|
|
72521
72531
|
if (features) {
|
|
72522
72532
|
|
|
72523
72533
|
for (let feature of features) {
|
|
72524
|
-
|
|
72525
72534
|
const regionKey = createRegionKey(chr, feature.start, feature.end);
|
|
72526
72535
|
|
|
72527
72536
|
const {
|
|
@@ -72566,8 +72575,7 @@
|
|
|
72566
72575
|
event.stopPropagation();
|
|
72567
72576
|
|
|
72568
72577
|
translateMouseCoordinates(event, columnContainer);
|
|
72569
|
-
|
|
72570
|
-
this.roiMenu.present(feature, isUserDefined, event, this, columnContainer, regionElement);
|
|
72578
|
+
this.roiMenu.present(feature, roiSet, event, this, columnContainer, regionElement);
|
|
72571
72579
|
});
|
|
72572
72580
|
|
|
72573
72581
|
|
|
@@ -73736,7 +73744,7 @@
|
|
|
73736
73744
|
}
|
|
73737
73745
|
}
|
|
73738
73746
|
|
|
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 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';
|
|
73747
|
+
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';
|
|
73740
73748
|
|
|
73741
73749
|
/**
|
|
73742
73750
|
* Manages XQTL selections.
|
|
@@ -74619,6 +74627,10 @@
|
|
|
74619
74627
|
*/
|
|
74620
74628
|
async loadGenome(idOrConfig) {
|
|
74621
74629
|
|
|
74630
|
+
if (idOrConfig.genarkAccession) {
|
|
74631
|
+
idOrConfig.url = convertToHubURL(idOrConfig.genarkAccession);
|
|
74632
|
+
}
|
|
74633
|
+
|
|
74622
74634
|
// Translate the generic "url" field, used by clients such as igv-webapp
|
|
74623
74635
|
if (idOrConfig.url) {
|
|
74624
74636
|
if (isString$2(idOrConfig.url) && idOrConfig.url.endsWith("/hub.txt")) {
|
|
@@ -75911,7 +75923,7 @@
|
|
|
75911
75923
|
|
|
75912
75924
|
if (dragObject && dragObject.viewport.referenceFrame.start !== dragObject.start) {
|
|
75913
75925
|
this.updateViews();
|
|
75914
|
-
this.fireEvent('trackdragend');
|
|
75926
|
+
this.fireEvent('trackdragend', [dragObject.viewport]);
|
|
75915
75927
|
}
|
|
75916
75928
|
}
|
|
75917
75929
|
|