sequenceserver 2.2.0 → 3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sequenceserver might be problematic. Click here for more details.
- 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
|