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.
- checksums.yaml +5 -5
- data/.eslintrc.json +36 -0
- data/.rubocop.yml +1 -1
- data/.travis.yml +53 -20
- data/AppImage/recipe.yml +15 -0
- data/AppImage/sequenceserver.desktop +8 -0
- data/AppImage/sequenceserver.png +0 -0
- data/AppImage/sequenceserver.sh +11 -0
- data/README.md +79 -46
- data/bin/sequenceserver +4 -4
- data/lib/sequenceserver/version.rb +1 -1
- data/package.json +2 -0
- data/public/css/grapher.css +3 -0
- data/public/css/sequenceserver.css +17 -6
- data/public/css/sequenceserver.min.css +3 -3
- data/public/js/circos.js +515 -491
- data/public/js/grapher.js +12 -6
- data/public/js/hits_overview.js +321 -308
- data/public/js/hsp.js +12 -7
- data/public/js/length_distribution.js +241 -234
- data/public/js/report.js +196 -174
- data/public/js/search.js +3 -3
- data/public/js/sequenceserver.js +9 -9
- data/public/js/utils.js +17 -10
- data/public/js/visualisation_helpers.js +77 -77
- data/public/sequenceserver-report.min.js +17 -17
- data/public/sequenceserver-search.min.js +1 -1
- data/public/vendor/github/nicgirault/circosJs@1.7.0/dist/circosJS.js +1 -5
- data/sequenceserver.gemspec +1 -2
- data/spec/blast_versions/blast_2.2.30/blast_2.2.30_spec.rb +13 -13
- data/spec/blast_versions/blast_2.2.30/import_spec_capybara_local_2.2.30.rb +555 -25
- data/spec/blast_versions/blast_2.2.31/blast_2.2.31_spec.rb +13 -13
- data/spec/blast_versions/blast_2.2.31/import_spec_capybara_local_2.2.31.rb +558 -24
- data/spec/blast_versions/blast_2.3.0/blast_2.3.0_spec.rb +13 -13
- data/spec/blast_versions/blast_2.3.0/import_spec_capybara_local_2.3.0.rb +561 -26
- data/spec/blast_versions/blast_2.4.0/blast_2.4.0_spec.rb +13 -13
- data/spec/blast_versions/blast_2.4.0/import_spec_capybara_local_2.4.0.rb +561 -25
- data/spec/blast_versions/blast_2.5.0/blast_2.5.0_spec.rb +13 -13
- data/spec/blast_versions/blast_2.5.0/import_spec_capybara_local_2.5.0.rb +558 -24
- data/spec/blast_versions/blast_2.6.0/blast_2.6.0_spec.rb +13 -13
- data/spec/blast_versions/blast_2.6.0/import_spec_capybara_local_2.6.0.rb +559 -24
- data/spec/blast_versions/blast_2.7.1/blast_2.7.1_spec.rb +13 -13
- data/spec/blast_versions/blast_2.7.1/import_spec_capybara_local_2.7.1.rb +559 -28
- data/spec/blast_versions/blast_2.8.1/blast_2.8.1_spec.rb +13 -13
- data/spec/blast_versions/blast_2.8.1/import_spec_capybara_local_2.8.1.rb +559 -27
- data/spec/blast_versions/blast_2.9.0/blast_2.9.0_spec.rb +13 -13
- data/spec/blast_versions/blast_2.9.0/import_spec_capybara_local_2.9.0.rb +557 -25
- data/spec/blast_versions/diamond_0.9.24/diamond_0.9.24_spec.rb +13 -13
- data/spec/blast_versions/diamond_0.9.24/import_spec_capybara_local_0.9.24.rb +219 -21
- data/spec/capybara_spec.rb +25 -28
- data/spec/download_helper.rb +6 -3
- data/spec/sequences/MH011443_1_gi_1486783306_gb_MH011443_1.txt +6 -0
- data/spec/sequences/MH011443_1_gi_1486783307_gb_AYF55702_1.txt +6 -0
- data/spec/sequences/MH011443_1_gi_1528997474_gb_MH447967_1.txt +30 -0
- data/spec/sequences/MH011443_1_sp_P04637_P53_HUMAN.txt +6 -0
- data/spec/sequences/alignment-35_hits_diamond_blastp.txt +210 -0
- data/spec/sequences/alignment-35_hits_diamond_blastx.txt +210 -0
- data/spec/sequences/alignment-3_hits.txt +18 -0
- data/spec/sequences/alignment-40_hits_blastn.txt +246 -0
- data/spec/sequences/alignment-40_hits_blastp.txt +240 -0
- data/spec/sequences/alignment-40_hits_blastp_2.2.30.txt +240 -0
- data/spec/sequences/alignment-40_hits_blastx.txt +240 -0
- data/spec/sequences/alignment-40_hits_tblastn.txt +240 -0
- data/spec/sequences/alignment-40_hits_tblastn_2.2.30.txt +240 -0
- data/spec/sequences/alignment-40_hits_tblastx.txt +2664 -0
- data/spec/sequences/alignment-4_hits.txt +24 -0
- data/spec/sequences/alignment-4_hits_blastn.txt +24 -0
- data/spec/sequences/alignment-4_hits_blastp.txt +24 -0
- data/spec/sequences/alignment-4_hits_blastp_2.2.30.txt +24 -0
- data/spec/sequences/alignment-4_hits_blastx.txt +24 -0
- data/spec/sequences/alignment-4_hits_diamond_blastp.txt +24 -0
- data/spec/sequences/alignment-4_hits_diamond_blastx.txt +24 -0
- data/spec/sequences/alignment-4_hits_tblastn.txt +24 -0
- data/spec/sequences/alignment-4_hits_tblastn_2.2.30.txt +24 -0
- data/spec/sequences/alignment-4_hits_tblastx.txt +318 -0
- data/spec/sequences/sp_P04637_P53_HUMAN_gi_1099170394_ref_XP_018868681_1.txt +6 -0
- data/spec/sequences/sp_P04637_P53_HUMAN_gi_120407068_ref_NP_000537_3.txt +6 -0
- data/spec/sequences/sp_P04637_P53_HUMAN_gi_1484127324_gb_MG595988_1.txt +6 -0
- data/spec/sequences/sp_P04637_P53_HUMAN_gi_395440626_gb_JQ694049_1.txt +6 -0
- data/spec/sequences/sp_P04637_P53_HUMAN_sp_P04637_P53_HUMAN.txt +6 -0
- data/spec/spec_helper.rb +3 -3
- metadata +67 -57
- data/.eslintrc +0 -213
- data/Rakefile +0 -8
- data/spec/dotdir/blast_2.4.0/blastn/TBLASTN_XML_2.4.0.xml +0 -1181
- data/spec/dotdir/blast_2.5.0/blastn/BLASTN_LONG_XML_2.5.0.xml +0 -18813
- 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={
|
33
|
+
data-target={'#'+this.collapseId()}>
|
27
34
|
{ this.state.collapsed ?
|
28
35
|
this.plusIcon() : this.minusIcon() }
|
29
36
|
|
@@ -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',
|
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',
|
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 });
|
data/public/js/hits_overview.js
CHANGED
@@ -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
|
-
|
9
|
-
|
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 ')
|
86
|
-
.append(
|
87
|
-
$('<i/>')
|
88
|
-
.html(' ')
|
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 ')
|
96
|
-
.append(
|
97
|
-
$('<i/>')
|
98
|
-
.html(' ')
|
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
|
-
|
129
|
-
|
130
|
-
setupButtons($queryDiv, $graphDiv);
|
131
|
-
initButtons();
|
13
|
+
static className() {
|
14
|
+
return 'alignment-overview';
|
132
15
|
}
|
133
16
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
initButtons();
|
138
|
-
this.setupTooltip();
|
139
|
-
e.stopPropagation();
|
140
|
-
},this));
|
17
|
+
static collapseId(props) {
|
18
|
+
return 'alignment_'+props.query.number;
|
19
|
+
}
|
141
20
|
|
142
|
-
|
143
|
-
|
144
|
-
|
21
|
+
static dataName(props) {
|
22
|
+
return 'Alignment-Overview-'+props.query.id;
|
23
|
+
}
|
145
24
|
|
146
|
-
|
147
|
-
|
148
|
-
|
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 ')
|
91
|
+
.append(
|
92
|
+
$('<i/>')
|
93
|
+
.html(' ')
|
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 ')
|
101
|
+
.append(
|
102
|
+
$('<i/>')
|
103
|
+
.html(' ')
|
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
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
-
|
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
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
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
|
-
|
222
|
-
|
226
|
+
// Don't draw anything when no hits are obtained.
|
227
|
+
if (hits.length < 1) return false;
|
223
228
|
|
224
|
-
|
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
|
-
|
228
|
-
|
232
|
+
$graphDiv.find('svg').remove();
|
233
|
+
}
|
229
234
|
|
230
|
-
|
231
|
-
|
235
|
+
var queryLen = $queryDiv.data().queryLen;
|
236
|
+
var q_i = $queryDiv.attr('id');
|
232
237
|
|
233
|
-
|
234
|
-
|
238
|
+
var width = $graphDiv.width();
|
239
|
+
var height = hits.length * (options.barHeight) +
|
235
240
|
2 * options.legend + 5 * options.margin;
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
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
|
-
|
258
|
+
.append('g')
|
254
259
|
.attr('transform', 'translate(' + options.margin / 2 + ', ' + (1.5 * options.margin) + ')');
|
255
260
|
|
256
|
-
|
257
|
-
|
258
|
-
|
261
|
+
var x = d3.scale
|
262
|
+
.linear()
|
263
|
+
.range([0, width - options.margin]);
|
259
264
|
|
260
|
-
|
265
|
+
x.domain([1, queryLen]);
|
261
266
|
|
262
|
-
|
263
|
-
|
267
|
+
var algorithm = $queryDiv.data().algorithm;
|
268
|
+
var formatter = Helpers.tick_formatter(x, SEQ_TYPES[algorithm]);
|
264
269
|
|
265
|
-
|
266
|
-
|
270
|
+
var _tValues = x.ticks(11);
|
271
|
+
_tValues.pop();
|
267
272
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
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
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
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
|
-
|
283
|
-
|
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
|
-
|
289
|
-
|
290
|
-
|
293
|
+
var y = d3.scale
|
294
|
+
.ordinal()
|
295
|
+
.rangeBands([0, height - 3 * options.margin - 2 * options.legend], 0.3);
|
291
296
|
|
292
|
-
|
293
|
-
|
294
|
-
|
297
|
+
y.domain(hits.map(function (d) {
|
298
|
+
return d.hitId;
|
299
|
+
}));
|
295
300
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
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
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
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
|
-
|
343
|
-
|
344
|
-
.
|
345
|
-
|
346
|
-
|
347
|
-
|
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
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
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',
|
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
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
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);
|