sequenceserver 2.0.0.beta3 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +5 -5
  2. data/.eslintrc.json +36 -0
  3. data/.rubocop.yml +1 -1
  4. data/.travis.yml +53 -20
  5. data/AppImage/recipe.yml +15 -0
  6. data/AppImage/sequenceserver.desktop +8 -0
  7. data/AppImage/sequenceserver.png +0 -0
  8. data/AppImage/sequenceserver.sh +11 -0
  9. data/README.md +79 -46
  10. data/bin/sequenceserver +4 -4
  11. data/lib/sequenceserver/version.rb +1 -1
  12. data/package.json +2 -0
  13. data/public/css/grapher.css +3 -0
  14. data/public/css/sequenceserver.css +17 -6
  15. data/public/css/sequenceserver.min.css +3 -3
  16. data/public/js/circos.js +515 -491
  17. data/public/js/grapher.js +12 -6
  18. data/public/js/hits_overview.js +321 -308
  19. data/public/js/hsp.js +12 -7
  20. data/public/js/length_distribution.js +241 -234
  21. data/public/js/report.js +196 -174
  22. data/public/js/search.js +3 -3
  23. data/public/js/sequenceserver.js +9 -9
  24. data/public/js/utils.js +17 -10
  25. data/public/js/visualisation_helpers.js +77 -77
  26. data/public/sequenceserver-report.min.js +17 -17
  27. data/public/sequenceserver-search.min.js +1 -1
  28. data/public/vendor/github/nicgirault/circosJs@1.7.0/dist/circosJS.js +1 -5
  29. data/sequenceserver.gemspec +1 -2
  30. data/spec/blast_versions/blast_2.2.30/blast_2.2.30_spec.rb +13 -13
  31. data/spec/blast_versions/blast_2.2.30/import_spec_capybara_local_2.2.30.rb +555 -25
  32. data/spec/blast_versions/blast_2.2.31/blast_2.2.31_spec.rb +13 -13
  33. data/spec/blast_versions/blast_2.2.31/import_spec_capybara_local_2.2.31.rb +558 -24
  34. data/spec/blast_versions/blast_2.3.0/blast_2.3.0_spec.rb +13 -13
  35. data/spec/blast_versions/blast_2.3.0/import_spec_capybara_local_2.3.0.rb +561 -26
  36. data/spec/blast_versions/blast_2.4.0/blast_2.4.0_spec.rb +13 -13
  37. data/spec/blast_versions/blast_2.4.0/import_spec_capybara_local_2.4.0.rb +561 -25
  38. data/spec/blast_versions/blast_2.5.0/blast_2.5.0_spec.rb +13 -13
  39. data/spec/blast_versions/blast_2.5.0/import_spec_capybara_local_2.5.0.rb +558 -24
  40. data/spec/blast_versions/blast_2.6.0/blast_2.6.0_spec.rb +13 -13
  41. data/spec/blast_versions/blast_2.6.0/import_spec_capybara_local_2.6.0.rb +559 -24
  42. data/spec/blast_versions/blast_2.7.1/blast_2.7.1_spec.rb +13 -13
  43. data/spec/blast_versions/blast_2.7.1/import_spec_capybara_local_2.7.1.rb +559 -28
  44. data/spec/blast_versions/blast_2.8.1/blast_2.8.1_spec.rb +13 -13
  45. data/spec/blast_versions/blast_2.8.1/import_spec_capybara_local_2.8.1.rb +559 -27
  46. data/spec/blast_versions/blast_2.9.0/blast_2.9.0_spec.rb +13 -13
  47. data/spec/blast_versions/blast_2.9.0/import_spec_capybara_local_2.9.0.rb +557 -25
  48. data/spec/blast_versions/diamond_0.9.24/diamond_0.9.24_spec.rb +13 -13
  49. data/spec/blast_versions/diamond_0.9.24/import_spec_capybara_local_0.9.24.rb +219 -21
  50. data/spec/capybara_spec.rb +25 -28
  51. data/spec/download_helper.rb +6 -3
  52. data/spec/sequences/MH011443_1_gi_1486783306_gb_MH011443_1.txt +6 -0
  53. data/spec/sequences/MH011443_1_gi_1486783307_gb_AYF55702_1.txt +6 -0
  54. data/spec/sequences/MH011443_1_gi_1528997474_gb_MH447967_1.txt +30 -0
  55. data/spec/sequences/MH011443_1_sp_P04637_P53_HUMAN.txt +6 -0
  56. data/spec/sequences/alignment-35_hits_diamond_blastp.txt +210 -0
  57. data/spec/sequences/alignment-35_hits_diamond_blastx.txt +210 -0
  58. data/spec/sequences/alignment-3_hits.txt +18 -0
  59. data/spec/sequences/alignment-40_hits_blastn.txt +246 -0
  60. data/spec/sequences/alignment-40_hits_blastp.txt +240 -0
  61. data/spec/sequences/alignment-40_hits_blastp_2.2.30.txt +240 -0
  62. data/spec/sequences/alignment-40_hits_blastx.txt +240 -0
  63. data/spec/sequences/alignment-40_hits_tblastn.txt +240 -0
  64. data/spec/sequences/alignment-40_hits_tblastn_2.2.30.txt +240 -0
  65. data/spec/sequences/alignment-40_hits_tblastx.txt +2664 -0
  66. data/spec/sequences/alignment-4_hits.txt +24 -0
  67. data/spec/sequences/alignment-4_hits_blastn.txt +24 -0
  68. data/spec/sequences/alignment-4_hits_blastp.txt +24 -0
  69. data/spec/sequences/alignment-4_hits_blastp_2.2.30.txt +24 -0
  70. data/spec/sequences/alignment-4_hits_blastx.txt +24 -0
  71. data/spec/sequences/alignment-4_hits_diamond_blastp.txt +24 -0
  72. data/spec/sequences/alignment-4_hits_diamond_blastx.txt +24 -0
  73. data/spec/sequences/alignment-4_hits_tblastn.txt +24 -0
  74. data/spec/sequences/alignment-4_hits_tblastn_2.2.30.txt +24 -0
  75. data/spec/sequences/alignment-4_hits_tblastx.txt +318 -0
  76. data/spec/sequences/sp_P04637_P53_HUMAN_gi_1099170394_ref_XP_018868681_1.txt +6 -0
  77. data/spec/sequences/sp_P04637_P53_HUMAN_gi_120407068_ref_NP_000537_3.txt +6 -0
  78. data/spec/sequences/sp_P04637_P53_HUMAN_gi_1484127324_gb_MG595988_1.txt +6 -0
  79. data/spec/sequences/sp_P04637_P53_HUMAN_gi_395440626_gb_JQ694049_1.txt +6 -0
  80. data/spec/sequences/sp_P04637_P53_HUMAN_sp_P04637_P53_HUMAN.txt +6 -0
  81. data/spec/spec_helper.rb +3 -3
  82. metadata +67 -57
  83. data/.eslintrc +0 -213
  84. data/Rakefile +0 -8
  85. data/spec/dotdir/blast_2.4.0/blastn/TBLASTN_XML_2.4.0.xml +0 -1181
  86. data/spec/dotdir/blast_2.5.0/blastn/BLASTN_LONG_XML_2.5.0.xml +0 -18813
  87. data/spec/import_spec_capybara_local.rb +0 -61
