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
@@ -1,6 +1,6 @@
1
- import SequenceServer from './sequenceserver';
2
- import _ from 'underscore';
1
+ import './jquery_world';
3
2
  import React from 'react';
3
+ import _ from 'underscore';
4
4
 
5
5
  /**
6
6
  * Load necessary polyfills.
@@ -20,7 +20,7 @@ var Page = React.createClass({
20
20
  componentDidMount: function () {
21
21
  this.refs.dnd.setState({
22
22
  query: this.refs.form.refs.query
23
- })
23
+ });
24
24
  }
25
25
  });
26
26
 
@@ -31,14 +31,14 @@ var DnD = React.createClass({
31
31
  getInitialState: function () {
32
32
  return {
33
33
  query: null
34
- }
34
+ };
35
35
  },
36
36
 
37
37
  render: function () {
38
38
  return (
39
39
  <div
40
40
  className="dnd-overlay"
41
- style={{display: "none"}}>
41
+ style={{display: 'none'}}>
42
42
  <div
43
43
  className="container dnd-overlay-container">
44
44
  <div
@@ -47,15 +47,15 @@ var DnD = React.createClass({
47
47
  className="col-md-offset-2 col-md-10">
48
48
  <p
49
49
  className="dnd-overlay-drop"
50
- style={{display: "none"}}>
50
+ style={{display: 'none'}}>
51
51
  <i className="fa fa-2x fa-file-o"></i>
52
52
  Drop query sequence file here
53
53
  </p>
54
54
  <p
55
55
  className="dnd-overlay-overwrite"
56
- style={{display: "none"}}>
56
+ style={{display: 'none'}}>
57
57
  <i className="fa fa-2x fa-file-o"></i>
58
- <span style={{color: "red"}}>Overwrite</span> query sequence file
58
+ <span style={{color: 'red'}}>Overwrite</span> query sequence file
59
59
  </p>
60
60
 
61
61
  <div
@@ -63,7 +63,7 @@ var DnD = React.createClass({
63
63
  <div
64
64
  className="dnd-error row"
65
65
  id="dnd-multi-notification"
66
- style={{display: "none"}}>
66
+ style={{display: 'none'}}>
67
67
  <div
68
68
  className="col-md-6 col-md-offset-3">
69
69
  One file at a time please.
@@ -73,7 +73,7 @@ var DnD = React.createClass({
73
73
  <div
74
74
  className="dnd-error row"
75
75
  id="dnd-large-file-notification"
76
- style={{display: "none"}}>
76
+ style={{display: 'none'}}>
77
77
  <div
78
78
  className="col-md-6 col-md-offset-3">
79
79
  Too big a file. Can only do less than 10 MB. &gt;_&lt;
@@ -83,7 +83,7 @@ var DnD = React.createClass({
83
83
  <div
84
84
  className="dnd-error row"
85
85
  id="dnd-format-notification"
86
- style={{display: "none"}}>
86
+ style={{display: 'none'}}>
87
87
  <div
88
88
  className="col-md-6 col-md-offset-3">
89
89
  Only FASTA files please.
@@ -99,6 +99,7 @@ var DnD = React.createClass({
99
99
 
100
100
  componentDidMount: function () {
101
101
  var self = this;
102
+ var FASTA_FORMAT = /^>/;
102
103
 
103
104
  $(document).ready(function(){
104
105
  var tgtMarker = $('.dnd-overlay');
@@ -110,80 +111,80 @@ var DnD = React.createClass({
110
111
  };
111
112
 
112
113
  $(document)
113
- .on('dragenter', function (evt) {
114
+ .on('dragenter', function (evt) {
114
115
  // Do not activate DnD if a modal is active.
115
- if ($.modalActive()) return;
116
+ if ($.modalActive()) return;
116
117
 
117
- // Based on http://stackoverflow.com/a/8494918/1205465.
118
- // Contrary to what the above link says, the snippet below can't
119
- // distinguish directories from files. We handle that on drop.
120
- var dt = evt.originalEvent.dataTransfer;
121
- var isFile = dt.types && ((dt.types.indexOf && // Chrome and Safari
118
+ // Based on http://stackoverflow.com/a/8494918/1205465.
119
+ // Contrary to what the above link says, the snippet below can't
120
+ // distinguish directories from files. We handle that on drop.
121
+ var dt = evt.originalEvent.dataTransfer;
122
+ var isFile = dt.types && ((dt.types.indexOf && // Chrome and Safari
122
123
  dt.types.indexOf('Files') != -1) ||
123
124
  (dt.types.contains && // Firefox
124
125
  dt.types.contains('application/x-moz-file')));
125
126
 
126
- if (!isFile) { return; }
127
+ if (!isFile) { return; }
127
128
 
128
- $('.dnd-error').hide();
129
- tgtMarker.stop(true, true);
130
- tgtMarker.show();
131
- dt.effectAllowed = 'copy';
132
- if (self.state.query.isEmpty()) {
133
- $('.dnd-overlay-overwrite').hide();
134
- $('.dnd-overlay-drop').show('drop', {direction: 'down'}, 'fast');
135
- }
136
- else {
129
+ $('.dnd-error').hide();
130
+ tgtMarker.stop(true, true);
131
+ tgtMarker.show();
132
+ dt.effectAllowed = 'copy';
133
+ if (self.state.query.isEmpty()) {
134
+ $('.dnd-overlay-overwrite').hide();
135
+ $('.dnd-overlay-drop').show('drop', {direction: 'down'}, 'fast');
136
+ }
137
+ else {
138
+ $('.dnd-overlay-drop').hide();
139
+ $('.dnd-overlay-overwrite').show('drop', {direction: 'down'}, 'fast');
140
+ }
141
+ })
142
+ .on('dragleave', '.dnd-overlay', function (evt) {
143
+ tgtMarker.hide();
137
144
  $('.dnd-overlay-drop').hide();
138
- $('.dnd-overlay-overwrite').show('drop', {direction: 'down'}, 'fast');
139
- }
140
- })
141
- .on('dragleave', '.dnd-overlay', function (evt) {
142
- tgtMarker.hide();
143
- $('.dnd-overlay-drop').hide();
144
- $('.dnd-overlay-overwrite').hide();
145
- })
146
- .on('dragover', '.dnd-overlay', function (evt) {
147
- evt.originalEvent.dataTransfer.dropEffect = 'copy';
148
- evt.preventDefault();
149
- })
150
- .on('drop', '.dnd-overlay', function (evt) {
151
- evt.preventDefault();
152
- evt.stopPropagation();
153
-
154
- var indicator = $('#sequence-file');
155
- self.state.query.focus();
156
-
157
- var files = evt.originalEvent.dataTransfer.files;
158
- if (files.length > 1) {
159
- dndError('dnd-multi');
160
- return;
161
- }
145
+ $('.dnd-overlay-overwrite').hide();
146
+ })
147
+ .on('dragover', '.dnd-overlay', function (evt) {
148
+ evt.originalEvent.dataTransfer.dropEffect = 'copy';
149
+ evt.preventDefault();
150
+ })
151
+ .on('drop', '.dnd-overlay', function (evt) {
152
+ evt.preventDefault();
153
+ evt.stopPropagation();
154
+
155
+ var indicator = $('#sequence-file');
156
+ self.state.query.focus();
157
+
158
+ var files = evt.originalEvent.dataTransfer.files;
159
+ if (files.length > 1) {
160
+ dndError('dnd-multi');
161
+ return;
162
+ }
162
163
 
163
- var file = files[0];
164
- if (file.size > 10 * 1048576) {
165
- dndError('dnd-large-file');
166
- return;
167
- }
164
+ var file = files[0];
165
+ if (file.size > 10 * 1048576) {
166
+ dndError('dnd-large-file');
167
+ return;
168
+ }
168
169
 
169
- var reader = new FileReader();
170
- reader.onload = function (e) {
171
- var content = e.target.result;
172
- if (SequenceServer.FASTA_FORMAT.test(content)) {
173
- indicator.text(file.name + ' ');
174
- self.state.query.value(content);
175
- tgtMarker.hide();
176
- } else {
170
+ var reader = new FileReader();
171
+ reader.onload = function (e) {
172
+ var content = e.target.result;
173
+ if (FASTA_FORMAT.test(content)) {
174
+ indicator.text(file.name + ' ');
175
+ self.state.query.value(content);
176
+ tgtMarker.hide();
177
+ } else {
177
178
  // apparently not FASTA
178
- dndError('dnd-format');
179
- }
180
- };
181
- reader.onerror = function (e) {
179
+ dndError('dnd-format');
180
+ }
181
+ };
182
+ reader.onerror = function (e) {
182
183
  // Couldn't read. Means dropped stuff wasn't FASTA file.
183
- dndError('dnd-format');
184
- };
185
- reader.readAsText(file);
186
- });
184
+ dndError('dnd-format');
185
+ };
186
+ reader.readAsText(file);
187
+ });
187
188
  });
188
189
  }
189
190
  });
@@ -203,32 +204,39 @@ var Form = React.createClass({
203
204
  componentDidMount: function () {
204
205
  /* Fetch data to initialise the search interface from the server. These
205
206
  * include list of databases to search against, advanced options to
206
- * apply when an algorithm is selected, and a query sequenced that
207
+ * apply when an algorithm is selected, and a query sequence that
207
208
  * the user may want to search in the databases.
208
209
  */
