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,116 @@
1
+ Genoverse.Track.Model.File.BED = Genoverse.Track.Model.File.extend({
2
+ parseData: function (data, chr) {
3
+ var lines = typeof data === 'string' ? data.split('\n') : data;
4
+ var thinHeight = this.prop('thinHeight');
5
+ var thickHeight = this.prop('thickHeight');
6
+ var fields, len, feature, subfeatures, subfeature, blockSizes, blockStarts, j, thinFeature, thinFeature1, thinFeature2, thickFeature;
7
+
8
+ function filterNumber(n) {
9
+ return !isNaN(n);
10
+ }
11
+
12
+ for (var i = 0; i < lines.length; i++) {
13
+ fields = lines[i].split('\t').filter(function (f) { return f; });
14
+
15
+ if (fields.length < 3 || fields[0] === 'track' || fields[0] === 'browser') {
16
+ continue;
17
+ }
18
+
19
+ len = fields.length;
20
+
21
+ if (fields[0] === String(chr) || fields[0].toLowerCase() === 'chr' + chr || fields[0].match('[^1-9]' + chr + '$')) {
22
+ feature = {
23
+ chr : chr,
24
+ start : parseInt(fields[1], 10) + 1,
25
+ end : parseInt(fields[2], 10),
26
+ name : fields[3],
27
+ color : '#000000',
28
+ originalFeature : fields
29
+ };
30
+
31
+ if (len > 3) { feature.score = parseFloat(fields[4], 10); }
32
+ if (len > 5) { feature.strand = fields[5]; }
33
+
34
+ if (len > 7) {
35
+ feature.thickStart = parseInt(fields[6], 10) + 1;
36
+ feature.thickEnd = parseInt(fields[7], 10);
37
+ feature.drawThick = fields[6] !== fields[7];
38
+ }
39
+
40
+ if (fields[8]) {
41
+ feature.color = 'rgb(' + fields[8] + ')';
42
+ } else {
43
+ feature.color = this.scoreColor(isNaN(feature.score) ? 1000 : feature.score);
44
+ }
45
+
46
+ if (len === 12) { // subfeatures present
47
+ feature.blockCount = parseInt(fields[9], 10);
48
+
49
+ subfeatures = [];
50
+ blockSizes = fields[10].split(',').filter(filterNumber);
51
+ blockStarts = fields[11].split(',').filter(filterNumber);
52
+
53
+ for (j = 0; j < blockSizes.length; j++) {
54
+ subfeature = {
55
+ start : feature.start + parseInt(blockStarts[j], 10),
56
+ height : thinHeight // if subfeature lies entirely left / right to [ thickStart, thickEnd ]
57
+ };
58
+
59
+ subfeature.end = subfeature.start + parseInt(blockSizes[j], 10) - 1;
60
+
61
+ if (feature.drawThick && subfeature.start <= feature.thickEnd && subfeature.end >= feature.thickStart) {
62
+ // some kind of an overlap for sure
63
+ if (subfeature.start >= feature.thickStart && subfeature.end <= feature.thickEnd) {
64
+ // subfeature within thickBlock, draw thick
65
+ subfeature.height = thickHeight;
66
+ subfeatures.push(subfeature);
67
+ } else if (subfeature.start < feature.thickStart && subfeature.end <= feature.thickEnd) {
68
+ // left overlap, split subfeature into 2 - thin | thick
69
+ thinFeature = $.extend({}, subfeature, { end: feature.thickStart });
70
+ thickFeature = $.extend({}, subfeature, { start: feature.thickStart, height: thickHeight });
71
+
72
+ subfeatures = subfeatures.concat([ thinFeature, thickFeature ]);
73
+ } else if (subfeature.start >= feature.thickStart && subfeature.end > feature.thickEnd) {
74
+ // right overlap, split subfeature into 2 - thick | thin
75
+ thinFeature = $.extend({}, subfeature, { start: feature.thickEnd });
76
+ thickFeature = $.extend({}, subfeature, { end: feature.thickEnd, height: thickHeight });
77
+
78
+ subfeatures = subfeatures.concat([ thickFeature, thinFeature ]);
79
+ } else {
80
+ // thickBlock lies within subfeature, split into 3 - thin | thick | thin
81
+ // the least possible case but lets be prepared for the outliers
82
+ thinFeature1 = $.extend({}, subfeature, { end: feature.thickStart });
83
+ thinFeature2 = $.extend({}, subfeature, { start: feature.thickEnd });
84
+ thickFeature = { start: feature.thickStart, end: feature.thickEnd, height: thickHeight };
85
+
86
+ subfeatures = subfeatures.concat([ thinFeature1, thickFeature, thinFeature2 ]);
87
+ }
88
+ } else {
89
+ // no thick block
90
+ subfeatures.push(subfeature);
91
+ }
92
+ }
93
+
94
+ if (subfeatures.length) {
95
+ feature.subFeatures = subfeatures;
96
+ }
97
+ }
98
+
99
+ this.insertFeature(feature);
100
+ }
101
+ }
102
+ },
103
+
104
+ // As per https://genome.ucsc.edu/FAQ/FAQformat.html#format1 specification
105
+ scoreColor: function (score) {
106
+ if (score <= 166) { return 'rgb(219,219,219)'; }
107
+ if (score <= 277) { return 'rgb(186,186,186)'; }
108
+ if (score <= 388) { return 'rgb(154,154,154)'; }
109
+ if (score <= 499) { return 'rgb(122,122,122)'; }
110
+ if (score <= 611) { return 'rgb(94,94,94)'; }
111
+ if (score <= 722) { return 'rgb(67,67,67)'; }
112
+ if (score <= 833) { return 'rgb(42,42,42)'; }
113
+ if (score <= 944) { return 'rgb(21,21,21)'; }
114
+ return '#000000';
115
+ }
116
+ });
@@ -0,0 +1,40 @@
1
+ Genoverse.Track.Model.File.GFF = Genoverse.Track.Model.File.extend({
2
+ parseData: function (text, chr) {
3
+ var lines = text.split('\n');
4
+
5
+ for (var i = 0; i < lines.length; i++) {
6
+ if (!lines[i].length || lines[i].indexOf('#') === 0) {
7
+ continue;
8
+ }
9
+
10
+ var fields = lines[i].split('\t');
11
+
12
+ if (fields.length < 5) {
13
+ continue;
14
+ }
15
+
16
+ var seqId = fields[0].toLowerCase();
17
+
18
+ if (
19
+ seqId === String(chr) ||
20
+ seqId === 'chr' + chr ||
21
+ seqId.match('[^1-9]' + chr + '$') ||
22
+ seqId.match('^' + chr + '\\b')
23
+ ) {
24
+ this.insertFeature({
25
+ id : fields.slice(0, 5).join('|'),
26
+ chr : chr,
27
+ start : parseInt(fields[3], 10),
28
+ end : parseInt(fields[4], 10),
29
+ source : fields[1],
30
+ type : fields[2],
31
+ score : fields[5],
32
+ strand : fields[6] === '-' ? -1 : 1,
33
+ label : fields[1] + ' ' + fields[2] + ' ' + fields[3] + '-' + fields[4]
34
+ });
35
+ }
36
+ }
37
+ }
38
+ });
39
+
40
+ Genoverse.Track.Model.File.GTF = Genoverse.Track.Model.File.GFF;
@@ -0,0 +1,101 @@
1
+ Genoverse.Track.Model.File.VCF = Genoverse.Track.Model.File.extend({
2
+ getData: function (chr, start, end) {
3
+ var deferred = $.Deferred();
4
+ var model = this;
5
+
6
+ if (!this.prop('gz')) {
7
+ return this.base.apply(this, arguments);
8
+ }
9
+
10
+ if (!this.vcfFile) {
11
+ if (this.url) {
12
+ this.vcfFile = new dallianceLib.URLFetchable(this.url);
13
+ this.tbiFile = new dallianceLib.URLFetchable(this.url + this.prop('indexExt'));
14
+ } else if (this.dataFile && this.indexFile) {
15
+ this.vcfFile = new dallianceLib.BlobFetchable(this.dataFile);
16
+ this.tbiFile = new dallianceLib.BlobFetchable(this.indexFile);
17
+ } else {
18
+ return deferred.rejectWith(model, [ 'GZipped VCF files must be accompanied by a .tbi index file' ]);
19
+ }
20
+ }
21
+
22
+ this.makeVCF(this.vcfFile, this.tbiFile).then(function (vcf) {
23
+ model.cachedVCF = vcf;
24
+
25
+ vcf.getRecords(chr, start, end, function (records) {
26
+ model.receiveData(records, chr, start, end);
27
+ deferred.resolveWith(model);
28
+ });
29
+ });
30
+
31
+ return deferred;
32
+ },
33
+
34
+ makeVCF: function (vcfFile, tbiFile) {
35
+ var deferred = $.Deferred();
36
+
37
+ if (this.cachedVCF) {
38
+ deferred.resolve(this.cachedVCF);
39
+ } else {
40
+ var vcf = new VCFReader(vcfFile, tbiFile);
41
+
42
+ vcf.readTabix(function (tabix) {
43
+ vcf.tabix = tabix;
44
+ deferred.resolve(vcf);
45
+ });
46
+ }
47
+
48
+ return deferred;
49
+ },
50
+
51
+ parseData: function (text, chr) {
52
+ var lines = text.split('\n');
53
+ var maxQual = this.allData ? this.prop('maxQual') || 0 : false;
54
+
55
+ for (var i = 0; i < lines.length; i++) {
56
+ if (!lines[i].length || lines[i].indexOf('#') === 0) {
57
+ continue;
58
+ }
59
+
60
+ var fields = lines[i].split('\t');
61
+
62
+ if (fields.length < 5) {
63
+ continue;
64
+ }
65
+
66
+ if (fields[0] === String(chr) || fields[0] === 'chr' + chr) {
67
+ var id = fields.slice(0, 3).join('|');
68
+ var start = parseInt(fields[1], 10);
69
+ var alleles = fields[4].split(',');
70
+
71
+ alleles.unshift(fields[3]);
72
+
73
+ for (var j = 0; j < alleles.length; j++) {
74
+ var end = start + alleles[j].length - 1;
75
+
76
+ this.insertFeature({
77
+ id : id + '|' + alleles[j],
78
+ sort : j,
79
+ chr : chr,
80
+ start : start,
81
+ end : end,
82
+ width : end - start,
83
+ allele : j === 0 ? 'REF' : 'ALT',
84
+ sequence : alleles[j],
85
+ label : alleles[j],
86
+ labelColor : '#FFFFFF',
87
+ originalFeature : fields
88
+ });
89
+ }
90
+
91
+ if (maxQual !== false) {
92
+ maxQual = Math.max(maxQual, fields[5]);
93
+ }
94
+ }
95
+ }
96
+
97
+ if (maxQual) {
98
+ this.prop('maxQual', maxQual);
99
+ }
100
+ }
101
+ });
@@ -0,0 +1,67 @@
1
+ Genoverse.Track.Model.File.WIG = Genoverse.Track.Model.Graph.Bar.extend({
2
+ dataType: 'text',
3
+
4
+ getData: function () {
5
+ if (!this.url) {
6
+ this.isLocal = true;
7
+ this.dataFile = this.track.dataFile;
8
+
9
+ return Genoverse.Track.Model.File.prototype.getData.apply(this, arguments);
10
+ }
11
+
12
+ return this.base.apply(this, arguments);
13
+ },
14
+
15
+ parseData: function (text, chr, s, e) {
16
+ var lines = text.split('\n');
17
+ var features = [];
18
+ var fields, chrom, start, step, span, line, feature, i;
19
+
20
+ while (lines.length && (line = lines.shift())) {
21
+ if (line.indexOf('#') !== -1 || line.indexOf('browser') !== -1 || line.indexOf('track') !== -1) {
22
+ continue;
23
+ } else {
24
+ break;
25
+ }
26
+ }
27
+
28
+ if (line) {
29
+ fields = line.split(/\s+/);
30
+ chrom = parseInt(fields[1].split('=')[1].replace('chr', ''), 10);
31
+
32
+ if (fields[0] === 'fixedStep') {
33
+ start = parseInt(fields[2].split('=')[1], 10);
34
+ step = parseInt(fields[3].split('=')[1], 10);
35
+ span = fields[4] ? parseInt(fields[4].split('=')[1], 10) : 1;
36
+
37
+ for (i = 0; i < lines.length; i++) {
38
+ features.push({
39
+ chr : chrom,
40
+ start : start,
41
+ end : start + span,
42
+ height : parseFloat(lines[i])
43
+ });
44
+
45
+ start += step;
46
+ }
47
+ } else if (fields[0] === 'variableStep') {
48
+ span = fields[2] ? parseInt(fields[2].split('=')[1], 10) : 1;
49
+
50
+ for (i = 0; i < lines.length; i++) {
51
+ fields = lines[i].split(/\s+/);
52
+ feature = {
53
+ chr : chrom,
54
+ start : parseInt(fields[0], 10),
55
+ height : parseFloat(fields[1])
56
+ };
57
+
58
+ feature.end = feature.start + span;
59
+
60
+ features.push(feature);
61
+ }
62
+ }
63
+ }
64
+
65
+ return this.base.call(this, features, chr, s, e);
66
+ }
67
+ });
@@ -0,0 +1,36 @@
1
+ Genoverse.Track.Model.File = Genoverse.Track.Model.extend({
2
+ dataType: 'text',
3
+
4
+ init: function () {
5
+ if (this.isLocal) {
6
+ this.url = false;
7
+ }
8
+
9
+ if (!(this.largeFile || this.indexFile)) {
10
+ this.allData = true;
11
+ }
12
+
13
+ this.base.apply(this, arguments);
14
+ },
15
+
16
+ getData: function (chr) {
17
+ var model = this;
18
+
19
+ if (this.isLocal && this.dataFile) {
20
+ var reader = new FileReader();
21
+ var deferred = $.Deferred();
22
+
23
+ reader.onload = function (e) {
24
+ deferred.done(function () {
25
+ this.receiveData(e.target.result, chr, 1, this.browser.getChromosomeSize(chr));
26
+ }).resolveWith(model);
27
+ };
28
+
29
+ reader.readAsText(this.dataFile);
30
+
31
+ return deferred;
32
+ }
33
+
34
+ return this.base.apply(this, arguments);
35
+ }
36
+ });
@@ -0,0 +1,22 @@
1
+ // Ensembl REST API Gene model
2
+ Genoverse.Track.Model.Gene.Ensembl = Genoverse.Track.Model.Gene.extend({
3
+ url : '//rest.ensembl.org/overlap/region/human/__CHR__:__START__-__END__?feature=gene;content-type=application/json',
4
+ dataRequestLimit : 5000000, // As per e! REST API restrictions
5
+
6
+ // The url above responds in json format, data is an array
7
+ // We assume that parents always preceed children in data array, gene -> transcript -> exon
8
+ // See rest.ensembl.org/documentation/info/feature_region for more details
9
+ parseData: function (data, chr) {
10
+ for (var i = 0; i < data.length; i++) {
11
+ var feature = data[i];
12
+
13
+ if (feature.feature_type === 'gene' && !this.featuresById[feature.id]) {
14
+ feature.chr = feature.chr || chr;
15
+ feature.label = parseInt(feature.strand, 10) === 1 ? (feature.external_name || feature.id) + ' >' : '< ' + (feature.external_name || feature.id);
16
+ feature.transcripts = [];
17
+
18
+ this.insertFeature(feature);
19
+ }
20
+ }
21
+ }
22
+ });
@@ -0,0 +1,5 @@
1
+ // Abstract Gene model
2
+ // see sub-models for more specific examples
3
+ Genoverse.Track.Model.Gene = Genoverse.Track.Model.extend({
4
+
5
+ });
@@ -0,0 +1,4 @@
1
+ Genoverse.Track.Model.Sequence.Ensembl = Genoverse.Track.Model.Sequence.extend({
2
+ url : '//rest.ensembl.org/sequence/region/human/__CHR__:__START__-__END__?content-type=text/plain', // Example url
3
+ dataRequestLimit : 10000000 // As per e! REST API restrictions
4
+ });
@@ -0,0 +1,60 @@
1
+ Genoverse.Track.Model.Sequence.Fasta = Genoverse.Track.Model.Sequence.extend({
2
+ url: 'https://wtsi-web.github.io/Genoverse/data/Homo_sapiens.GRCh37.72.dna.chromosome.1.fa', // Example url
3
+
4
+ // Following settings could be left undefined and will be detected automatically via .getStartByte()
5
+ startByte : undefined, // Byte in the file where the sequence actually starts
6
+ lineLength : undefined, // Length of the sequence line in the file
7
+
8
+ // TODO: Check if URL provided
9
+
10
+ getData: function (chr, start, end) {
11
+ var deferred = $.Deferred();
12
+
13
+ $.when(this.getStartByte()).done(function () {
14
+ start = start - (start % this.chunkSize) + 1;
15
+ end = end + this.chunkSize - (end % this.chunkSize);
16
+
17
+ var startByte = start - 1 + Math.floor((start - 1) / this.lineLength) + this.startByte;
18
+ var endByte = end - 1 + Math.floor((end - 1) / this.lineLength) + this.startByte;
19
+
20
+ $.ajax({
21
+ url : this.parseURL(),
22
+ dataType : this.dataType,
23
+ context : this,
24
+ headers : { Range: 'bytes=' + startByte + '-' + endByte },
25
+ xhrFields : this.xhrFields,
26
+ success : function (data) { this.receiveData(data, chr, start, end); },
27
+ error : this.track.controller.showError
28
+ }).done(function () { deferred.resolveWith(this); }).fail(function () { deferred.rejectWith(this); });
29
+ }).fail(function () { deferred.rejectWith(this); });
30
+
31
+ return deferred;
32
+ },
33
+
34
+ getStartByte: function () {
35
+ if (this.startByteRequest) {
36
+ return this.startByteRequest;
37
+ }
38
+
39
+ if (this.startByte === undefined || this.lineLength === undefined) {
40
+ this.startByteRequest = $.ajax({
41
+ url : this.parseURL(),
42
+ dataType : 'text',
43
+ context : this,
44
+ headers : { 'Range': 'bytes=0-300' },
45
+ xhrFields : this.xhrFields,
46
+ success : function (data) {
47
+ if (data.indexOf('>') === 0) {
48
+ this.startByte = data.indexOf('\n') + 1;
49
+ } else {
50
+ this.startByte = 0;
51
+ }
52
+
53
+ this.lineLength = data.indexOf('\n', this.startByte) - this.startByte;
54
+ }
55
+ });
56
+
57
+ return this.startByteRequest;
58
+ }
59
+ }
60
+ });
@@ -0,0 +1,50 @@
1
+ // Abstract Sequence model
2
+ // assumes that the data source responds with raw sequence text
3
+ // see Fasta model for more specific example
4
+ Genoverse.Track.Model.Sequence = Genoverse.Track.Model.extend({
5
+ threshold : 100000,
6
+ chunkSize : 1000,
7
+ buffer : 0,
8
+ dataType : 'text',
9
+
10
+ setChrProps: function () {
11
+ var chr = this.browser.chr;
12
+
13
+ this.base();
14
+
15
+ this.chunksByChr = this.chunksByChr || {};
16
+ this.chunksByChr[chr] = this.chunksByChr[chr] || {};
17
+ },
18
+
19
+ getData: function (chr, start, end) {
20
+ start = start - (start % this.chunkSize) + 1;
21
+ end = end + this.chunkSize - (end % this.chunkSize);
22
+ return this.base(chr, start, end);
23
+ },
24
+
25
+ parseData: function (data, chr, start) {
26
+ data = data.replace(/\n/g, '');
27
+
28
+ if (this.prop('lowerCase')) {
29
+ data = data.toLowerCase();
30
+ }
31
+
32
+ for (var i = 0; i < data.length; i += this.chunkSize) {
33
+ if (this.chunksByChr[chr][start + i]) {
34
+ continue;
35
+ }
36
+
37
+ var feature = {
38
+ id : chr + ':' + start + ':' + i,
39
+ chr : chr,
40
+ start : start + i,
41
+ end : start + i + this.chunkSize - 1,
42
+ sequence : data.substr(i, this.chunkSize),
43
+ sort : start + i
44
+ };
45
+
46
+ this.chunksByChr[chr][feature.start] = feature;
47
+ this.insertFeature(feature);
48
+ }
49
+ }
50
+ });
@@ -0,0 +1,41 @@
1
+ Genoverse.Track.Model.SequenceVariation = Genoverse.Track.Model.extend({
2
+ seqModel: Genoverse.Track.Model.Sequence.Ensembl,
3
+
4
+ getSeqModel: function () {
5
+ var models = this.prop('models');
6
+ models.seq = models.seq || this.track.newMVC(this.seqModel);
7
+ return models.seq;
8
+ },
9
+
10
+ getData: function (chr, start, end) {
11
+ var model = this;
12
+ var deferred = $.Deferred();
13
+ var seqData = this.getSeqModel().checkDataRange(chr, start, end);
14
+
15
+ this.base(chr, start, end).done(function () {
16
+ if (seqData) {
17
+ deferred.resolve();
18
+ } else {
19
+ model.getSeqModel().getData(chr, start, end).done(deferred.resolve);
20
+ }
21
+ });
22
+
23
+ return deferred;
24
+ },
25
+
26
+ insertFeature: function (feature) {
27
+ return this.base($.extend(feature, {
28
+ end : feature.start + feature.alt_allele.length - 1,
29
+ length : feature.alt_allele.length,
30
+ sequence : feature.alt_allele
31
+ }));
32
+ },
33
+
34
+ checkDataRange: function (chr, start, end) {
35
+ return this.base(chr, start, end) && this.getSeqModel().checkDataRange(chr, start, end);
36
+ },
37
+
38
+ findFeatures: function (chr, start, end) {
39
+ return this.getSeqModel().findFeatures(chr, start, end).concat(this.base(chr, start, end));
40
+ }
41
+ });
@@ -0,0 +1,28 @@
1
+ Genoverse.Track.Model.Stranded = Genoverse.Track.Model.extend({
2
+ init: function (reset) {
3
+ this.base(reset);
4
+
5
+ if (!reset) {
6
+ var otherTrack = this.prop('forwardTrack');
7
+
8
+ if (otherTrack) {
9
+ this.featuresByChr = otherTrack.prop('featuresByChr');
10
+ this.features = otherTrack.prop('features');
11
+ this.featuresById = otherTrack.prop('featuresById');
12
+ }
13
+ }
14
+ },
15
+
16
+ parseURL: function () {
17
+ if (!this.urlParams.strand) {
18
+ this.urlParams.strand = this.prop('featureStrand');
19
+ }
20
+
21
+ return this.base.apply(this, arguments);
22
+ },
23
+
24
+ findFeatures: function () {
25
+ var strand = this.track.featureStrand;
26
+ return $.grep(this.base.apply(this, arguments), function (feature) { return feature.strand === strand; });
27
+ }
28
+ });
@@ -0,0 +1,67 @@
1
+ // Ensembl REST API Transcript model
2
+ Genoverse.Track.Model.Transcript.Ensembl = Genoverse.Track.Model.Transcript.extend({
3
+ url : '//rest.ensembl.org/overlap/region/human/__CHR__:__START__-__END__?feature=transcript;feature=exon;feature=cds;content-type=application/json',
4
+ dataRequestLimit : 5000000, // As per e! REST API restrictions
5
+
6
+ setDefaults: function () {
7
+ this.geneIds = {};
8
+ this.seenGenes = 0;
9
+
10
+ this.base.apply(this, arguments);
11
+ },
12
+
13
+ // The url above responds in json format, data is an array
14
+ // See rest.ensembl.org/documentation/info/overlap_region for more details
15
+ parseData: function (data, chr) {
16
+ var model = this;
17
+ var featuresById = this.featuresById;
18
+ var ids = [];
19
+
20
+ data.filter(function (d) { return d.feature_type === 'transcript'; }).forEach(function (feature, i) {
21
+ if (!featuresById[feature.id]) {
22
+ model.geneIds[feature.Parent] = model.geneIds[feature.Parent] || ++model.seenGenes;
23
+
24
+ feature.chr = feature.chr || chr;
25
+ feature.label = parseInt(feature.strand, 10) === 1 ? (feature.external_name || feature.id) + ' >' : '< ' + (feature.external_name || feature.id);
26
+ feature.sort = (model.geneIds[feature.Parent] * 1e10) + (feature.logic_name.indexOf('ensembl_havana') === 0 ? 0 : 2e9) + (feature.biotype === 'protein_coding' ? 0 : 1e9) + feature.start + i;
27
+ feature.cdsStart = Infinity;
28
+ feature.cdsEnd = -Infinity;
29
+ feature.exons = {};
30
+ feature.subFeatures = [];
31
+
32
+ model.insertFeature(feature);
33
+ }
34
+
35
+ ids.push(feature.id);
36
+ });
37
+
38
+ data.filter(function (d) { return d.feature_type === 'cds' && featuresById[d.Parent]; }).forEach(function (cds) {
39
+ featuresById[cds.Parent].cdsStart = Math.min(featuresById[cds.Parent].cdsStart, cds.start);
40
+ featuresById[cds.Parent].cdsEnd = Math.max(featuresById[cds.Parent].cdsEnd, cds.end);
41
+ });
42
+
43
+ data.filter(function (d) { return d.feature_type === 'exon' && featuresById[d.Parent] && !featuresById[d.Parent].exons[d.id]; }).forEach(function (exon) {
44
+ if (exon.end < featuresById[exon.Parent].cdsStart || exon.start > featuresById[exon.Parent].cdsEnd) {
45
+ featuresById[exon.Parent].subFeatures.push($.extend({ utr: true }, exon));
46
+ } else {
47
+ if (exon.start < featuresById[exon.Parent].cdsStart) {
48
+ featuresById[exon.Parent].subFeatures.push($.extend({ utr: true }, exon, { end: featuresById[exon.Parent].cdsStart }));
49
+ }
50
+
51
+ featuresById[exon.Parent].subFeatures.push($.extend({}, exon, {
52
+ start : Math.max(exon.start, featuresById[exon.Parent].cdsStart),
53
+ end : Math.min(exon.end, featuresById[exon.Parent].cdsEnd),
54
+ strand : featuresById[exon.Parent].strand
55
+ }));
56
+
57
+ if (exon.end > featuresById[exon.Parent].cdsEnd) {
58
+ featuresById[exon.Parent].subFeatures.push($.extend({ utr: true }, exon, { start: featuresById[exon.Parent].cdsEnd }));
59
+ }
60
+ }
61
+ });
62
+
63
+ ids.forEach(function (id) {
64
+ featuresById[id].subFeatures.sort(function (a, b) { return a.start - b.start; });
65
+ });
66
+ }
67
+ });
@@ -0,0 +1,5 @@
1
+ // Abstract Transcript model
2
+ // see sub-models for more specific examples
3
+ Genoverse.Track.Model.Transcript = Genoverse.Track.Model.extend({
4
+
5
+ });