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.
Files changed (148) hide show
  1. package/.eslintrc.js +197 -0
  2. package/.github/workflows/test.yml +24 -0
  3. package/LICENSE.TXT +24 -0
  4. package/README.md +11 -0
  5. package/css/controlPanel.css +200 -0
  6. package/css/fileDrop.css +22 -0
  7. package/css/font-awesome.css +3 -0
  8. package/css/fullscreen.css +19 -0
  9. package/css/genoverse.css +466 -0
  10. package/css/karyotype.css +85 -0
  11. package/css/resizer.css +36 -0
  12. package/css/tooltips.css +26 -0
  13. package/css/trackControls.css +111 -0
  14. package/expanded.html +120 -0
  15. package/fontawesome/css/fontawesome.min.css +5 -0
  16. package/fontawesome/css/regular.min.css +5 -0
  17. package/fontawesome/css/solid.min.css +5 -0
  18. package/fontawesome/webfonts/fa-brands-400.ttf +0 -0
  19. package/fontawesome/webfonts/fa-brands-400.woff +0 -0
  20. package/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
  21. package/fontawesome/webfonts/fa-regular-400.ttf +0 -0
  22. package/fontawesome/webfonts/fa-regular-400.woff +0 -0
  23. package/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
  24. package/fontawesome/webfonts/fa-solid-900.ttf +0 -0
  25. package/fontawesome/webfonts/fa-solid-900.woff +0 -0
  26. package/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
  27. package/help.pdf +0 -0
  28. package/i/sort_handle.png +0 -0
  29. package/index.html +68 -0
  30. package/index.js +83 -0
  31. package/jest.config.js +4 -0
  32. package/js/Genoverse.js +1681 -0
  33. package/js/Track/Controller/Sequence.js +17 -0
  34. package/js/Track/Controller/Stranded.js +73 -0
  35. package/js/Track/Controller.js +620 -0
  36. package/js/Track/Model/File/BAM.js +44 -0
  37. package/js/Track/Model/File/BED.js +116 -0
  38. package/js/Track/Model/File/GFF.js +40 -0
  39. package/js/Track/Model/File/VCF.js +101 -0
  40. package/js/Track/Model/File/WIG.js +67 -0
  41. package/js/Track/Model/File.js +36 -0
  42. package/js/Track/Model/Gene/Ensembl.js +22 -0
  43. package/js/Track/Model/Gene.js +5 -0
  44. package/js/Track/Model/Sequence/Ensembl.js +4 -0
  45. package/js/Track/Model/Sequence/Fasta.js +60 -0
  46. package/js/Track/Model/Sequence.js +50 -0
  47. package/js/Track/Model/SequenceVariation.js +41 -0
  48. package/js/Track/Model/Stranded.js +28 -0
  49. package/js/Track/Model/Transcript/Ensembl.js +67 -0
  50. package/js/Track/Model/Transcript.js +5 -0
  51. package/js/Track/Model.js +303 -0
  52. package/js/Track/View/Gene/Ensembl.js +46 -0
  53. package/js/Track/View/Gene.js +6 -0
  54. package/js/Track/View/Sequence/Variation.js +115 -0
  55. package/js/Track/View/Sequence.js +63 -0
  56. package/js/Track/View/Transcript/Ensembl.js +12 -0
  57. package/js/Track/View/Transcript.js +28 -0
  58. package/js/Track/View.js +566 -0
  59. package/js/Track/library/Chromosome.js +145 -0
  60. package/js/Track/library/File/BAM.js +30 -0
  61. package/js/Track/library/File/BED.js +24 -0
  62. package/js/Track/library/File/BIGBED.js +47 -0
  63. package/js/Track/library/File/BIGWIG.js +52 -0
  64. package/js/Track/library/File/GFF.js +9 -0
  65. package/js/Track/library/File/VCF.js +71 -0
  66. package/js/Track/library/File/WIG.js +5 -0
  67. package/js/Track/library/File.js +10 -0
  68. package/js/Track/library/Gene.js +37 -0
  69. package/js/Track/library/Graph/Bar.js +235 -0
  70. package/js/Track/library/Graph/Line.js +296 -0
  71. package/js/Track/library/Graph.js +355 -0
  72. package/js/Track/library/HighlightRegion.js +292 -0
  73. package/js/Track/library/Legend.js +224 -0
  74. package/js/Track/library/Scalebar.js +227 -0
  75. package/js/Track/library/Scaleline.js +91 -0
  76. package/js/Track/library/Static.js +78 -0
  77. package/js/Track/library/dbSNP.js +142 -0
  78. package/js/Track.js +632 -0
  79. package/js/genomes/grch37.js +990 -0
  80. package/js/genomes/grch38.js +990 -0
  81. package/js/genoverse.min.js +2 -0
  82. package/js/genoverse.min.js.map +1 -0
  83. package/js/lib/BWReader.js +578 -0
  84. package/js/lib/Base.js +145 -0
  85. package/js/lib/VCFReader.js +286 -0
  86. package/js/lib/dalliance/js/bam.js +494 -0
  87. package/js/lib/dalliance/js/bin.js +185 -0
  88. package/js/lib/dalliance/js/das.js +749 -0
  89. package/js/lib/dalliance/js/utils.js +370 -0
  90. package/js/lib/dalliance-lib.js +3594 -0
  91. package/js/lib/dalliance-lib.min.js +68 -0
  92. package/js/lib/jDataView.js +2 -0
  93. package/js/lib/jParser.js +192 -0
  94. package/js/lib/jquery-ui.js +8 -0
  95. package/js/lib/jquery.js +2 -0
  96. package/js/lib/jquery.mousehold.js +53 -0
  97. package/js/lib/jquery.mousewheel.js +84 -0
  98. package/js/lib/jquery.tipsy.js +258 -0
  99. package/js/lib/rtree.js +1 -0
  100. package/js/plugins/controlPanel.js +395 -0
  101. package/js/plugins/fileDrop.js +62 -0
  102. package/js/plugins/focusRegion.js +12 -0
  103. package/js/plugins/fullscreen.js +77 -0
  104. package/js/plugins/karyotype.js +210 -0
  105. package/js/plugins/resizer.js +45 -0
  106. package/js/plugins/tooltips.js +94 -0
  107. package/js/plugins/trackControls.js +143 -0
  108. package/package.json +43 -0
  109. package/test/View/__snapshots__/render-bar-graph.test.js.snap +111 -0
  110. package/test/View/__snapshots__/render-blocks.test.js.snap +105 -0
  111. package/test/View/__snapshots__/render-chromosome.test.js.snap +5 -0
  112. package/test/View/__snapshots__/render-highlights.test.js.snap +73 -0
  113. package/test/View/__snapshots__/render-insert-variants.test.js.snap +9 -0
  114. package/test/View/__snapshots__/render-labels.test.js.snap +241 -0
  115. package/test/View/__snapshots__/render-legends.test.js.snap +13 -0
  116. package/test/View/__snapshots__/render-line-graph.test.js.snap +349 -0
  117. package/test/View/__snapshots__/render-scalebar.test.js.snap +49 -0
  118. package/test/View/__snapshots__/render-scaleline.test.js.snap +31 -0
  119. package/test/View/__snapshots__/render-sequence.test.js.snap +23 -0
  120. package/test/View/__snapshots__/render-stranded.test.js.snap +5 -0
  121. package/test/View/__snapshots__/render-transcripts.test.js.snap +193 -0
  122. package/test/View/render-bar-graph.test.js +87 -0
  123. package/test/View/render-blocks.test.js +171 -0
  124. package/test/View/render-chromosome.test.js +40 -0
  125. package/test/View/render-highlights.test.js +67 -0
  126. package/test/View/render-insert-variants.test.js +11 -0
  127. package/test/View/render-labels.test.js +266 -0
  128. package/test/View/render-legends.test.js +31 -0
  129. package/test/View/render-line-graph.test.js +169 -0
  130. package/test/View/render-scalebar.test.js +36 -0
  131. package/test/View/render-scaleline.test.js +28 -0
  132. package/test/View/render-sequence.test.js +49 -0
  133. package/test/View/render-stranded.test.js +10 -0
  134. package/test/View/render-transcripts.test.js +165 -0
  135. package/test/create-and-destroy.test.js +63 -0
  136. package/test/track-ordering.test.js +514 -0
  137. package/test/track_config/__snapshots__/config-settings.test.js.snap +23 -0
  138. package/test/track_config/config-settings.test.js +321 -0
  139. package/test/track_config/zoom-level-settings.test.js +98 -0
  140. package/test/utils.js +80 -0
  141. package/utils/createGenome.js +52 -0
  142. package/utils/devServer.js +36 -0
  143. package/utils/expandedTemplate.html +46 -0
  144. package/utils/git-hooks/post-commit +9 -0
  145. package/utils/git-hooks/pre-commit +7 -0
  146. package/utils/git-hooks/setup +6 -0
  147. package/utils/makeExpanded.js +19 -0
  148. 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,6 @@
1
+ Genoverse.Track.View.Gene = Genoverse.Track.View.extend({
2
+ featureHeight : 5,
3
+ labels : true,
4
+ repeatLabels : true,
5
+ bump : true
6
+ });
@@ -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
+ });