sequenceserver 3.1.2 → 3.2.0
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.
- checksums.yaml +4 -4
- data/lib/sequenceserver/api_errors.rb +18 -0
- data/lib/sequenceserver/blast/hit.rb +1 -2
- data/lib/sequenceserver/blast/job.rb +15 -5
- data/lib/sequenceserver/blast/report.rb +5 -1
- data/lib/sequenceserver/blast/tasks.rb +1 -1
- data/lib/sequenceserver/database.rb +5 -1
- data/lib/sequenceserver/job.rb +26 -10
- data/lib/sequenceserver/makeblastdb.rb +2 -1
- data/lib/sequenceserver/routes.rb +38 -13
- data/lib/sequenceserver/version.rb +1 -1
- data/lib/sequenceserver/zip_file_generator.rb +1 -1
- data/lib/sequenceserver.rb +1 -1
- data/public/404.html +8 -16
- data/public/css/app.css +794 -111
- data/public/css/app.min.css +1 -1
- data/public/css/sequenceserver.min.css +1 -8
- data/public/js/alignment_exporter.js +16 -28
- data/public/js/circos.js +103 -96
- data/public/{packages/circosJS@1.7.0.js → js/circosjs.js} +156 -778
- data/public/js/cloud_share_modal.js +95 -98
- data/public/js/collapse_preferences.js +13 -15
- data/public/js/databases.js +4 -3
- data/public/js/databases_tree.js +51 -30
- data/public/js/error_modal.js +35 -22
- data/public/js/exporter.js +2 -0
- data/public/js/form.js +55 -17
- data/public/js/grapher.js +94 -92
- data/public/js/hit.js +24 -22
- data/public/js/hits.js +283 -0
- data/public/js/hits_overview.js +8 -27
- data/public/js/hsp.js +140 -144
- data/public/js/jquery_world.js +25 -16
- data/public/js/kablammo.js +14 -10
- data/public/js/length_distribution.js +23 -37
- data/public/js/mailto.js +1 -3
- data/public/js/null_plugins/report_plugins.js +4 -5
- data/public/js/options.js +19 -25
- data/public/js/query.js +36 -35
- data/public/js/report/alignment_results.js +18 -0
- data/public/js/report/graphical_overview.js +38 -0
- data/public/js/report/report_header.js +28 -0
- data/public/js/report/run_summary.js +43 -0
- data/public/js/report.js +124 -348
- data/public/js/report_root.js +10 -8
- data/public/js/search.js +31 -11
- data/public/js/search_button.js +85 -78
- data/public/js/sequence.js +169 -180
- data/public/js/sequence_modal.js +58 -65
- data/public/js/share_url.js +5 -5
- data/public/js/sidebar.js +152 -89
- data/public/js/svgExporter.js +8 -3
- data/public/js/tests/alignment_exporter.spec.js +38 -0
- data/public/js/tests/cloud_share_modal.spec.js +75 -0
- data/public/js/tests/form.spec.js +1 -1
- data/public/js/tests/report.spec.js +37 -15
- data/public/js/tests/visualisation_helpers.spec.js +28 -0
- data/public/js/visualisation_helpers.js +24 -27
- data/public/packages/jquery-ui@1.13.3.js +19070 -0
- data/public/sequenceserver-report.min.js +3 -2481
- data/public/sequenceserver-report.min.js.LICENSE.txt +213 -0
- data/public/sequenceserver-report.min.js.map +1 -0
- data/public/sequenceserver-search.min.js +3 -2382
- data/public/sequenceserver-search.min.js.LICENSE.txt +262 -0
- data/public/sequenceserver-search.min.js.map +1 -0
- data/views/error.erb +1 -1
- data/views/layout.erb +122 -133
- data/views/report.erb +3 -2
- data/views/search.erb +10 -14
- metadata +21 -28
- data/public/config.js +0 -147
- data/public/css/fonts.css +0 -31
- data/public/css/grapher.css +0 -670
- data/public/css/sequenceserver.css +0 -527
- data/public/packages/jquery-ui@1.11.4.js +0 -16624
- data/public/vendor/github/mbostock/d3@3.5.6/d3.js +0 -9506
- data/public/vendor/github/mbostock/d3@3.5.6.js +0 -1
- data/views/search_layout.erb +0 -153
data/public/js/report.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import './jquery_world'; // for custom $.tooltip function
|
|
2
2
|
import React, { Component } from 'react';
|
|
3
|
-
import _ from 'underscore';
|
|
4
3
|
|
|
5
4
|
import Sidebar from './sidebar';
|
|
6
|
-
import Circos from './circos';
|
|
7
|
-
import { ReportQuery } from './query';
|
|
8
|
-
import Hit from './hit';
|
|
9
|
-
import HSP from './hsp';
|
|
10
5
|
import AlignmentExporter from './alignment_exporter';
|
|
11
6
|
import ReportPlugins from 'report_plugins';
|
|
7
|
+
import RunSummary from './report/run_summary';
|
|
8
|
+
import GraphicalOverview from './report/graphical_overview';
|
|
9
|
+
import AlignmentResults from './report/alignment_results';
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* Renders entire report.
|
|
@@ -21,11 +19,6 @@ class Report extends Component {
|
|
|
21
19
|
super(props);
|
|
22
20
|
// Properties below are internal state used to render results in small
|
|
23
21
|
// slices (see updateState).
|
|
24
|
-
this.numUpdates = 0;
|
|
25
|
-
this.nextQuery = 0;
|
|
26
|
-
this.nextHit = 0;
|
|
27
|
-
this.nextHSP = 0;
|
|
28
|
-
this.maxHSPs = 3; // max HSPs to render in a cycle
|
|
29
22
|
this.state = {
|
|
30
23
|
user_warning: null,
|
|
31
24
|
download_links: [],
|
|
@@ -34,8 +27,8 @@ class Report extends Component {
|
|
|
34
27
|
program: '',
|
|
35
28
|
program_version: '',
|
|
36
29
|
submitted_at: '',
|
|
37
|
-
queries: [],
|
|
38
30
|
results: [],
|
|
31
|
+
queries: [],
|
|
39
32
|
querydb: [],
|
|
40
33
|
params: [],
|
|
41
34
|
stats: [],
|
|
@@ -43,7 +36,6 @@ class Report extends Component {
|
|
|
43
36
|
allQueriesLoaded: false,
|
|
44
37
|
cloud_sharing_enabled: false,
|
|
45
38
|
};
|
|
46
|
-
this.prepareAlignmentOfSelectedHits = this.prepareAlignmentOfSelectedHits.bind(this);
|
|
47
39
|
this.prepareAlignmentOfAllHits = this.prepareAlignmentOfAllHits.bind(this);
|
|
48
40
|
this.setStateFromJSON = this.setStateFromJSON.bind(this);
|
|
49
41
|
this.plugins = new ReportPlugins(this);
|
|
@@ -58,31 +50,66 @@ class Report extends Component {
|
|
|
58
50
|
}
|
|
59
51
|
|
|
60
52
|
pollPeriodically(path, callback, errCallback) {
|
|
61
|
-
|
|
53
|
+
var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];
|
|
62
54
|
function poll() {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
55
|
+
fetch(path)
|
|
56
|
+
.then(response => {
|
|
57
|
+
// Handle HTTP status codes
|
|
58
|
+
if (!response.ok) throw response;
|
|
59
|
+
|
|
60
|
+
return response.text().then(data => {
|
|
61
|
+
if (data) {
|
|
62
|
+
data = parseJSON(data);
|
|
63
|
+
};
|
|
64
|
+
return { status: response.status, data }
|
|
65
|
+
});
|
|
66
|
+
})
|
|
67
|
+
.then(({ status, data }) => {
|
|
68
|
+
switch (status) {
|
|
69
|
+
case 202:
|
|
70
|
+
var interval;
|
|
71
|
+
if (intervals.length === 1) {
|
|
72
|
+
interval = intervals[0];
|
|
73
|
+
} else {
|
|
74
|
+
interval = intervals.shift();
|
|
75
|
+
}
|
|
76
|
+
setTimeout(poll, interval);
|
|
77
|
+
break;
|
|
78
|
+
case 200:
|
|
79
|
+
callback(data);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
.catch(error => {
|
|
84
|
+
if (error.text) {
|
|
85
|
+
error.text().then(errData => {
|
|
86
|
+
errData = parseJSON(errData);
|
|
87
|
+
switch (error.status) {
|
|
88
|
+
case 400:
|
|
89
|
+
case 422:
|
|
90
|
+
case 500:
|
|
91
|
+
errCallback(errData);
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
console.error("Unhandled error:", error.status);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
69
97
|
} else {
|
|
70
|
-
|
|
98
|
+
console.error("Network error:", error);
|
|
71
99
|
}
|
|
72
|
-
|
|
73
|
-
break;
|
|
74
|
-
case 200:
|
|
75
|
-
callback(jqXHR.responseJSON);
|
|
76
|
-
break;
|
|
77
|
-
case 400:
|
|
78
|
-
case 422:
|
|
79
|
-
case 500:
|
|
80
|
-
errCallback(jqXHR.responseJSON);
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
});
|
|
100
|
+
});
|
|
84
101
|
}
|
|
85
102
|
|
|
103
|
+
function parseJSON(str) {
|
|
104
|
+
let parsedJson = str;
|
|
105
|
+
try {
|
|
106
|
+
parsedJson = JSON.parse(str);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
console.error("Error parsing JSON:", e);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return parsedJson;
|
|
112
|
+
}
|
|
86
113
|
poll();
|
|
87
114
|
}
|
|
88
115
|
|
|
@@ -106,146 +133,12 @@ class Report extends Component {
|
|
|
106
133
|
*/
|
|
107
134
|
componentDidMount() {
|
|
108
135
|
this.fetchResults();
|
|
109
|
-
this.plugins.init();
|
|
110
136
|
// This sets up an event handler which enables users to select text from
|
|
111
137
|
// hit header without collapsing the hit.
|
|
112
138
|
this.preventCollapseOnSelection();
|
|
113
139
|
this.toggleTable();
|
|
114
140
|
}
|
|
115
141
|
|
|
116
|
-
/**
|
|
117
|
-
* Called for the first time after as BLAST results have been retrieved from
|
|
118
|
-
* the server and added to this.state by fetchResults. Only summary overview
|
|
119
|
-
* and circos would have been rendered at this point. At this stage we kick
|
|
120
|
-
* start iteratively adding 1 HSP to the page every 25 milli-seconds.
|
|
121
|
-
*/
|
|
122
|
-
componentDidUpdate(prevProps, prevState) {
|
|
123
|
-
// Log to console how long the last update take?
|
|
124
|
-
// console.log((Date.now() - this.lastTimeStamp) / 1000);
|
|
125
|
-
|
|
126
|
-
// Lock sidebar in its position on the first update.
|
|
127
|
-
if (this.nextQuery == 0 && this.nextHit == 0 && this.nextHSP == 0) {
|
|
128
|
-
this.affixSidebar();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Queue next update if we have not rendered all results yet.
|
|
132
|
-
if (this.nextQuery < this.state.queries.length) {
|
|
133
|
-
// setTimeout is used to clear call stack and space out
|
|
134
|
-
// the updates giving the browser a chance to respond
|
|
135
|
-
// to user interactions.
|
|
136
|
-
setTimeout(() => this.updateState(), 25);
|
|
137
|
-
} else {
|
|
138
|
-
this.componentFinishedUpdating();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
this.plugins.componentDidUpdate(prevProps, prevState);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Push next slice of results to React for rendering.
|
|
146
|
-
*/
|
|
147
|
-
updateState() {
|
|
148
|
-
var results = [];
|
|
149
|
-
var numHSPsProcessed = 0;
|
|
150
|
-
while (this.nextQuery < this.state.queries.length) {
|
|
151
|
-
var query = this.state.queries[this.nextQuery];
|
|
152
|
-
|
|
153
|
-
// We may see a query multiple times during rendering because only
|
|
154
|
-
// 3 hsps are rendered in each cycle, but we want to create the
|
|
155
|
-
// corresponding Query component only the first time we see it.
|
|
156
|
-
if (this.nextHit == 0 && this.nextHSP == 0) {
|
|
157
|
-
results.push(
|
|
158
|
-
<ReportQuery
|
|
159
|
-
key={'Query_' + query.id}
|
|
160
|
-
query={query}
|
|
161
|
-
program={this.state.program}
|
|
162
|
-
querydb={this.state.querydb}
|
|
163
|
-
showQueryCrumbs={this.state.queries.length > 1}
|
|
164
|
-
non_parse_seqids={this.state.non_parse_seqids}
|
|
165
|
-
imported_xml={this.state.imported_xml}
|
|
166
|
-
veryBig={this.state.veryBig}
|
|
167
|
-
/>
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
results.push(...this.plugins.queryResults(query));
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
while (this.nextHit < query.hits.length) {
|
|
174
|
-
var hit = query.hits[this.nextHit];
|
|
175
|
-
// We may see a hit multiple times during rendering because only
|
|
176
|
-
// 10 hsps are rendered in each cycle, but we want to create the
|
|
177
|
-
// corresponding Hit component only the first time we see it.
|
|
178
|
-
if (this.nextHSP == 0) {
|
|
179
|
-
results.push(
|
|
180
|
-
<Hit
|
|
181
|
-
key={'Query_' + query.number + '_Hit_' + hit.number}
|
|
182
|
-
query={query}
|
|
183
|
-
hit={hit}
|
|
184
|
-
algorithm={this.state.program}
|
|
185
|
-
querydb={this.state.querydb}
|
|
186
|
-
selectHit={this.selectHit}
|
|
187
|
-
imported_xml={this.state.imported_xml}
|
|
188
|
-
non_parse_seqids={this.state.non_parse_seqids}
|
|
189
|
-
showQueryCrumbs={this.state.queries.length > 1}
|
|
190
|
-
showHitCrumbs={query.hits.length > 1}
|
|
191
|
-
veryBig={this.state.veryBig}
|
|
192
|
-
onChange={this.prepareAlignmentOfSelectedHits}
|
|
193
|
-
{...this.props}
|
|
194
|
-
/>
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
while (this.nextHSP < hit.hsps.length) {
|
|
199
|
-
// Get nextHSP and increment the counter.
|
|
200
|
-
var hsp = hit.hsps[this.nextHSP++];
|
|
201
|
-
results.push(
|
|
202
|
-
<HSP
|
|
203
|
-
key={
|
|
204
|
-
'Query_' +
|
|
205
|
-
query.number +
|
|
206
|
-
'_Hit_' +
|
|
207
|
-
hit.number +
|
|
208
|
-
'_HSP_' +
|
|
209
|
-
hsp.number
|
|
210
|
-
}
|
|
211
|
-
query={query}
|
|
212
|
-
hit={hit}
|
|
213
|
-
hsp={hsp}
|
|
214
|
-
algorithm={this.state.program}
|
|
215
|
-
showHSPNumbers={hit.hsps.length > 1}
|
|
216
|
-
{...this.props}
|
|
217
|
-
/>
|
|
218
|
-
);
|
|
219
|
-
numHSPsProcessed++;
|
|
220
|
-
if (numHSPsProcessed == this.maxHSPs) break;
|
|
221
|
-
}
|
|
222
|
-
// Are we here because we have iterated over all hsps of a hit,
|
|
223
|
-
// or because of the break clause in the inner loop?
|
|
224
|
-
if (this.nextHSP == hit.hsps.length) {
|
|
225
|
-
this.nextHit = this.nextHit + 1;
|
|
226
|
-
this.nextHSP = 0;
|
|
227
|
-
}
|
|
228
|
-
if (numHSPsProcessed == this.maxHSPs) break;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Are we here because we have iterated over all hits of a query,
|
|
232
|
-
// or because of the break clause in the inner loop?
|
|
233
|
-
if (this.nextHit == query.hits.length) {
|
|
234
|
-
this.nextQuery = this.nextQuery + 1;
|
|
235
|
-
this.nextHit = 0;
|
|
236
|
-
}
|
|
237
|
-
if (numHSPsProcessed == this.maxHSPs) break;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Push the components to react for rendering.
|
|
241
|
-
this.numUpdates++;
|
|
242
|
-
this.lastTimeStamp = Date.now();
|
|
243
|
-
this.setState({
|
|
244
|
-
results: this.state.results.concat(results),
|
|
245
|
-
veryBig: this.numUpdates >= 250,
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
142
|
/**
|
|
250
143
|
* Called after all results have been rendered.
|
|
251
144
|
*/
|
|
@@ -260,21 +153,20 @@ class Report extends Component {
|
|
|
260
153
|
*/
|
|
261
154
|
loadingJSX() {
|
|
262
155
|
return (
|
|
263
|
-
<div className="
|
|
264
|
-
<div className="col-
|
|
265
|
-
<h1>
|
|
156
|
+
<div className="grid grid-cols-6 gap-4">
|
|
157
|
+
<div className="col-start-1 col-end-7 text-center pt-3">
|
|
158
|
+
<h1 className="mb-8 text-4xl">
|
|
266
159
|
<i className="fa fa-cog fa-spin"></i> BLAST-ing
|
|
267
160
|
</h1>
|
|
268
|
-
<
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
<br />
|
|
161
|
+
<div className="mb-5 w-full">
|
|
162
|
+
<p className="m-auto w-full md:w-6/12 text-sm">This can take some time depending on the size of your query and
|
|
163
|
+
database(s). The page will update automatically when BLAST is done.</p>
|
|
164
|
+
</div>
|
|
165
|
+
<p className="mb-9 text-sm">
|
|
166
|
+
You can bookmark the page and come back to it later or share the
|
|
167
|
+
link with someone.
|
|
168
|
+
</p>
|
|
169
|
+
<p className="text-sm">
|
|
278
170
|
{ process.env.targetEnv === 'cloud' && <b>If the job takes more than 10 minutes to complete, we will send you an email upon completion.</b> }
|
|
279
171
|
</p>
|
|
280
172
|
</div>
|
|
@@ -282,13 +174,14 @@ class Report extends Component {
|
|
|
282
174
|
);
|
|
283
175
|
}
|
|
284
176
|
|
|
177
|
+
/* eslint-disable */
|
|
285
178
|
/**
|
|
286
179
|
* Return results JSX.
|
|
287
180
|
*/
|
|
288
181
|
resultsJSX() {
|
|
289
182
|
return (
|
|
290
|
-
<div className="
|
|
291
|
-
<div className="col-
|
|
183
|
+
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 print:grid-cols-1" id="results">
|
|
184
|
+
<div className="hidden md:col-span-1 md:block print:hidden">
|
|
292
185
|
<Sidebar
|
|
293
186
|
data={this.state}
|
|
294
187
|
atLeastOneHit={this.atLeastOneHit()}
|
|
@@ -297,31 +190,46 @@ class Report extends Component {
|
|
|
297
190
|
cloudSharingEnabled={this.state.cloud_sharing_enabled}
|
|
298
191
|
/>
|
|
299
192
|
</div>
|
|
300
|
-
<div className="col-md-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
193
|
+
<div className="col-span-1 md:col-span-3 print:col-span-1">
|
|
194
|
+
<RunSummary
|
|
195
|
+
seqserv_version={this.state.seqserv_version}
|
|
196
|
+
program_version={this.state.program_version}
|
|
197
|
+
submitted_at={this.state.submitted_at}
|
|
198
|
+
querydb={this.state.querydb}
|
|
199
|
+
stats={this.state.stats}
|
|
200
|
+
params={this.state.params}
|
|
201
|
+
/>
|
|
202
|
+
<GraphicalOverview
|
|
203
|
+
queries={this.state.queries}
|
|
204
|
+
prorgam={this.state.program}
|
|
205
|
+
plugins={this.plugins}
|
|
206
|
+
/>
|
|
207
|
+
<AlignmentResults
|
|
208
|
+
state={this.state}
|
|
209
|
+
populate_hsp_array={this.populate_hsp_array.bind(this)}
|
|
210
|
+
componentFinishedUpdating={(_) => this.componentFinishedUpdating(_)}
|
|
211
|
+
plugins={this.plugins}
|
|
212
|
+
{...this.props}
|
|
213
|
+
/>
|
|
305
214
|
</div>
|
|
306
215
|
</div>
|
|
307
216
|
);
|
|
308
217
|
}
|
|
218
|
+
/* eslint-enable */
|
|
309
219
|
|
|
310
220
|
|
|
311
221
|
warningJSX() {
|
|
312
222
|
return(
|
|
313
|
-
<div className="
|
|
314
|
-
<div className="
|
|
315
|
-
<div className="col-
|
|
316
|
-
<h1>
|
|
223
|
+
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|
224
|
+
<div className="grid grid-cols-6 gap-4">
|
|
225
|
+
<div className="col-start-1 col-end-7 text-center">
|
|
226
|
+
<h1 className="mb-4 text-4xl">
|
|
317
227
|
<i className="fa fa-exclamation-triangle"></i> Warning
|
|
318
228
|
</h1>
|
|
319
|
-
<p>
|
|
320
|
-
<br />
|
|
229
|
+
<p className="mb-2">
|
|
321
230
|
The BLAST result might be too large to load in the browser. If you have a powerful machine you can try loading the results anyway. Otherwise, you can download the results and view them locally.
|
|
322
231
|
</p>
|
|
323
|
-
<
|
|
324
|
-
<p>
|
|
232
|
+
<p className="mb-2">
|
|
325
233
|
{this.state.download_links.map((link, index) => {
|
|
326
234
|
return (
|
|
327
235
|
<a href={link.url} className="btn btn-secondary" key={'download_link_' + index} >
|
|
@@ -330,9 +238,8 @@ class Report extends Component {
|
|
|
330
238
|
);
|
|
331
239
|
})}
|
|
332
240
|
</p>
|
|
333
|
-
<br />
|
|
334
241
|
<p>
|
|
335
|
-
<a href={location.pathname + '?bypass_file_size_warning=true'} className="
|
|
242
|
+
<a href={location.pathname + '?bypass_file_size_warning=true'} className="py-2 px-3 border border-transparent rounded-md shadow-sm text-white bg-seqblue hover:bg-seqorange focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-seqorange">
|
|
336
243
|
View results in browser anyway
|
|
337
244
|
</a>
|
|
338
245
|
</p>
|
|
@@ -341,57 +248,6 @@ class Report extends Component {
|
|
|
341
248
|
</div>
|
|
342
249
|
);
|
|
343
250
|
}
|
|
344
|
-
/**
|
|
345
|
-
* Renders report overview.
|
|
346
|
-
*/
|
|
347
|
-
overviewJSX() {
|
|
348
|
-
return (
|
|
349
|
-
<div className="overview">
|
|
350
|
-
<p>
|
|
351
|
-
<strong>SequenceServer {this.state.seqserv_version}</strong> using{' '}
|
|
352
|
-
<strong>{this.state.program_version}</strong>
|
|
353
|
-
{this.state.submitted_at &&
|
|
354
|
-
`, query submitted on ${this.state.submitted_at}`}
|
|
355
|
-
</p>
|
|
356
|
-
<p>
|
|
357
|
-
<strong> Databases: </strong>
|
|
358
|
-
{this.state.querydb
|
|
359
|
-
.map((db) => {
|
|
360
|
-
return db.title;
|
|
361
|
-
})
|
|
362
|
-
.join(', ')}{' '}
|
|
363
|
-
({this.state.stats.nsequences} sequences,
|
|
364
|
-
{this.state.stats.ncharacters} characters)
|
|
365
|
-
</p>
|
|
366
|
-
<p>
|
|
367
|
-
<strong>Parameters: </strong>{' '}
|
|
368
|
-
{_.map(this.state.params, function (val, key) {
|
|
369
|
-
return key + ' ' + val;
|
|
370
|
-
}).join(', ')}
|
|
371
|
-
</p>
|
|
372
|
-
<p>
|
|
373
|
-
Please cite:{' '}
|
|
374
|
-
<a href="https://doi.org/10.1093/molbev/msz185">
|
|
375
|
-
https://doi.org/10.1093/molbev/msz185
|
|
376
|
-
</a>
|
|
377
|
-
</p>
|
|
378
|
-
</div>
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Return JSX for circos if we have at least one hit.
|
|
384
|
-
*/
|
|
385
|
-
circosJSX() {
|
|
386
|
-
return this.atLeastTwoHits() ? (
|
|
387
|
-
<Circos
|
|
388
|
-
queries={this.state.queries}
|
|
389
|
-
program={this.state.program}
|
|
390
|
-
/>
|
|
391
|
-
) : (
|
|
392
|
-
<span></span>
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
251
|
|
|
396
252
|
// Controller //
|
|
397
253
|
|
|
@@ -420,18 +276,6 @@ class Report extends Component {
|
|
|
420
276
|
return this.state.queries.some((query) => query.hits.length > 0);
|
|
421
277
|
}
|
|
422
278
|
|
|
423
|
-
/**
|
|
424
|
-
* Does the report have at least two hits? This is used to determine
|
|
425
|
-
* whether Circos should be enabled or not.
|
|
426
|
-
*/
|
|
427
|
-
atLeastTwoHits() {
|
|
428
|
-
var hit_num = 0;
|
|
429
|
-
return this.state.queries.some((query) => {
|
|
430
|
-
hit_num += query.hits.length;
|
|
431
|
-
return hit_num > 1;
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
|
|
435
279
|
/**
|
|
436
280
|
* Returns true if index should be shown in the sidebar. Index is shown
|
|
437
281
|
* only for 2 and 8 queries.
|
|
@@ -452,7 +296,8 @@ class Report extends Component {
|
|
|
452
296
|
// user wants to toggle
|
|
453
297
|
var hitID = $this.parents('.hit').attr('id');
|
|
454
298
|
$(`div[data-parent-hit=${hitID}]`).toggle();
|
|
455
|
-
$this.find('i').toggleClass('fa-
|
|
299
|
+
$this.find('i').toggleClass('fa-square-minus fa-square-plus');
|
|
300
|
+
$($this.data('parent-id')).toggleClass('print:hidden');
|
|
456
301
|
} else {
|
|
457
302
|
// user wants to select
|
|
458
303
|
$this.attr('data-toggle', '');
|
|
@@ -463,6 +308,7 @@ class Report extends Component {
|
|
|
463
308
|
}
|
|
464
309
|
|
|
465
310
|
/* Handling the fa icon when Hit Table is collapsed */
|
|
311
|
+
/* TODO:JOKO check if this method still being used? */
|
|
466
312
|
toggleTable() {
|
|
467
313
|
$('body').on(
|
|
468
314
|
'mousedown',
|
|
@@ -470,113 +316,43 @@ class Report extends Component {
|
|
|
470
316
|
function (event) {
|
|
471
317
|
var $this = $(this);
|
|
472
318
|
$this.on('mouseup mousemove', function handler(event) {
|
|
473
|
-
$this.find('i').toggleClass('fa-
|
|
319
|
+
$this.find('i').toggleClass('fa-square-minus fa-square-plus');
|
|
474
320
|
$this.off('mouseup mousemove', handler);
|
|
475
321
|
});
|
|
476
322
|
}
|
|
477
323
|
);
|
|
478
324
|
}
|
|
479
325
|
|
|
480
|
-
|
|
481
|
-
* Affixes the sidebar.
|
|
482
|
-
*/
|
|
483
|
-
affixSidebar() {
|
|
484
|
-
var $sidebar = $('.sidebar');
|
|
485
|
-
var sidebarOffset = $sidebar.offset();
|
|
486
|
-
if (sidebarOffset) {
|
|
487
|
-
$sidebar.affix({
|
|
488
|
-
offset: {
|
|
489
|
-
top: sidebarOffset.top,
|
|
490
|
-
},
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
}
|
|
326
|
+
|
|
494
327
|
|
|
495
328
|
/**
|
|
496
329
|
* For the query in viewport, highlights corresponding entry in the index.
|
|
497
330
|
*/
|
|
498
331
|
setupScrollSpy() {
|
|
499
|
-
$('
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* Event-handler when hit is selected
|
|
504
|
-
* Adds glow to hit component.
|
|
505
|
-
* Updates number of Fasta that can be downloaded
|
|
506
|
-
*/
|
|
507
|
-
selectHit(id) {
|
|
508
|
-
var checkbox = $('#' + id);
|
|
509
|
-
var num_checked = $('.hit-links :checkbox:checked').length;
|
|
332
|
+
var sectionIds = $('a.side-nav');
|
|
510
333
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
334
|
+
$(document).scroll(function(){
|
|
335
|
+
sectionIds.each(function(){
|
|
514
336
|
|
|
515
|
-
|
|
337
|
+
var container = $(this).attr('href');
|
|
338
|
+
var containerOffset = $(container).offset().top;
|
|
339
|
+
var containerHeight = $(container).outerHeight();
|
|
340
|
+
var containerBottom = containerOffset + containerHeight;
|
|
341
|
+
var scrollPosition = $(document).scrollTop();
|
|
516
342
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
} else {
|
|
525
|
-
$hit.removeClass('glow');
|
|
526
|
-
$hit.next('.hsp').removeClass('glow');
|
|
527
|
-
$('.download-fasta-of-selected').attr('href', '#').removeAttr('download');
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
var $a = $('.download-fasta-of-selected');
|
|
531
|
-
var $b = $('.download-alignment-of-selected');
|
|
532
|
-
|
|
533
|
-
if (num_checked >= 1) {
|
|
534
|
-
$a.find('.text-bold').html(num_checked);
|
|
535
|
-
$b.find('.text-bold').html(num_checked);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (num_checked == 0) {
|
|
539
|
-
$a.addClass('disabled').find('.text-bold').html('');
|
|
540
|
-
$b.addClass('disabled').find('.text-bold').html('');
|
|
541
|
-
}
|
|
343
|
+
if(scrollPosition < containerBottom - 20 && scrollPosition >= containerOffset - 20){
|
|
344
|
+
$(this).addClass('active');
|
|
345
|
+
} else {
|
|
346
|
+
$(this).removeClass('active');
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
});
|
|
542
350
|
}
|
|
351
|
+
|
|
543
352
|
populate_hsp_array(hit, query_id){
|
|
544
353
|
return hit.hsps.map(hsp => Object.assign(hsp, {hit_id: hit.id, query_id}));
|
|
545
354
|
}
|
|
546
355
|
|
|
547
|
-
prepareAlignmentOfSelectedHits() {
|
|
548
|
-
var sequence_ids = $('.hit-links :checkbox:checked').map(function () {
|
|
549
|
-
return this.value;
|
|
550
|
-
}).get();
|
|
551
|
-
|
|
552
|
-
if(!sequence_ids.length){
|
|
553
|
-
// remove attributes from link if sequence_ids array is empty
|
|
554
|
-
$('.download-alignment-of-selected').attr('href', '#').removeAttr('download');
|
|
555
|
-
return;
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
if(this.state.alignment_blob_url){
|
|
559
|
-
// always revoke existing url if any because this method will always create a new url
|
|
560
|
-
window.URL.revokeObjectURL(this.state.alignment_blob_url);
|
|
561
|
-
}
|
|
562
|
-
var hsps_arr = [];
|
|
563
|
-
var aln_exporter = new AlignmentExporter();
|
|
564
|
-
const self = this;
|
|
565
|
-
_.each(this.state.queries, _.bind(function (query) {
|
|
566
|
-
_.each(query.hits, function (hit) {
|
|
567
|
-
if (_.indexOf(sequence_ids, hit.id) != -1) {
|
|
568
|
-
hsps_arr = hsps_arr.concat(self.populate_hsp_array(hit, query.id));
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
}, this));
|
|
572
|
-
const filename = 'alignment-' + sequence_ids.length + '_hits.txt';
|
|
573
|
-
const blob_url = aln_exporter.prepare_alignments_for_export(hsps_arr, filename);
|
|
574
|
-
// set required download attributes for link
|
|
575
|
-
$('.download-alignment-of-selected').attr('href', blob_url).attr('download', filename);
|
|
576
|
-
// track new url for future removal
|
|
577
|
-
this.setState({alignment_blob_url: blob_url});
|
|
578
|
-
}
|
|
579
|
-
|
|
580
356
|
prepareAlignmentOfAllHits() {
|
|
581
357
|
// Get number of hits and array of all hsps.
|
|
582
358
|
var num_hits = 0;
|