sequenceserver 2.0.0.beta4 → 2.0.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +5 -5
  2. data/.dockerignore +1 -0
  3. data/.travis.yml +7 -4
  4. data/AppImage/sequenceserver.sh +5 -0
  5. data/Dockerfile +14 -12
  6. data/bin/sequenceserver +37 -28
  7. data/lib/sequenceserver.rb +35 -7
  8. data/lib/sequenceserver/blast/job.rb +18 -25
  9. data/lib/sequenceserver/blast/report.rb +68 -34
  10. data/lib/sequenceserver/config.rb +1 -1
  11. data/lib/sequenceserver/database.rb +0 -129
  12. data/lib/sequenceserver/makeblastdb.rb +243 -0
  13. data/lib/sequenceserver/routes.rb +28 -2
  14. data/lib/sequenceserver/version.rb +1 -1
  15. data/public/SequenceServer_logo.png +0 -0
  16. data/public/css/grapher.css +8 -15
  17. data/public/css/sequenceserver.css +119 -55
  18. data/public/css/sequenceserver.min.css +3 -3
  19. data/public/js/circos.js +1 -1
  20. data/public/js/download_fasta.js +17 -0
  21. data/public/js/grapher.js +7 -9
  22. data/public/js/hit.js +217 -0
  23. data/public/js/hits_overview.js +12 -13
  24. data/public/js/hsp.js +104 -84
  25. data/public/js/{sequenceserver.js → jquery_world.js} +1 -18
  26. data/public/js/kablammo.js +337 -334
  27. data/public/js/length_distribution.js +1 -1
  28. data/public/js/query.js +147 -0
  29. data/public/js/report.js +216 -836
  30. data/public/js/search.js +194 -192
  31. data/public/js/sequence_modal.js +167 -0
  32. data/public/js/sidebar.js +210 -0
  33. data/public/js/utils.js +2 -19
  34. data/public/js/visualisation_helpers.js +2 -2
  35. data/public/sequenceserver-report.min.js +19 -19
  36. data/public/sequenceserver-search.min.js +11 -11
  37. data/public/vendor/github/twbs/bootstrap@3.3.5/js/bootstrap.js +2 -2
  38. data/spec/blast_versions/blast_2.2.30/import_spec_capybara_local_2.2.30.rb +15 -15
  39. data/spec/blast_versions/blast_2.2.31/import_spec_capybara_local_2.2.31.rb +15 -15
  40. data/spec/blast_versions/blast_2.3.0/import_spec_capybara_local_2.3.0.rb +15 -15
  41. data/spec/blast_versions/blast_2.4.0/import_spec_capybara_local_2.4.0.rb +15 -15
  42. data/spec/blast_versions/blast_2.5.0/import_spec_capybara_local_2.5.0.rb +15 -15
  43. data/spec/blast_versions/blast_2.6.0/import_spec_capybara_local_2.6.0.rb +15 -15
  44. data/spec/blast_versions/blast_2.7.1/import_spec_capybara_local_2.7.1.rb +15 -15
  45. data/spec/blast_versions/blast_2.8.1/import_spec_capybara_local_2.8.1.rb +15 -15
  46. data/spec/blast_versions/blast_2.9.0/import_spec_capybara_local_2.9.0.rb +15 -15
  47. data/spec/blast_versions/diamond_0.9.24/import_spec_capybara_local_0.9.24.rb +6 -6
  48. data/spec/capybara_spec.rb +14 -3
  49. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.ndb +0 -0
  50. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nhr +0 -0
  51. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nin +0 -0
  52. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nos +0 -0
  53. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.not +0 -0
  54. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.ntf +0 -0
  55. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nto +0 -0
  56. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pdb +0 -0
  57. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.phr +0 -0
  58. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pin +0 -0
  59. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pos +0 -0
  60. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pot +0 -0
  61. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.ptf +0 -0
  62. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pto +0 -0
  63. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pdb +0 -0
  64. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.phr +0 -0
  65. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pin +0 -0
  66. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pos +0 -0
  67. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pot +0 -0
  68. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.ptf +0 -0
  69. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pto +0 -0
  70. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.ndb +0 -0
  71. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nhr +0 -0
  72. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nin +0 -0
  73. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nos +0 -0
  74. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.not +0 -0
  75. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nsq +0 -0
  76. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.ntf +0 -0
  77. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nto +0 -0
  78. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nhd +8 -0
  79. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nhi +0 -0
  80. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nhr +0 -0
  81. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nin +0 -0
  82. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nog +0 -0
  83. data/spec/database/{sample → v4}/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nsd +0 -0
  84. data/spec/database/{sample → v4}/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nsi +0 -0
  85. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nsq +0 -0
  86. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.txt +8 -0
  87. data/spec/database/v4/links.rb +23 -0
  88. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta +6449 -0
  89. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.phd +1189 -0
  90. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.phi +0 -0
  91. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.phr +0 -0
  92. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pin +0 -0
  93. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pog +0 -0
  94. data/spec/database/{sample → v4}/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.psd +0 -0
  95. data/spec/database/{sample → v4}/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.psi +0 -0
  96. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.psq +0 -0
  97. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.phd +9140 -0
  98. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.phi +0 -0
  99. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.phr +0 -0
  100. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pin +0 -0
  101. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pog +0 -0
  102. data/spec/database/{sample → v4}/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.psd +0 -0
  103. data/spec/database/{sample → v4}/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.psi +0 -0
  104. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.psq +0 -0
  105. data/spec/database/v4/proteins/uniprot/URL +1 -0
  106. data/spec/database/v4/si_uniprot_idmap.yml +14180 -0
  107. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta +5486 -0
  108. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nhd +473 -0
  109. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nhi +0 -0
  110. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nhr +0 -0
  111. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nin +0 -0
  112. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nog +0 -0
  113. data/spec/database/{sample → v4}/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nsd +0 -0
  114. data/spec/database/{sample → v4}/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nsi +0 -0
  115. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nsq +0 -0
  116. data/spec/database_spec.rb +0 -76
  117. data/spec/makeblastdb_spec.rb +121 -0
  118. data/views/layout.erb +5 -1
  119. metadata +75 -15
