sequenceserver 1.1.0.beta8 → 1.1.0.beta10

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,23 @@ import React from 'react';
7
7
  */
8
8
  $.webshims.polyfill('forms');
9
9
 
10
+ var Page = React.createClass({
11
+ render: function () {
12
+ return (
13
+ <div>
14
+ <DnD ref="dnd"/>
15
+ <Form ref="form"/>
16
+ </div>
17
+ );
18
+ },
19
+
20
+ componentDidMount: function () {
21
+ this.refs.dnd.setState({
22
+ query: this.refs.form.refs.query
23
+ })
24
+ }
25
+ });
26
+
10
27
  /** Drag n drop widget.
11
28
  */
12
29
  var DnD = React.createClass({
@@ -171,6 +188,135 @@ var DnD = React.createClass({
171
188
  }
172
189
  });
173
190
 
191
+ /**
192
+ * Search form.
193
+ *
194
+ * Top level component that initialises and holds all other components, and
195
+ * facilitates communication between them.
196
+ */
197
+ var Form = React.createClass({
198
+
199
+ getInitialState: function () {
200
+ return {
201
+ databases: {},
202
+ preDefinedOpts: {
203
+ 'blastn': ['-task blastn', '-evalue 1e-5'],
204
+ 'blastp': ['-evalue 1e-5'],
205
+ 'blastx': ['-evalue 1e-5'],
206
+ 'tblastx': ['-evalue 1e-5'],
207
+ 'tblastn': ['-evalue 1e-5']
208
+ }
209
+ };
210
+ },
211
+
212
+ componentDidMount: function () {
213
+ $.getJSON("searchdata.json", _.bind(function(data) {
214
+ this.setState({
215
+ databases: data["database"], preDefinedOpts: $.extend({},
216
+ this.state.preDefinedOpts, data["options"])
217
+ });
218
+ }, this));
219
+
220
+ $(document).bind("keydown", _.bind(function (e) {
221
+ if (e.ctrlKey && e.keyCode === 13 &&
222
+ !$('#method').is(':disabled')) {
223
+ $(this.getDOMNode()).trigger('submit');
224
+ }
225
+ }, this));
226
+ },
227
+
228
+ determineBlastMethod: function () {
229
+ var database_type = this.databaseType;
230
+ var sequence_type = this.sequenceType;
231
+
232
+ if (this.refs.query.isEmpty()) {
233
+ return [];
234
+ }
235
+
236
+ //database type is always known
237
+ switch (database_type) {
238
+ case 'protein':
239
+ switch (sequence_type) {
240
+ case undefined:
241
+ return ['blastp', 'blastx'];
242
+ case 'protein':
243
+ return ['blastp'];
244
+ case 'nucleotide':
245
+ return ['blastx'];
246
+ }
247
+ break;
248
+ case 'nucleotide':
249
+ switch (sequence_type) {
250
+ case undefined:
251
+ return ['tblastn', 'blastn', 'tblastx'];
252
+ case 'protein':
253
+ return ['tblastn'];
254
+ case 'nucleotide':
255
+ return ['blastn', 'tblastx'];
256
+ }
257
+ break;
258
+ }
259
+
260
+ return [];
261
+ },
262
+
263
+ handleSequenceTypeChanged: function (type) {
264
+ this.sequenceType = type;
265
+ this.refs.button.setState({
266
+ hasQuery: !this.refs.query.isEmpty(),
267
+ hasDatabases: !!this.databaseType,
268
+ methods: this.determineBlastMethod()
269
+ });
270
+ },
271
+
272
+ handleDatabaseTypeChanaged: function (type) {
273
+ this.databaseType = type;
274
+ this.refs.button.setState({
275
+ hasQuery: !this.refs.query.isEmpty(),
276
+ hasDatabases: !!this.databaseType,
277
+ methods: this.determineBlastMethod()
278
+ });
279
+ },
280
+
281
+ handleAlgoChanged: function (algo) {
282
+ if (this.state.preDefinedOpts.hasOwnProperty(algo)) {
283
+ this.refs.opts.setState({
284
+ preOpts: this.state.preDefinedOpts[algo].join(" ")
285
+ });
286
+ }
287
+ else {
288
+ this.refs.opts.setState({preOpts: ""});
289
+ }
290
+ },
291
+
292
+ render: function () {
293
+ return (
294
+ <div
295
+ className="container">
296
+ <form
297
+ id="blast" method="post" className="form-horizontal">
298
+ <div
299
+ className="form-group query-container">
300
+ <Query ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged}/>
301
+ </div>
302
+ <div
303
+ className="notifications" id="notifications">
304
+ <NucleotideNotification/>
305
+ <ProteinNotification/>
306
+ <MixedNotification/>
307
+ </div>
308
+ <Databases ref="databases" onDatabaseTypeChanged={this.handleDatabaseTypeChanaged} databases={this.state.databases}/>
309
+ <div
310
+ className="form-group">
311
+ <Options ref="opts"/>
312
+ <SearchButton ref="button" onAlgoChanged={this.handleAlgoChanged}/>
313
+ </div>
314
+ </form>
315
+ </div>
316
+ );
317
+ }
318
+ });
319
+
174
320
  /**
175
321
  * Query widget.
176
322
  */
