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,303 @@
|
|
|
1
|
+
Genoverse.Track.Model = Base.extend({
|
|
2
|
+
dataType : 'json',
|
|
3
|
+
allData : false,
|
|
4
|
+
dataBuffer : undefined, // e.g. { start: 0, end: 0 } - basepairs to extend data region for, when getting data from the origin
|
|
5
|
+
xhrFields : undefined,
|
|
6
|
+
url : undefined,
|
|
7
|
+
urlParams : undefined, // hash of URL params
|
|
8
|
+
urlParamsGenerator : function () { return {}; }, // function to return URL params (only used if urlParams property is falsy)
|
|
9
|
+
data : undefined, // if defined, will be used instead of fetching data from a source
|
|
10
|
+
dataRequestLimit : undefined, // if defined, multiple requests will be made by getData if the region size exceeds its value
|
|
11
|
+
showServerErrors : false, // if true, error messages return from the server by getData requests will be shown on the track
|
|
12
|
+
|
|
13
|
+
constructor: function (properties) {
|
|
14
|
+
$.extend(this, properties);
|
|
15
|
+
Genoverse.wrapFunctions(this);
|
|
16
|
+
this.init();
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
init: function (reset) {
|
|
20
|
+
this.setDefaults(reset);
|
|
21
|
+
|
|
22
|
+
if (reset) {
|
|
23
|
+
for (var i in this.featuresById) {
|
|
24
|
+
delete this.featuresById[i].position;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!reset || this.data) {
|
|
29
|
+
delete this.dataRangesByChr;
|
|
30
|
+
delete this.featuresByChr;
|
|
31
|
+
this.featuresById = {};
|
|
32
|
+
this.setChrProps();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.dataLoading = []; // tracks incomplete requests for data
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
setDefaults: function (reset) {
|
|
39
|
+
this.dataBuffer = this.dataBuffer || { start: 0, end: 0 }; // basepairs to extend data region for, when getting data from the origin
|
|
40
|
+
this.urlParams = this.urlParams || this.urlParamsGenerator(); // hash of URL params
|
|
41
|
+
this.xhrFields = this.xhrFields || {};
|
|
42
|
+
|
|
43
|
+
this.dataBufferStart = this.dataBuffer.start; // Remember original dataBuffer.start, since dataBuffer.start is updated based on browser scale, in setLabelBuffer
|
|
44
|
+
|
|
45
|
+
if (!this._url) {
|
|
46
|
+
this._url = this.url; // Remember original url
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (reset && !this.url && this._url) {
|
|
50
|
+
this.url = this._url;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
setChrProps: function () {
|
|
55
|
+
var chr = this.browser.chr;
|
|
56
|
+
|
|
57
|
+
this.dataRangesByChr = this.dataRangesByChr || {};
|
|
58
|
+
this.featuresByChr = this.featuresByChr || {};
|
|
59
|
+
|
|
60
|
+
this.dataRangesByChr[chr] = this.dataRangesByChr[chr] || new RTree();
|
|
61
|
+
this.featuresByChr[chr] = this.featuresByChr[chr] || new RTree();
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
features : function (chr) { return this.featuresByChr[chr]; },
|
|
65
|
+
dataRanges : function (chr) { return this.dataRangesByChr[chr]; },
|
|
66
|
+
|
|
67
|
+
parseURL: function (chr, start, end, url) {
|
|
68
|
+
if (this.allData) {
|
|
69
|
+
start = 1;
|
|
70
|
+
end = this.browser.getChromosomeSize(chr);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (url || this.url).replace(/__ASSEMBLY__/, this.browser.assembly).replace(/__CHR__/, chr).replace(/__START__/, start).replace(/__END__/, end);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
setLabelBuffer: function (buffer) {
|
|
77
|
+
this.dataBuffer.start = Math.max(this.dataBufferStart, buffer);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
getData: function (chr, start, end, done) {
|
|
81
|
+
start = Math.max(1, start);
|
|
82
|
+
end = Math.min(this.browser.getChromosomeSize(chr), end);
|
|
83
|
+
|
|
84
|
+
var deferred = $.Deferred();
|
|
85
|
+
|
|
86
|
+
if (typeof this.data !== 'undefined') {
|
|
87
|
+
this.receiveData(typeof this.data.sort === 'function' ? this.data.sort(function (a, b) { return a.start - b.start; }) : this.data, chr, start, end);
|
|
88
|
+
return deferred.resolveWith(this);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
var model = this;
|
|
92
|
+
var bins = [];
|
|
93
|
+
var length = end - start + 1;
|
|
94
|
+
|
|
95
|
+
if (!this.url) {
|
|
96
|
+
return deferred.resolveWith(this);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.dataRequestLimit && length > this.dataRequestLimit) {
|
|
100
|
+
var i = Math.ceil(length / this.dataRequestLimit);
|
|
101
|
+
|
|
102
|
+
while (i--) {
|
|
103
|
+
bins.push([ start, i ? start += this.dataRequestLimit - 1 : end ]);
|
|
104
|
+
start++;
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
bins.push([ start, end ]);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
$.when.apply($, $.map(bins, function (bin) {
|
|
111
|
+
var request = $.ajax({
|
|
112
|
+
url : model.parseURL(chr, bin[0], bin[1]),
|
|
113
|
+
data : model.urlParams,
|
|
114
|
+
dataType : model.dataType,
|
|
115
|
+
context : model,
|
|
116
|
+
xhrFields : model.xhrFields,
|
|
117
|
+
success : function (data) {
|
|
118
|
+
this.receiveData(data, chr, bin[0], bin[1]);
|
|
119
|
+
},
|
|
120
|
+
error: function (xhr, statusText) {
|
|
121
|
+
this.track.controller.showError(
|
|
122
|
+
this.showServerErrors && (xhr.responseJSON || {}).message
|
|
123
|
+
? xhr.responseJSON.message
|
|
124
|
+
: statusText + ' while getting the data, see console for more details',
|
|
125
|
+
arguments
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
complete: function (xhr) {
|
|
129
|
+
this.dataLoading = $.grep(this.dataLoading, function (t) { return xhr !== t; });
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
request.coords = [ chr, bin[0], bin[1] ]; // store actual chr, start and end on the request, in case they are needed
|
|
134
|
+
|
|
135
|
+
if (typeof done === 'function') {
|
|
136
|
+
request.done(done);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
model.dataLoading.push(request);
|
|
140
|
+
|
|
141
|
+
return request;
|
|
142
|
+
})).done(function () { deferred.resolveWith(model); });
|
|
143
|
+
|
|
144
|
+
return deferred;
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
receiveData: function (data, chr, start, end) {
|
|
148
|
+
start = Math.max(start, 1);
|
|
149
|
+
end = Math.min(end, this.browser.getChromosomeSize(chr));
|
|
150
|
+
|
|
151
|
+
this.setDataRange(chr, start, end);
|
|
152
|
+
this.parseData(data, chr, start, end);
|
|
153
|
+
|
|
154
|
+
if (this.allData) {
|
|
155
|
+
this.url = false;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* parseData(data, chr, start, end) - parse raw data from the data source (e.g. online web service)
|
|
161
|
+
* extract features and insert it into the internal features storage (RTree)
|
|
162
|
+
*
|
|
163
|
+
* >> data - raw data from the data source (e.g. ajax response)
|
|
164
|
+
* >> chr - chromosome of the data
|
|
165
|
+
* >> start - start location of the data
|
|
166
|
+
* >> end - end location of the data
|
|
167
|
+
* << nothing
|
|
168
|
+
*
|
|
169
|
+
* every feature extracted this routine must construct a hash with at least 3 values:
|
|
170
|
+
* {
|
|
171
|
+
* id : [unique feature id, string],
|
|
172
|
+
* start : [chromosomal start position, integer],
|
|
173
|
+
* end : [chromosomal end position, integer],
|
|
174
|
+
* [other optional key/value pairs]
|
|
175
|
+
* }
|
|
176
|
+
*
|
|
177
|
+
* and call this.insertFeature(feature)
|
|
178
|
+
*/
|
|
179
|
+
parseData: function (data, chr, start) { // end is also passed in, but not used in this case
|
|
180
|
+
var feature;
|
|
181
|
+
|
|
182
|
+
// Example of parseData function when data is an array of hashes like { start: ..., end: ... }
|
|
183
|
+
for (var i = 0; i < data.length; i++) {
|
|
184
|
+
feature = data[i];
|
|
185
|
+
|
|
186
|
+
feature.chr = feature.chr || chr;
|
|
187
|
+
feature.sort = start + i;
|
|
188
|
+
|
|
189
|
+
this.insertFeature(feature);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
updateData: function (data) {
|
|
194
|
+
this.data = data;
|
|
195
|
+
this.track.reset();
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
setDataRange: function (chr, start, end) {
|
|
199
|
+
if (this.allData) {
|
|
200
|
+
start = 1;
|
|
201
|
+
end = this.browser.getChromosomeSize(chr);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.dataRanges(chr).insert({ x: start, w: end - start + 1, y: 0, h: 1 }, [ start, end ]);
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
checkDataRange: function (chr, start, end) {
|
|
208
|
+
start = Math.max(1, start);
|
|
209
|
+
end = Math.min(this.browser.getChromosomeSize(chr), end);
|
|
210
|
+
|
|
211
|
+
var ranges = this.dataRanges(chr).search({ x: start, w: end - start + 1, y: 0, h: 1 }).sort(function (a, b) { return a[0] - b[0]; });
|
|
212
|
+
|
|
213
|
+
if (!ranges.length) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
var s = ranges.length === 1 ? ranges[0][0] : 9e99;
|
|
218
|
+
var e = ranges.length === 1 ? ranges[0][1] : -9e99;
|
|
219
|
+
|
|
220
|
+
for (var i = 0; i < ranges.length - 1; i++) {
|
|
221
|
+
// s0 <= s1 && ((e0 >= e1) || (e0 + 1 >= s1))
|
|
222
|
+
if (ranges[i][0] <= ranges[i + 1][0] && ((ranges[i][1] >= ranges[i + 1][1]) || (ranges[i][1] + 1 >= ranges[i + 1][0]))) {
|
|
223
|
+
s = Math.min(s, ranges[i][0]);
|
|
224
|
+
e = Math.max(e, ranges[i][1], ranges[i + 1][1]);
|
|
225
|
+
} else {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return start >= s && end <= e;
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
insertFeature: function (feature) {
|
|
234
|
+
if (!feature.chr) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Make sure we have a unique ID, this method is not efficient, so better supply your own id
|
|
239
|
+
if (!feature.id) {
|
|
240
|
+
feature.id = feature.ID || this.hashCode(JSON.stringify($.extend({}, feature, { sort: '' }))); // sort is dependant on the browser's region, so will change on zoom
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
var features = this.features(feature.chr);
|
|
244
|
+
|
|
245
|
+
if (features && !this.featuresById[feature.id]) {
|
|
246
|
+
if (feature.subFeatures) {
|
|
247
|
+
feature.subFeatures.sort(function (a, b) { return a.start - b.start; });
|
|
248
|
+
|
|
249
|
+
for (var i = 0; i < feature.subFeatures.length; i++) {
|
|
250
|
+
feature.subFeatures[i].start = Math.min(Math.max(feature.subFeatures[i].start, feature.start), feature.end);
|
|
251
|
+
feature.subFeatures[i].end = Math.max(Math.min(feature.subFeatures[i].end, feature.end), feature.start);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Add "fake" sub-features at the start and end of the feature - this will allow joins to be drawn when there are no sub-features in the current region.
|
|
255
|
+
feature.subFeatures.unshift({ start: feature.start, end: feature.start, fake: true });
|
|
256
|
+
feature.subFeatures.push({ start: feature.end, end: feature.end, fake: true });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
features.insert({ x: feature.start, y: 0, w: feature.end - feature.start + 1, h: 1 }, feature);
|
|
260
|
+
this.featuresById[feature.id] = feature;
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
findFeatures: function (chr, start, end) {
|
|
265
|
+
var features = this.features(chr).search({ x: start - this.dataBuffer.start, y: 0, w: end - start + this.dataBuffer.start + this.dataBuffer.end + 1, h: 1 });
|
|
266
|
+
var filters = this.prop('featureFilters') || [];
|
|
267
|
+
|
|
268
|
+
for (var i = 0; i < filters.length; i++) {
|
|
269
|
+
features = $.grep(features, $.proxy(filters[i], this));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return this.sortFeatures(features);
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
sortFeatures: function (features) {
|
|
276
|
+
return features.sort(function (a, b) { return a.sort - b.sort; });
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
abort: function () {
|
|
280
|
+
for (var i = 0; i < this.dataLoading.length; i++) {
|
|
281
|
+
this.dataLoading[i].abort();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
this.dataLoading = [];
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
hashCode: function (string) {
|
|
288
|
+
var hash = 0;
|
|
289
|
+
var c;
|
|
290
|
+
|
|
291
|
+
if (!string.length) {
|
|
292
|
+
return hash;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
for (var i = 0; i < string.length; i++) {
|
|
296
|
+
c = string.charCodeAt(i);
|
|
297
|
+
hash = ((hash << 5) - hash) + c;
|
|
298
|
+
hash &= hash; // Convert to 32bit integer
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return '' + hash;
|
|
302
|
+
}
|
|
303
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Genoverse.Track.View.Gene.Ensembl = Genoverse.Track.View.Gene.extend({
|
|
2
|
+
setFeatureColor: function (feature) {
|
|
3
|
+
var processedTranscript = {
|
|
4
|
+
'sense_intronic' : 1,
|
|
5
|
+
'sense_overlapping' : 1,
|
|
6
|
+
'processed_transcript' : 1,
|
|
7
|
+
'nonsense_mediated_decay' : 1,
|
|
8
|
+
'non_stop_decay' : 1,
|
|
9
|
+
'antisense' : 1,
|
|
10
|
+
'retained_intron' : 1,
|
|
11
|
+
'tec' : 1,
|
|
12
|
+
'non_coding' : 1,
|
|
13
|
+
'ambiguous_orf' : 1,
|
|
14
|
+
'disrupted_domain' : 1,
|
|
15
|
+
'3prime_overlapping_ncrna' : 1
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
feature.color = '#000000';
|
|
19
|
+
|
|
20
|
+
if (feature.logic_name.indexOf('ensembl_havana') === 0) {
|
|
21
|
+
feature.color = '#CD9B1D';
|
|
22
|
+
feature.labelColor = '#B78000';
|
|
23
|
+
feature.legend = 'Merged Ensembl/Havana';
|
|
24
|
+
} else if (processedTranscript[feature.biotype.toLowerCase()]) {
|
|
25
|
+
feature.color = '#0000FF';
|
|
26
|
+
feature.legend = 'Processed transcript';
|
|
27
|
+
} else if (feature.biotype === 'protein_coding') {
|
|
28
|
+
feature.color = '#A00000';
|
|
29
|
+
feature.legend = 'Protein coding';
|
|
30
|
+
} else if (feature.biotype.indexOf('pseudogene') > -1) {
|
|
31
|
+
feature.color = '#666666';
|
|
32
|
+
feature.legend = 'Pseudogene';
|
|
33
|
+
} else if (/rna/i.test(feature.biotype) || feature.biotype === 'ribozyme') {
|
|
34
|
+
feature.color = '#8B668B';
|
|
35
|
+
feature.legend = 'RNA gene';
|
|
36
|
+
} else if (/^tr_.+_gene$/i.test(feature.biotype)) {
|
|
37
|
+
feature.color = '#CD6600';
|
|
38
|
+
feature.legend = 'TR gene';
|
|
39
|
+
} else if (/^ig_.+_gene$/i.test(feature.biotype)) {
|
|
40
|
+
feature.color = '#8B4500';
|
|
41
|
+
feature.legend = 'IG gene';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
feature.labelColor = feature.labelColor || feature.color;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Genoverse.Track.View.SequenceVariation = Genoverse.Track.View.Sequence.extend({
|
|
2
|
+
featureHeight : 15,
|
|
3
|
+
featureMargin : { top: 0, right: 0, bottom: 4, left: 0 },
|
|
4
|
+
bump : true,
|
|
5
|
+
showLegend : false,
|
|
6
|
+
|
|
7
|
+
positionFeature: function (feature, params) {
|
|
8
|
+
var position = feature.position[params.scale];
|
|
9
|
+
|
|
10
|
+
if (feature.alt_allele) {
|
|
11
|
+
if (!position.positioned) {
|
|
12
|
+
position.reference = { end: position.start + feature.ref_allele.length * params.scale };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
position.reference.x = position.reference.end - params.scaledStart;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
this.base(feature, params);
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
bumpFeature: function (bounds, feature) {
|
|
22
|
+
if (feature.alt_allele) {
|
|
23
|
+
this.base.apply(this, arguments);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
draw: function (features, featureContext, labelContext, scale) {
|
|
28
|
+
var drawing = { seq: [], snv: [] };
|
|
29
|
+
|
|
30
|
+
for (var i = 0; i < features.length; i++) {
|
|
31
|
+
drawing[features[i].alt_allele ? 'snv' : 'seq'].push(features[i]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.base(drawing.seq, featureContext, labelContext, scale);
|
|
35
|
+
this.highlightSNVs(drawing.snv, featureContext, scale);
|
|
36
|
+
this.base(drawing.snv, featureContext, labelContext, scale);
|
|
37
|
+
this.outlineSNVs(drawing.snv, featureContext, scale); // Redraw the outline for SNVs, since the feature will have been drawn on top of some of the outline created by highlightSNVs
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
highlightSNVs: function (features, context, scale) {
|
|
41
|
+
var position, positionX, positionY;
|
|
42
|
+
|
|
43
|
+
for (var i = 0; i < features.length; i++) {
|
|
44
|
+
position = features[i].position[scale];
|
|
45
|
+
positionX = [ position.X, position.reference.x, position.X + position.width ];
|
|
46
|
+
|
|
47
|
+
if (positionX[2] < 0 || positionX[0] > this.width) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (positionX[0] < 0 || positionX[2] > this.width) {
|
|
52
|
+
this.truncateForDrawing(positionX);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
positionY = [ 0, position.Y - this.featureMargin.bottom / 2, position.Y, position.Y + this.featureHeight ];
|
|
56
|
+
|
|
57
|
+
if (!features[i].highlightColor) {
|
|
58
|
+
this.setHighlightColor(features[i]);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
context.strokeStyle = context.fillStyle = features[i].highlightColor;
|
|
62
|
+
context.lineWidth = 2;
|
|
63
|
+
|
|
64
|
+
context.beginPath();
|
|
65
|
+
context.moveTo(positionX[0], positionY[0]);
|
|
66
|
+
context.lineTo(positionX[1], positionY[0]);
|
|
67
|
+
context.lineTo(positionX[1], positionY[1]);
|
|
68
|
+
context.lineTo(positionX[2], positionY[2]);
|
|
69
|
+
context.lineTo(positionX[2], positionY[3]);
|
|
70
|
+
context.lineTo(positionX[0], positionY[3]);
|
|
71
|
+
context.closePath();
|
|
72
|
+
context.stroke();
|
|
73
|
+
|
|
74
|
+
context.lineWidth = 1;
|
|
75
|
+
context.globalAlpha = 0.5;
|
|
76
|
+
|
|
77
|
+
context.fill();
|
|
78
|
+
|
|
79
|
+
context.globalAlpha = 1;
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
outlineSNVs: function (features, context, scale) {
|
|
84
|
+
var position, positionX, positionY;
|
|
85
|
+
|
|
86
|
+
for (var i = 0; i < features.length; i++) {
|
|
87
|
+
position = features[i].position[scale];
|
|
88
|
+
positionX = [ position.X, position.X + position.width ];
|
|
89
|
+
positionY = [ position.Y, position.Y + this.featureHeight ];
|
|
90
|
+
|
|
91
|
+
context.strokeStyle = features[i].highlightColor;
|
|
92
|
+
|
|
93
|
+
context.lineWidth = 2;
|
|
94
|
+
|
|
95
|
+
context.beginPath();
|
|
96
|
+
context.moveTo(positionX[1], positionY[0]);
|
|
97
|
+
context.lineTo(positionX[1], positionY[1]);
|
|
98
|
+
context.lineTo(positionX[0], positionY[1]);
|
|
99
|
+
context.lineTo(positionX[0], positionY[0]);
|
|
100
|
+
context.stroke();
|
|
101
|
+
|
|
102
|
+
context.lineWidth = 1;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
truncateForDrawing: function (positionX) {
|
|
107
|
+
for (var i in positionX) {
|
|
108
|
+
positionX[i] = Math.min(Math.max(positionX[i], -1), this.width + 1);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
setHighlightColor: function (feature) {
|
|
113
|
+
feature.highlightColor = feature.alt_allele === '-' || feature.alt_allele.length < feature.ref_allele.length ? '#D31D00' : '#1DD300';
|
|
114
|
+
}
|
|
115
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Genoverse.Track.View.Sequence = Genoverse.Track.View.extend({
|
|
2
|
+
featureMargin : { top: 0, right: 0, bottom: 0, left: 0 },
|
|
3
|
+
colors : { 'default': '#CCCCCC', A: '#73E973', T: '#DE4C61', G: '#FFFF77', C: '#688EC0' },
|
|
4
|
+
labelColors : { 'default': '#000000', T: '#FFFFFF', C: '#FFFFFF' },
|
|
5
|
+
labels : 'overlay',
|
|
6
|
+
|
|
7
|
+
setDefaults: function () {
|
|
8
|
+
this.base.apply(this, arguments);
|
|
9
|
+
|
|
10
|
+
var lowerCase = this.prop('lowerCase');
|
|
11
|
+
var key;
|
|
12
|
+
|
|
13
|
+
this.labelYOffset = typeof this.labelYOffset === 'number' ? this.labelYOffset : (this.featureHeight + 1) / 2;
|
|
14
|
+
this.widestLabel = typeof this.widestLabel === 'string' ? this.widestLabel : lowerCase ? 'g' : 'G';
|
|
15
|
+
this.labelWidth = {};
|
|
16
|
+
|
|
17
|
+
this.labelWidth[this.widestLabel] = Math.ceil(this.context.measureText(this.widestLabel).width) + 1;
|
|
18
|
+
|
|
19
|
+
if (lowerCase) {
|
|
20
|
+
for (key in this.colors) {
|
|
21
|
+
this.colors[key.toLowerCase()] = this.colors[key];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (key in this.labelColors) {
|
|
25
|
+
this.labelColors[key.toLowerCase()] = this.labelColors[key];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
draw: function (features, featureContext, labelContext, scale) {
|
|
31
|
+
featureContext.textBaseline = 'middle';
|
|
32
|
+
featureContext.textAlign = 'center';
|
|
33
|
+
|
|
34
|
+
var width = Math.max(scale, this.minScaledWidth);
|
|
35
|
+
|
|
36
|
+
for (var i = 0; i < features.length; i++) {
|
|
37
|
+
this.drawSequence(features[i], featureContext, scale, width);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
drawSequence: function (feature, context, scale, width) {
|
|
42
|
+
var drawLabels = this.labelWidth[this.widestLabel] < width - 1;
|
|
43
|
+
var start, bp;
|
|
44
|
+
|
|
45
|
+
for (var i = 0; i < feature.sequence.length; i++) {
|
|
46
|
+
start = feature.position[scale].X + i * scale;
|
|
47
|
+
|
|
48
|
+
if (start < -scale || start > context.canvas.width) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
bp = feature.sequence.charAt(i);
|
|
53
|
+
|
|
54
|
+
context.fillStyle = this.colors[bp] || this.colors.default;
|
|
55
|
+
context.fillRect(start, feature.position[scale].Y, width, this.featureHeight);
|
|
56
|
+
|
|
57
|
+
if (drawLabels) {
|
|
58
|
+
context.fillStyle = this.labelColors[bp] || this.labelColors.default;
|
|
59
|
+
context.fillText(bp, start + (width / 2), feature.position[scale].Y + this.labelYOffset);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Genoverse.Track.View.Transcript.Ensembl = Genoverse.Track.View.Transcript.extend({
|
|
2
|
+
setFeatureColor: function (feature) {
|
|
3
|
+
Genoverse.Track.View.Gene.Ensembl.prototype.setFeatureColor(feature);
|
|
4
|
+
|
|
5
|
+
for (var i = 0; i < (feature.subFeatures || []).length; i++) {
|
|
6
|
+
if (feature.subFeatures[i].utr) {
|
|
7
|
+
feature.subFeatures[i].color = false;
|
|
8
|
+
feature.subFeatures[i].borderColor = feature.color;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Genoverse.Track.View.Transcript = Genoverse.Track.View.extend({
|
|
2
|
+
featureHeight : 12,
|
|
3
|
+
utrHeight : 7,
|
|
4
|
+
labels : true,
|
|
5
|
+
repeatLabels : true,
|
|
6
|
+
bump : true,
|
|
7
|
+
subFeatureJoinStyle : 'curve',
|
|
8
|
+
|
|
9
|
+
scaleFeatures: function (features, scale) {
|
|
10
|
+
var subFeatures, j;
|
|
11
|
+
|
|
12
|
+
for (var i = 0; i < features.length; i++) {
|
|
13
|
+
subFeatures = features[i].subFeatures || [];
|
|
14
|
+
|
|
15
|
+
if (subFeatures.length) {
|
|
16
|
+
for (j = 0; j < subFeatures.length; j++) {
|
|
17
|
+
if (subFeatures[j].utr) {
|
|
18
|
+
subFeatures[j].height = this.utrHeight;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
features[i].height = Math.max.apply(Math, subFeatures.map(function (c) { return c.fake ? 0 : c.height || 0; }).concat(this.featureHeight));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return this.base(features, scale);
|
|
27
|
+
}
|
|
28
|
+
});
|