data/public/js/report.js CHANGED
@@ -1,4 +1,4 @@
1
- import './sequenceserver' // for custom $.tooltip function
1
+ import './sequenceserver'; // for custom $.tooltip function
2
2
  import React from 'react';
3
3
  import _ from 'underscore';
4
4
 
@@ -19,8 +19,8 @@ import showErrorModal from './error_modal';
19
19
  */
20
20
  var downloadFASTA = function (sequence_ids, database_ids) {
21
21
  var form = $('<form/>').attr('method', 'post').attr('action', 'get_sequence');
22
- addField("sequence_ids", sequence_ids);
23
- addField("database_ids", database_ids);
22
+ addField('sequence_ids', sequence_ids);
23
+ addField('database_ids', database_ids);
24
24
  form.appendTo('body').submit().remove();
25
25
 
26
26
  function addField(name, val) {
@@ -28,7 +28,7 @@ var downloadFASTA = function (sequence_ids, database_ids) {
28
28
  $('<input>').attr('type', 'hidden').attr('name', name).val(val)
29
29
  );
30
30
  }
31
- }
31
+ };
32
32
 
33
33
  /**
34
34
  * Base component of report page. This component is later rendered into page's
@@ -86,24 +86,24 @@ var Report = React.createClass({
86
86
  $.getJSON(location.pathname + '.json')
87
87
  .complete(function (jqXHR) {
88
88
  switch (jqXHR.status) {
89
- case 202:
90
- var interval;
91
- if (intervals.length === 1) {
92
- interval = intervals[0];
93
- }
94
- else {
95
- interval = intervals.shift();
96
- }
97
- setTimeout(poll, interval);
98
- break;
99
- case 200:
100
- component.updateState(jqXHR.responseJSON);
101
- break;
102
- case 404:
103
- case 400:
104
- case 500:
105
- showErrorModal(jqXHR.responseJSON);
106
- break;
89
+ case 202:
90
+ var interval;
91
+ if (intervals.length === 1) {
92
+ interval = intervals[0];
93
+ }
94
+ else {
95
+ interval = intervals.shift();
96
+ }
97
+ setTimeout(poll, interval);
98
+ break;
99
+ case 200:
100
+ component.updateState(jqXHR.responseJSON);
101
+ break;
102
+ case 404:
103
+ case 400:
104
+ case 500:
105
+ showErrorModal(jqXHR.responseJSON);
106
+ break;
107
107
  }
108
108
  });
109
109
  }
@@ -188,19 +188,14 @@ var Report = React.createClass({
188
188
  <div className={this.shouldShowSidebar() ?
189
189
  'col-md-9' : 'col-md-12'}>
190
190
  { this.overviewJSX() }
191
- { this.isHitsAvailable()
192
- ? <Circos queries={this.state.queries}
193
- program={this.state.program} collapsed="true"/>
194
- : <span></span> }
191
+ { this.circosJSX() }
195
192
  {
196
193
  _.map(this.state.queries, _.bind(function (query) {
197
194
  return (
198
- <Query key={"Query_"+query.id}
199
- program={this.state.program} querydb={this.state.querydb}
200
- query={query} num_queries={this.state.num_queries}
201
- veryBig={this.state.veryBig} selectHit={this.selectHit}
202
- imported_xml={this.state.imported_xml} />
203
- );
195
+ <Query key={'Query_'+query.id} query={query} showQueryCrumbs={this.state.num_queries > 1}
196
+ selectHit={this.selectHit} program={this.state.program} querydb={this.state.querydb}
197
+ veryBig={this.state.veryBig} imported_xml={this.state.imported_xml} />
198
+ );
204
199
  }, this))
205
200
  }
206
201
  </div>
@@ -214,25 +209,36 @@ var Report = React.createClass({
214
209
  overviewJSX: function () {
215
210
  return (
216
211
  <div className="overview">
217
- <pre className="pre-reset">
212
+ <p className="text-monospace">
218
213
  {this.state.program_version}{this.state.submitted_at
219
- && `; query submitted on ${this.state.submitted_at}`}
220
- <br/>
221
- Databases ({this.state.stats.nsequences} sequences,&nbsp;
222
- {this.state.stats.ncharacters} characters): {
223
- this.state.querydb.map((db) => { return db.title }).join(", ")
224
- }
225
- <br/>
214
+ && `, query submitted on ${this.state.submitted_at}`}
215
+ </p>
216
+ <p className="text-monospace">
217
+ Databases: {
218
+ this.state.querydb.map((db) => { return db.title; }).join(', ')
219
+ } ({this.state.stats.nsequences} sequences,&nbsp;
220
+ {this.state.stats.ncharacters} characters)
221
+ </p>
222
+ <p className="text-monospace">
226
223
  Parameters: {
227
224
  _.map(this.state.params, function (val, key) {
228
- return key + " " + val;
229
- }).join(", ")
225
+ return key + ' ' + val;
226
+ }).join(', ')
230
227
  }
231
- </pre>
228
+ </p>
232
229
  </div>
233
230
  );
234
- },
231
+ },
235
232
 
233
+ /**
234
+ * Return JSX for circos if we have at least one hit.
235
+ */
236
+ circosJSX: function () {
237
+ return this.atLeastTwoHits()
238
+ ? <Circos queries={this.state.queries}
239
+ program={this.state.program} collapsed="true"/>
240
+ : <span></span>;
241
+ },
236
242
 