@@ -487,42 +633,70 @@ var Databases = React.createClass({
487
633
  },
488
634
 
489
635
  handleClick: function (database) {
490
- var type = this.nselected() ? database.type : ''
636
+ var type = this.nselected() ? database.type : '';
491
637
  this.setState({type: type});
492
638
  },
493
639
 
640
+ handleToggle: function (toggleState, type) {
641
+ switch (toggleState) {
642
+ case '[Select all]':
643
+ $(`.${type} .database input:not(:checked)`).click();
644
+ break;
645
+ case '[Deselect all]':
646
+ $(`.${type} .database input:checked`).click();
647
+ break;
648
+ }
649
+ },
650
+
494
651
  render: function () {
495
652
  return (
496
- <div
497
- className="form-group databases-container">
498
- {
499
- _.map(this.categories(), _.bind(function (category) {
500
- return (
501
- <div
502
- className={this.categories().length === 1 ? 'col-md-12' : 'col-md-6'}>
503
- <div
504
- className="panel panel-default">
505
- <div
506
- className="panel-heading">
507
- <h4>{category[0].toUpperCase() + category.substring(1).toLowerCase() + " databases"}</h4>
508
- </div>
509
- <ul
510
- className={"list-group databases " + category}>
511
- {
512
- _.map(this.databases(category), _.bind(function (database) {
513
- return (
514
- <li className="list-group-item">
515
- { this.renderDatabase(database) }
516
- </li>
517
- );
518
- }, this))
519
- }
520
- </ul>
521
- </div>
522
- </div>
523
- )
524
- }, this))
525
- }
653
+ <div className="form-group databases-container">
654
+ { _.map(this.categories(), this.renderDatabases) }
655
+ </div>
656
+ );
657
+ },
658
+
659
+ renderDatabases: function (category) {
660
+ // Panel name and column width.
661
+ var panelTitle = category[0].toUpperCase() +
662
+ category.substring(1).toLowerCase() + " databases";
663
+ var columnClass = this.categories().length === 1 ? 'col-md-12' :
664
+ 'col-md-6';
665
+
666
+ // Toggle button.
667
+ var toggleState = '[Select all]';
668
+ var toggleClass = 'btn btn-link';
669
+ var toggleShown = this.databases(category).length > 1 ;
670
+ var toggleDisabled = this.state.type && this.state.type !== category;
671
+ if (toggleShown && toggleDisabled) toggleClass += ' disabled';
672
+ if (!toggleShown) toggleClass += ' hidden';
673
+ if (this.nselected() === this.databases(category).length) {
674
+ toggleState = '[Deselect all]';
675
+ }
676
+
677
+ // JSX.
678
+ return (
679
+ <div className={columnClass}>
680
+ <div className="panel panel-default">
681
+ <div className="panel-heading">
682
+ <h4 style={{display: "inline"}}>{panelTitle}</h4> &nbsp;&nbsp;
683
+ <button type="button" className={toggleClass} disabled={toggleDisabled}
684
+ onClick={ function () { this.handleToggle(toggleState, category) }.bind(this) }>
685
+ {toggleState}
686
+ </button>
687
+ </div>
688
+ <ul className={"list-group databases " + category}>
689
+ {
690
+ _.map(this.databases(category), _.bind(function (database) {
691
+ return (
692
+ <li className="list-group-item">
693
+ { this.renderDatabase(database) }
694
+ </li>
695
+ );
696
+ }, this))
697
+ }
698
+ </ul>
699
+ </div>
526
700
  </div>
527
701
  );
528
702
  },
@@ -547,9 +721,9 @@ var Databases = React.createClass({
547
721
  );
548
722
  },
549
723
 
550
- shouldComponentUpdate: function (props, state) {
551
- return !(state.type && state.type === this.state.type);
552
- },
724
+ //shouldComponentUpdate: function (props, state) {
725
+ //return !(state.type && state.type === this.state.type);
726
+ //},
553
727
 
554
728
  componentDidUpdate: function () {
555
729
  if (this.databases() && this.databases().length === 1) {
@@ -788,150 +962,4 @@ var SearchButton = React.createClass({
788
962
  }
789
963
  });
790
964
 
791
- /**
792
- * Search form.
793
- *
794
- * Top level component that initialises and holds all other components, and
795
- * facilitates communication between them.
796
- */
797
- var Form = React.createClass({
798
-
799
- getInitialState: function () {
800
- return {
801
- databases: {},
802
- preDefinedOpts: {
803
- 'blastn': ['-task blastn', '-evalue 1e-5'],
804
- 'blastp': ['-evalue 1e-5'],
805
- 'blastx': ['-evalue 1e-5'],
806
- 'tblastx': ['-evalue 1e-5'],
807
- 'tblastn': ['-evalue 1e-5']
808
- }
809
- };
810
- },
811
-
812
- componentDidMount: function () {
813
- $.getJSON("searchdata.json", _.bind(function(data) {
814
- this.setState({
815
- databases: data["database"], preDefinedOpts: $.extend({},
816
- this.state.preDefinedOpts, data["options"])
817
- });
818
- }, this));
819
-
820
- $(document).bind("keydown", _.bind(function (e) {
821
- if (e.ctrlKey && e.keyCode === 13 &&
822
- !$('#method').is(':disabled')) {
823
- $(this.getDOMNode()).trigger('submit');
824
- }
825
- }, this));
826
- },
827
-
828
- determineBlastMethod: function () {
829
- var database_type = this.databaseType;
830
- var sequence_type = this.sequenceType;
831
-
832
- if (this.refs.query.isEmpty()) {
833
- return [];
834
- }
835
-
836
- //database type is always known
837
- switch (database_type) {
838
- case 'protein':
839
- switch (sequence_type) {
840
- case undefined:
841
- return ['blastp', 'blastx'];
842
- case 'protein':
843
- return ['blastp'];
844
- case 'nucleotide':
845
- return ['blastx'];
846
- }
847
- break;
848
- case 'nucleotide':
849
- switch (sequence_type) {
850
- case undefined:
851
- return ['tblastn', 'blastn', 'tblastx'];
852
- case 'protein':
853
- return ['tblastn'];
854
- case 'nucleotide':
855
- return ['blastn', 'tblastx'];
856
- }
857
- break;
858
- }
859
-
860
- return [];
861
- },
862
-
863
- handleSequenceTypeChanged: function (type) {
864
- this.sequenceType = type;
865
- this.refs.button.setState({
866
- hasQuery: !this.refs.query.isEmpty(),
867
- hasDatabases: !!this.databaseType,
868
- methods: this.determineBlastMethod()
869
- });
870
- },
871
-
872
- handleDatabaseTypeChanaged: function (type) {
873
- this.databaseType = type;
874
- this.refs.button.setState({
875
- hasQuery: !this.refs.query.isEmpty(),
876
- hasDatabases: !!this.databaseType,
877
- methods: this.determineBlastMethod()
878
- });
879
- },
880
-
881
- handleAlgoChanged: function (algo) {
882
- if (this.state.preDefinedOpts.hasOwnProperty(algo)) {
883
- this.refs.opts.setState({
884
- preOpts: this.state.preDefinedOpts[algo].join(" ")
885
- });
886
- }
887
- else {
888
- this.refs.opts.setState({preOpts: ""});
889
- }
890
- },
891
-
892
- render: function () {
893
- return (
894
- <div
895
- className="container">
896
- <form
897
- id="blast" method="post" className="form-horizontal">
898
- <div
899
- className="form-group query-container">
900
- <Query ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged}/>
901
- </div>
902
- <div
903
- className="notifications" id="notifications">
904
- <NucleotideNotification/>
905
- <ProteinNotification/>
906
- <MixedNotification/>
907
- </div>
908
- <Databases ref="databases" onDatabaseTypeChanged={this.handleDatabaseTypeChanaged} databases={this.state.databases}/>
909
- <div
910
- className="form-group">
911
- <Options ref="opts"/>
912
- <SearchButton ref="button" onAlgoChanged={this.handleAlgoChanged}/>
913
- </div>
914
- </form>
915
- </div>
916
- );
917
- }
918
- });
919
-
920
- var Page = React.createClass({
921
- render: function () {
922
- return (
923
- <div>
924
- <DnD ref="dnd"/>
925
- <Form ref="form"/>
926
- </div>
927
- );
928
- },
929
-
930
- componentDidMount: function () {
931
- this.refs.dnd.setState({
932
- query: this.refs.form.refs.query
933
- })
934
- }
935
- });
936
-
937
965
  React.render(<Page/>, document.getElementById('view'));
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+
3
+ var Utils = {
4
+
5
+ /**
6
+ * Render URL for sequence-viewer.
7
+ */
8
+ a: function (link) {
9
+ if (link.title && link.url)
10
+ {
11
+ return (
12
+ <a href={link.url} className={link.class} target='_blank'>
13
+ {link.icon && <i className={"fa " + link.icon}></i>}
14
+ {" " + link.title + " "}
15
+ </a>
16
+ );
17
+ }
18
+ },
19
+
20
+
21
+ /***********************************
22
+ * Formatters for hits & hsp table *
23
+ ***********************************/
24
+
25
+ // Formats an array of two elements as "first (last)".
26
+ format_2_tuple: function (tuple) {
27
+ return (tuple[0] + " (" + tuple[tuple.length - 1] + ")");
28
+ },
29
+
30
+ /**
31
+ * Returns fraction as percentage
32
+ */
33
+ inPercentage: function (num , den) {
34
+ return `${(num * 100.0 / den).toFixed(2)}%`;
35
+ },
36
+
37
+ /**
38
+ * Returns fractional representation as String.
39
+ */
40
+ inFraction: function (num , den) {
41
+ return num + "/" + den;
42
+ },
43
+
44
+ /**
45
+ * Returns given Float as String formatted to two decimal places.
46
+ */
47
+ inTwoDecimal: function (num) {
48
+ return num.toFixed(2)
49
+ },
50
+
51
+ /**
52
+ * Returns zero if num is zero. Returns two decimal representation of num
53
+ * if num is between [1..10). Returns num in scientific notation otherwise.
54
+ */
55
+ inExponential: function (num) {
56
+ // Nothing to do if num is 0.
57
+ if (num === 0) {
58
+ return 0
59
+ }
60
+
61
+ // Round to two decimal places if in the rane [1..10).
62
+ if (num >= 1 && num < 10)
63
+ {
64
+ return this.inTwoDecimal(num)
65
+ }
66
+
67
+ // Return numbers in the range [0..1) and [10..Inf] in
68
+ // scientific format.
69
+ var exp = num.toExponential(2);
70
+ var parts = exp.split("e");
71
+ var base = parts[0];
72
+ var power = parts[1];
73
+ return <span>{base} &times; 10<sup>{power}</sup></span>;
74
+ }
75
+ };
76
+
77
+ export default Utils;