genoverse 3.2.0
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/.eslintrc.js +197 -0
- package/.github/workflows/test.yml +24 -0
- package/LICENSE.TXT +24 -0
- package/README.md +11 -0
- package/css/controlPanel.css +200 -0
- package/css/fileDrop.css +22 -0
- package/css/font-awesome.css +3 -0
- package/css/fullscreen.css +19 -0
- package/css/genoverse.css +466 -0
- package/css/karyotype.css +85 -0
- package/css/resizer.css +36 -0
- package/css/tooltips.css +26 -0
- package/css/trackControls.css +111 -0
- package/expanded.html +120 -0
- package/fontawesome/css/fontawesome.min.css +5 -0
- package/fontawesome/css/regular.min.css +5 -0
- package/fontawesome/css/solid.min.css +5 -0
- package/fontawesome/webfonts/fa-brands-400.ttf +0 -0
- package/fontawesome/webfonts/fa-brands-400.woff +0 -0
- package/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
- package/fontawesome/webfonts/fa-regular-400.ttf +0 -0
- package/fontawesome/webfonts/fa-regular-400.woff +0 -0
- package/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
- package/fontawesome/webfonts/fa-solid-900.ttf +0 -0
- package/fontawesome/webfonts/fa-solid-900.woff +0 -0
- package/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
- package/help.pdf +0 -0
- package/i/sort_handle.png +0 -0
- package/index.html +68 -0
- package/index.js +83 -0
- package/jest.config.js +4 -0
- package/js/Genoverse.js +1681 -0
- package/js/Track/Controller/Sequence.js +17 -0
- package/js/Track/Controller/Stranded.js +73 -0
- package/js/Track/Controller.js +620 -0
- package/js/Track/Model/File/BAM.js +44 -0
- package/js/Track/Model/File/BED.js +116 -0
- package/js/Track/Model/File/GFF.js +40 -0
- package/js/Track/Model/File/VCF.js +101 -0
- package/js/Track/Model/File/WIG.js +67 -0
- package/js/Track/Model/File.js +36 -0
- package/js/Track/Model/Gene/Ensembl.js +22 -0
- package/js/Track/Model/Gene.js +5 -0
- package/js/Track/Model/Sequence/Ensembl.js +4 -0
- package/js/Track/Model/Sequence/Fasta.js +60 -0
- package/js/Track/Model/Sequence.js +50 -0
- package/js/Track/Model/SequenceVariation.js +41 -0
- package/js/Track/Model/Stranded.js +28 -0
- package/js/Track/Model/Transcript/Ensembl.js +67 -0
- package/js/Track/Model/Transcript.js +5 -0
- package/js/Track/Model.js +303 -0
- package/js/Track/View/Gene/Ensembl.js +46 -0
- package/js/Track/View/Gene.js +6 -0
- package/js/Track/View/Sequence/Variation.js +115 -0
- package/js/Track/View/Sequence.js +63 -0
- package/js/Track/View/Transcript/Ensembl.js +12 -0
- package/js/Track/View/Transcript.js +28 -0
- package/js/Track/View.js +566 -0
- package/js/Track/library/Chromosome.js +145 -0
- package/js/Track/library/File/BAM.js +30 -0
- package/js/Track/library/File/BED.js +24 -0
- package/js/Track/library/File/BIGBED.js +47 -0
- package/js/Track/library/File/BIGWIG.js +52 -0
- package/js/Track/library/File/GFF.js +9 -0
- package/js/Track/library/File/VCF.js +71 -0
- package/js/Track/library/File/WIG.js +5 -0
- package/js/Track/library/File.js +10 -0
- package/js/Track/library/Gene.js +37 -0
- package/js/Track/library/Graph/Bar.js +235 -0
- package/js/Track/library/Graph/Line.js +296 -0
- package/js/Track/library/Graph.js +355 -0
- package/js/Track/library/HighlightRegion.js +292 -0
- package/js/Track/library/Legend.js +224 -0
- package/js/Track/library/Scalebar.js +227 -0
- package/js/Track/library/Scaleline.js +91 -0
- package/js/Track/library/Static.js +78 -0
- package/js/Track/library/dbSNP.js +142 -0
- package/js/Track.js +632 -0
- package/js/genomes/grch37.js +990 -0
- package/js/genomes/grch38.js +990 -0
- package/js/genoverse.min.js +2 -0
- package/js/genoverse.min.js.map +1 -0
- package/js/lib/BWReader.js +578 -0
- package/js/lib/Base.js +145 -0
- package/js/lib/VCFReader.js +286 -0
- package/js/lib/dalliance/js/bam.js +494 -0
- package/js/lib/dalliance/js/bin.js +185 -0
- package/js/lib/dalliance/js/das.js +749 -0
- package/js/lib/dalliance/js/utils.js +370 -0
- package/js/lib/dalliance-lib.js +3594 -0
- package/js/lib/dalliance-lib.min.js +68 -0
- package/js/lib/jDataView.js +2 -0
- package/js/lib/jParser.js +192 -0
- package/js/lib/jquery-ui.js +8 -0
- package/js/lib/jquery.js +2 -0
- package/js/lib/jquery.mousehold.js +53 -0
- package/js/lib/jquery.mousewheel.js +84 -0
- package/js/lib/jquery.tipsy.js +258 -0
- package/js/lib/rtree.js +1 -0
- package/js/plugins/controlPanel.js +395 -0
- package/js/plugins/fileDrop.js +62 -0
- package/js/plugins/focusRegion.js +12 -0
- package/js/plugins/fullscreen.js +77 -0
- package/js/plugins/karyotype.js +210 -0
- package/js/plugins/resizer.js +45 -0
- package/js/plugins/tooltips.js +94 -0
- package/js/plugins/trackControls.js +143 -0
- package/package.json +43 -0
- package/test/View/__snapshots__/render-bar-graph.test.js.snap +111 -0
- package/test/View/__snapshots__/render-blocks.test.js.snap +105 -0
- package/test/View/__snapshots__/render-chromosome.test.js.snap +5 -0
- package/test/View/__snapshots__/render-highlights.test.js.snap +73 -0
- package/test/View/__snapshots__/render-insert-variants.test.js.snap +9 -0
- package/test/View/__snapshots__/render-labels.test.js.snap +241 -0
- package/test/View/__snapshots__/render-legends.test.js.snap +13 -0
- package/test/View/__snapshots__/render-line-graph.test.js.snap +349 -0
- package/test/View/__snapshots__/render-scalebar.test.js.snap +49 -0
- package/test/View/__snapshots__/render-scaleline.test.js.snap +31 -0
- package/test/View/__snapshots__/render-sequence.test.js.snap +23 -0
- package/test/View/__snapshots__/render-stranded.test.js.snap +5 -0
- package/test/View/__snapshots__/render-transcripts.test.js.snap +193 -0
- package/test/View/render-bar-graph.test.js +87 -0
- package/test/View/render-blocks.test.js +171 -0
- package/test/View/render-chromosome.test.js +40 -0
- package/test/View/render-highlights.test.js +67 -0
- package/test/View/render-insert-variants.test.js +11 -0
- package/test/View/render-labels.test.js +266 -0
- package/test/View/render-legends.test.js +31 -0
- package/test/View/render-line-graph.test.js +169 -0
- package/test/View/render-scalebar.test.js +36 -0
- package/test/View/render-scaleline.test.js +28 -0
- package/test/View/render-sequence.test.js +49 -0
- package/test/View/render-stranded.test.js +10 -0
- package/test/View/render-transcripts.test.js +165 -0
- package/test/create-and-destroy.test.js +63 -0
- package/test/track-ordering.test.js +514 -0
- package/test/track_config/__snapshots__/config-settings.test.js.snap +23 -0
- package/test/track_config/config-settings.test.js +321 -0
- package/test/track_config/zoom-level-settings.test.js +98 -0
- package/test/utils.js +80 -0
- package/utils/createGenome.js +52 -0
- package/utils/devServer.js +36 -0
- package/utils/expandedTemplate.html +46 -0
- package/utils/git-hooks/post-commit +9 -0
- package/utils/git-hooks/pre-commit +7 -0
- package/utils/git-hooks/setup +6 -0
- package/utils/makeExpanded.js +19 -0
- package/webpack.config.js +39 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Genoverse.Track.File.BAM = Genoverse.Track.File.extend({
|
|
2
|
+
name : 'BAM',
|
|
3
|
+
indexExt : '.bai',
|
|
4
|
+
threshold : 100000,
|
|
5
|
+
largeFile : true,
|
|
6
|
+
model : Genoverse.Track.Model.File.BAM,
|
|
7
|
+
view : Genoverse.Track.View.Sequence.extend({
|
|
8
|
+
bump : true,
|
|
9
|
+
autoHeight : true
|
|
10
|
+
}),
|
|
11
|
+
|
|
12
|
+
click: function () {
|
|
13
|
+
var menu = this.base.apply(this, arguments);
|
|
14
|
+
|
|
15
|
+
if (menu) {
|
|
16
|
+
menu.addClass('gv-wrap-values');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return menu;
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
populateMenu: function (feature) {
|
|
23
|
+
var f = $.extend({ title: feature.readName }, feature);
|
|
24
|
+
|
|
25
|
+
delete f.sequence;
|
|
26
|
+
delete f.id;
|
|
27
|
+
|
|
28
|
+
return this.base(f);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Genoverse.Track.File.BED = Genoverse.Track.File.extend({
|
|
2
|
+
name : 'BED',
|
|
3
|
+
model : Genoverse.Track.Model.File.BED,
|
|
4
|
+
bump : true,
|
|
5
|
+
featureHeight : 10,
|
|
6
|
+
thickHeight : 10,
|
|
7
|
+
thinHeight : 7,
|
|
8
|
+
subFeatureJoinStyle : 'curve',
|
|
9
|
+
|
|
10
|
+
populateMenu: function (feature) {
|
|
11
|
+
var fields = [ false, false, false, 'name', 'score', 'strand', 'thickStart', 'thickEnd', 'itemRgb', 'blockCount', 'blockSizes', 'blockStarts' ]; // First three fields are chr, start, end which are covered by Location
|
|
12
|
+
|
|
13
|
+
return feature.originalFeature.reduce(function (menu, val, i) {
|
|
14
|
+
if (fields[i]) {
|
|
15
|
+
menu[fields[i]] = val;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return menu;
|
|
19
|
+
}, {
|
|
20
|
+
title : '<a target="_blank" href="https://genome.ucsc.edu/FAQ/FAQformat.html#format1">BED feature details</a>',
|
|
21
|
+
Location : feature.chr + ':' + feature.start + '-' + feature.end
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Genoverse.Track.File.BIGBED = Genoverse.Track.File.BED.extend({
|
|
2
|
+
name : 'bigbed',
|
|
3
|
+
model : Genoverse.Track.Model.File.BED.extend({
|
|
4
|
+
getData: function (chr, start, end) {
|
|
5
|
+
var model = this;
|
|
6
|
+
var deferred = $.Deferred();
|
|
7
|
+
|
|
8
|
+
if (!this.bigbedFile) {
|
|
9
|
+
this.bigbedFile = this.bigbedFile || (this.url ? new dallianceLib.URLFetchable(this.url) : new dallianceLib.BlobFetchable(this.track.dataFile));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
var d = $.Deferred().done(function () {
|
|
13
|
+
model.bwReader.getValues(chr, start, end, function (features, error) {
|
|
14
|
+
if (!error) {
|
|
15
|
+
features.sort(function (a, b) { return a.start - b.start; });
|
|
16
|
+
|
|
17
|
+
if (features.length) {
|
|
18
|
+
model.receiveData(features, chr, features[0].start, features[features.length - 1].end);
|
|
19
|
+
} else {
|
|
20
|
+
model.receiveData(features, chr, start, end);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
deferred.resolveWith(model);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (this.bwReader) {
|
|
29
|
+
d.resolve();
|
|
30
|
+
} else {
|
|
31
|
+
new BWReader(this.bigbedFile, function (bwReader) {
|
|
32
|
+
if (bwReader) {
|
|
33
|
+
model.bwReader = bwReader;
|
|
34
|
+
d.resolve();
|
|
35
|
+
} else {
|
|
36
|
+
model.receiveData([], chr, start, end);
|
|
37
|
+
return deferred.resolveWith(model);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return deferred;
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
Genoverse.Track.File.BB = Genoverse.Track.File.BIGBED;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Genoverse.Track.File.BIGWIG = Genoverse.Track.Graph.Bar.extend({
|
|
2
|
+
name : 'bigwig',
|
|
3
|
+
height : 100,
|
|
4
|
+
|
|
5
|
+
setDefaults: function () {
|
|
6
|
+
this.bwReader = null; // Not part of model since it needs to be shared between bar and line graphs
|
|
7
|
+
this.base.apply(this, arguments);
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
getData: function (chr, start, end) {
|
|
11
|
+
var model = this;
|
|
12
|
+
var deferred = $.Deferred();
|
|
13
|
+
|
|
14
|
+
if (!this.bigwigFile) {
|
|
15
|
+
this.bigwigFile = this.bigwigFile || (this.url ? new dallianceLib.URLFetchable(this.url) : new dallianceLib.BlobFetchable(this.track.dataFile));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
var d = $.Deferred().done(function () {
|
|
19
|
+
model.prop('bwReader').getValues(chr, start, end, function (features, error) {
|
|
20
|
+
if (!error) {
|
|
21
|
+
features.sort(function (a, b) { return a.start - b.start; });
|
|
22
|
+
|
|
23
|
+
if (features.length) {
|
|
24
|
+
model.receiveData(features, chr, features[0].start, features[features.length - 1].end);
|
|
25
|
+
} else {
|
|
26
|
+
model.receiveData(features, chr, start, end);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
deferred.resolveWith(model);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (this.prop('bwReader')) {
|
|
35
|
+
d.resolve();
|
|
36
|
+
} else {
|
|
37
|
+
new BWReader(this.bigwigFile, function (bwReader) {
|
|
38
|
+
if (bwReader) {
|
|
39
|
+
model.prop('bwReader', bwReader);
|
|
40
|
+
d.resolve();
|
|
41
|
+
} else {
|
|
42
|
+
model.receiveData([], chr, start, end);
|
|
43
|
+
return deferred.resolveWith(model);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return deferred;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
Genoverse.Track.File.BW = Genoverse.Track.File.BIGWIG;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Genoverse.Track.File.VCF = Genoverse.Track.File.extend({
|
|
2
|
+
name : 'VCF',
|
|
3
|
+
indexExt : '.tbi',
|
|
4
|
+
model : Genoverse.Track.Model.File.VCF,
|
|
5
|
+
autoHeight : false,
|
|
6
|
+
maxQual : undefined, // Set this to the maximum value of the QUAL field in the file in order to color features by QUAL. Only required for large (tabix indexed) files - small ones can calculate this value automatically
|
|
7
|
+
|
|
8
|
+
afterSetMVC: function () {
|
|
9
|
+
if (this.prop('gz')) {
|
|
10
|
+
this.prop('threshold', 1e5);
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
populateMenu: function (feature) {
|
|
15
|
+
return {
|
|
16
|
+
title : '<a target="_blank" href="https://www.internationalgenome.org/wiki/Analysis/vcf4.0">VCF feature details</a>',
|
|
17
|
+
CHROM : feature.originalFeature[0],
|
|
18
|
+
POS : feature.originalFeature[1],
|
|
19
|
+
ID : feature.originalFeature[2],
|
|
20
|
+
REF : feature.originalFeature[3],
|
|
21
|
+
ALT : feature.originalFeature[4],
|
|
22
|
+
QUAL : feature.originalFeature[5],
|
|
23
|
+
FILTER : feature.originalFeature[6],
|
|
24
|
+
INFO : feature.originalFeature[7].split(';').join('<br />')
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
1: {
|
|
29
|
+
view: Genoverse.Track.View.Sequence.extend({
|
|
30
|
+
bump : true,
|
|
31
|
+
labels : false,
|
|
32
|
+
featureMargin : { top: 0, right: 0, bottom: 0, left: 0 },
|
|
33
|
+
|
|
34
|
+
draw: function (features, featureContext, labelContext, scale) {
|
|
35
|
+
this.base.apply(this, arguments);
|
|
36
|
+
this.highlightRef(features, featureContext, scale);
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
highlightRef: function (features, context, scale) {
|
|
40
|
+
context.strokeStyle = 'black';
|
|
41
|
+
|
|
42
|
+
for (var i = 0; i < features.length; i++) {
|
|
43
|
+
if (features[i].allele === 'REF') {
|
|
44
|
+
context.strokeRect(features[i].position[scale].X, features[i].position[scale].Y, features[i].position[scale].width, features[i].position[scale].height);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
1000: {
|
|
52
|
+
view: Genoverse.Track.View.extend({
|
|
53
|
+
bump : false,
|
|
54
|
+
labels : false,
|
|
55
|
+
|
|
56
|
+
drawFeature: function (feature) {
|
|
57
|
+
var maxQual = this.prop('maxQual');
|
|
58
|
+
|
|
59
|
+
if (maxQual && !feature.color) {
|
|
60
|
+
var heat = Math.min(255, Math.floor((255 * (feature.originalFeature[5] || 0)) / maxQual)) - 127;
|
|
61
|
+
var red = heat > 0 ? 255 : 127 + heat;
|
|
62
|
+
var green = heat < 0 ? 255 : 127 - heat;
|
|
63
|
+
|
|
64
|
+
feature.color = 'rgb(' + red + ',' + green + ',0)';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.base.apply(this, arguments);
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Genoverse.Track.Gene = Genoverse.Track.extend({
|
|
2
|
+
id : 'genes',
|
|
3
|
+
name : 'Genes',
|
|
4
|
+
height : 200,
|
|
5
|
+
legend : true,
|
|
6
|
+
|
|
7
|
+
populateMenu: function (feature) {
|
|
8
|
+
var url = 'https://www.ensembl.org/Homo_sapiens/' + (feature.feature_type === 'transcript' ? 'Transcript' : 'Gene') + '/Summary?' + (feature.feature_type === 'transcript' ? 't' : 'g') + '=' + feature.id;
|
|
9
|
+
var menu = {
|
|
10
|
+
title : '<a target="_blank" href="' + url + '">' + (feature.external_name ? feature.external_name + ' (' + feature.id + ')' : feature.id) + '</a>',
|
|
11
|
+
Location : feature.chr + ':' + feature.start + '-' + feature.end,
|
|
12
|
+
Source : feature.source,
|
|
13
|
+
Biotype : feature.biotype
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (feature.feature_type === 'transcript') {
|
|
17
|
+
menu.Gene = '<a target="_blank" href="https://www.ensembl.org/Homo_sapiens/Gene/Summary?g=' + feature.Parent + '">' + feature.Parent + '</a>';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return menu;
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Different settings for different zoom level
|
|
24
|
+
2000000: { // This one applies when > 2M base-pairs per screen
|
|
25
|
+
labels: false
|
|
26
|
+
},
|
|
27
|
+
100000: { // more than 100K but less then 2M
|
|
28
|
+
labels : true,
|
|
29
|
+
model : Genoverse.Track.Model.Gene.Ensembl,
|
|
30
|
+
view : Genoverse.Track.View.Gene.Ensembl
|
|
31
|
+
},
|
|
32
|
+
1: { // > 1 base-pair, but less then 100K
|
|
33
|
+
labels : true,
|
|
34
|
+
model : Genoverse.Track.Model.Transcript.Ensembl,
|
|
35
|
+
view : Genoverse.Track.View.Transcript.Ensembl
|
|
36
|
+
}
|
|
37
|
+
});
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Genoverse.Track.Controller.Graph.Bar = {
|
|
2
|
+
getClickedFeatures: function (x, y) {
|
|
3
|
+
var yZero = this.prop('marginTop') - (this.prop('range')[0] * this.track.getYScale());
|
|
4
|
+
var scale = this.scale;
|
|
5
|
+
var tolerance = scale > 1 ? 0 : 1;
|
|
6
|
+
|
|
7
|
+
// Bars with negative values are stored in featurePositions with h < 0.
|
|
8
|
+
// While this works to a certain degree (fillRect allows negative height, drawing upwards from y), it makes them hard to search for in the RTree - to find such a feature you need to search with y = -h and h = y - h + 1
|
|
9
|
+
// It is therefore easier to search featuresByChr (i.e. the genomic positions) for a feature overlapping the x of the click, and then filter those results for y position manually.
|
|
10
|
+
var features = this.prop('featuresByChr')[this.browser.chr].search({
|
|
11
|
+
x : (x - (tolerance / 2)) / scale,
|
|
12
|
+
y : 0,
|
|
13
|
+
w : (1 + tolerance) / scale,
|
|
14
|
+
h : 1
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (features.length) {
|
|
18
|
+
if (
|
|
19
|
+
(y < yZero && features.filter(function (f) { return f.position[scale].bounds.y + f.position[scale].bounds.h <= y && f.position[scale].bounds.y >= y; }).length === 0) ||
|
|
20
|
+
(y >= yZero && this.featurePositions.search({ x: x, y: y, w: 1, h: 1 }).length === 0)
|
|
21
|
+
) {
|
|
22
|
+
features = [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return features.length ? [ this.model.sortFeatures(features) ] : [];
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
populateMenu: function (features) {
|
|
30
|
+
if (!features.length) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var start = features[0].start;
|
|
35
|
+
var end = features[features.length - 1].end;
|
|
36
|
+
var avg = features[0].start !== features[features.length - 1].start;
|
|
37
|
+
var menu = { title: features[0].chr + ':' + (start === end ? start : start + '-' + end) };
|
|
38
|
+
var values, i;
|
|
39
|
+
|
|
40
|
+
function getValues(_features) {
|
|
41
|
+
var vals = _features.map(function (f) { return f.height; }).sort(function (a, b) { return a - b; });
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
avg : vals.reduce(function (n, v) { return n + v; }, 0) / vals.length,
|
|
45
|
+
min : vals[0],
|
|
46
|
+
max : vals[vals.length - 1]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (avg) {
|
|
51
|
+
if (features.length === 1) {
|
|
52
|
+
values = getValues(features);
|
|
53
|
+
|
|
54
|
+
menu['Average value'] = values.avg;
|
|
55
|
+
menu['Min value'] = values.min;
|
|
56
|
+
menu['Max value'] = values.max;
|
|
57
|
+
} else {
|
|
58
|
+
menu = [ menu ];
|
|
59
|
+
|
|
60
|
+
var datasets = this.prop('datasets');
|
|
61
|
+
var featuresByDataset;
|
|
62
|
+
|
|
63
|
+
if (datasets.length) {
|
|
64
|
+
featuresByDataset = datasets.reduce(function (hash, d) { hash[d.name] = []; return hash; }, {});
|
|
65
|
+
|
|
66
|
+
for (i = 0; i < features.length; i++) {
|
|
67
|
+
featuresByDataset[features[i].dataset].push(features[i]);
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
datasets = [{ name: '' }];
|
|
71
|
+
featuresByDataset = { '': features };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (i = 0; i < datasets.length; i++) {
|
|
75
|
+
values = getValues(featuresByDataset[datasets[i].name]);
|
|
76
|
+
|
|
77
|
+
menu.push($.extend({
|
|
78
|
+
Average : values.avg,
|
|
79
|
+
Min : values.min,
|
|
80
|
+
Max : values.max
|
|
81
|
+
}, datasets[i].name ? { title: datasets[i].name } : {}));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} else if (features.length === 1) {
|
|
85
|
+
menu.Value = features[0].height;
|
|
86
|
+
} else {
|
|
87
|
+
for (i = 0; i < features.length; i++) {
|
|
88
|
+
menu[features[i].dataset] = features[i].height;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return menu;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
Genoverse.Track.Model.Graph.Bar = Genoverse.Track.Model.Graph.extend({
|
|
97
|
+
insertFeature: function (feature) {
|
|
98
|
+
var datasets = this.prop('datasets');
|
|
99
|
+
|
|
100
|
+
if (datasets.length) {
|
|
101
|
+
feature.legend = feature.dataset;
|
|
102
|
+
feature.color = (datasets.filter(function (s) { return s.name === feature.dataset; })[0] || { color: this.color }).color;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
feature.id = feature.id || [ feature.chr, feature.start, feature.end, feature.dataset || '' ].join(':');
|
|
106
|
+
|
|
107
|
+
return this.base.apply(this, arguments);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
Genoverse.Track.View.Graph.Bar = Genoverse.Track.View.Graph.extend({
|
|
112
|
+
scaleFeatures: function (features, scale) {
|
|
113
|
+
var yScale = this.track.getYScale();
|
|
114
|
+
var zeroY = this.prop('marginTop') - this.prop('range')[0] * yScale;
|
|
115
|
+
|
|
116
|
+
features = this.base(features, scale);
|
|
117
|
+
|
|
118
|
+
for (var i = 0; i < features.length; i++) {
|
|
119
|
+
features[i].position[scale].height = features[i].height * yScale;
|
|
120
|
+
features[i].position[scale].y = zeroY;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return features;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
draw: function (features, featureContext, labelContext, scale) {
|
|
127
|
+
var datasets = this.featureDataSets(features);
|
|
128
|
+
var marginBottom = this.prop('margin');
|
|
129
|
+
var binSize = scale < 1 ? Math.ceil(1 / scale) : 0;
|
|
130
|
+
var conf, set, setFeatures, j, binnedFeatures, binStart, bin, f;
|
|
131
|
+
|
|
132
|
+
var defaults = {
|
|
133
|
+
color : this.color,
|
|
134
|
+
globalAlpha : this.prop('globalAlpha')
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
for (var i = 0; i < datasets.list.length; i++) {
|
|
138
|
+
conf = $.extend({}, defaults, datasets.list[i]);
|
|
139
|
+
set = datasets.list[i].name;
|
|
140
|
+
setFeatures = $.extend(true, [], datasets.features[set] || []);
|
|
141
|
+
|
|
142
|
+
if (!setFeatures.length) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (binSize) {
|
|
147
|
+
binnedFeatures = [];
|
|
148
|
+
j = 0;
|
|
149
|
+
|
|
150
|
+
while (j < setFeatures.length) {
|
|
151
|
+
binStart = setFeatures[j].start;
|
|
152
|
+
bin = [];
|
|
153
|
+
|
|
154
|
+
while (setFeatures[j] && setFeatures[j].start - binStart < binSize) {
|
|
155
|
+
bin.push(setFeatures[j++]);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
f = $.extend(true, {}, bin[0], {
|
|
159
|
+
height : bin.reduce(function (a, b) { return a + b.height; }, 0) / bin.length,
|
|
160
|
+
end : bin[bin.length - 1].end
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
[ 'H', 'W', 'height', 'width' ].forEach(function (attr) { // eslint-disable-line no-loop-func
|
|
164
|
+
f.position[scale][attr] = bin.reduce(function (a, b) { return a + b.position[scale][attr]; }, 0) / bin.length;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
binnedFeatures.push(f);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
setFeatures = binnedFeatures;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (j = 0; j < setFeatures.length; j++) {
|
|
174
|
+
setFeatures[j].color = conf.color;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
featureContext.globalAlpha = conf.globalAlpha;
|
|
178
|
+
|
|
179
|
+
this.base(setFeatures, featureContext, labelContext, scale);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Don't allow features to be drawn in the margins
|
|
183
|
+
featureContext.clearRect(0, 0, this.width, this.prop('marginTop') - 1);
|
|
184
|
+
featureContext.clearRect(0, this.prop('height') - marginBottom, this.width, marginBottom);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
Genoverse.Track.Graph.Bar = Genoverse.Track.Graph.extend({
|
|
189
|
+
type : 'Bar',
|
|
190
|
+
model : Genoverse.Track.Model.Graph.Bar,
|
|
191
|
+
view : Genoverse.Track.View.Graph.Bar,
|
|
192
|
+
threshold : 500000,
|
|
193
|
+
|
|
194
|
+
10000: $.extend( // Switch to line graph at 10000bp region
|
|
195
|
+
Object.keys(Genoverse.Track.Graph.Line.prototype).reduce(function (hash, key) {
|
|
196
|
+
if (Genoverse.Track.Graph.Line.prototype.hasOwnProperty(key) && !Base.prototype[key]) {
|
|
197
|
+
hash[key] = Genoverse.Track.Graph.Line.prototype[key];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return hash;
|
|
201
|
+
}, {}), {
|
|
202
|
+
fill : true,
|
|
203
|
+
model : Genoverse.Track.Model.Graph.Line.extend({
|
|
204
|
+
parseData: function (data, chr, start, end) {
|
|
205
|
+
var coords = [];
|
|
206
|
+
var j;
|
|
207
|
+
|
|
208
|
+
for (var i = 0; i < data.length; i++) {
|
|
209
|
+
for (j = data[i].start; j < data[i].end; j++) {
|
|
210
|
+
coords.push([ j, data[i].height ]);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return this.base([{ chr: chr, start: start, end: end, coords: coords }], chr, start, end);
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
),
|
|
219
|
+
50000: $.extend( // Switch to sparser line graph at 50000bp region
|
|
220
|
+
Object.keys(Genoverse.Track.Graph.Line.prototype).reduce(function (hash, key) {
|
|
221
|
+
if (Genoverse.Track.Graph.Line.prototype.hasOwnProperty(key) && !Base.prototype[key]) {
|
|
222
|
+
hash[key] = Genoverse.Track.Graph.Line.prototype[key];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return hash;
|
|
226
|
+
}, {}), {
|
|
227
|
+
fill : true,
|
|
228
|
+
model : Genoverse.Track.Model.Graph.Line.extend({
|
|
229
|
+
parseData: function (data, chr, start, end) {
|
|
230
|
+
return this.base([{ chr: chr, start: start, end: end, coords: data.map(function (d) { return [ d.start, d.height ]; }) }], chr, start, end);
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
});
|