237
243
  // Controller //
238
244
 
@@ -245,12 +251,19 @@ var Report = React.createClass({
245
251
  return this.state.queries.length >= 1;
246
252
  },
247
253
 
248
- isHitsAvailable: function () {
249
- var cnt = 0;
250
- _.each(this.state.queries, function (query) {
251
- if(query.hits.length == 0) cnt++;
254
+ /**
255
+ * Returns true if we have at least one hit.
256
+ */
257
+ atLeastOneHit: function () {
258
+ return this.state.queries.some(query => query.hits.length > 0);
259
+ },
260
+
261
+ atLeastTwoHits: function () {
262
+ var hit_num = 0;
263
+ return this.state.queries.some(query => {
264
+ hit_num += query.hits.length;
265
+ return hit_num > 1;
252
266
  });
253
- return !(cnt == this.state.queries.length);
254
267
  },
255
268
 
256
269
  /**
@@ -265,13 +278,12 @@ var Report = React.createClass({
265
278
  },
266
279
 
267
280
  /**
268
- * Returns true if index should be shown in the sidebar.
269
- *
270
- * Index is not shown in the sidebar if there are more than eight queries
271
- * in total.
281
+ * Returns true if index should be shown in the sidebar. Index is shown
282
+ * only for 2 and 8 queries.
272
283
  */
273
284
  shouldShowIndex: function () {
274
- return this.state.queries.length <= 8;
285
+ var num_queries = this.state.queries.length;
286
+ return num_queries >= 2 && num_queries <= 8;
275
287
  },
276
288
 
277
289
  /**
@@ -309,7 +321,7 @@ var Report = React.createClass({
309
321
  * Prevents folding of hits during text-selection.
310
322
  */
311
323
  preventCollapseOnSelection: function () {
312
- $('body').on('mousedown', ".hit > .section-header > h4", function (event) {
324
+ $('body').on('mousedown', '.hit > .section-header > h4', function (event) {
313
325
  var $this = $(this);
314
326
  $this.on('mouseup mousemove', function handler(event) {
315
327
  if (event.type === 'mouseup') {
@@ -355,7 +367,7 @@ var Report = React.createClass({
355
367
  */
356
368
  selectHit: function (id) {
357
369
 
358
- var checkbox = $("#" + id);
370
+ var checkbox = $('#' + id);
359
371
  var num_checked = $('.hit-links :checkbox:checked').length;
360
372
 
361
373
  if (!checkbox || !checkbox.val()) {
@@ -366,7 +378,7 @@ var Report = React.createClass({
366
378
 
367
379
  // Highlight selected hit and enable 'Download FASTA/Alignment of
368
380
  // selected' links.
369
- if (checkbox.is(":checked")) {
381
+ if (checkbox.is(':checked')) {
370
382
  $hit.find('.section-content').addClass('glow');
371
383
  $('.download-alignment-of-selected').enable();
372
384
  $('.download-fasta-of-selected').enable();
@@ -406,7 +418,11 @@ var Query = React.createClass({
406
418
  * Returns the id of query.
407
419
  */
408
420
  domID: function () {
409
- return "Query_" + this.props.query.number;
421
+ return 'Query_' + this.props.query.number;
422
+ },
423
+
424
+ queryLength: function () {
425
+ return this.props.query.length;
410
426
  },
411
427
 
412
428
  /**
@@ -423,59 +439,57 @@ var Query = React.createClass({
423
439
  <div className="resultn" id={this.domID()}
424
440
  data-query-len={this.props.query.length}
425
441
  data-algorithm={this.props.program}>
426
- <div className="section-header">
427
- <h3>
428
- Query= {this.props.query.id}
429
- &nbsp;
430
- <small>
431
- {this.props.query.title}
432
- </small>
433
- </h3>
434
- <span
435
- className="label label-reset pos-label"
436
- title={"Query" + this.props.query.number + "."}
437
- data-toggle="tooltip">
438
- {this.props.query.number + "/" + this.props.num_queries}
439
- </span>
440
- </div>
441
- {this.numhits() &&
442
- (
443
- <div className="section-content">
444
- <HitsOverview key={"GO_"+this.props.query.number} query={this.props.query} program={this.props.program} collapsed={this.props.veryBig}/>
445
- <LengthDistribution key={"LD_"+this.props.query.id} query={this.props.query} algorithm={this.props.program} collapsed="true"/>
446
- <HitsTable key={"HT_"+this.props.query.number} query={this.props.query} imported_xml={this.props.imported_xml} />
447
- <div id="hits">
448
- {
449
- _.map(this.props.query.hits, _.bind(function (hit) {
450
- return (
451
- <Hit hit={hit}
452
- key={"HIT_"+hit.number}
453
- algorithm={this.props.program}
454
- querydb={this.props.querydb}
455
- query={this.props.query}
456
- imported_xml={this.props.imported_xml}
457
- selectHit={this.props.selectHit}/>
458
- );
459
- }, this))
460
- }
461
- </div>
462
- </div>
463
- ) || (
464
- <div
465
- className="section-content">
466
- <p>
467
- Query length: {this.props.query.length}
468
- </p>
469
- <br/>
470
- <br/>
471
- <p>
472
- <strong> ****** No hits found ****** </strong>
473
- </p>
474
- </div>
475
- )
442
+ { this.headerJSX() }
443
+ { this.numhits() && this.hitsListJSX() || this.noHitsJSX() }
444
+ </div>
445
+ );
446
+ },
447
+
448
+ headerJSX: function () {
449
+ var meta = `length: ${this.queryLength().toLocaleString()}`;
450
+ if (this.props.showQueryCrumbs) {
451
+ meta = `query ${this.props.query.number}, ` + meta;
452
+ }
453
+ return <div className="section-header">
454
+ <h3>
455
+ Query= {this.props.query.id}&nbsp;
456
+ <small>{this.props.query.title}</small>
457
+ </h3>
458
+ <span className="label label-reset pos-label">{ meta }</span>
459
+ </div>;
460
+ },
461
+
462
+ hitsListJSX: function () {
463
+ return <div className="section-content">
464
+ <HitsOverview key={'GO_' + this.props.query.number} query={this.props.query} program={this.props.program} collapsed={this.props.veryBig} />
465
+ <LengthDistribution key={'LD_' + this.props.query.id} query={this.props.query} algorithm={this.props.program} collapsed="true" />
466
+ <HitsTable key={'HT_' + this.props.query.number} query={this.props.query} imported_xml={this.props.imported_xml} />
467
+ <div id="hits">
468
+ {
469
+ _.map(this.props.query.hits, _.bind(function (hit) {
470
+ return (
471
+ <Hit key={'HIT_' + hit.number} hit={hit}
472
+ algorithm={this.props.program}
473
+ querydb={this.props.querydb}
474
+ query={this.props.query}
475
+ imported_xml={this.props.imported_xml}
476
+ selectHit={this.props.selectHit}
477
+ showHitCrumbs={this.numhits() > 1}
478
+ showQueryCrumbs={this.props.showQueryCrumbs} />
479
+ );
480
+ }, this))
476
481
  }
477
482
  </div>
478
- )
483
+ </div>;
484
+ },
485
+
486
+ noHitsJSX: function () {
487
+ return <div className="section-content">
488
+ <br />
489
+ <p>
490
+ <strong> ****** No hits found ****** </strong>
491
+ </p>
492
+ </div>;
479
493
  },
480
494
 
481
495
  shouldComponentUpdate: function (nextProps, nextState) {
@@ -490,9 +504,9 @@ var HitsTable = React.createClass({
490
504
  mixins: [Utils],
491
505
  render: function () {
492
506
  var count = 0,
493
- hasName = _.every(this.props.query.hits, function(hit) {
494
- return hit.sciname !== '';
495
- });
507
+ hasName = _.every(this.props.query.hits, function(hit) {
508
+ return hit.sciname !== '';
509
+ });
496
510
 
497
511
  return (
498
512
  <table
@@ -514,9 +528,9 @@ var HitsTable = React.createClass({
514
528
  _.map(this.props.query.hits, _.bind(function (hit) {
515
529
  return (
516
530
  <tr key={hit.number}>
517
- <td className="text-left">{hit.number + "."}</td>
531
+ <td className="text-left">{hit.number + '.'}</td>
518
532
  <td>
519
- <a href={"#Query_" + this.props.query.number + "_hit_" + hit.number}>
533
+ <a href={'#Query_' + this.props.query.number + '_hit_' + hit.number}>
520
534
  {hit.id}
521
535
  </a>
522
536
  </td>
@@ -526,7 +540,7 @@ var HitsTable = React.createClass({
526
540
  <td className="text-right">{this.inExponential(hit.hsps[0].evalue)}</td>
527
541
  <td className="text-right">{hit.identity}</td>
528
542
  </tr>
529
- )
543
+ );
530
544
  }, this))
531
545
  }
532
546
  </tbody>
@@ -551,7 +565,7 @@ var Hit = React.createClass({
551
565
  /**
552
566
  * Returns length of the hit sequence.
553
567
  */
554
- length: function () {
568
+ hitLength: function () {
555
569
  return this.props.hit.length;
556
570
  },
557
571
 
@@ -561,7 +575,7 @@ var Hit = React.createClass({
561
575
  * Returns id that will be used for the DOM node corresponding to the hit.
562
576
  */
563
577
  domID: function () {
564
- return "Query_" + this.props.query.number + "_hit_" + this.props.hit.number;
578
+ return 'Query_' + this.props.query.number + '_hit_' + this.props.hit.number;
565
579
  },
566
580
 
567
581
  databaseIDs: function () {
@@ -593,10 +607,10 @@ var Hit = React.createClass({
593
607
  hsp.query_id = this.props.query.id;
594
608
  hsp.hit_id = this.props.hit.id;
595
609
  return hsp;
596
- }, this))
610
+ }, this));
597
611
 
598
612
  var aln_exporter = new AlignmentExporter();
599
- aln_exporter.export_alignments(hsps, this.props.query.id+"_"+this.props.hit.id);
613
+ aln_exporter.export_alignments(hsps, this.props.query.id+'_'+this.props.hit.id);
600
614
  },
601
615
 
602
616
 
@@ -608,7 +622,7 @@ var Hit = React.createClass({
608
622
 
609
623
  // Return JSX for view sequence button.
610
624
  viewSequenceButton: function () {
611
- if (this.length() > 10000) {
625
+ if (this.hitLength() > 10000) {
612
626
  return (
613
627
  <button
614
628
  className="btn btn-link view-sequence disabled"
@@ -630,49 +644,57 @@ var Hit = React.createClass({
630
644
 
631
645
  render: function () {
632
646
  return (
633
- <div className="hit" id={this.domID()}
634
- data-hit-def={this.props.hit.id} data-hit-evalue={this.props.hit.evalue}
635
- data-hit-len={this.props.hit.length}>
636
- <div className="section-header">
637
- <h4 data-toggle="collapse"
638
- data-target={this.domID() + "_content"}>
639
- <i className="fa fa-chevron-down"></i>
640
- &nbsp;
641
- <span>
642
- {this.props.hit.id}
643
- &nbsp;
644
- <small>
645
- {this.props.hit.title}
646
- </small>
647
- </span>
648
- </h4>
649
- <span className="label label-reset pos-label"
650
- title={"Query " + this.props.query.number + ". Hit "
651
- + this.props.hit.number + " of "
652
- + this.props.query.hits.length + "."}
653
- data-toggle="tooltip">
654
- {this.props.hit.number + "/" + this.props.query.hits.length}
655
- </span>
656
- </div>
657
- <div id={this.domID() + "_content"}
658
- className="section-content collapse in">
659
- { this.hitLinks() }
660
- <HSPOverview key={"kablammo"+this.props.query.id}
661
- query={this.props.query} hit={this.props.hit}
662
- algorithm={this.props.algorithm}/>
663
- { this.hspListJSX() }
664
- </div>
647
+ <div className="hit" id={this.domID()} data-hit-def={this.props.hit.id}
648
+ data-hit-len={this.props.hit.length} data-hit-evalue={this.props.hit.evalue}>
649
+ { this.headerJSX() } { this.contentJSX() }
665
650
  </div>
666
651
  );
667
652
  },
668
653
 
654
+ headerJSX: function () {
655
+ var meta = `length: ${this.hitLength().toLocaleString()}`;
656
+
657
+ if (this.props.showQueryCrumbs && this.props.showHitCrumbs) {
658
+ // Multiper queries, multiple hits
659
+ meta = `hit ${this.props.hit.number} of query ${this.props.query.number}, ` + meta;
660
+ }
661
+ else if (this.props.showQueryCrumbs && !this.props.showHitCrumbs) {
662
+ // Multiple queries, single hit
663
+ meta = `the only hit of query ${this.props.query.number}, ` + meta;
664
+ }
665
+ else if (!this.props.showQueryCrumbs && this.props.showHitCrumbs) {
666
+ // Single query, multiple hits
667
+ meta = `hit ${this.props.hit.number}, ` + meta;
668
+ }
669
+
670
+ return <div className="section-header">
671
+ <h4 data-toggle="collapse" data-target={this.domID() + '_content'}>
672
+ <i className="fa fa-chevron-down"></i>&nbsp;
673
+ <span>
674
+ {this.props.hit.id}&nbsp;
675
+ <small>{this.props.hit.title}</small>
676
+ </span>
677
+ </h4>
678
+ <span className="label label-reset pos-label">{ meta }</span>
679
+ </div>;
680
+ },
681
+
682
+ contentJSX: function () {
683
+ return <div id={this.domID() + '_content'} className="section-content collapse in">
684
+ { this.hitLinks() }
685
+ <HSPOverview key={'kablammo' + this.props.query.id} query={this.props.query}
686
+ hit={this.props.hit} algorithm={this.props.algorithm} />
687
+ { this.hspListJSX() }
688
+ </div>;
689
+ },
690
+
669
691
  hitLinks: function () {
670
692
  return (
671
693
  <div className="hit-links">
672
694
  <label>
673
- <input type="checkbox" id={this.domID() + "_checkbox"}
695
+ <input type="checkbox" id={this.domID() + '_checkbox'}
674
696
  value={this.accession()} onChange={function () {
675
- this.props.selectHit(this.domID() + "_checkbox");
697
+ this.props.selectHit(this.domID() + '_checkbox');
676
698
  }.bind(this)} data-target={'#' + this.domID()}
677
699
  /> Select
678
700
  </label>
@@ -714,10 +736,10 @@ var Hit = React.createClass({
714
736
  return <HSP key={hsp.number}
715
737
  algorithm={this.props.algorithm}
716
738
  queryNumber={this.props.query.number}
717
- hitNumber={this.props.hit.number} hsp={hsp}/>
739
+ hitNumber={this.props.hit.number} hsp={hsp}/>;
718
740
  }, this)
719
741
  }
720
- </div>
742
+ </div>;
721
743
  }
722
744
  });
723
745
 
@@ -886,7 +908,7 @@ var SequenceViewer = (function () {
886
908
  sequences: response.sequences,
887
909
  error_msgs: response.error_msgs,
888
910
  requestCompleted: true
889
- })
911
+ });
890
912
  }, this))
891
913
  .fail(function (jqXHR, status, error) {
892
914
  showErrorModal(jqXHR, function () {
@@ -941,11 +963,11 @@ var SideBar = React.createClass({
941
963
  hsp.hit_id = hit.id;
942
964
  hsp.query_id = query.id;
943
965
  hsps_arr.push(hsp);
944
- })
945
- })
966
+ });
967
+ });
946
968
  }, this));
947
969
  console.log('len '+hsps_arr.length);
948
- aln_exporter.export_alignments(hsps_arr, "alignment-"+sequence_ids.length+"_hits");
970
+ aln_exporter.export_alignments(hsps_arr, 'alignment-'+sequence_ids.length+'_hits');
949
971
  return false;
950
972
  },
951
973
 
@@ -967,7 +989,7 @@ var SideBar = React.createClass({
967
989
  }
968
990
  });
969
991
  }, this));
970
- aln_exporter.export_alignments(hsps_arr, "alignment-"+sequence_ids.length+"_hits");
992
+ aln_exporter.export_alignments(hsps_arr, 'alignment-'+sequence_ids.length+'_hits');
971
993
  return false;
972
994
  },
973
995
 
@@ -979,29 +1001,29 @@ var SideBar = React.createClass({
979
1001
  { this.props.shouldShowIndex && this.index() }
980
1002
  { this.downloads() }
981
1003
  </div>
982
- )
1004
+ );
983
1005
  },
984
1006
 
985
1007
  index: function () {
986
1008
  return (
987
1009
  <div className="index">
988
1010
  <div
989
- className="section-header">
990
- <h4>
991
- { this.summary() }
992
- </h4>
1011
+ className="section-header">
1012
+ <h4>
1013
+ { this.summary() }
1014
+ </h4>
993
1015
  </div>
994
1016
  <ul
995
1017
  className="nav hover-reset active-bold">
996
1018
  {
997
1019
  _.map(this.props.data.queries, _.bind(function (query) {
998
1020
  return (
999
- <li key={"Side_bar_"+query.id}>
1021
+ <li key={'Side_bar_'+query.id}>
1000
1022
  <a
1001
1023
  className="nowrap-ellipsis hover-bold"
1002
- href={"#Query_" + query.number}
1003
- title={"Query= " + query.id + ' ' + query.title}>
1004
- {"Query= " + query.id}
1024
+ href={'#Query_' + query.number}
1025
+ title={'Query= ' + query.id + ' ' + query.title}>
1026
+ {'Query= ' + query.id}
1005
1027
  </a>
1006
1028
  </li>
1007
1029
  );
@@ -1019,7 +1041,7 @@ var SideBar = React.createClass({
1019
1041
 
1020
1042
  return (
1021
1043
  program.toUpperCase() + ': ' +
1022
- numqueries + ' ' + (numqueries > 1 ? 'queries' : 'query') + ", " +
1044
+ numqueries + ' ' + (numqueries > 1 ? 'queries' : 'query') + ', ' +
1023
1045
  numquerydb + ' ' + (numquerydb > 1 ? 'databases' : 'database')
1024
1046
  );
1025
1047
  },
@@ -1068,7 +1090,7 @@ var SideBar = React.createClass({
1068
1090
  name, alignment length, mismatches, gaps, identity,
1069
1091
  start and end coordinates, e value, bitscore, query
1070
1092
  coverage per subject and per HSP."
1071
- href={"download/" + this.props.data.search_id + ".std_tsv"}>
1093
+ href={'download/' + this.props.data.search_id + '.std_tsv'}>
1072
1094
  Standard tabular report
1073
1095
  </a>
1074
1096
  </li>
@@ -1080,7 +1102,7 @@ var SideBar = React.createClass({
1080
1102
  accessions, and length; alignment details;
1081
1103
  taxonomy details of subject sequence(s) and
1082
1104
  query coverage per subject and per HSP."
1083
- href={"download/" + this.props.data.search_id + ".full_tsv"}>
1105
+ href={'download/' + this.props.data.search_id + '.full_tsv'}>
1084
1106
  Full tabular report
1085
1107
  </a>
1086
1108
  </li>
@@ -1089,7 +1111,7 @@ var SideBar = React.createClass({
1089
1111
  !this.props.data.imported_xml && <li>
1090
1112
  <a className="download" data-toggle="tooltip"
1091
1113
  title="Results in XML format."
1092
- href={"download/" + this.props.data.search_id + ".xml"}>
1114
+ href={'download/' + this.props.data.search_id + '.xml'}>
1093
1115
  Full XML report
1094
1116
  </a>
1095
1117
  </li>