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/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);