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,227 @@
|
|
|
1
|
+
Genoverse.Track.Scalebar = Genoverse.Track.extend({
|
|
2
|
+
unsortable : true,
|
|
3
|
+
fixedOrder : true,
|
|
4
|
+
order : 0,
|
|
5
|
+
orderReverse : 1e5,
|
|
6
|
+
featureStrand : 1,
|
|
7
|
+
controls : 'off',
|
|
8
|
+
height : 20,
|
|
9
|
+
featureHeight : 3,
|
|
10
|
+
featureMargin : { top: 0, right: 0, bottom: 2, left: 0 },
|
|
11
|
+
margin : 0,
|
|
12
|
+
minPixPerMajor : 100, // Least number of pixels per written number
|
|
13
|
+
color : '#000000',
|
|
14
|
+
autoHeight : false,
|
|
15
|
+
labels : true,
|
|
16
|
+
bump : false,
|
|
17
|
+
resizable : false,
|
|
18
|
+
click : $.noop,
|
|
19
|
+
colors : {
|
|
20
|
+
majorGuideLine : '#CCCCCC',
|
|
21
|
+
minorGuideLine : '#E5E5E5'
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
setEvents: function () {
|
|
25
|
+
var browser = this.browser;
|
|
26
|
+
|
|
27
|
+
function resize() {
|
|
28
|
+
$('.gv-bg.gv-full-height', browser.container).height(function () {
|
|
29
|
+
return browser.wrapper.outerHeight(true) - $(this).parents('.gv-track-container').position().top;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
browser.on('afterAddTracks', resize);
|
|
34
|
+
browser.on('afterResize', this, resize);
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
setScale: function () {
|
|
38
|
+
var max = this.prop('width') / this.prop('minPixPerMajor');
|
|
39
|
+
var divisor = 5;
|
|
40
|
+
var majorUnit = -1;
|
|
41
|
+
var fromDigit = ('' + this.browser.start).split(''); // Split into array of digits
|
|
42
|
+
var toDigit = ('' + this.browser.end).split('');
|
|
43
|
+
var features = {};
|
|
44
|
+
var divisions, i;
|
|
45
|
+
|
|
46
|
+
for (i = fromDigit.length; i < toDigit.length; i++) {
|
|
47
|
+
fromDigit.unshift('0');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (i = toDigit.length; i < fromDigit.length; i++) {
|
|
51
|
+
toDigit.unshift('0');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// How many divisions would there be if we only kept i digits?
|
|
55
|
+
for (i = 0; i < fromDigit.length; i++) {
|
|
56
|
+
divisions = parseInt(toDigit.slice(0, fromDigit.length - i).join(''), 10) - parseInt(fromDigit.slice(0, fromDigit.length - i).join(''), 10);
|
|
57
|
+
|
|
58
|
+
if (divisions && divisions <= max) {
|
|
59
|
+
majorUnit = parseInt('1' + $.map(new Array(i), function () { return '0'; }).join(''), 10);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (majorUnit === -1) {
|
|
65
|
+
majorUnit = this.browser.length === 1 ? 1 : parseInt('1' + $.map(new Array(fromDigit.length), function () { return '0'; }).join(''), 10);
|
|
66
|
+
divisor = 1;
|
|
67
|
+
} else if (divisions * 5 <= max) { // Improve things by trying simple multiples of 1<n zeroes>. (eg if 100 will fit will 200, 400, 500).
|
|
68
|
+
majorUnit /= 5;
|
|
69
|
+
divisor = 2;
|
|
70
|
+
} else if (divisions * 4 <= max) {
|
|
71
|
+
majorUnit /= 4;
|
|
72
|
+
divisor = 1;
|
|
73
|
+
} else if (divisions * 2 <= max) {
|
|
74
|
+
majorUnit /= 2;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
majorUnit = Math.max(majorUnit, 1);
|
|
78
|
+
|
|
79
|
+
features[this.browser.chr] = new RTree();
|
|
80
|
+
|
|
81
|
+
this.prop('minorUnit', Math.max(majorUnit / divisor, 1));
|
|
82
|
+
this.prop('majorUnit', majorUnit);
|
|
83
|
+
this.prop('featuresByChr', features);
|
|
84
|
+
this.prop('featuresById', {});
|
|
85
|
+
this.prop('seen', {});
|
|
86
|
+
|
|
87
|
+
this.base();
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
setFeatures: function (chr, start, end) {
|
|
91
|
+
var minorUnit = this.prop('minorUnit');
|
|
92
|
+
var majorUnit = this.prop('majorUnit');
|
|
93
|
+
var seen = this.prop('seen');
|
|
94
|
+
|
|
95
|
+
start = Math.max(start - (start % minorUnit) - majorUnit, 0);
|
|
96
|
+
|
|
97
|
+
var flip = (start / minorUnit) % 2 ? 1 : -1;
|
|
98
|
+
var feature, major, label;
|
|
99
|
+
|
|
100
|
+
for (var x = start; x < end + minorUnit; x += minorUnit) {
|
|
101
|
+
flip *= -1;
|
|
102
|
+
|
|
103
|
+
if (seen[x]) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
seen[x] = 1;
|
|
108
|
+
|
|
109
|
+
feature = { id: chr + ':' + x, chr: chr, strand: 1, sort: x };
|
|
110
|
+
major = x && x % majorUnit === 0;
|
|
111
|
+
|
|
112
|
+
if (flip === 1) {
|
|
113
|
+
feature.start = x;
|
|
114
|
+
feature.end = x + minorUnit - 1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (major) {
|
|
118
|
+
label = this.view.formatLabel(x);
|
|
119
|
+
|
|
120
|
+
if (label !== this.lastLabel) {
|
|
121
|
+
feature.label = label;
|
|
122
|
+
|
|
123
|
+
if (!feature.end) {
|
|
124
|
+
feature.start = x;
|
|
125
|
+
feature.end = x - 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.lastLabel = label;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (feature.end) {
|
|
133
|
+
this.model.insertFeature(feature);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
makeFirstImage: function (moveTo) {
|
|
139
|
+
if (this.prop('strand') === -1) {
|
|
140
|
+
moveTo = this.track.forwardTrack.prop('scrollStart');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return this.base(moveTo);
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
makeImage: function (params) {
|
|
147
|
+
params.background = 'gv-guidelines gv-full-height';
|
|
148
|
+
params.featureHeight = this.prop('height');
|
|
149
|
+
|
|
150
|
+
this.track.setFeatures(params.chr, params.start, params.end);
|
|
151
|
+
|
|
152
|
+
var rtn = this.base(params);
|
|
153
|
+
|
|
154
|
+
params.container.addClass('gv-full-height');
|
|
155
|
+
|
|
156
|
+
return rtn;
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
makeReverseImage: function (params) {
|
|
160
|
+
this.imgContainers.push(params.container.clone().html(params.container.children('.gv-data').clone(true).css({ opacity: 1, background: this.browser.wrapper.css('backgroundColor') }))[0]);
|
|
161
|
+
this.scrollContainer.append(this.imgContainers);
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
renderBackground: function (f, bgImage) {
|
|
165
|
+
this.base(f, bgImage);
|
|
166
|
+
bgImage.height(this.browser.wrapper.outerHeight(true));
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
draw: function (features, featureContext, labelContext, scale) {
|
|
170
|
+
var i = features.length;
|
|
171
|
+
var minorUnit = this.prop('minorUnit');
|
|
172
|
+
var width = Math.ceil(minorUnit * scale);
|
|
173
|
+
var feature, start, end;
|
|
174
|
+
|
|
175
|
+
featureContext.textBaseline = 'top';
|
|
176
|
+
featureContext.fillStyle = this.color;
|
|
177
|
+
|
|
178
|
+
this.guideLines = { major: {} }; // FIXME: pass params to draw, rather than scale. set guideLines on params
|
|
179
|
+
|
|
180
|
+
while (i--) {
|
|
181
|
+
feature = features[i];
|
|
182
|
+
start = Math.round(feature.position[scale].X);
|
|
183
|
+
end = start + width - 1;
|
|
184
|
+
|
|
185
|
+
this.drawFeature($.extend({}, feature, {
|
|
186
|
+
x : start,
|
|
187
|
+
y : 0,
|
|
188
|
+
width : Math.ceil(feature.position[scale].width),
|
|
189
|
+
height : this.featureHeight
|
|
190
|
+
}), featureContext, labelContext, scale);
|
|
191
|
+
|
|
192
|
+
if (feature.label) {
|
|
193
|
+
if (start > -1) {
|
|
194
|
+
featureContext.fillRect(start, this.featureHeight, 1, this.featureHeight);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.guideLines.major[feature.start] = true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Fiddle the location so that these [additional major] lines overlap with normal lines
|
|
201
|
+
if (feature.end < feature.start) {
|
|
202
|
+
start--;
|
|
203
|
+
end++;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.guideLines[feature.start] = start;
|
|
207
|
+
this.guideLines[feature.start + minorUnit] = end;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
featureContext.fillRect(0, 0, featureContext.canvas.width, 1);
|
|
211
|
+
featureContext.fillRect(0, this.featureHeight, featureContext.canvas.width, 1);
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// Draw guidelines
|
|
215
|
+
drawBackground: function (f, context) {
|
|
216
|
+
for (var i in this.guideLines) {
|
|
217
|
+
if (this.guideLines[i] >= 0 && this.guideLines[i] <= this.width) {
|
|
218
|
+
context.fillStyle = this.track.colors[this.guideLines.major[i] ? 'majorGuideLine' : 'minorGuideLine'];
|
|
219
|
+
context.fillRect(this.guideLines[i], 0, 1, context.canvas.height);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
formatLabel: function (label) {
|
|
225
|
+
return this.prop('minorUnit') < 1000 ? label.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') : this.base(label);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Genoverse.Track.Scaleline = Genoverse.Track.Static.extend({
|
|
2
|
+
strand : 1,
|
|
3
|
+
color : '#000000',
|
|
4
|
+
height : 12,
|
|
5
|
+
labels : 'overlay',
|
|
6
|
+
unsortable : true,
|
|
7
|
+
fixedOrder : true,
|
|
8
|
+
arrowWidth : 7,
|
|
9
|
+
|
|
10
|
+
resize: $.noop,
|
|
11
|
+
|
|
12
|
+
makeFirstImage: function () {
|
|
13
|
+
this.prop('scaleline', false);
|
|
14
|
+
this.base.apply(this, arguments);
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
render: function (f, img) {
|
|
18
|
+
this.base(f, img);
|
|
19
|
+
this.prop('drawnScale', img.data('scale'));
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
positionFeatures: function (features, params) {
|
|
23
|
+
var scaleline = this.prop('scaleline');
|
|
24
|
+
|
|
25
|
+
if (params.scale === this.drawnScale) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (scaleline) {
|
|
30
|
+
return scaleline;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var strand = this.prop('strand');
|
|
34
|
+
var height = this.prop('height');
|
|
35
|
+
var text = this.formatLabel(this.browser.length);
|
|
36
|
+
var width = this.context.measureText(text).width;
|
|
37
|
+
var textMargin = 10; // 5px each side
|
|
38
|
+
var y = height / 2;
|
|
39
|
+
var x1 = 0;
|
|
40
|
+
var x2 = (this.width - width - textMargin) / 2;
|
|
41
|
+
|
|
42
|
+
if (strand) {
|
|
43
|
+
var strandText = strand === 1 ? 'Forward strand' : 'Reverse strand';
|
|
44
|
+
var strandWidth = this.context.measureText(strandText).width;
|
|
45
|
+
var x3 = (
|
|
46
|
+
strand === 1
|
|
47
|
+
? this.width - this.prop('arrowWidth') - strandWidth - (1.5 * textMargin)
|
|
48
|
+
: this.prop('arrowWidth') + (0.5 * textMargin)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
scaleline = [
|
|
52
|
+
{ x: x1, y: y, width: this.width, height: 1, decorations: true },
|
|
53
|
+
{ x: x2, y: 0, width: width + textMargin, height: height, clear: true, color: false, labelColor: this.color, labelWidth: width, label: text },
|
|
54
|
+
{ x: x3, y: 0, width: strandWidth + textMargin, height: height, clear: true, color: false, labelColor: this.color, labelWidth: strandWidth, label: strandText }
|
|
55
|
+
];
|
|
56
|
+
} else {
|
|
57
|
+
scaleline = [
|
|
58
|
+
{ x: x1, y: y, width: this.width, height: 1, decorations: true },
|
|
59
|
+
{ x: x2, y: 0, width: width + textMargin, height: height, clear: true, color: false, labelColor: this.color, labelWidth: width, label: text }
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return this.base(this.prop('scaleline', scaleline), params);
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
decorateFeature: function (feature, context) {
|
|
67
|
+
var strand = this.prop('strand');
|
|
68
|
+
var height = this.prop('height');
|
|
69
|
+
var arrowWidth = this.prop('arrowWidth');
|
|
70
|
+
var width = this.width;
|
|
71
|
+
|
|
72
|
+
context.strokeStyle = this.color;
|
|
73
|
+
|
|
74
|
+
[ -1, 1 ].filter(
|
|
75
|
+
function (dir) { return strand ? dir === strand : true; }
|
|
76
|
+
).forEach(
|
|
77
|
+
function (dir) {
|
|
78
|
+
var x1 = dir === 1 ? width - arrowWidth : arrowWidth;
|
|
79
|
+
var x2 = x1 + (dir * arrowWidth);
|
|
80
|
+
|
|
81
|
+
context.beginPath();
|
|
82
|
+
context.moveTo(x1, height * 0.25);
|
|
83
|
+
context.lineTo(x2, height * 0.5);
|
|
84
|
+
context.lineTo(x1, height * 0.75);
|
|
85
|
+
context.closePath();
|
|
86
|
+
context.stroke();
|
|
87
|
+
context.fill();
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Genoverse.Track.Controller.Static = Genoverse.Track.Controller.extend({
|
|
2
|
+
addDomElements: function () {
|
|
3
|
+
this.base();
|
|
4
|
+
|
|
5
|
+
this.image = $('<img>').appendTo(this.imgContainer);
|
|
6
|
+
|
|
7
|
+
this.container.toggleClass('gv-track-container gv-track-container-static').prepend(this.imgContainer);
|
|
8
|
+
this.scrollContainer.add(this.messageContainer).remove();
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
reset: function () {
|
|
12
|
+
delete this.stringified;
|
|
13
|
+
this.base.apply(this, arguments);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
setWidth: function (width) {
|
|
17
|
+
this.base(width);
|
|
18
|
+
this.image.width = this.width;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
makeFirstImage: function () {
|
|
22
|
+
this.base.apply(this, arguments);
|
|
23
|
+
this.container.css('left', 0);
|
|
24
|
+
this.imgContainer.show();
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
makeImage: function (params) {
|
|
28
|
+
if (this.prop('disabled')) {
|
|
29
|
+
return $.Deferred().resolve();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var features = this.view.positionFeatures(this.model.findFeatures(params.chr, params.start, params.end), params);
|
|
33
|
+
|
|
34
|
+
if (features) {
|
|
35
|
+
var string = JSON.stringify(features);
|
|
36
|
+
|
|
37
|
+
if (this.stringified !== string) {
|
|
38
|
+
var height = this.prop('height');
|
|
39
|
+
|
|
40
|
+
params.width = this.width;
|
|
41
|
+
params.featureHeight = height;
|
|
42
|
+
|
|
43
|
+
this.render(features, this.image.data(params));
|
|
44
|
+
this.imgContainer.children(':last').show();
|
|
45
|
+
this.resize(height, undefined, false);
|
|
46
|
+
|
|
47
|
+
this.stringified = string;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return $.Deferred().resolve();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
Genoverse.Track.Model.Static = Genoverse.Track.Model.extend({
|
|
56
|
+
url : false,
|
|
57
|
+
checkDataRange : function () { return true; }
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
Genoverse.Track.View.Static = Genoverse.Track.View.extend({
|
|
61
|
+
featureMargin : { top: 0, right: 1, bottom: 0, left: 1 },
|
|
62
|
+
positionFeature : $.noop,
|
|
63
|
+
scaleFeatures : function (features) { return features; },
|
|
64
|
+
|
|
65
|
+
draw: function (features, featureContext, labelContext, scale) {
|
|
66
|
+
for (var i = 0; i < features.length; i++) {
|
|
67
|
+
this.drawFeature(features[i], featureContext, labelContext, scale);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
Genoverse.Track.Static = Genoverse.Track.extend({
|
|
73
|
+
controls : 'off',
|
|
74
|
+
resizable : false,
|
|
75
|
+
controller : Genoverse.Track.Controller.Static,
|
|
76
|
+
model : Genoverse.Track.Model.Static,
|
|
77
|
+
view : Genoverse.Track.View.Static
|
|
78
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Genoverse.Track.dbSNP = Genoverse.Track.extend({
|
|
2
|
+
id : 'dbSNP',
|
|
3
|
+
name : 'dbSNP',
|
|
4
|
+
info : 'All sequence variants from the database of Single Nucleotide Polymorphisms (dbSNP)',
|
|
5
|
+
url : '//rest.ensembl.org/overlap/region/human/__CHR__:__START__-__END__?feature=variation;content-type=application/json',
|
|
6
|
+
dataRequestLimit : 5000000, // As per e! REST API restrictions
|
|
7
|
+
threshold : 1e5,
|
|
8
|
+
labels : false,
|
|
9
|
+
legend : true,
|
|
10
|
+
autoHeight : true,
|
|
11
|
+
colorMap : {
|
|
12
|
+
transcript_ablation : '#ff0000',
|
|
13
|
+
splice_acceptor_variant : '#FF581A',
|
|
14
|
+
splice_donor_variant : '#FF581A',
|
|
15
|
+
stop_gained : '#ff0000',
|
|
16
|
+
frameshift_variant : '#9400D3',
|
|
17
|
+
stop_lost : '#ff0000',
|
|
18
|
+
start_lost : '#ffd700',
|
|
19
|
+
transcript_amplification : '#ff69b4',
|
|
20
|
+
inframe_insertion : '#ff69b4',
|
|
21
|
+
inframe_deletion : '#ff69b4',
|
|
22
|
+
missense_variant : '#ffd700',
|
|
23
|
+
protein_altering_variant : '#FF0080',
|
|
24
|
+
splice_region_variant : '#ff7f50',
|
|
25
|
+
incomplete_terminal_codon_variant : '#ff00ff',
|
|
26
|
+
stop_retained_variant : '#76ee00',
|
|
27
|
+
synonymous_variant : '#76ee00',
|
|
28
|
+
coding_sequence_variant : '#458b00',
|
|
29
|
+
mature_miRNA_variant : '#458b00',
|
|
30
|
+
'5_prime_UTR_variant' : '#7ac5cd',
|
|
31
|
+
'3_prime_UTR_variant' : '#7ac5cd',
|
|
32
|
+
non_coding_transcript_exon_variant : '#32cd32',
|
|
33
|
+
intron_variant : '#02599c',
|
|
34
|
+
NMD_transcript_variant : '#ff4500',
|
|
35
|
+
non_coding_transcript_variant : '#32cd32',
|
|
36
|
+
upstream_gene_variant : '#a2b5cd',
|
|
37
|
+
downstream_gene_variant : '#a2b5cd',
|
|
38
|
+
TFBS_ablation : '#a52a2a',
|
|
39
|
+
TFBS_amplification : '#a52a2a',
|
|
40
|
+
TF_binding_site_variant : '#a52a2a',
|
|
41
|
+
regulatory_region_ablation : '#a52a2a',
|
|
42
|
+
regulatory_region_amplification : '#a52a2a',
|
|
43
|
+
feature_elongation : '#7f7f7f',
|
|
44
|
+
regulatory_region_variant : '#a52a2a',
|
|
45
|
+
feature_truncation : '#7f7f7f',
|
|
46
|
+
intergenic_variant : '#636363'
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
insertFeature: function (feature) {
|
|
50
|
+
feature.color = this.prop('colorMap')[feature.consequence_type];
|
|
51
|
+
feature.legend = feature.consequence_type;
|
|
52
|
+
|
|
53
|
+
if (feature.start > feature.end) {
|
|
54
|
+
feature.decorations = true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.base(feature);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
decorateFeature: function (feature, context, scale) {
|
|
61
|
+
context.fillStyle = feature.color;
|
|
62
|
+
context.beginPath();
|
|
63
|
+
context.moveTo(feature.position[scale].X - 3, feature.position[scale].Y + this.featureHeight);
|
|
64
|
+
context.lineTo(feature.position[scale].X, feature.position[scale].Y + this.featureHeight - 4);
|
|
65
|
+
context.lineTo(feature.position[scale].X + 3, feature.position[scale].Y + this.featureHeight);
|
|
66
|
+
context.fill();
|
|
67
|
+
|
|
68
|
+
if (scale > 1) {
|
|
69
|
+
context.fillRect(feature.position[scale].X - 0.5, feature.position[scale].Y, 1.5, feature.position[scale].height);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
populateMenu: function (feature) {
|
|
74
|
+
var deferred = $.Deferred();
|
|
75
|
+
var menu = [{
|
|
76
|
+
title : '<a href="https://www.ensembl.org/Homo_sapiens/Variation/Summary?v=' + feature.id + '" target="_blank">' + feature.id + '</a>',
|
|
77
|
+
Location : feature.chr + ':' + feature.start + '-' + feature.end,
|
|
78
|
+
Consequence : feature.consequence_type,
|
|
79
|
+
Alleles : feature.alleles.join(', ')
|
|
80
|
+
}];
|
|
81
|
+
|
|
82
|
+
$.ajax({
|
|
83
|
+
url : '//rest.ensembl.org/variation/human/' + feature.id + '?population_genotypes=1;content-type=application/json',
|
|
84
|
+
dataType : 'json',
|
|
85
|
+
success : function (data) {
|
|
86
|
+
var populationGenotypes = $.grep(data.population_genotypes, function (pop) { return /1000GENOMES.+ALL/.test(pop.population); }); // Only considering 1000 Genomes: ALL population
|
|
87
|
+
var frequencies = {};
|
|
88
|
+
var pop, i, j;
|
|
89
|
+
|
|
90
|
+
if (populationGenotypes.length) {
|
|
91
|
+
for (i = 0; i < populationGenotypes.length; i++) {
|
|
92
|
+
pop = populationGenotypes[i];
|
|
93
|
+
pop.frequency = parseFloat(pop.frequency, 10);
|
|
94
|
+
pop.count = parseInt(pop.count, 10);
|
|
95
|
+
|
|
96
|
+
frequencies[pop.population] = frequencies[pop.population] || [];
|
|
97
|
+
frequencies[pop.population].push(pop);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (i in frequencies) {
|
|
101
|
+
frequencies[i].sort(function (a, b) { return a.count < b.count; });
|
|
102
|
+
|
|
103
|
+
pop = {
|
|
104
|
+
title : i + ' population genotypes',
|
|
105
|
+
Genotype : [ 'Frequency', 'Count' ],
|
|
106
|
+
start : false,
|
|
107
|
+
end : false
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
for (j in frequencies[i]) {
|
|
111
|
+
pop[frequencies[i][j].genotype] = [ (frequencies[i][j].frequency * 100).toFixed(2) + '%', frequencies[i][j].count ];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
menu.push(pop);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
pop = {
|
|
118
|
+
start : false,
|
|
119
|
+
end : false
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
pop['<a href="https://www.ensembl.org/Homo_sapiens/Variation/Population?v=' + feature.id + '" target="_blank">See all population genotypes</a>'] = '';
|
|
123
|
+
|
|
124
|
+
menu.push(pop);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
deferred.resolve(menu);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return deferred;
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// Different settings for different zoom level
|
|
135
|
+
5000: { // more than 5k
|
|
136
|
+
bump: false
|
|
137
|
+
},
|
|
138
|
+
1: { // > 1 base-pair, but less then 5k
|
|
139
|
+
bump: true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
});
|