sequenceserver 2.2.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/COPYRIGHT.txt +1 -1
- data/bin/sequenceserver +4 -2
- data/lib/sequenceserver/blast/error.rb +53 -0
- data/lib/sequenceserver/blast/job.rb +2 -43
- data/lib/sequenceserver/job.rb +21 -11
- data/lib/sequenceserver/makeblastdb-modified-with-cache.rb +345 -0
- data/lib/sequenceserver/makeblastdb.rb +26 -12
- data/lib/sequenceserver/routes.rb +29 -3
- data/lib/sequenceserver/server.rb +1 -1
- data/lib/sequenceserver/version.rb +1 -1
- data/lib/sequenceserver.rb +3 -0
- data/public/404.html +27 -0
- data/public/config.js +0 -6
- data/public/css/grapher.css +1 -1
- data/public/css/sequenceserver.css +22 -11
- data/public/css/sequenceserver.min.css +2 -2
- data/public/js/circos.js +7 -3
- data/public/js/dnd.js +3 -3
- data/public/js/fastq_to_fasta.js +35 -0
- data/public/js/form.js +30 -11
- data/public/js/grapher.js +123 -113
- data/public/js/hit.js +8 -2
- data/public/js/hits_overview.js +4 -1
- data/public/js/jquery_world.js +0 -1
- data/public/js/kablammo.js +4 -0
- data/public/js/length_distribution.js +5 -1
- data/public/js/null_plugins/download_links.js +7 -0
- data/public/js/null_plugins/hit_buttons.js +11 -0
- data/public/js/null_plugins/report_plugins.js +18 -0
- data/public/js/query.js +26 -6
- data/public/js/report.js +33 -17
- data/public/js/search.js +0 -8
- data/public/js/sidebar.js +11 -1
- data/public/js/tests/mock_data/sequences.js +18 -1
- data/public/js/tests/search_query.spec.js +12 -3
- data/public/sequenceserver-report.min.js +76 -42
- data/public/sequenceserver-search.min.js +34 -33
- data/views/layout.erb +9 -12
- metadata +32 -23
data/public/js/circos.js
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
import d3 from 'd3';
|
2
2
|
import Circos from '../packages/circosJS@1.7.0';
|
3
|
-
import _ from 'underscore';
|
3
|
+
import _ from 'underscore';
|
4
4
|
|
5
5
|
import Grapher from './grapher';
|
6
6
|
import * as Helpers from './visualisation_helpers';
|
7
7
|
import Utils from './utils';
|
8
8
|
|
9
9
|
class Graph {
|
10
|
+
static canCollapse() {
|
11
|
+
return true;
|
12
|
+
}
|
13
|
+
|
10
14
|
static name() {
|
11
|
-
return '
|
15
|
+
return 'Chord diagram of queries and their top hits';
|
12
16
|
}
|
13
17
|
|
14
18
|
static className() {
|
@@ -408,7 +412,7 @@ class Graph {
|
|
408
412
|
.attr('dy', '-0.25em')
|
409
413
|
.attr('x', -175)
|
410
414
|
.style('font-size', '14px')
|
411
|
-
.text('
|
415
|
+
.text('Chord diagram looks great with fewer than 16 queries');
|
412
416
|
}
|
413
417
|
|
414
418
|
layoutReset() {
|
data/public/js/dnd.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import React, { Component } from 'react';
|
2
2
|
|
3
|
-
/**
|
3
|
+
/**
|
4
4
|
* Drag n drop widget.
|
5
5
|
*/
|
6
6
|
export class DnD extends Component {
|
@@ -74,7 +74,7 @@ export class DnD extends Component {
|
|
74
74
|
}
|
75
75
|
|
76
76
|
var file = files[0];
|
77
|
-
if (file.size >
|
77
|
+
if (file.size > 250 * 1048576) {
|
78
78
|
dndError('dnd-large-file');
|
79
79
|
return;
|
80
80
|
}
|
@@ -141,7 +141,7 @@ export class DnD extends Component {
|
|
141
141
|
style={{ display: 'none' }}>
|
142
142
|
<div
|
143
143
|
className="col-md-6 col-md-offset-3">
|
144
|
-
Too big a file. Can only do less than
|
144
|
+
Too big a file. Can only do less than 250 MB. >_<
|
145
145
|
</div>
|
146
146
|
</div>
|
147
147
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
const convertChunk = (fastqChunk) => {
|
2
|
+
fastqChunk[0] = '>' + fastqChunk[0].substring(1);
|
3
|
+
return fastqChunk.slice(0, 2);
|
4
|
+
};
|
5
|
+
|
6
|
+
const isValidFastq = (fastqChunk) => {
|
7
|
+
if (fastqChunk.length !== 4) {
|
8
|
+
return false;
|
9
|
+
}
|
10
|
+
|
11
|
+
return fastqChunk[0][0] === '@' && fastqChunk[2][0] === '+' && fastqChunk[1].length === fastqChunk[3].length;
|
12
|
+
};
|
13
|
+
|
14
|
+
export const fastqToFasta = (sequence) => {
|
15
|
+
let trimmedSequence = sequence.trim();
|
16
|
+
// return unmodified if sequence does not look like fastq
|
17
|
+
if (!trimmedSequence.startsWith('@')) {
|
18
|
+
return sequence;
|
19
|
+
}
|
20
|
+
|
21
|
+
const sequenceLines = trimmedSequence.split('\n');
|
22
|
+
const fastaChunks = [];
|
23
|
+
|
24
|
+
for (let i = 0; i < sequenceLines.length; i += 4) {
|
25
|
+
const fastqChunk = sequenceLines.slice(i, i + 4);
|
26
|
+
if (isValidFastq(fastqChunk)) {
|
27
|
+
fastaChunks.push(...convertChunk(fastqChunk));
|
28
|
+
} else {
|
29
|
+
// return unmodified sequence if it does not look like valid fastq
|
30
|
+
return sequence;
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
return fastaChunks.join('\n');
|
35
|
+
};
|
data/public/js/form.js
CHANGED
@@ -21,14 +21,14 @@ export class Form extends Component {
|
|
21
21
|
this.useTreeWidget = this.useTreeWidget.bind(this);
|
22
22
|
this.determineBlastMethod = this.determineBlastMethod.bind(this);
|
23
23
|
this.handleSequenceTypeChanged = this.handleSequenceTypeChanged.bind(this);
|
24
|
-
this.
|
24
|
+
this.handleDatabaseTypeChanged = this.handleDatabaseTypeChanged.bind(this);
|
25
25
|
this.handleAlgoChanged = this.handleAlgoChanged.bind(this);
|
26
26
|
this.handleFormSubmission = this.handleFormSubmission.bind(this);
|
27
27
|
this.formRef = createRef();
|
28
28
|
}
|
29
29
|
|
30
30
|
componentDidMount() {
|
31
|
-
/**
|
31
|
+
/**
|
32
32
|
* Fetch data to initialise the search interface from the server. These
|
33
33
|
* include list of databases to search against, advanced options to
|
34
34
|
* apply when an algorithm is selected, and a query sequence that
|
@@ -71,7 +71,7 @@ export class Form extends Component {
|
|
71
71
|
}
|
72
72
|
});
|
73
73
|
|
74
|
-
// show overlay to create visual feedback on button click
|
74
|
+
// show overlay to create visual feedback on button click
|
75
75
|
$('#method').on('click', () => {
|
76
76
|
$('#overlay').css('display', 'block');
|
77
77
|
});
|
@@ -102,6 +102,7 @@ export class Form extends Component {
|
|
102
102
|
}
|
103
103
|
});
|
104
104
|
}
|
105
|
+
|
105
106
|
determineBlastMethod() {
|
106
107
|
var database_type = this.databaseType;
|
107
108
|
var sequence_type = this.sequenceType;
|
@@ -146,7 +147,7 @@ export class Form extends Component {
|
|
146
147
|
});
|
147
148
|
}
|
148
149
|
|
149
|
-
|
150
|
+
handleDatabaseTypeChanged(type) {
|
150
151
|
this.databaseType = type;
|
151
152
|
this.refs.button.setState({
|
152
153
|
hasQuery: !this.refs.query.isEmpty(),
|
@@ -174,24 +175,27 @@ export class Form extends Component {
|
|
174
175
|
return (
|
175
176
|
<div className="container">
|
176
177
|
<div id="overlay" style={{ position: 'absolute', top: 0, left: 0, width: '100vw', height: '100vw', background: 'rgba(0, 0, 0, 0.2)', display: 'none', zIndex: 99 }} />
|
178
|
+
|
179
|
+
<div className="notifications" id="notifications">
|
180
|
+
<FastqNotification />
|
181
|
+
<NucleotideNotification />
|
182
|
+
<ProteinNotification />
|
183
|
+
<MixedNotification />
|
184
|
+
</div>
|
185
|
+
|
177
186
|
<form id="blast" ref={this.formRef} onSubmit={this.handleFormSubmission} className="form-horizontal">
|
178
187
|
<div className="form-group query-container">
|
179
188
|
<SearchQueryWidget ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged} />
|
180
189
|
</div>
|
181
|
-
<div className="notifications" id="notifications">
|
182
|
-
<NucleotideNotification />
|
183
|
-
<ProteinNotification />
|
184
|
-
<MixedNotification />
|
185
|
-
</div>
|
186
190
|
{this.useTreeWidget() ?
|
187
191
|
<DatabasesTree ref="databases"
|
188
192
|
databases={this.state.databases} tree={this.state.tree}
|
189
193
|
preSelectedDbs={this.state.preSelectedDbs}
|
190
|
-
onDatabaseTypeChanged={this.
|
194
|
+
onDatabaseTypeChanged={this.handleDatabaseTypeChanged} />
|
191
195
|
:
|
192
196
|
<Databases ref="databases" databases={this.state.databases}
|
193
197
|
preSelectedDbs={this.state.preSelectedDbs}
|
194
|
-
onDatabaseTypeChanged={this.
|
198
|
+
onDatabaseTypeChanged={this.handleDatabaseTypeChanged} />
|
195
199
|
}
|
196
200
|
<div className="form-group">
|
197
201
|
<Options ref="opts" />
|
@@ -243,6 +247,21 @@ class NucleotideNotification extends Component {
|
|
243
247
|
}
|
244
248
|
}
|
245
249
|
|
250
|
+
class FastqNotification extends Component {
|
251
|
+
render() {
|
252
|
+
return (<div
|
253
|
+
className="notification row"
|
254
|
+
id="fastq-sequence-notification"
|
255
|
+
style={{ display: 'none' }}>
|
256
|
+
<div
|
257
|
+
className="alert-info col-md-6 col-md-offset-3">
|
258
|
+
Detected FASTQ and automatically converted to FASTA.
|
259
|
+
</div>
|
260
|
+
</div>
|
261
|
+
);
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
246
265
|
class MixedNotification extends Component {
|
247
266
|
render() {
|
248
267
|
return (
|
data/public/js/grapher.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
import _ from
|
2
|
-
import React, { createRef } from
|
1
|
+
import _ from 'underscore';
|
2
|
+
import React, { createRef } from 'react';
|
3
3
|
|
4
|
-
import
|
4
|
+
import './svgExporter'; // create handlers for SVG and PNG download buttons
|
5
5
|
|
6
6
|
// Each instance of Grapher is added to this object once the component has been
|
7
7
|
// mounted. This is so that grapher can be iterated over and redrawn on window
|
@@ -13,123 +13,133 @@ var Graphers = {};
|
|
13
13
|
// graphs collapsible, to redraw graphs when window is resized, and SVG and PNG
|
14
14
|
// export buttons and functionality.
|
15
15
|
export default function Grapher(Graph) {
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
16
|
+
return class extends React.Component {
|
17
|
+
constructor(props) {
|
18
|
+
super(props);
|
19
|
+
this.state = { collapsed: Graph.canCollapse() && this.props.collapsed };
|
20
|
+
this.svgContainerRef = createRef();
|
21
|
+
}
|
22
|
+
|
23
|
+
collapseId() {
|
24
|
+
return Graph.collapseId(this.props);
|
25
|
+
}
|
26
|
+
|
27
|
+
render() {
|
28
|
+
var cssClasses = Graph.className() + ' grapher';
|
29
|
+
return (
|
30
|
+
<div ref="grapher" className={cssClasses}>
|
31
|
+
{this.header()}
|
32
|
+
{this.svgContainerJSX()}
|
33
|
+
</div>
|
34
|
+
);
|
35
|
+
}
|
36
|
+
|
37
|
+
header() {
|
38
|
+
if(Graph.canCollapse()) {
|
39
|
+
return <div className="grapher-header">
|
40
|
+
<h4
|
41
|
+
className="caption"
|
42
|
+
data-toggle="collapse"
|
43
|
+
data-target={'#' + this.collapseId()}
|
44
|
+
>
|
45
|
+
{this.state.collapsed ? this.plusIcon() : this.minusIcon()}
|
38
46
|
{Graph.name()}
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
47
|
+
</h4>
|
48
|
+
{!this.state.collapsed && this.graphLinksJSX()}
|
49
|
+
</div>;
|
50
|
+
} else {
|
51
|
+
return <div className="grapher-header">
|
52
|
+
{!this.state.collapsed && this.graphLinksJSX()}
|
53
|
+
</div>;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
minusIcon() {
|
58
|
+
return <i className="fa fa-minus-square-o"></i>;
|
59
|
+
}
|
60
|
+
|
61
|
+
plusIcon() {
|
62
|
+
return <i className="fa fa-plus-square-o"></i>;
|
63
|
+
}
|
64
|
+
|
65
|
+
graphLinksJSX() {
|
66
|
+
return (
|
67
|
+
<div className="hit-links graph-links">
|
68
|
+
<a href="#" className="btn-link export-to-svg">
|
69
|
+
<i className="fa fa-download" /> SVG
|
70
|
+
</a>
|
71
|
+
<span className="line">|</span>
|
72
|
+
<a href="#" className="btn-link export-to-png">
|
73
|
+
<i className="fa fa-download" /> PNG
|
74
|
+
</a>
|
75
|
+
</div>
|
76
|
+
);
|
77
|
+
}
|
78
|
+
|
79
|
+
svgContainerJSX() {
|
80
|
+
var cssClasses = Graph.className() + ' svg-container collapse';
|
81
|
+
if (!this.state.collapsed) cssClasses += ' in';
|
82
|
+
return (
|
83
|
+
<div
|
84
|
+
ref={this.svgContainerRef}
|
85
|
+
id={this.collapseId()}
|
86
|
+
className={cssClasses}
|
87
|
+
></div>
|
88
|
+
);
|
89
|
+
}
|
90
|
+
|
91
|
+
componentDidMount() {
|
92
|
+
Graphers[this.collapseId()] = this;
|
93
|
+
|
94
|
+
// Draw visualisation for the first time. Visualisations are
|
95
|
+
// redrawn when browser window is resized.
|
96
|
+
this.draw();
|
97
|
+
}
|
98
|
+
|
99
|
+
componentDidUpdate() {
|
100
|
+
// Re-draw visualisation when the component change state.
|
101
|
+
this.draw();
|
102
|
+
}
|
103
|
+
svgContainer() {
|
104
|
+
return $(this.svgContainerRef.current);
|
105
|
+
}
|
106
|
+
|
107
|
+
draw() {
|
108
|
+
// Clean slate.
|
109
|
+
this.svgContainer().empty();
|
110
|
+
this.graph = null;
|
111
|
+
|
112
|
+
// Draw if uncollapsed.
|
113
|
+
if (this.state.collapsed) {
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
this.graph = new Graph(this.svgContainer(), this.props);
|
117
|
+
this.svgContainer()
|
118
|
+
.find('svg')
|
119
|
+
.attr('data-name', Graph.dataName(this.props));
|
120
|
+
}
|
121
|
+
};
|
112
122
|
}
|
113
123
|
|
114
124
|
// Redraw if window resized.
|
115
125
|
$(window).resize(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
126
|
+
_.debounce(function () {
|
127
|
+
_.each(Graphers, (grapher) => {
|
128
|
+
grapher.draw();
|
129
|
+
});
|
130
|
+
}, 125)
|
121
131
|
);
|
122
132
|
|
123
133
|
// Swap-icon and toggle .graph-links on collapse.
|
124
|
-
$(
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
134
|
+
$('body').on('hidden.bs.collapse', '.collapse', function () {
|
135
|
+
var component = Graphers[$(this).attr('id')];
|
136
|
+
if (component) {
|
137
|
+
component.setState({ collapsed: true });
|
138
|
+
}
|
129
139
|
});
|
130
|
-
$(
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
140
|
+
$('body').on('shown.bs.collapse', '.collapse', function () {
|
141
|
+
var component = Graphers[$(this).attr('id')];
|
142
|
+
if (component) {
|
143
|
+
component.setState({ collapsed: false });
|
144
|
+
}
|
135
145
|
});
|
data/public/js/hit.js
CHANGED
@@ -4,6 +4,7 @@ import _ from 'underscore';
|
|
4
4
|
import HSPOverview from './kablammo';
|
5
5
|
import downloadFASTA from './download_fasta';
|
6
6
|
import AlignmentExporter from './alignment_exporter'; // to download textual alignment
|
7
|
+
import HitButtons from 'hit_buttons';
|
7
8
|
|
8
9
|
/**
|
9
10
|
* Component for each hit. Receives props from Report. Has no state.
|
@@ -26,13 +27,14 @@ export default class extends Component {
|
|
26
27
|
this.hitLinks = this.hitLinks.bind(this);
|
27
28
|
this.viewSequenceButton = this.viewSequenceButton.bind(this);
|
28
29
|
this.downloadFASTAButton = this.downloadFASTAButton.bind(this);
|
30
|
+
this.hit_buttons = new HitButtons(this);
|
29
31
|
}
|
30
32
|
shouldComponentUpdate() {
|
31
33
|
return !this.props.hit;
|
32
34
|
}
|
33
35
|
/**
|
34
|
-
|
35
|
-
|
36
|
+
* Returns accession number of the hit sequence.
|
37
|
+
*/
|
36
38
|
accession() {
|
37
39
|
return this.props.hit.accession;
|
38
40
|
}
|
@@ -142,6 +144,10 @@ export default class extends Component {
|
|
142
144
|
}
|
143
145
|
btns.push(this.downloadAlignmentButton());
|
144
146
|
|
147
|
+
this.hit_buttons.buttons().forEach((button) => {
|
148
|
+
btns.push(button);
|
149
|
+
});
|
150
|
+
|
145
151
|
return (
|
146
152
|
<div className="hit-links">
|
147
153
|
<label>
|
data/public/js/hits_overview.js
CHANGED
@@ -5,9 +5,12 @@ import * as Helpers from './visualisation_helpers';
|
|
5
5
|
import Utils from './utils';
|
6
6
|
|
7
7
|
class Graph {
|
8
|
+
static canCollapse() {
|
9
|
+
return true;
|
10
|
+
}
|
8
11
|
|
9
12
|
static name() {
|
10
|
-
return 'Graphical overview of
|
13
|
+
return 'Graphical overview of aligning hit sequences to the query';
|
11
14
|
}
|
12
15
|
|
13
16
|
static className() {
|
data/public/js/jquery_world.js
CHANGED
data/public/js/kablammo.js
CHANGED
@@ -8,8 +8,12 @@ import * as Helpers from './visualisation_helpers';
|
|
8
8
|
*/
|
9
9
|
|
10
10
|
class Graph {
|
11
|
+
static canCollapse() {
|
12
|
+
return true;
|
13
|
+
}
|
14
|
+
|
11
15
|
static name() {
|
12
|
-
return 'Length distribution of matching sequences';
|
16
|
+
return 'Length distribution of matching hit sequences';
|
13
17
|
}
|
14
18
|
|
15
19
|
static className() {
|
data/public/js/query.js
CHANGED
@@ -4,6 +4,7 @@ import _ from 'underscore';
|
|
4
4
|
import HitsOverview from './hits_overview';
|
5
5
|
import LengthDistribution from './length_distribution'; // length distribution of hits
|
6
6
|
import Utils from './utils';
|
7
|
+
import { fastqToFasta } from './fastq_to_fasta';
|
7
8
|
|
8
9
|
/**
|
9
10
|
* Query component displays query defline, graphical overview, length
|
@@ -65,9 +66,10 @@ export class ReportQuery extends Component {
|
|
65
66
|
|
66
67
|
noHitsJSX() {
|
67
68
|
return <div className="section-content">
|
68
|
-
<strong> ****** No hits found ****** </strong>
|
69
|
+
<strong> ****** No BLAST hits found ****** </strong>
|
69
70
|
</div>;
|
70
71
|
}
|
72
|
+
|
71
73
|
render() {
|
72
74
|
return (
|
73
75
|
<div className="resultn" id={this.domID()}
|
@@ -102,6 +104,7 @@ export class SearchQueryWidget extends Component {
|
|
102
104
|
this.indicateNormal = this.indicateNormal.bind(this);
|
103
105
|
this.type = this.type.bind(this);
|
104
106
|
this.guessSequenceType = this.guessSequenceType.bind(this);
|
107
|
+
this.preProcessSequence = this.preProcessSequence.bind(this);
|
105
108
|
this.notify = this.notify.bind(this);
|
106
109
|
|
107
110
|
this.textareaRef = createRef()
|
@@ -119,6 +122,8 @@ export class SearchQueryWidget extends Component {
|
|
119
122
|
|
120
123
|
componentDidUpdate() {
|
121
124
|
this.hideShowButton();
|
125
|
+
this.preProcessSequence();
|
126
|
+
|
122
127
|
var type = this.type();
|
123
128
|
if (!type || type !== this._type) {
|
124
129
|
this._type = type;
|
@@ -131,7 +136,7 @@ export class SearchQueryWidget extends Component {
|
|
131
136
|
|
132
137
|
/**
|
133
138
|
* Returns query sequence if no argument is provided (or null or undefined
|
134
|
-
* is provided as argument). Otherwise, sets query
|
139
|
+
* is provided as argument). Otherwise, sets query sequence to the given
|
135
140
|
* value and returns `this`.
|
136
141
|
*
|
137
142
|
* Default/initial state of query sequence is an empty string. Caller must
|
@@ -239,7 +244,12 @@ export class SearchQueryWidget extends Component {
|
|
239
244
|
* of directly calling this method.
|
240
245
|
*/
|
241
246
|
type() {
|
242
|
-
|
247
|
+
let sequence = this.value().trim();
|
248
|
+
// FASTQ detected, but we don't know if conversion has succeeded yet
|
249
|
+
// will notify separately if it does
|
250
|
+
if (sequence.startsWith('@') ) { return undefined; }
|
251
|
+
|
252
|
+
var sequences = sequence.split(/>.*/);
|
243
253
|
|
244
254
|
var type, tmp;
|
245
255
|
|
@@ -262,6 +272,16 @@ export class SearchQueryWidget extends Component {
|
|
262
272
|
return type;
|
263
273
|
}
|
264
274
|
|
275
|
+
preProcessSequence() {
|
276
|
+
var sequence = this.value();
|
277
|
+
var updatedSequence = fastqToFasta(sequence);
|
278
|
+
|
279
|
+
if (sequence !== updatedSequence) {
|
280
|
+
this.value(updatedSequence);
|
281
|
+
this.notify('fastq');
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
265
285
|
/**
|
266
286
|
* Guesses and returns the type of the given sequence (nucleotide,
|
267
287
|
* protein).
|
@@ -289,9 +309,9 @@ export class SearchQueryWidget extends Component {
|
|
289
309
|
}
|
290
310
|
|
291
311
|
notify(type) {
|
292
|
-
clearTimeout(this.notification_timeout);
|
293
312
|
this.indicateNormal();
|
294
|
-
|
313
|
+
clearTimeout(this.notification_timeout);
|
314
|
+
// $('.notifications .active').hide().removeClass('active');
|
295
315
|
|
296
316
|
if (type) {
|
297
317
|
$('#' + type + '-sequence-notification').show('drop', { direction: 'up' }).addClass('active');
|
@@ -369,7 +389,7 @@ class HitsTable extends Component {
|
|
369
389
|
<div className="table-hit-overview">
|
370
390
|
<h4 className="caption" data-toggle="collapse" data-target={'#Query_' + this.props.query.number + 'HT_' + this.props.query.number}>
|
371
391
|
<i className="fa fa-minus-square-o"></i>
|
372
|
-
<span>
|
392
|
+
<span>Hit sequences producing significant alignments</span>
|
373
393
|
</h4>
|
374
394
|
<div className="collapsed in" id={'Query_' + this.props.query.number + 'HT_' + this.props.query.number}>
|
375
395
|
<table
|