209
- $.getJSON("searchdata.json" + window.location.search, function(data) {
210
+ var search = location.search.split(/\?|&/).filter(Boolean);
211
+ var job_id = sessionStorage.getItem('job_id');
212
+ if (job_id) {
213
+ search.unshift(`job_id=${job_id}`);
214
+ }
215
+ $.getJSON(`searchdata.json?${search.join('&')}`, function(data) {
210
216
  /* Update form state (i.e., list of databases and predefined
211
217
  * advanced options.
212
218
  */
213
219
  this.setState({
214
- databases: data["database"], preDefinedOpts: data["options"]
220
+ databases: data['database'],
221
+ preSelectedDbs: data['preSelectedDbs'],
222
+ preDefinedOpts: data['options']
215
223
  });
216
224
 
217
225
  /* Pre-populate the form with server sent query sequences
218
226
  * (if any).
219
227
  */
220
- if (data["query"]) {
221
- this.refs.query.value(data["query"]);
228
+ if (data['query']) {
229
+ this.refs.query.value(data['query']);
222
230
  }
223
231
  }.bind(this));
224
232
 
225
233
  /* Enable submitting form on Cmd+Enter */
226
- $(document).bind("keydown", _.bind(function (e) {
227
- if (e.ctrlKey && e.keyCode === 13 &&
234
+ $(document).bind('keydown', _.bind(function (e) {
235
+ if (e.ctrlKey && e.keyCode === 13 &&
228
236
  !$('#method').is(':disabled')) {
229
- $(this.getDOMNode()).trigger('submit');
230
- }
231
- }, this));
237
+ $(this.getDOMNode()).trigger('submit');
238
+ }
239
+ }, this));
232
240
  },
233
241
 
234
242
  determineBlastMethod: function () {
@@ -241,26 +249,26 @@ var Form = React.createClass({
241
249
 
242
250
  //database type is always known
243
251
  switch (database_type) {
252
+ case 'protein':
253
+ switch (sequence_type) {
254
+ case undefined:
255
+ return ['blastp', 'blastx'];
244
256
  case 'protein':
245
- switch (sequence_type) {
246
- case undefined:
247
- return ['blastp', 'blastx'];
248
- case 'protein':
249
- return ['blastp'];
250
- case 'nucleotide':
251
- return ['blastx'];
252
- }
253
- break;
257
+ return ['blastp'];
254
258
  case 'nucleotide':
255
- switch (sequence_type) {
256
- case undefined:
257
- return ['tblastn', 'blastn', 'tblastx'];
258
- case 'protein':
259
- return ['tblastn'];
260
- case 'nucleotide':
261
- return ['blastn', 'tblastx'];
262
- }
263
- break;
259
+ return ['blastx'];
260
+ }
261
+ break;
262
+ case 'nucleotide':
263
+ switch (sequence_type) {
264
+ case undefined:
265
+ return ['tblastn', 'blastn', 'tblastx'];
266
+ case 'protein':
267
+ return ['tblastn'];
268
+ case 'nucleotide':
269
+ return ['blastn', 'tblastx'];
270
+ }
271
+ break;
264
272
  }
265
273
 
266
274
  return [];
@@ -285,35 +293,32 @@ var Form = React.createClass({
285
293
  },
286
294
 
287
295
  handleAlgoChanged: function (algo) {
288
- if (this.state.preDefinedOpts.hasOwnProperty(algo)) {
289
- this.refs.opts.setState({
290
- preOpts: this.state.preDefinedOpts[algo].join(" ")
291
- });
292
- }
293
- else {
294
- this.refs.opts.setState({preOpts: ""});
295
- }
296
+ if (this.state.preDefinedOpts.hasOwnProperty(algo)) {
297
+ this.refs.opts.setState({
298
+ preOpts: this.state.preDefinedOpts[algo].join(' ')
299
+ });
300
+ }
301
+ else {
302
+ this.refs.opts.setState({preOpts: ''});
303
+ }
296
304
  },
297
305
 
298
306
  render: function () {
299
307
  return (
300
- <div
301
- className="container">
302
- <form
303
- id="blast" method="post" className="form-horizontal">
304
- <div
305
- className="form-group query-container">
308
+ <div className="container">
309
+ <form id="blast" method="post" className="form-horizontal">
310
+ <div className="form-group query-container">
306
311
  <Query ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged}/>
307
312
  </div>
308
- <div
309
- className="notifications" id="notifications">
313
+ <div className="notifications" id="notifications">
310
314
  <NucleotideNotification/>
311
315
  <ProteinNotification/>
312
316
  <MixedNotification/>
313
317
  </div>
314
- <Databases ref="databases" onDatabaseTypeChanged={this.handleDatabaseTypeChanaged} databases={this.state.databases}/>
315
- <div
316
- className="form-group">
318
+ <Databases ref="databases" databases={this.state.databases}
319
+ preSelectedDbs={this.state.preSelectedDbs}
320
+ onDatabaseTypeChanged={this.handleDatabaseTypeChanaged} />
321
+ <div className="form-group">
317
322
  <Options ref="opts"/>
318
323
  <SearchButton ref="button" onAlgoChanged={this.handleAlgoChanged}/>
319
324
  </div>
@@ -346,7 +351,7 @@ var Query = React.createClass({
346
351
  else {
347
352
  this.setState({
348
353
  value: val
349
- })
354
+ });
350
355
  return this;
351
356
  }
352
357
  },
@@ -451,12 +456,12 @@ var Query = React.createClass({
451
456
  if (!tmp) { continue; }
452
457
 
453
458
  if (!type) {
454
- // successfully guessed the type of atleast one sequence
455
- type = tmp;
459
+ // successfully guessed the type of atleast one sequence
460
+ type = tmp;
456
461
  }
457
462
  else if (tmp !== type) {
458
- // user has mixed different type of sequences
459
- return 'mixed';
463
+ // user has mixed different type of sequences
464
+ return 'mixed';
460
465
  }
461
466
  }
462
467
 
@@ -511,7 +516,7 @@ var Query = React.createClass({
511
516
  // Lifecycle methods. //
512
517
 
513
518
  getInitialState: function () {
514
- var input_sequence = $("input#input_sequence").val() || '';
519
+ var input_sequence = $('input#input_sequence').val() || '';
515
520
  return {
516
521
  value: input_sequence
517
522
  };
@@ -619,21 +624,16 @@ var MixedNotification = React.createClass({
619
624
 
620
625
  var Databases = React.createClass({
621
626
  getInitialState: function () {
622
- return {
623
- type: '',
624
- databases: []
625
- }
627
+ return { type: '' };
626
628
  },
627
629
 
628
630
  databases: function (category) {
629
- if (!category) {
630
- return this.props.databases.slice();
631
+ var databases = this.props.databases;
632
+ if (category) {
633
+ databases = _.select(databases, database => database.type === category);
631
634
  }
632
635
 
633
- return _.select(this.props.databases,
634
- function (database) {
635
- return database.type === category;
636
- });
636
+ return _.sortBy(databases, 'title');
637
637
  },
638
638
 
639
639
  nselected: function () {
@@ -642,7 +642,7 @@ var Databases = React.createClass({
642
642
 
643
643
  categories: function () {
644
644
  return _.uniq(_.map(this.props.databases,
645
- _.iteratee('type'))).sort();
645
+ _.iteratee('type'))).sort();
646
646
  },
647
647
 
648
648
  handleClick: function (database) {
@@ -652,12 +652,12 @@ var Databases = React.createClass({
652
652
 
653
653
  handleToggle: function (toggleState, type) {
654
654
  switch (toggleState) {
655
- case '[Select all]':
656
- $(`.${type} .database input:not(:checked)`).click();
657
- break;
658
- case '[Deselect all]':
659
- $(`.${type} .database input:checked`).click();
660
- break;
655
+ case '[Select all]':
656
+ $(`.${type} .database input:not(:checked)`).click();
657
+ break;
658
+ case '[Deselect all]':
659
+ $(`.${type} .database input:checked`).click();
660
+ break;
661
661
  }
662
662
  },
663
663
 
@@ -672,13 +672,13 @@ var Databases = React.createClass({
672
672
  renderDatabases: function (category) {
673
673
  // Panel name and column width.
674
674
  var panelTitle = category[0].toUpperCase() +
675
- category.substring(1).toLowerCase() + " databases";
675
+ category.substring(1).toLowerCase() + ' databases';
676
676
  var columnClass = this.categories().length === 1 ? 'col-md-12' :
677
677
  'col-md-6';
678
678
 
679
679
  // Toggle button.
680
680
  var toggleState = '[Select all]';
681
- var toggleClass = 'btn btn-link';
681
+ var toggleClass = 'btn-link';
682
682
  var toggleShown = this.databases(category).length > 1 ;
683
683
  var toggleDisabled = this.state.type && this.state.type !== category;
684
684
  if (toggleShown && toggleDisabled) toggleClass += ' disabled';
@@ -689,20 +689,20 @@ var Databases = React.createClass({
689
689
 
690
690
  // JSX.
691
691
  return (
692
- <div className={columnClass} key={"DB_"+category}>
692
+ <div className={columnClass} key={'DB_'+category}>
693
693
  <div className="panel panel-default">
694
694
  <div className="panel-heading">
695
- <h4 style={{display: "inline"}}>{panelTitle}</h4> &nbsp;&nbsp;
695
+ <h4 style={{display: 'inline'}}>{panelTitle}</h4> &nbsp;&nbsp;
696
696
  <button type="button" className={toggleClass} disabled={toggleDisabled}
697
- onClick={ function () { this.handleToggle(toggleState, category) }.bind(this) }>
697
+ onClick={ function () { this.handleToggle(toggleState, category); }.bind(this) }>
698
698
  {toggleState}
699
699
  </button>
700
700
  </div>
701
- <ul className={"list-group databases " + category}>
701
+ <ul className={'list-group databases ' + category}>
702
702
  {
703
703
  _.map(this.databases(category), _.bind(function (database,index) {
704
704
  return (
705
- <li className="list-group-item" key={"DB_"+category+index}>
705
+ <li className="list-group-item" key={'DB_'+category+index}>
706
706
  { this.renderDatabase(database) }
707
707
  </li>
708
708
  );
@@ -724,18 +724,18 @@ var Databases = React.createClass({
724
724
  type="checkbox" name="databases[]" value={database.id}
725
725
  data-type={database.type} disabled={disabled}
726
726
  onChange=
727
- {
728
- _.bind(function () {
729
- this.handleClick(database)
730
- }, this)
731
- }/>
732
- {" " + (database.title || database.name)}
727
+ {
728
+ _.bind(function () {
729
+ this.handleClick(database);
730
+ }, this)
731
+ }/>
732
+ {' ' + (database.title || database.name)}
733
733
  </label>
734
734
  );
735
735
  },
736
736
 
737
737
  //shouldComponentUpdate: function (props, state) {
738
- //return !(state.type && state.type === this.state.type);
738
+ //return !(state.type && state.type === this.state.type);
739
739
  //},
740
740
 
741
741
  componentDidUpdate: function () {
@@ -744,47 +744,49 @@ var Databases = React.createClass({
744
744
  this.handleClick(this.databases()[0]);
745
745
  }
746
746
 
747
+ if (this.props.preSelectedDbs) {
748
+ var selectors = this.props.preSelectedDbs.map(db => `input[value=${db.id}]`);
749
+ $(...selectors).prop('checked',true);
750
+ setTimeout(() => {
751
+ this.handleClick(this.props.preSelectedDbs[0]);
752
+ this.props.preSelectedDbs = null;
753
+ });
754
+ }
747
755
  this.props.onDatabaseTypeChanged(this.state.type);
748
756
  }
749
757
  });
750
758
 
759
+ // Component for the advanced params input field.
751
760
  var Options = React.createClass({
752
-
753
- updateBox: function (evt) {
754
- this.setState({
755
- preOpts: evt.target.value
756
- });
761
+ // State of this component is the advanced params text.
762
+ getInitialState: function () {
763
+ return { preOpts: '' };
757
764
  },
758
765
 
759
- getInitialState: function () {
760
- return {
761
- preOpts: ""
762
- }
766
+ updateBox: function (evt) {
767
+ this.setState({
768
+ preOpts: evt.target.value
769
+ });
763
770
  },
764
771
 
765
772
  render: function () {
773
+ var classNames = 'form-control';
774
+ if (this.state.preOpts.trim()) {
775
+ classNames += ' yellow-background';
776
+ }
766
777
  return (
767
- <div
768
- className="col-md-8">
769
- <div
770
- className="form-group">
771
- <div
772
- className="col-md-12">
773
- <div
774
- className="input-group">
775
- <label
776
- className="control-label"
777
- htmlFor="advanced">
778
+ <div className="col-md-8">
779
+ <div className="form-group">
780
+ <div className="col-md-12">
781
+ <div className="input-group">
782
+ <label className="control-label" htmlFor="advanced">
778
783
  Advanced parameters:
779
784
  </label>
780
- <input
781
- type="text"
782
- className="form-control" name="advanced" id="advanced"
783
- title="View, and enter advanced parameters."
785
+ <input type="text" className={classNames} onChange={this.updateBox}
786
+ id="advanced" name="advanced" value={this.state.preOpts}
784
787
  placeholder="eg: -evalue 1.0e-5 -num_alignments 100"
785
- value={this.state.preOpts}
786
- onChange={this.updateBox}
787
- />
788
+ title="View, and enter advanced parameters."
789
+ />
788
790
  <div
789
791
  className="input-group-addon cursor-pointer"
790
792
  data-toggle="modal" data-target="#help">
@@ -827,23 +829,23 @@ var SearchButton = React.createClass({
827
829
  trigger: 'manual',
828
830
  title: _.bind(function () {
829
831
  if (!this.state.hasQuery && !this.state.hasDatabases) {
830
- return "You must enter a query sequence and select one or more databases above before you can run a search!";
832
+ return 'You must enter a query sequence and select one or more databases above before you can run a search!';
831
833
  }
832
834
  else if (this.state.hasQuery && !this.state.hasDatabases) {
833
- return "You must select one or more databases above before you can run a search!";
835
+ return 'You must select one or more databases above before you can run a search!';
834
836
  }
835
837
  else if (!this.state.hasQuery && this.state.hasDatabases) {
836
- return "You must enter a query sequence above before you can run a search!";
838
+ return 'You must enter a query sequence above before you can run a search!';
837
839
  }
838
840
  }, this)
839
841
  });
840
842
 
841
843
  this.submitButton().tooltip({
842
844
  title: _.bind(function () {
843
- var title = "Click to BLAST or press Ctrl+Enter.";
845
+ var title = 'Click to BLAST or press Ctrl+Enter.';
844
846
  if (this.state.methods.length > 1) {
845
- title += " Click dropdown button on the right for other" +
846
- " BLAST algorithms that can be used.";
847
+ title += ' Click dropdown button on the right for other' +
848
+ ' BLAST algorithms that can be used.';
847
849
  }
848
850
  return title;
849
851
  }, this)
@@ -902,7 +904,7 @@ var SearchButton = React.createClass({
902
904
  methods: [],
903
905
  hasQuery: false,
904
906
  hasDatabases: false
905
- }
907
+ };
906
908
  },
907
909
 
908
910
  render: function () {
@@ -970,7 +972,7 @@ var SearchButton = React.createClass({
970
972
  this.props.onAlgoChanged(this.state.methods[0]);
971
973
  }
972
974
  else {
973
- this.props.onAlgoChanged("");
975
+ this.props.onAlgoChanged('');
974
976
  }
975
977
  }
976
978
  });