sequenceserver 1.1.0.beta8 → 1.1.0.beta10

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.

@@ -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;