sequenceserver 2.0.0.beta4 → 2.0.0.rc5

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 (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
  });