sequenceserver 2.0.0.beta4 → 2.0.0.rc1

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.

Potentially problematic release.


This version of sequenceserver might be problematic. Click here for more details.

Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -4
  3. data/AppImage/sequenceserver.sh +5 -0
  4. data/lib/sequenceserver.rb +9 -5
  5. data/lib/sequenceserver/blast/job.rb +7 -24
  6. data/lib/sequenceserver/blast/report.rb +66 -33
  7. data/lib/sequenceserver/routes.rb +28 -2
  8. data/lib/sequenceserver/version.rb +1 -1
  9. data/public/SequenceServer_logo.png +0 -0
  10. data/public/css/grapher.css +8 -15
  11. data/public/css/sequenceserver.css +115 -55
  12. data/public/css/sequenceserver.min.css +3 -3
  13. data/public/js/circos.js +1 -1
  14. data/public/js/download_fasta.js +17 -0
  15. data/public/js/grapher.js +7 -9
  16. data/public/js/hit.js +217 -0
  17. data/public/js/hits_overview.js +12 -13
  18. data/public/js/hsp.js +104 -84
  19. data/public/js/{sequenceserver.js → jquery_world.js} +1 -18
  20. data/public/js/kablammo.js +337 -334
  21. data/public/js/length_distribution.js +1 -1
  22. data/public/js/query.js +147 -0
  23. data/public/js/report.js +203 -830
  24. data/public/js/search.js +176 -169
  25. data/public/js/sequence_modal.js +167 -0
  26. data/public/js/sidebar.js +210 -0
  27. data/public/js/utils.js +2 -19
  28. data/public/js/visualisation_helpers.js +2 -2
  29. data/public/sequenceserver-report.min.js +19 -19
  30. data/public/sequenceserver-search.min.js +11 -11
  31. data/public/vendor/github/twbs/bootstrap@3.3.5/js/bootstrap.js +2 -2
  32. data/spec/blast_versions/blast_2.2.30/import_spec_capybara_local_2.2.30.rb +5 -5
  33. data/spec/blast_versions/blast_2.2.31/import_spec_capybara_local_2.2.31.rb +5 -5
  34. data/spec/blast_versions/blast_2.3.0/import_spec_capybara_local_2.3.0.rb +5 -5
  35. data/spec/blast_versions/blast_2.4.0/import_spec_capybara_local_2.4.0.rb +5 -5
  36. data/spec/blast_versions/blast_2.5.0/import_spec_capybara_local_2.5.0.rb +5 -5
  37. data/spec/blast_versions/blast_2.6.0/import_spec_capybara_local_2.6.0.rb +5 -5
  38. data/spec/blast_versions/blast_2.7.1/import_spec_capybara_local_2.7.1.rb +5 -5
  39. data/spec/blast_versions/blast_2.8.1/import_spec_capybara_local_2.8.1.rb +5 -5
  40. data/spec/blast_versions/blast_2.9.0/import_spec_capybara_local_2.9.0.rb +5 -5
  41. data/spec/blast_versions/diamond_0.9.24/import_spec_capybara_local_0.9.24.rb +2 -2
  42. data/spec/capybara_spec.rb +1 -1
  43. data/views/layout.erb +1 -1
  44. metadata +9 -3
@@ -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,10 +624,7 @@ 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) {
@@ -631,9 +633,9 @@ var Databases = React.createClass({
631
633
  }
632
634
 
633
635
  return _.select(this.props.databases,
634
- function (database) {
635
- return database.type === category;
636
- });
636
+ function (database) {
637
+ return database.type === category;
638
+ });
637
639
  },
638
640
 
