sequenceserver 2.0.0.beta3 → 2.0.0.beta4

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 (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/grapher.js CHANGED
@@ -3,8 +3,15 @@ import React from 'react';
3
3
 
4
4
  import './svgExporter'; // create handlers for SVG and PNG download buttons
5
5
 
6
+ // Each instance of Grapher is added to this object once the component has been
7
+ // mounted. This is so that grapher can be iterated over and redrawn on window
8
+ // resize event.
6
9
  var Graphers = {};
7
10
 
11
+ // Grapher is a function that takes a Graph class and returns a React component.
12
+ // This React component provides HTML boilerplate to add heading, to make the
13
+ // graphs collapsible, to redraw graphs when window is resized, and SVG and PNG
14
+ // export buttons and functionality.
8
15
  export default function Grapher(Graph) {
9
16
 
10
17
  return class extends React.Component {
@@ -23,7 +30,7 @@ export default function Grapher(Graph) {
23
30
  <div ref="grapher" className={cssClasses}>
24
31
  <div className="grapher-header">
25
32
  <h5 className="caption" data-toggle="collapse"
26
- data-target={"#"+this.collapseId()}>
33
+ data-target={'#'+this.collapseId()}>
27
34
  { this.state.collapsed ?
28
35
  this.plusIcon() : this.minusIcon() }
29
36
  &nbsp;&nbsp;
@@ -66,8 +73,7 @@ export default function Grapher(Graph) {
66
73
  var cssClasses = Graph.className() + ' svg-container collapse';
67
74
  if (!this.state.collapsed) cssClasses += ' in';
68
75
  return (
69
- <div ref="svgContainer" id={this.collapseId()}
70
- className={cssClasses}>
76
+ <div ref="svgContainer" id={this.collapseId()} className={cssClasses}>
71
77
  </div>
72
78
  );
73
79
  }
@@ -95,7 +101,7 @@ export default function Grapher(Graph) {
95
101
  this.graph = null;
96
102
 
97
103
  // Draw if uncollapsed.
98
- if (this.state.collapsed) { return }
104
+ if (this.state.collapsed) { return; }
99
105
  this.graph = new Graph(this.svgContainer(), this.props);
100
106
  this.svgContainer().find('svg').attr('data-name', Graph.dataName(this.props));
101
107
  }
@@ -110,13 +116,13 @@ $(window).resize(_.debounce(function () {
110
116
  }, 125));
111
117
 
112
118
  // Swap-icon and toggle .graph-links on collapse.
113
- $('body').on('hidden.bs.collapse', ".collapse", function () {
119
+ $('body').on('hidden.bs.collapse', '.collapse', function () {
114
120
  var component = Graphers[$(this).attr('id')];
115
121
  if (component) {
116
122
  component.setState({ collapsed: true });
117
123
  }
118
124
  });
119
- $('body').on('shown.bs.collapse', ".collapse", function () {
125
+ $('body').on('shown.bs.collapse', '.collapse', function () {
120
126
  var component = Graphers[$(this).attr('id')];
121
127
  if (component) {
122
128
  component.setState({ collapsed: false });
@@ -2,360 +2,373 @@ import d3 from 'd3';
2
2
  import _ from 'underscore';
3
3
  import Grapher from './grapher';
4
4
  import * as Helpers from './visualisation_helpers';
5
+ import Utils from './utils';
5
6
 
6
7
  class Graph {
7
8
 
8
- static name() {
9
- return 'Graphical overview of hits';
10
- }
11
-
12
- static className() {
13
- return 'alignment-overview';
14
- }
15
-
16
- static collapseId(props) {
17
- return 'alignment_'+props.query.number;
18
- }
19
-
20
- static dataName(props) {
21
- return 'Alignment-Overview-'+props.query.id;
22
- }
23
-
24
- constructor($svgContainer, props) {
25
- this.svg_container = $svgContainer;
26
- $queryDiv = $svgContainer.parents('.resultn');
27
- hits = this.extractData(props.query.hits, props.query.number);
28
- this.graphIt($queryDiv, $svgContainer, 0, 20, null, hits);
29
- }
30
-
31
- extractData(query_hits, number) {
32
- var hits = [];
33
- query_hits.map(function (hit) {
34
- var _hsps = [];
35
- var hsps = hit.hsps;
36
- _.each(hsps, function (hsp) {
37
- var _hsp = {};
38
- _hsp.hspEvalue = hsp.evalue;
39
- _hsp.hspStart = hsp.qstart;
40
- _hsp.hspEnd = hsp.qend;
41
- _hsp.hspFrame = hsp.sframe;
42
- _hsp.hspId = "Query_"+number+"_hit_"+hit.number+"_hsp_"+hsp.number;
43
- _hsps.push(_hsp);
44
- });
45
- _hsps.hitId = hit.id;
46
- _hsps.hitDef = "Query_"+number+"_hit_"+hit.number;
47
- _hsps.hitEvalue = hit.evalue;
48
- hits.push(_hsps);
49
- });
50
- return hits;
51
- }
52
-
53
- setupTooltip() {
54
- this.svg_container.find('[data-toggle="tooltip"]').tooltip({
55
- 'placement': 'top', 'container': 'body', 'html': 'true',
56
- 'delay': 0, 'white-space': 'nowrap'
57
- });
58
- }
59
-
60
- setupClick($graphDiv) {
61
- $('a', $graphDiv).click(function (evt) {
62
- evt.preventDefault();
63
- evt.stopPropagation();
64
- window.location.hash = $(this).attr('href');
65
- });
66
- }
67
-
68
- graphControls($queryDiv, $graphDiv, isInit, opts, hits) {
69
- var MIN_HITS_TO_SHOW = 20;
70
-
71
- var totalHits, shownHits, lessButton, moreButton;
72
-
73
- var countHits = function () {
74
- totalHits = hits.length;
75
- shownHits = $queryDiv.find('.ghit > g').length;
76
- };
77
-
78
- var setupButtons = function($queryDiv, $graphDiv) {
79
- $graphDiv
80
- .append(
81
- $('<button/>')
82
- .addClass('btn btn-link more')
83
- .attr('type', 'button')
84
- .attr('data-parent-query', $queryDiv.attr('id'))
85
- .html('View More&nbsp;')
86
- .append(
87
- $('<i/>')
88
- .html('&nbsp;&nbsp;')
89
- .addClass('fa fa-angle-double-down')
90
- ),
91
- $('<button/>')
92
- .addClass('btn btn-link less')
93
- .attr('type', 'button')
94
- .attr('data-parent-query', $queryDiv.attr('id'))
95
- .html('View Less&nbsp;')
96
- .append(
97
- $('<i/>')
98
- .html('&nbsp;&nbsp;')
99
- .addClass('fa fa-angle-double-up')
100
- )
101
- );
102
-
103
- lessButton = $('.less', $graphDiv);
104
- moreButton = $('.more', $graphDiv);
105
- };
106
-
107
- var initButtons = function () {
108
- countHits();
109
- if (totalHits === MIN_HITS_TO_SHOW ||
110
- shownHits < MIN_HITS_TO_SHOW) {
111
- lessButton.hide();
112
- moreButton.hide();
113
- }
114
- else if (shownHits === totalHits) {
115
- moreButton.hide();
116
- lessButton.show();
117
- }
118
- else if (shownHits === MIN_HITS_TO_SHOW) {
119
- lessButton.hide();
120
- moreButton.show();
121
- }
122
- else {
123
- lessButton.show();
124
- moreButton.show();
125
- }
126
- };
9
+ static name() {
10
+ return 'Graphical overview of hits';
11
+ }
127
12
 
128
- // Setup view buttons' state properly if called for first time.
129
- if (isInit === true) {
130
- setupButtons($queryDiv, $graphDiv);
131
- initButtons();
13
+ static className() {
14
+ return 'alignment-overview';
132
15
  }
133
16
 
134
- moreButton.on('click', _.bind(function (e) {
135
- countHits();
136
- this.graphIt($queryDiv, $graphDiv, shownHits, MIN_HITS_TO_SHOW, opts, hits);
137
- initButtons();
138
- this.setupTooltip();
139
- e.stopPropagation();
140
- },this));
17
+ static collapseId(props) {
18
+ return 'alignment_'+props.query.number;
19
+ }
141
20
 
142
- lessButton.on('click', _.bind(function (e) {
143
- countHits();
144
- var diff = shownHits - MIN_HITS_TO_SHOW;
21
+ static dataName(props) {
22
+ return 'Alignment-Overview-'+props.query.id;
23
+ }
145
24
 
146
- // Decrease number of shown hits by defined constant.
147
- if (diff >= MIN_HITS_TO_SHOW) {
148
- this.graphIt($queryDiv, $graphDiv, shownHits, -MIN_HITS_TO_SHOW, opts, hits);
25
+ constructor($svgContainer, props) {
26
+ this.svg_container = $svgContainer;
27
+ $queryDiv = $svgContainer.parents('.resultn');
28
+ hits = this.extractData(props.query.hits, props.query.number);
29
+ this.graphIt($queryDiv, $svgContainer, 0, 20, null, hits);
30
+ }
31
+
32
+ extractData(query_hits, number) {
33
+ var hits = [];
34
+ query_hits.map(function (hit) {
35
+ var _hsps = [];
36
+ var hsps = hit.hsps;
37
+ _.each(hsps, function (hsp) {
38
+ var _hsp = {};
39
+ _hsp.hspEvalue = hsp.evalue;
40
+ _hsp.hspStart = hsp.qstart;
41
+ _hsp.hspEnd = hsp.qend;
42
+ _hsp.hspFrame = hsp.sframe;
43
+ _hsp.hspId = 'Query_' + number + '_hit_' + hit.number + '_hsp_' + hsp.number;
44
+ _hsp.hspIdentity = hsp.identity;
45
+ _hsp.hspGaps = hsp.gaps;
46
+ _hsp.hspPositives = hsp.positives;
47
+ _hsp.hspLength = hsp.length;
48
+ _hsps.push(_hsp);
49
+ });
50
+ _hsps.hitId = hit.id;
51
+ _hsps.hitDef = 'Query_'+number+'_hit_'+hit.number;
52
+ _hsps.hitEvalue = hit.evalue;
53
+ hits.push(_hsps);
54
+ });
55
+ return hits;
56
+ }
57
+
58
+ setupTooltip() {
59
+ this.svg_container.find('[data-toggle="tooltip"]').tooltip({
60
+ 'placement': 'top', 'container': 'body', 'html': 'true',
61
+ 'delay': 0, 'white-space': 'nowrap'
62
+ });
63
+ }
64
+
65
+ setupClick($graphDiv) {
66
+ $('a', $graphDiv).click(function (evt) {
67
+ evt.preventDefault();
68
+ evt.stopPropagation();
69
+ window.location.hash = $(this).attr('href');
70
+ });
71
+ }
72
+
73
+ graphControls($queryDiv, $graphDiv, isInit, opts, hits) {
74
+ var MIN_HITS_TO_SHOW = 20;
75
+
76
+ var totalHits, shownHits, lessButton, moreButton;
77
+
78
+ var countHits = function () {
79
+ totalHits = hits.length;
80
+ shownHits = $queryDiv.find('.ghit > g').length;
81
+ };
82
+
83
+ var setupButtons = function($queryDiv, $graphDiv) {
84
+ $graphDiv
85
+ .append(
86
+ $('<button/>')
87
+ .addClass('btn btn-link more')
88
+ .attr('type', 'button')
89
+ .attr('data-parent-query', $queryDiv.attr('id'))
90
+ .html('View More&nbsp;')
91
+ .append(
92
+ $('<i/>')
93
+ .html('&nbsp;&nbsp;')
94
+ .addClass('fa fa-angle-double-down')
95
+ ),
96
+ $('<button/>')
97
+ .addClass('btn btn-link less')
98
+ .attr('type', 'button')
99
+ .attr('data-parent-query', $queryDiv.attr('id'))
100
+ .html('View Less&nbsp;')
101
+ .append(
102
+ $('<i/>')
103
+ .html('&nbsp;&nbsp;')
104
+ .addClass('fa fa-angle-double-up')
105
+ )
106
+ );
107
+
108
+ lessButton = $('.less', $graphDiv);
109
+ moreButton = $('.more', $graphDiv);
110
+ };
111
+
112
+ var initButtons = function () {
113
+ countHits();
114
+ if (totalHits === MIN_HITS_TO_SHOW ||
115
+ shownHits < MIN_HITS_TO_SHOW) {
116
+ lessButton.hide();
117
+ moreButton.hide();
118
+ }
119
+ else if (shownHits === totalHits) {
120
+ moreButton.hide();
121
+ lessButton.show();
122
+ }
123
+ else if (shownHits === MIN_HITS_TO_SHOW) {
124
+ lessButton.hide();
125
+ moreButton.show();
126
+ }
127
+ else {
128
+ lessButton.show();
129
+ moreButton.show();
130
+ }
131
+ };
132
+
133
+ // Setup view buttons' state properly if called for first time.
134
+ if (isInit === true) {
135
+ setupButtons($queryDiv, $graphDiv);
149
136
  initButtons();
150
137
  }
151
- else if (diff !== 0) {
152
- // Ensure a certain number of hits always stay in graph.
153
- this.graphIt($queryDiv, $graphDiv, shownHits, MIN_HITS_TO_SHOW - shownHits, opts, hits);
138
+
139
+ moreButton.on('click', _.bind(function (e) {
140
+ countHits();
141
+ this.graphIt($queryDiv, $graphDiv, shownHits, MIN_HITS_TO_SHOW, opts, hits);
154
142
  initButtons();
155
- }
156
- this.setupTooltip();
157
- e.stopPropagation();
158
- },this));
159
- }
160
-
161
- drawLegend(svg, options, width, height, hits) {
162
- var svg_legend = svg.append('g')
163
- .attr('transform',
164
- 'translate(0,' + (height - 1.88 * options.margin) + ')');
165
-
166
- svg_legend.append('rect')
167
- .attr('x', 7 * (width - 2 * options.margin) / 10)
168
- .attr('width', 2 * (width - 4 * options.margin) / 10)
169
- .attr('height', options.legend)
170
- .attr('fill', 'url(#legend-grad)');
171
-
172
- svg_legend.append('text')
173
- .attr('class',' legend-text')
174
- .attr('transform', 'translate(0, ' +options.legend +')')
175
- .attr('x', 6 * (width - 2 * options.margin) / 10 - options.margin / 2)
176
- .text("Weaker hits");
143
+ this.setupTooltip();
144
+ e.stopPropagation();
145
+ },this));
146
+
147
+ lessButton.on('click', _.bind(function (e) {
148
+ countHits();
149
+ var diff = shownHits - MIN_HITS_TO_SHOW;
150
+
151
+ // Decrease number of shown hits by defined constant.
152
+ if (diff >= MIN_HITS_TO_SHOW) {
153
+ this.graphIt($queryDiv, $graphDiv, shownHits, -MIN_HITS_TO_SHOW, opts, hits);
154
+ initButtons();
155
+ }
156
+ else if (diff !== 0) {
157
+ // Ensure a certain number of hits always stay in graph.
158
+ this.graphIt($queryDiv, $graphDiv, shownHits, MIN_HITS_TO_SHOW - shownHits, opts, hits);
159
+ initButtons();
160
+ }
161
+ this.setupTooltip();
162
+ e.stopPropagation();
163
+ },this));
164
+ }
165
+
166
+ drawLegend(svg, options, width, height, hits) {
167
+ var svg_legend = svg.append('g')
168
+ .attr('transform',
169
+ 'translate(0,' + (height - 1.88 * options.margin) + ')');
170
+
171
+ svg_legend.append('rect')
172
+ .attr('x', 7 * (width - 2 * options.margin) / 10)
173
+ .attr('width', 2 * (width - 4 * options.margin) / 10)
174
+ .attr('height', options.legend)
175
+ .attr('fill', 'url(#legend-grad)');
176
+
177
+ svg_legend.append('text')
178
+ .attr('class',' legend-text')
179
+ .attr('transform', 'translate(0, ' +options.legend +')')
180
+ .attr('x', 6 * (width - 2 * options.margin) / 10 - options.margin / 2)
181
+ .text('Weaker hits');
177
182
  // .text(function() {
178
183
  // return Helpers.prettify_evalue(hits[hits.length-1].hitEvalue);
179
184
  // })
180
185
 
181
- svg_legend.append('text')
182
- .attr('class',' legend-text')
183
- .attr('transform', 'translate(0, ' + options.legend + ')')
184
- .attr('x', 9 * (width - 2 * options.margin) / 10 + options.margin / 2)
185
- .text("Stronger hits");
186
+ svg_legend.append('text')
187
+ .attr('class',' legend-text')
188
+ .attr('transform', 'translate(0, ' + options.legend + ')')
189
+ .attr('x', 9 * (width - 2 * options.margin) / 10 + options.margin / 2)
190
+ .text('Stronger hits');
186
191
  // .text(function () {
187
192
  // return Helpers.prettify_evalue(hits[0].hitEvalue);
188
193
  // })
189
194
 
190
- svg.append('linearGradient')
191
- .attr('id', 'legend-grad')
192
- .selectAll('stop')
193
- .data([
194
- {offset: "0%", color: "#ccc"},
195
- {offset: '50%', color: '#888'},
196
- {offset: "100%", color: "#000"}
197
- ])
198
- .enter()
199
- .append('stop')
200
- .attr('offset', function (d) {
201
- return d.offset;
202
- })
203
- .attr('stop-color', function (d) {
204
- return d.color;
205
- });
206
- }
195
+ svg.append('linearGradient')
196
+ .attr('id', 'legend-grad')
197
+ .selectAll('stop')
198
+ .data([
199
+ {offset: '0%', color: '#ccc'},
200
+ {offset: '50%', color: '#888'},
201
+ {offset: '100%', color: '#000'}
202
+ ])
203
+ .enter()
204
+ .append('stop')
205
+ .attr('offset', function (d) {
206
+ return d.offset;
207
+ })
208
+ .attr('stop-color', function (d) {
209
+ return d.color;
210
+ });
211
+ }
207
212
 
208
- graphIt($queryDiv, $graphDiv, index, howMany, opts, inhits) {
213
+ graphIt($queryDiv, $graphDiv, index, howMany, opts, inhits) {
209
214
  /* barHeight: Height of each hit track.
210
215
  * legend: Height reserved for the overview legend.
211
216
  * margin: Margin around the svg element.
212
217
  */
213
- var defaults = {
214
- barHeight: 3,
215
- legend: inhits.length > 1 ? 3 : 0,
216
- margin: 20
217
- },
218
- options = $.extend(defaults, opts);
218
+ var defaults = {
219
+ barHeight: 3,
220
+ legend: inhits.length > 1 ? 3 : 0,
221
+ margin: 20
222
+ },
223
+ options = $.extend(defaults, opts);
219
224
  var hits = inhits.slice(0 , index + howMany);
220
225
 
221
- // Don't draw anything when no hits are obtained.
222
- if (hits.length < 1) return false;
226
+ // Don't draw anything when no hits are obtained.
227
+ if (hits.length < 1) return false;
223
228
 
224
- if (index !== 0) {
229
+ if (index !== 0) {
225
230
  // Currently, we have no good way to extend pre-existing graph
226
231
  // and hence, are removing the old one and redrawing.
227
- $graphDiv.find('svg').remove();
228
- }
232
+ $graphDiv.find('svg').remove();
233
+ }
229
234
 
230
- var queryLen = $queryDiv.data().queryLen;
231
- var q_i = $queryDiv.attr('id');
235
+ var queryLen = $queryDiv.data().queryLen;
236
+ var q_i = $queryDiv.attr('id');
232
237
 
233
- var width = $graphDiv.width();
234
- var height = hits.length * (options.barHeight) +
238
+ var width = $graphDiv.width();
239
+ var height = hits.length * (options.barHeight) +
235
240
  2 * options.legend + 5 * options.margin;
236
- // var height = $graphDiv.height();
237
-
238
- var SEQ_TYPES = {
239
- blastn: 'nucleic_acid',
240
- blastp: 'amino_acid',
241
- blastx: 'nucleic_acid',
242
- tblastx: 'nucleic_acid',
243
- tblastn: 'amino_acid'
244
- };
245
-
246
- var svg = d3.select($graphDiv[0])
247
- .selectAll('svg')
248
- .data([hits])
249
- .enter()
250
- .insert('svg', ':first-child')
241
+ // var height = $graphDiv.height();
242
+
243
+ var SEQ_TYPES = {
244
+ blastn: 'nucleic_acid',
245
+ blastp: 'amino_acid',
246
+ blastx: 'nucleic_acid',
247
+ tblastx: 'nucleic_acid',
248
+ tblastn: 'amino_acid'
249
+ };
250
+
251
+ var svg = d3.select($graphDiv[0])
252
+ .selectAll('svg')
253
+ .data([hits])
254
+ .enter()
255
+ .insert('svg', ':first-child')
251
256
  .attr('width', width)
252
257
  .attr('height', height)
253
- .append('g')
258
+ .append('g')
254
259
  .attr('transform', 'translate(' + options.margin / 2 + ', ' + (1.5 * options.margin) + ')');
255
260
 
256
- var x = d3.scale
257
- .linear()
258
- .range([0, width - options.margin]);
261
+ var x = d3.scale
262
+ .linear()
263
+ .range([0, width - options.margin]);
259
264
 
260
- x.domain([1, queryLen]);
265
+ x.domain([1, queryLen]);
261
266
 
262
- var algorithm = $queryDiv.data().algorithm;
263
- var formatter = Helpers.tick_formatter(x, SEQ_TYPES[algorithm]);
267
+ var algorithm = $queryDiv.data().algorithm;
268
+ var formatter = Helpers.tick_formatter(x, SEQ_TYPES[algorithm]);
264
269
 
265
- var _tValues = x.ticks(11);
266
- _tValues.pop();
270
+ var _tValues = x.ticks(11);
271
+ _tValues.pop();
267
272
 
268
- var xAxis = d3.svg
269
- .axis()
270
- .scale(x)
271
- .orient('top')
272
- .tickValues(_tValues.concat([1, queryLen]))
273
- .tickFormat(formatter);
273
+ var xAxis = d3.svg
274
+ .axis()
275
+ .scale(x)
276
+ .orient('top')
277
+ .tickValues(_tValues.concat([1, queryLen]))
278
+ .tickFormat(formatter);
274
279
 
275
- // Attach the axis to DOM (<svg> element)
276
- var container = svg.append('g')
277
- .attr('transform', 'translate(0, ' + options.margin + ')')
278
- .append('g')
280
+ // Attach the axis to DOM (<svg> element)
281
+ var container = svg.append('g')
282
+ .attr('transform', 'translate(0, ' + options.margin + ')')
283
+ .append('g')
279
284
  .attr('class', 'x axis')
280
285
  .call(xAxis);
281
286
 
282
- // Vertical alignment of ticks
283
- container.selectAll('text')
287
+ // Vertical alignment of ticks
288
+ container.selectAll('text')
284
289
  .attr('x','25px')
285
290
  .attr('y','2px')
286
291
  .attr('transform','rotate(-90)');
287
292
 
288
- var y = d3.scale
289
- .ordinal()
290
- .rangeBands([0, height - 3 * options.margin - 2 * options.legend], 0.3);
293
+ var y = d3.scale
294
+ .ordinal()
295
+ .rangeBands([0, height - 3 * options.margin - 2 * options.legend], 0.3);
291
296
 
292
- y.domain(hits.map(function (d) {
293
- return d.hitId;
294
- }));
297
+ y.domain(hits.map(function (d) {
298
+ return d.hitId;
299
+ }));
295
300
 
296
- var gradScale = d3.scale
297
- .log()
298
- .domain([
299
- d3.min([1e-5, d3.min(hits.map(function (d) {
300
- if (parseFloat(d.hitEvalue) === 0.0) return undefined;
301
- return d.hitEvalue;
301
+ var gradScale = d3.scale
302
+ .log()
303
+ .domain([
304
+ d3.min([1e-5, d3.min(hits.map(function (d) {
305
+ if (parseFloat(d.hitEvalue) === 0.0) return undefined;
306
+ return d.hitEvalue;
307
+ }))
308
+ ]),
309
+ d3.max(hits.map(function (d) {
310
+ return d.hitEvalue;
302
311
  }))
303
- ]),
304
- d3.max(hits.map(function (d) {
305
- return d.hitEvalue;
306
- }))
307
- ])
308
- .range([40,150]);
309
-
310
- svg.append('g')
311
- .attr('class', 'ghit')
312
- .attr('transform', 'translate(0, ' + 1.65 * (options.margin - options.legend) + ')')
313
- .selectAll('.hits')
314
- .data(hits)
315
- .enter()
316
- .append('g')
312
+ ])
313
+ .range([40,150]);
314
+
315
+ svg.append('g')
316
+ .attr('class', 'ghit')
317
+ .attr('transform', 'translate(0, ' + 1.65 * (options.margin - options.legend) + ')')
318
+ .selectAll('.hits')
319
+ .data(hits)
320
+ .enter()
321
+ .append('g')
317
322
  .each(function (d,i) {
318
323
  // TODO: Avoid too many variables and improve naming.
319
324
 
320
325
  d3.select(this)
321
- .selectAll('.hsp')
322
- .data(d).enter()
323
- .append('a')
324
- .each(function (v, j) {
326
+ .selectAll('.hsp')
327
+ .data(d).enter()
328
+ .append('a')
329
+ .each(function (v, j) {
325
330
  // Drawing the HSPs connector line using the same
326
331
  // color as that of the hit track (using lookahead).
327
- var yHspline = y(d.hitId) + options.barHeight / 2;
328
- var hsplineColor = d3.rgb(gradScale(v.hspEvalue),
329
- gradScale(v.hspEvalue),
330
- gradScale(v.hspEvalue));
331
-
332
- if (j+1 < d.length) {
333
- if (d[j].hspEnd <= d[j+1].hspStart) {
334
- d3.select(this.parentNode)
335
- .append('line')
336
- .attr('x1', x(d[j].hspEnd))
337
- .attr('y1', yHspline)
338
- .attr('x2', x(d[j+1].hspStart))
339
- .attr('y2', yHspline)
340
- .attr('stroke', hsplineColor);
332
+ var yHspline = y(d.hitId) + options.barHeight / 2;
333
+ var hsplineColor = d3.rgb(gradScale(v.hspEvalue),
334
+ gradScale(v.hspEvalue),
335
+ gradScale(v.hspEvalue));
336
+
337
+ if (j+1 < d.length) {
338
+ if (d[j].hspEnd <= d[j+1].hspStart) {
339
+ d3.select(this.parentNode)
340
+ .append('line')
341
+ .attr('x1', x(d[j].hspEnd))
342
+ .attr('y1', yHspline)
343
+ .attr('x2', x(d[j+1].hspStart))
344
+ .attr('y2', yHspline)
345
+ .attr('stroke', hsplineColor);
346
+ }
347
+ else if (d[j].hspStart > d[j+1].hspEnd) {
348
+ d3.select(this.parentNode)
349
+ .append('line')
350
+ .attr('x1', x(d[j+1].hspEnd))
351
+ .attr('y1', yHspline)
352
+ .attr('x2', x(d[j].hspStart))
353
+ .attr('y2', yHspline)
354
+ .attr('stroke', hsplineColor);
355
+ }
341
356
  }
342
- else if (d[j].hspStart > d[j+1].hspEnd) {
343
- d3.select(this.parentNode)
344
- .append('line')
345
- .attr('x1', x(d[j+1].hspEnd))
346
- .attr('y1', yHspline)
347
- .attr('x2', x(d[j].hspStart))
348
- .attr('y2', yHspline)
349
- .attr('stroke', hsplineColor);
357
+
358
+ var alt_tooltip = d.hitId + '<br>E value: ' + Helpers.prettify_evalue(v.hspEvalue) +
359
+ `<br>Identities: ${Utils.inPercentage(v.hspIdentity, v.hspLength)}`;
360
+ // if chosen algorithm was blastn, the tooltip won't show the Positives% value in the tooltip
361
+ if (algorithm != 'blastn'){
362
+ alt_tooltip += `<br>Positives: ${Utils.inPercentage(v.hspPositives, v.hspLength)}`;
350
363
  }
351
- }
364
+ alt_tooltip += `, Gaps: ${Utils.inPercentage(v.hspGaps, v.hspLength)}`;
352
365
 
353
- // Draw the rectangular hit tracks itself.
354
- d3.select(this)
355
- .attr('xlink:href', '#' + q_i + '_hit_' + (i+1))
356
- .append('rect')
366
+ // Draw the rectangular hit tracks itself.
367
+ d3.select(this)
368
+ .attr('xlink:href', '#' + q_i + '_hit_' + (i+1))
369
+ .append('rect')
357
370
  .attr('data-toggle', 'tooltip')
358
- .attr('title', d.hitId + '<br><strong>E value:</strong> '+Helpers.prettify_evalue(v.hspEvalue))
371
+ .attr('title', alt_tooltip)
359
372
  .attr('class','bar')
360
373
  .attr('x', function (d) {
361
374
  return x(d.hspStart);
@@ -366,24 +379,24 @@ class Graph {
366
379
  })
367
380
  .attr('height', options.barHeight)
368
381
  .attr('fill', d3.rgb(hsplineColor));
369
- });
382
+ });
370
383
  });
371
384
 
372
- // Draw legend only when more than one hit present
373
- if (hits.length > 1) {
374
- this.drawLegend(svg, options, width, height, inhits);
375
- }
376
- // Bind listener events once all the graphical elements have
377
- // been drawn for first time.
378
- if (index === 0) {
379
- this.graphControls($queryDiv, $graphDiv, true, opts, inhits);
385
+ // Draw legend only when more than one hit present
386
+ if (hits.length > 1) {
387
+ this.drawLegend(svg, options, width, height, inhits);
388
+ }
389
+ // Bind listener events once all the graphical elements have
390
+ // been drawn for first time.
391
+ if (index === 0) {
392
+ this.graphControls($queryDiv, $graphDiv, true, opts, inhits);
393
+ }
394
+ // Refresh tooltip each time graph is redrawn.
395
+ this.setupTooltip();
396
+ // Ensure clicking on 'rect' takes user to the relevant hit on all
397
+ // browsers.
398
+ this.setupClick($graphDiv);
380
399
  }
381
- // Refresh tooltip each time graph is redrawn.
382
- this.setupTooltip();
383
- // Ensure clicking on 'rect' takes user to the relevant hit on all
384
- // browsers.
385
- this.setupClick($graphDiv);
386
- }
387
400
  }
388
401
 
389
402
  var HitsOverview = Grapher(Graph);