genoverse 3.2.0 → 4.0.1

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