639
641
  nselected: function () {
@@ -642,7 +644,7 @@ var Databases = React.createClass({
642
644
 
643
645
  categories: function () {
644
646
  return _.uniq(_.map(this.props.databases,
645
- _.iteratee('type'))).sort();
647
+ _.iteratee('type'))).sort();
646
648
  },
647
649
 
648
650
  handleClick: function (database) {
@@ -652,12 +654,12 @@ var Databases = React.createClass({
652
654
 
653
655
  handleToggle: function (toggleState, type) {
654
656
  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;
657
+ case '[Select all]':
658
+ $(`.${type} .database input:not(:checked)`).click();
659
+ break;
660
+ case '[Deselect all]':
661
+ $(`.${type} .database input:checked`).click();
662
+ break;
661
663
  }
662
664
  },
663
665
 
@@ -672,13 +674,13 @@ var Databases = React.createClass({
672
674
  renderDatabases: function (category) {
673
675
  // Panel name and column width.
674
676
  var panelTitle = category[0].toUpperCase() +
675
- category.substring(1).toLowerCase() + " databases";
677
+ category.substring(1).toLowerCase() + ' databases';
676
678
  var columnClass = this.categories().length === 1 ? 'col-md-12' :
677
679
  'col-md-6';
678
680
 
679
681
  // Toggle button.
680
682
  var toggleState = '[Select all]';
681
- var toggleClass = 'btn btn-link';
683
+ var toggleClass = 'btn-link';
682
684
  var toggleShown = this.databases(category).length > 1 ;
683
685
  var toggleDisabled = this.state.type && this.state.type !== category;
684
686
  if (toggleShown && toggleDisabled) toggleClass += ' disabled';
@@ -689,20 +691,20 @@ var Databases = React.createClass({
689
691
 
690
692
  // JSX.
691
693
  return (
692
- <div className={columnClass} key={"DB_"+category}>
694
+ <div className={columnClass} key={'DB_'+category}>
693
695
  <div className="panel panel-default">
694
696
  <div className="panel-heading">
695
- <h4 style={{display: "inline"}}>{panelTitle}</h4> &nbsp;&nbsp;
697
+ <h4 style={{display: 'inline'}}>{panelTitle}</h4> &nbsp;&nbsp;
696
698
  <button type="button" className={toggleClass} disabled={toggleDisabled}
697
- onClick={ function () { this.handleToggle(toggleState, category) }.bind(this) }>
699
+ onClick={ function () { this.handleToggle(toggleState, category); }.bind(this) }>
698
700
  {toggleState}
699
701
  </button>
700
702
  </div>
701
- <ul className={"list-group databases " + category}>
703
+ <ul className={'list-group databases ' + category}>
702
704
  {
703
705
  _.map(this.databases(category), _.bind(function (database,index) {
704
706
  return (
705
- <li className="list-group-item" key={"DB_"+category+index}>
707
+ <li className="list-group-item" key={'DB_'+category+index}>
706
708
  { this.renderDatabase(database) }
707
709
  </li>
708
710
  );
@@ -724,18 +726,18 @@ var Databases = React.createClass({
724
726
  type="checkbox" name="databases[]" value={database.id}
725
727
  data-type={database.type} disabled={disabled}
726
728
  onChange=
727
- {
728
- _.bind(function () {
729
- this.handleClick(database)
730
- }, this)
731
- }/>
732
- {" " + (database.title || database.name)}
729
+ {
730
+ _.bind(function () {
731
+ this.handleClick(database);
732
+ }, this)
733
+ }/>
734
+ {' ' + (database.title || database.name)}
733
735
  </label>
734
736
  );
735
737
  },
736
738
 
737
739
  //shouldComponentUpdate: function (props, state) {
738
- //return !(state.type && state.type === this.state.type);
740
+ //return !(state.type && state.type === this.state.type);
739
741
  //},
740
742
 
741
743
  componentDidUpdate: function () {
@@ -744,6 +746,11 @@ var Databases = React.createClass({
744
746
  this.handleClick(this.databases()[0]);
745
747
  }
746
748
 
749
+ if (this.props.preSelectedDbs) {
750
+ var selectors = this.props.preSelectedDbs.map(db => `input[value=${db.id}]`);
751
+ $(...selectors).prop('checked',true);
752
+ setTimeout(() => this.handleClick(this.props.preSelectedDbs[0]));
753
+ }
747
754
  this.props.onDatabaseTypeChanged(this.state.type);
748
755
  }
749
756
  });
@@ -751,15 +758,15 @@ var Databases = React.createClass({
751
758
  var Options = React.createClass({
752
759
 
753
760
  updateBox: function (evt) {
754
- this.setState({
755
- preOpts: evt.target.value
756
- });
761
+ this.setState({
762
+ preOpts: evt.target.value
763
+ });
757
764
  },
758
765
 
759
766
  getInitialState: function () {
760
- return {
761
- preOpts: ""
762
- }
767
+ return {
768
+ preOpts: ''
769
+ };
763
770
  },
764
771
 
765
772
  render: function () {
@@ -784,7 +791,7 @@ var Options = React.createClass({
784
791
  placeholder="eg: -evalue 1.0e-5 -num_alignments 100"
785
792
  value={this.state.preOpts}
786
793
  onChange={this.updateBox}
787
- />
794
+ />
788
795
  <div
789
796
  className="input-group-addon cursor-pointer"
790
797
  data-toggle="modal" data-target="#help">
@@ -827,23 +834,23 @@ var SearchButton = React.createClass({
827
834
  trigger: 'manual',
828
835
  title: _.bind(function () {
829
836
  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!";
837
+ return 'You must enter a query sequence and select one or more databases above before you can run a search!';
831
838
  }
832
839
  else if (this.state.hasQuery && !this.state.hasDatabases) {
833
- return "You must select one or more databases above before you can run a search!";
840
+ return 'You must select one or more databases above before you can run a search!';
834
841
  }
835
842
  else if (!this.state.hasQuery && this.state.hasDatabases) {
836
- return "You must enter a query sequence above before you can run a search!";
843
+ return 'You must enter a query sequence above before you can run a search!';
837
844
  }
838
845
  }, this)
839
846
  });
840
847
 
841
848
  this.submitButton().tooltip({
842
849
  title: _.bind(function () {
843
- var title = "Click to BLAST or press Ctrl+Enter.";
850
+ var title = 'Click to BLAST or press Ctrl+Enter.';
844
851
  if (this.state.methods.length > 1) {
845
- title += " Click dropdown button on the right for other" +
846
- " BLAST algorithms that can be used.";
852
+ title += ' Click dropdown button on the right for other' +
853
+ ' BLAST algorithms that can be used.';
847
854
  }
848
855
  return title;
849
856
  }, this)
@@ -902,7 +909,7 @@ var SearchButton = React.createClass({
902
909
  methods: [],
903
910
  hasQuery: false,
904
911
  hasDatabases: false
905
- }
912
+ };
906
913
  },
907
914
 
908
915
  render: function () {
@@ -970,7 +977,7 @@ var SearchButton = React.createClass({
970
977
  this.props.onAlgoChanged(this.state.methods[0]);
971
978
  }
972
979
  else {
973
- this.props.onAlgoChanged("");
980
+ this.props.onAlgoChanged('');
974
981
  }
975
982
  }
976
983
  });