@@ -0,0 +1,217 @@
1
+ import React from 'react';
2
+ import _ from 'underscore';
3
+
4
+ import HSPOverview from './kablammo';
5
+ import downloadFASTA from './download_fasta';
6
+ import AlignmentExporter from './alignment_exporter'; // to download textual alignment
7
+
8
+ /**
9
+ * Component for each hit. Receives props from Report. Has no state.
10
+ */
11
+ export default React.createClass({
12
+ /**
13
+ * Returns accession number of the hit sequence.
14
+ */
15
+ accession: function () {
16
+ return this.props.hit.accession;
17
+ },
18
+
19
+ /**
20
+ * Returns length of the hit sequence.
21
+ */
22
+ hitLength: function () {
23
+ return this.props.hit.length;
24
+ },
25
+
26
+ // Internal helpers. //
27
+
28
+ /**
29
+ * Returns id that will be used for the DOM node corresponding to the hit.
30
+ */
31
+ domID: function () {
32
+ return 'Query_' + this.props.query.number + '_hit_' + this.props.hit.number;
33
+ },
34
+
35
+ databaseIDs: function () {
36
+ return _.map(this.props.querydb, _.iteratee('id'));
37
+ },
38
+
39
+ showSequenceViewer: function () {
40
+ this.props.showSequenceModal(this.viewSequenceLink());
41
+ },
42
+
43
+ viewSequenceLink: function () {
44
+ return encodeURI(`get_sequence/?sequence_ids=${this.accession()}&database_ids=${this.databaseIDs()}`);
45
+ },
46
+
47
+ downloadFASTA: function (event) {
48
+ var accessions = [this.accession()];
49
+ downloadFASTA(accessions, this.databaseIDs());
50
+ },
51
+
52
+ // Event-handler for exporting alignments.
53
+ // Calls relevant method on AlignmentExporter defined in alignment_exporter.js.
54
+ downloadAlignment: function (event) {
55
+ var hsps = _.map(this.props.hit.hsps, _.bind(function (hsp) {
56
+ hsp.query_id = this.props.query.id;
57
+ hsp.hit_id = this.props.hit.id;
58
+ return hsp;
59
+ }, this));
60
+
61
+ var aln_exporter = new AlignmentExporter();
62
+ aln_exporter.export_alignments(hsps, this.props.query.id+'_'+this.props.hit.id);
63
+ },
64
+
65
+
66
+ // Life cycle methods //
67
+
68
+ render: function () {
69
+ return (
70
+ <div className="hit" id={this.domID()} data-hit-def={this.props.hit.id}
71
+ data-hit-len={this.props.hit.length} data-hit-evalue={this.props.hit.evalue}>
72
+ { this.headerJSX() } { this.contentJSX() }
73
+ </div>
74
+ );
75
+ },
76
+
77
+ // See Query.shouldComponentUpdate. The same applies for hits.
78
+ shouldComponentUpdate: function () {
79
+ return !this.props.hit;
80
+ },
81
+
82
+ headerJSX: function () {
83
+ var meta = `length: ${this.hitLength().toLocaleString()}`;
84
+
85
+ if (this.props.showQueryCrumbs && this.props.showHitCrumbs) {
86
+ // Multiper queries, multiple hits
87
+ meta = `hit ${this.props.hit.number} of query ${this.props.query.number}, ` + meta;
88
+ }
89
+ else if (this.props.showQueryCrumbs && !this.props.showHitCrumbs) {
90
+ // Multiple queries, single hit
91
+ meta = `the only hit of query ${this.props.query.number}, ` + meta;
92
+ }
93
+ else if (!this.props.showQueryCrumbs && this.props.showHitCrumbs) {
94
+ // Single query, multiple hits
95
+ meta = `hit ${this.props.hit.number}, ` + meta;
96
+ }
97
+
98
+ return <div className="section-header">
99
+ <h4>
100
+ <i className="fa fa-minus-square-o"></i>&nbsp;
101
+ <strong>{this.props.hit.id}</strong>&nbsp;
102
+ {this.props.hit.title}
103
+ </h4>
104
+ <span className="label label-reset pos-label">{meta}</span>
105
+ </div>;
106
+ },
107
+
108
+ contentJSX: function () {
109
+ return <div className="section-content" data-parent-hit={this.domID()}>
110
+ { this.hitLinks() }
111
+ <HSPOverview key={'kablammo' + this.props.query.id} query={this.props.query}
112
+ hit={this.props.hit} algorithm={this.props.algorithm}
113
+ showHSPCrumbs={this.props.hit.hsps.length > 1}
114
+ collapsed={this.props.veryBig} />
115
+ </div>;
116
+ },
117
+
118
+ hitLinks: function () {
119
+ var btns = [];
120
+ if (!this.props.imported_xml) {
121
+ btns = btns.concat([
122
+ this.viewSequenceButton(),
123
+ this.downloadFASTAButton()
124
+ ]);
125
+ }
126
+ btns.push(this.downloadAlignmentButton());
127
+
128
+ return (
129
+ <div className="hit-links">
130
+ <label>
131
+ <input type="checkbox" id={this.domID() + '_checkbox'}
132
+ value={this.accession()} onChange={function () {
133
+ this.props.selectHit(this.domID() + '_checkbox');
134
+ }.bind(this)} data-target={'#' + this.domID()}
135
+ /> Select
136
+ </label>
137
+ {
138
+ btns.map((btn) => {
139
+ return [<span className="line">|</span>, this.button(btn)];
140
+ })
141
+ }
142
+ {
143
+ this.props.hit.links.map((link) => {
144
+ return [<span className="line">|</span>, this.a(link)];
145
+ })
146
+ }
147
+ </div>
148
+ );
149
+ },
150
+
151
+ // Return JSX for view sequence button.
152
+ viewSequenceButton: function () {
153
+ if (this.hitLength() > 10000) {
154
+ return {
155
+ text: 'Sequence',
156
+ icon: 'fa-eye',
157
+ className: 'view-sequence',
158
+ title: 'Sequence too long',
159
+ };
160
+ }
161
+ else {
162
+ return {
163
+ text: 'Sequence',
164
+ icon: 'fa-eye',
165
+ className: 'view-sequence',
166
+ onClick: () => this.showSequenceViewer()
167
+ };
168
+
169
+ }
170
+ },
171
+
172
+ downloadFASTAButton: function () {
173
+ return {
174
+ text: 'FASTA',
175
+ icon: 'fa-download',
176
+ className: 'download-fa',
177
+ onClick: () => this.downloadFASTA()
178
+ };
179
+ },
180
+
181
+ downloadAlignmentButton: function () {
182
+ return {
183
+ text: 'Alignment',
184
+ icon: 'fa-download',
185
+ className: 'download-aln',
186
+ onClick: () => this.downloadAlignment()
187
+ };
188
+ },
189
+
190
+ button: function ({text, icon, title, className, onClick}) {
191
+ if (onClick) {
192
+ return <button className={`btn-link ${className}`}
193
+ title={title} onClick={onClick}><i className={`fa ${icon}`}></i> {text}
194
+ </button>;
195
+ }
196
+ else {
197
+ return <button className="btn-link view-sequence disabled"
198
+ title={title} disabled="true">
199
+ <i className={`fa ${icon}`}></i> {text}
200
+ </button>;
201
+ }
202
+ },
203
+
204
+ /**
205
+ * Render URL for sequence-viewer.
206
+ */
207
+ a: function (link) {
208
+ if (!link.title || !link.url) return;
209
+
210
+ let className = 'btn btn-link';
211
+ if (link.class) className = `${className} ${link.class}`;
212
+ return <a href={link.url} className={className} target='_blank'>
213
+ {link.icon && <i className={'fa ' + link.icon}></i>}
214
+ {' ' + link.title + ' '}
215
+ </a>;
216
+ }
217
+ });
@@ -166,10 +166,10 @@ class Graph {
166
166
  drawLegend(svg, options, width, height, hits) {
167
167
  var svg_legend = svg.append('g')
168
168
  .attr('transform',
169
- 'translate(0,' + (height - 1.88 * options.margin) + ')');
169
+ 'translate(0,' + (height - 1.75 * options.margin) + ')');
170
170
 
171
171
  svg_legend.append('rect')
172
- .attr('x', 7 * (width - 2 * options.margin) / 10)
172
+ .attr('x', 7.5 * (width - 2 * options.margin) / 10)
173
173
  .attr('width', 2 * (width - 4 * options.margin) / 10)
174
174
  .attr('height', options.legend)
175
175
  .attr('fill', 'url(#legend-grad)');
@@ -177,7 +177,7 @@ class Graph {
177
177
  svg_legend.append('text')
178
178
  .attr('class',' legend-text')
179
179
  .attr('transform', 'translate(0, ' +options.legend +')')
180
- .attr('x', 6 * (width - 2 * options.margin) / 10 - options.margin / 2)
180
+ .attr('x', 9.5 * (width - 2 * options.margin) / 10 + options.margin / 2)
181
181
  .text('Weaker hits');
182
182
  // .text(function() {
183
183
  // return Helpers.prettify_evalue(hits[hits.length-1].hitEvalue);
@@ -186,7 +186,7 @@ class Graph {
186
186
  svg_legend.append('text')
187
187
  .attr('class',' legend-text')
188
188
  .attr('transform', 'translate(0, ' + options.legend + ')')
189
- .attr('x', 9 * (width - 2 * options.margin) / 10 + options.margin / 2)
189
+ .attr('x', 6.7 * (width - 2 * options.margin) / 10 - options.margin / 2)
190
190
  .text('Stronger hits');
191
191
  // .text(function () {
192
192
  // return Helpers.prettify_evalue(hits[0].hitEvalue);
@@ -196,9 +196,9 @@ class Graph {
196
196
  .attr('id', 'legend-grad')
197
197
  .selectAll('stop')
198
198
  .data([
199
- {offset: '0%', color: '#ccc'},
200
- {offset: '50%', color: '#888'},
201
- {offset: '100%', color: '#000'}
199
+ {offset: '0%', color: '#000'},
200
+ {offset: '45%', color: '#c74f14'},
201
+ {offset: '100%', color: '#f6bea2'}
202
202
  ])
203
203
  .enter()
204
204
  .append('stop')
@@ -216,7 +216,7 @@ class Graph {
216
216
  * margin: Margin around the svg element.
217
217
  */
218
218
  var defaults = {
219
- barHeight: 3,
219
+ barHeight: 4,
220
220
  legend: inhits.length > 1 ? 3 : 0,
221
221
  margin: 20
222
222
  },
@@ -237,7 +237,8 @@ class Graph {
237
237
 
238
238
  var width = $graphDiv.width();
239
239
  var height = hits.length * (options.barHeight) +
240
- 2 * options.legend + 5 * options.margin;
240
+ 2 * options.legend + 4 * options.margin;
241
+
241
242
  // var height = $graphDiv.height();
242
243
 
243
244
  var SEQ_TYPES = {
@@ -310,7 +311,7 @@ class Graph {
310
311
  return d.hitEvalue;
311
312
  }))
312
313
  ])
313
- .range([40,150]);
314
+ .range([0,0.8]);
314
315
 
315
316
  svg.append('g')
316
317
  .attr('class', 'ghit')
@@ -330,9 +331,7 @@ class Graph {
330
331
  // Drawing the HSPs connector line using the same
331
332
  // color as that of the hit track (using lookahead).
332
333
  var yHspline = y(d.hitId) + options.barHeight / 2;
333
- var hsplineColor = d3.rgb(gradScale(v.hspEvalue),
334
- gradScale(v.hspEvalue),
335
- gradScale(v.hspEvalue));
334
+ var hsplineColor = d3.hsl(20, 0.82, gradScale(v.hspEvalue));
336
335
 
337
336
  if (j+1 < d.length) {
338
337
  if (d[j].hspEnd <= d[j+1].hspStart) {
@@ -17,16 +17,22 @@ export default class HSP extends React.Component {
17
17
  }
18
18
 
19
19
  domID() {
20
- return "Query_" + this.props.queryNumber + "_hit_" +
21
- this.props.hitNumber + "_" + this.hsp.number;
20
+ return 'Query_' + this.props.query.number + '_hit_' +
21
+ this.props.hit.number + '_' + this.props.hsp.number;
22
+ }
23
+
24
+ hitDOM_ID() {
25
+ return 'Query_' + this.props.query.number + '_hit_' + this.props.hit.number;
22
26
  }
23
27
 
24
28
  // Renders pretty formatted alignment.
25
29
  render () {
26
30
  return (
27
- <div className="hsp" id={this.domID()} ref="hsp">
31
+ <div className="hsp" id={this.domID()} ref="hsp"
32
+ data-parent-hit={this.hitDOM_ID()}>
28
33
  <pre className="pre-reset hsp-stats">
29
- {Helpers.toLetters(this.hsp.number) + "."}&nbsp;{this.hspStats()}
34
+ {this.props.showHSPNumbers && `${Helpers.toLetters(this.hsp.number)}. `}
35
+ {this.hspStats().map((s, i) => <span key={i}>{s}</span>)}
30
36
  </pre>
31
37
  {this.hspLines()}
32
38
  </div>
@@ -39,11 +45,17 @@ export default class HSP extends React.Component {
39
45
  }
40
46
 
41
47
  draw () {
42
- var $container = $(React.findDOMNode(this.refs.hsp));
43
- this.chars = Math.floor($container.width() / 7.5);
48
+ var charWidth = this.props.getCharacterWidth();
49
+ var containerWidth = $(React.findDOMNode(this.refs.hsp)).width();
50
+ this.chars = Math.floor((containerWidth - 4) / charWidth);
44
51
  this.forceUpdate();
45
52
  }
46
53
 
54
+ // See Query.shouldComponentUpdate. The same applies for hsp.
55
+ shouldComponentUpdate () {
56
+ return !this.props.hsp;
57
+ }
58
+
47
59
  /**
48
60
  * Returns an array of span elements or plain strings (React automatically
49
61
  * adds span tag around strings). This array is passed as it is to JSX to be
@@ -60,17 +72,17 @@ export default class HSP extends React.Component {
60
72
  line.push(`Score: ${Utils.inTwoDecimal(this.hsp.bit_score)} (${this.hsp.score}), `);
61
73
 
62
74
  // E value
63
- line.push(`E value: `); line.push(Utils.inExponential(this.hsp.evalue)); line.push(', ');
75
+ line.push('E value: '); line.push(Utils.inExponential(this.hsp.evalue)); line.push(', ');
64
76
 
65
77
  // Identity
66
- line.push([`Identities: ${Utils.inFraction(this.hsp.identity, this.hsp.length)} (${Utils.inPercentage(this.hsp.identity, this.hsp.length)}), `]);
78
+ line.push([`Identity: ${Utils.inFraction(this.hsp.identity, this.hsp.length)} (${Utils.inPercentage(this.hsp.identity, this.hsp.length)}), `]);
67
79
 
68
80
  // Positives (for protein alignment).
69
81
  if (this.props.algorithm === 'blastp' ||
70
82
  this.props.algorithm === 'blastx' ||
71
83
  this.props.algorithm === 'tblastn' ||
72
84
  this.props.algorithm === 'tblastx') {
73
- line.push(`Positives: ${Utils.inFraction(this.hsp.positives, this.hsp.length)} (${Utils.inPercentage(this.hsp.positives, this.hsp.length)}), `)
85
+ line.push(`Positives: ${Utils.inFraction(this.hsp.positives, this.hsp.length)} (${Utils.inPercentage(this.hsp.positives, this.hsp.length)}), `);
74
86
  }
75
87
 
76
88
  // Gaps
@@ -80,23 +92,27 @@ export default class HSP extends React.Component {
80
92
  //line.push(`Query coverage: ${this.hsp.qcovhsp}%, `)
81
93
 
82
94
  switch (this.props.algorithm) {
83
- case 'tblastx':
84
- line.push(`, Frame: ${Utils.inFraction(this.hsp.qframe, this.hsp.sframe)}`)
85
- break;
86
- case 'blastn':
87
- line.push(`, Strand: ${(this.hsp.qframe > 0 ? '+' : '-')} / ${(this.hsp.sframe > 0 ? '+' : '-')}`)
88
- break;
89
- case 'blastx':
90
- line.push(`, Query Frame: ${this.hsp.qframe}`)
91
- break;
92
- case 'tblastn':
93
- line.push(`, Hit Frame: ${this.hsp.sframe}`)
94
- break;
95
+ case 'tblastx':
96
+ line.push(`, Frame: ${Utils.inFraction(this.hsp.qframe, this.hsp.sframe)}`);
97
+ break;
98
+ case 'blastn':
99
+ line.push(`, Strand: ${(this.hsp.qframe > 0 ? '+' : '-')} / ${(this.hsp.sframe > 0 ? '+' : '-')}`);
100
+ break;
101
+ case 'blastx':
102
+ line.push(`, Query Frame: ${this.hsp.qframe}`);
103
+ break;
104
+ case 'tblastn':
105
+ line.push(`, Hit Frame: ${this.hsp.sframe}`);
106
+ break;
95
107
  }
96
108
 
97
109
  return line;
98
110
  }
99
111
 
112
+ /**
113
+ * Returns array of pre tags containing the three query, middle, and subject
114
+ * lines that together comprise one 'rendered line' of HSP.
115
+ */
100
116
  hspLines () {
101
117
  // Space reserved for showing coordinates
102
118
  var width = this.width();
@@ -115,7 +131,6 @@ export default class HSP extends React.Component {
115
131
  var nqseq = this.nqseq();
116
132
  var nsseq = this.nsseq();
117
133
  for (let i = 1; i <= lines; i++) {
118
- let line = [];
119
134
  let seq_start_index = chars * (i - 1);
120
135
  let seq_stop_index = seq_start_index + chars;
121
136
 
@@ -133,32 +148,37 @@ export default class HSP extends React.Component {
133
148
  this.sframe_unit() * this.sframe_sign();
134
149
  nsseq = lsend + this.sframe_unit() * this.sframe_sign();
135
150
 
136
- line.push(this.spanCoords('Query ' + this.formatCoords(lqstart, width) + ' '));
137
- line.push(lqseq);
138
- line.push(this.spanCoords(' ' + lqend));
139
- line.push(<br/>);
140
-
141
- line.push(this.formatCoords('', width + 8) + ' ');
142
- line.push(lmseq);
143
- line.push(<br/>);
144
-
145
- line.push(this.spanCoords('Subject ' + this.formatCoords(lsstart, width) + ' '));
146
- line.push(lsseq);
147
- line.push(this.spanCoords(' ' + lsend))
148
- line.push(<br/>);
149
-
150
- pp.push(<pre key={this.hsp.number + ',' + i}
151
- className="pre-reset hsp-lines">{line}</pre>);
151
+ pp.push(
152
+ <pre key={this.hsp.number + ',' + i} className="pre-reset hsp-lines">
153
+ <span className="hsp-coords">
154
+ {`Query ${this.formatCoords(lqstart, width)} `}
155
+ </span>
156
+ <span>{lqseq}</span>
157
+ <span className="hsp-coords">{` ${lqend}`}</span>
158
+ <br/>
159
+ <span className="hsp-coords">
160
+ {`${this.formatCoords('', width + 8)} `}
161
+ </span>
162
+ <span>{lmseq}</span>
163
+ <br/>
164
+ <span className="hsp-coords">
165
+ {`Subject ${this.formatCoords(lsstart, width)} `}
166
+ </span>
167
+ <span>{lsseq}</span>
168
+ <span className="hsp-coords">{` ${lsend}`}</span>
169
+ <br/>
170
+ </pre>);
152
171
  }
153
172
 
154
173
  return pp;
155
174
  }
156
175
 
157
- // Width of each line of alignment.
158
- width() {
176
+ // Width of the coordinate part of hsp lines. Essentially the length of
177
+ // the largest coordinate.
178
+ width () {
159
179
  return _.max(_.map([this.hsp.qstart, this.hsp.qend,
160
- this.hsp.sstart, this.hsp.send],
161
- (n) => { return n.toString().length }));
180
+ this.hsp.sstart, this.hsp.send],
181
+ (n) => { return n.toString().length; }));
162
182
  }
163
183
 
164
184
  // Alignment start coordinate for query sequence.
@@ -167,16 +187,16 @@ export default class HSP extends React.Component {
167
187
  // (translated) query sequence aligned.
168
188
  nqseq () {
169
189
  switch (this.props.algorithm) {
170
- case 'blastp':
171
- case 'blastx':
172
- case 'tblastn':
173
- case 'tblastx':
174
- return this.hsp.qframe >= 0 ? this.hsp.qstart : this.hsp.qend;
175
- case 'blastn':
176
- // BLASTN is a bit weird in that, no matter which direction the query
177
- // sequence aligned in, qstart is taken as alignment start coordinate
178
- // for query.
179
- return this.hsp.qstart;
190
+ case 'blastp':
191
+ case 'blastx':
192
+ case 'tblastn':
193
+ case 'tblastx':
194
+ return this.hsp.qframe >= 0 ? this.hsp.qstart : this.hsp.qend;
195
+ case 'blastn':
196
+ // BLASTN is a bit weird in that, no matter which direction the query
197
+ // sequence aligned in, qstart is taken as alignment start coordinate
198
+ // for query.
199
+ return this.hsp.qstart;
180
200
  }
181
201
  }
182
202
 
@@ -186,16 +206,16 @@ export default class HSP extends React.Component {
186
206
  // (translated) subject sequence aligned.
187
207
  nsseq () {
188
208
  switch (this.props.algorithm) {
189
- case 'blastp':
190
- case 'blastx':
191
- case 'tblastn':
192
- case 'tblastx':
193
- return this.hsp.sframe >= 0 ? this.hsp.sstart : this.hsp.send;
194
- case 'blastn':
195
- // BLASTN is a bit weird in that, no matter which direction the
196
- // subject sequence aligned in, sstart is taken as alignment
197
- // start coordinate for subject.
198
- return this.hsp.sstart
209
+ case 'blastp':
210
+ case 'blastx':
211
+ case 'tblastn':
212
+ case 'tblastx':
213
+ return this.hsp.sframe >= 0 ? this.hsp.sstart : this.hsp.send;
214
+ case 'blastn':
215
+ // BLASTN is a bit weird in that, no matter which direction the
216
+ // subject sequence aligned in, sstart is taken as alignment
217
+ // start coordinate for subject.
218
+ return this.hsp.sstart;
199
219
  }
200
220
  }
201
221
 
@@ -209,16 +229,16 @@ export default class HSP extends React.Component {
209
229
  // translated or not.
210
230
  qframe_unit () {
211
231
  switch (this.props.algorithm) {
212
- case 'blastp':
213
- case 'blastn':
214
- case 'tblastn':
215
- return 1;
216
- case 'blastx':
217
- // _Translated_ nucleotide query against protein database.
218
- case 'tblastx':
219
- // _Translated_ nucleotide query against translated
220
- // nucleotide database.
221
- return 3;
232
+ case 'blastp':
233
+ case 'blastn':
234
+ case 'tblastn':
235
+ return 1;
236
+ case 'blastx':
237
+ // _Translated_ nucleotide query against protein database.
238
+ case 'tblastx':
239
+ // _Translated_ nucleotide query against translated
240
+ // nucleotide database.
241
+ return 3;
222
242
  }
223
243
  }
224
244
 
@@ -232,17 +252,17 @@ export default class HSP extends React.Component {
232
252
  // translated or not.
233
253
  sframe_unit () {
234
254
  switch (this.props.algorithm) {
235
- case 'blastp':
236
- case 'blastx':
237
- case 'blastn':
238
- return 1;
239
- case 'tblastn':
240
- // Protein query against _translated_ nucleotide database.
241
- return 3;
242
- case 'tblastx':
243
- // Translated nucleotide query against _translated_
244
- // nucleotide database.
245
- return 3;
255
+ case 'blastp':
256
+ case 'blastx':
257
+ case 'blastn':
258
+ return 1;
259
+ case 'tblastn':
260
+ // Protein query against _translated_ nucleotide database.
261
+ return 3;
262
+ case 'tblastx':
263
+ // Translated nucleotide query against _translated_
264
+ // nucleotide database.
265
+ return 3;
246
266
  }
247
267
  }
248
268
 
@@ -283,7 +303,7 @@ export default class HSP extends React.Component {
283
303
  }
284
304
 
285
305
  spanCoords (text) {
286
- return <span className="hsp-coords">{text}</span>
306
+ return <span className="hsp-coords">{text}</span>;
287
307
  }
288
308
  }
289
309