sequenceserver 3.1.3 → 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/database.rb +5 -1
- data/lib/sequenceserver/job.rb +26 -10
- data/lib/sequenceserver/makeblastdb.rb +2 -1
- data/lib/sequenceserver/routes.rb +37 -12
- data/lib/sequenceserver/version.rb +1 -1
- data/lib/sequenceserver/zip_file_generator.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/circos.js +103 -96
- data/public/{packages/circosJS@1.7.0.js → js/circosjs.js} +156 -778
- data/public/js/cloud_share_modal.js +55 -58
- 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 +43 -7
- data/public/js/grapher.js +94 -92
- data/public/js/hit.js +21 -19
- data/public/js/hits.js +45 -38
- data/public/js/hits_overview.js +8 -27
- data/public/js/hsp.js +140 -144
- data/public/js/jquery_world.js +24 -15
- data/public/js/kablammo.js +14 -10
- data/public/js/length_distribution.js +23 -37
- data/public/js/null_plugins/report_plugins.js +3 -5
- data/public/js/options.js +17 -19
- 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 +62 -102
- data/public/js/report_root.js +3 -3
- data/public/js/search.js +5 -2
- data/public/js/search_button.js +85 -78
- data/public/js/sequence.js +11 -22
- data/public/js/sequence_modal.js +38 -37
- data/public/js/share_url.js +5 -5
- data/public/js/sidebar.js +148 -86
- data/public/js/svgExporter.js +8 -3
- data/public/js/tests/form.spec.js +1 -1
- data/public/js/tests/visualisation_helpers.spec.js +28 -0
- data/public/js/visualisation_helpers.js +24 -27
- data/public/sequenceserver-report.min.js +1 -1
- data/public/sequenceserver-report.min.js.LICENSE.txt +1 -88
- data/public/sequenceserver-report.min.js.map +1 -1
- data/public/sequenceserver-search.min.js +1 -1
- data/public/sequenceserver-search.min.js.LICENSE.txt +0 -30
- data/public/sequenceserver-search.min.js.map +1 -1
- data/views/error.erb +1 -1
- data/views/layout.erb +123 -130
- data/views/report.erb +3 -2
- data/views/search.erb +10 -14
- metadata +13 -26
- data/public/css/fonts.css +0 -31
- data/public/css/grapher.css +0 -670
- data/public/css/sequenceserver.css +0 -509
- 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,12 +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 Hits from './hits';
|
|
7
|
-
import Circos from './circos';
|
|
8
5
|
import AlignmentExporter from './alignment_exporter';
|
|
9
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';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Renders entire report.
|
|
@@ -133,7 +133,6 @@ class Report extends Component {
|
|
|
133
133
|
*/
|
|
134
134
|
componentDidMount() {
|
|
135
135
|
this.fetchResults();
|
|
136
|
-
this.plugins.init();
|
|
137
136
|
// This sets up an event handler which enables users to select text from
|
|
138
137
|
// hit header without collapsing the hit.
|
|
139
138
|
this.preventCollapseOnSelection();
|
|
@@ -154,21 +153,20 @@ class Report extends Component {
|
|
|
154
153
|
*/
|
|
155
154
|
loadingJSX() {
|
|
156
155
|
return (
|
|
157
|
-
<div className="
|
|
158
|
-
<div className="col-
|
|
159
|
-
<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">
|
|
160
159
|
<i className="fa fa-cog fa-spin"></i> BLAST-ing
|
|
161
160
|
</h1>
|
|
162
|
-
<
|
|
163
|
-
<
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
<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">
|
|
172
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> }
|
|
173
171
|
</p>
|
|
174
172
|
</div>
|
|
@@ -182,8 +180,8 @@ class Report extends Component {
|
|
|
182
180
|
*/
|
|
183
181
|
resultsJSX() {
|
|
184
182
|
return (
|
|
185
|
-
<div className="
|
|
186
|
-
<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">
|
|
187
185
|
<Sidebar
|
|
188
186
|
data={this.state}
|
|
189
187
|
atLeastOneHit={this.atLeastOneHit()}
|
|
@@ -192,15 +190,24 @@ class Report extends Component {
|
|
|
192
190
|
cloudSharingEnabled={this.state.cloud_sharing_enabled}
|
|
193
191
|
/>
|
|
194
192
|
</div>
|
|
195
|
-
<div className="col-md-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
|
201
208
|
state={this.state}
|
|
202
|
-
componentFinishedUpdating={(_) => this.componentFinishedUpdating(_)}
|
|
203
209
|
populate_hsp_array={this.populate_hsp_array.bind(this)}
|
|
210
|
+
componentFinishedUpdating={(_) => this.componentFinishedUpdating(_)}
|
|
204
211
|
plugins={this.plugins}
|
|
205
212
|
{...this.props}
|
|
206
213
|
/>
|
|
@@ -213,18 +220,16 @@ class Report extends Component {
|
|
|
213
220
|
|
|
214
221
|
warningJSX() {
|
|
215
222
|
return(
|
|
216
|
-
<div className="
|
|
217
|
-
<div className="
|
|
218
|
-
<div className="col-
|
|
219
|
-
<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">
|
|
220
227
|
<i className="fa fa-exclamation-triangle"></i> Warning
|
|
221
228
|
</h1>
|
|
222
|
-
<p>
|
|
223
|
-
<br />
|
|
229
|
+
<p className="mb-2">
|
|
224
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.
|
|
225
231
|
</p>
|
|
226
|
-
<
|
|
227
|
-
<p>
|
|
232
|
+
<p className="mb-2">
|
|
228
233
|
{this.state.download_links.map((link, index) => {
|
|
229
234
|
return (
|
|
230
235
|
<a href={link.url} className="btn btn-secondary" key={'download_link_' + index} >
|
|
@@ -233,9 +238,8 @@ class Report extends Component {
|
|
|
233
238
|
);
|
|
234
239
|
})}
|
|
235
240
|
</p>
|
|
236
|
-
<br />
|
|
237
241
|
<p>
|
|
238
|
-
<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">
|
|
239
243
|
View results in browser anyway
|
|
240
244
|
</a>
|
|
241
245
|
</p>
|
|
@@ -244,57 +248,6 @@ class Report extends Component {
|
|
|
244
248
|
</div>
|
|
245
249
|
);
|
|
246
250
|
}
|
|
247
|
-
/**
|
|
248
|
-
* Renders report overview.
|
|
249
|
-
*/
|
|
250
|
-
overviewJSX() {
|
|
251
|
-
return (
|
|
252
|
-
<div className="overview">
|
|
253
|
-
<p>
|
|
254
|
-
<strong>SequenceServer {this.state.seqserv_version}</strong> using{' '}
|
|
255
|
-
<strong>{this.state.program_version}</strong>
|
|
256
|
-
{this.state.submitted_at &&
|
|
257
|
-
`, query submitted on ${this.state.submitted_at}`}
|
|
258
|
-
</p>
|
|
259
|
-
<p>
|
|
260
|
-
<strong> Databases: </strong>
|
|
261
|
-
{this.state.querydb
|
|
262
|
-
.map((db) => {
|
|
263
|
-
return db.title;
|
|
264
|
-
})
|
|
265
|
-
.join(', ')}{' '}
|
|
266
|
-
({this.state.stats.nsequences} sequences,
|
|
267
|
-
{this.state.stats.ncharacters} characters)
|
|
268
|
-
</p>
|
|
269
|
-
<p>
|
|
270
|
-
<strong>Parameters: </strong>{' '}
|
|
271
|
-
{_.map(this.state.params, function (val, key) {
|
|
272
|
-
return key + ' ' + val;
|
|
273
|
-
}).join(', ')}
|
|
274
|
-
</p>
|
|
275
|
-
<p>
|
|
276
|
-
Please cite:{' '}
|
|
277
|
-
<a href="https://doi.org/10.1093/molbev/msz185">
|
|
278
|
-
https://doi.org/10.1093/molbev/msz185
|
|
279
|
-
</a>
|
|
280
|
-
</p>
|
|
281
|
-
</div>
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Return JSX for circos if we have at least one hit.
|
|
287
|
-
*/
|
|
288
|
-
circosJSX() {
|
|
289
|
-
return this.atLeastTwoHits() ? (
|
|
290
|
-
<Circos
|
|
291
|
-
queries={this.state.queries}
|
|
292
|
-
program={this.state.program}
|
|
293
|
-
/>
|
|
294
|
-
) : (
|
|
295
|
-
<span></span>
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
251
|
|
|
299
252
|
// Controller //
|
|
300
253
|
|
|
@@ -323,18 +276,6 @@ class Report extends Component {
|
|
|
323
276
|
return this.state.queries.some((query) => query.hits.length > 0);
|
|
324
277
|
}
|
|
325
278
|
|
|
326
|
-
/**
|
|
327
|
-
* Does the report have at least two hits? This is used to determine
|
|
328
|
-
* whether Circos should be enabled or not.
|
|
329
|
-
*/
|
|
330
|
-
atLeastTwoHits() {
|
|
331
|
-
var hit_num = 0;
|
|
332
|
-
return this.state.queries.some((query) => {
|
|
333
|
-
hit_num += query.hits.length;
|
|
334
|
-
return hit_num > 1;
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
|
|
338
279
|
/**
|
|
339
280
|
* Returns true if index should be shown in the sidebar. Index is shown
|
|
340
281
|
* only for 2 and 8 queries.
|
|
@@ -355,7 +296,8 @@ class Report extends Component {
|
|
|
355
296
|
// user wants to toggle
|
|
356
297
|
var hitID = $this.parents('.hit').attr('id');
|
|
357
298
|
$(`div[data-parent-hit=${hitID}]`).toggle();
|
|
358
|
-
$this.find('i').toggleClass('fa-
|
|
299
|
+
$this.find('i').toggleClass('fa-square-minus fa-square-plus');
|
|
300
|
+
$($this.data('parent-id')).toggleClass('print:hidden');
|
|
359
301
|
} else {
|
|
360
302
|
// user wants to select
|
|
361
303
|
$this.attr('data-toggle', '');
|
|
@@ -366,6 +308,7 @@ class Report extends Component {
|
|
|
366
308
|
}
|
|
367
309
|
|
|
368
310
|
/* Handling the fa icon when Hit Table is collapsed */
|
|
311
|
+
/* TODO:JOKO check if this method still being used? */
|
|
369
312
|
toggleTable() {
|
|
370
313
|
$('body').on(
|
|
371
314
|
'mousedown',
|
|
@@ -373,7 +316,7 @@ class Report extends Component {
|
|
|
373
316
|
function (event) {
|
|
374
317
|
var $this = $(this);
|
|
375
318
|
$this.on('mouseup mousemove', function handler(event) {
|
|
376
|
-
$this.find('i').toggleClass('fa-
|
|
319
|
+
$this.find('i').toggleClass('fa-square-minus fa-square-plus');
|
|
377
320
|
$this.off('mouseup mousemove', handler);
|
|
378
321
|
});
|
|
379
322
|
}
|
|
@@ -386,7 +329,24 @@ class Report extends Component {
|
|
|
386
329
|
* For the query in viewport, highlights corresponding entry in the index.
|
|
387
330
|
*/
|
|
388
331
|
setupScrollSpy() {
|
|
389
|
-
$('
|
|
332
|
+
var sectionIds = $('a.side-nav');
|
|
333
|
+
|
|
334
|
+
$(document).scroll(function(){
|
|
335
|
+
sectionIds.each(function(){
|
|
336
|
+
|
|
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();
|
|
342
|
+
|
|
343
|
+
if(scrollPosition < containerBottom - 20 && scrollPosition >= containerOffset - 20){
|
|
344
|
+
$(this).addClass('active');
|
|
345
|
+
} else {
|
|
346
|
+
$(this).removeClass('active');
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
});
|
|
390
350
|
}
|
|
391
351
|
|
|
392
352
|
populate_hsp_array(hit, query_id){
|
data/public/js/report_root.js
CHANGED
|
@@ -43,9 +43,9 @@ class Page extends Component {
|
|
|
43
43
|
render() {
|
|
44
44
|
return (
|
|
45
45
|
<div>
|
|
46
|
-
{/* Provide
|
|
46
|
+
{/* Provide tailwind element inside the #view for
|
|
47
47
|
the Report component to render itself in. */}
|
|
48
|
-
<div className="
|
|
48
|
+
<div className="mx-auto">
|
|
49
49
|
<Report
|
|
50
50
|
showSequenceModal={(_) => this.showSequenceModal(_)}
|
|
51
51
|
getCharacterWidth={() => this.getCharacterWidth()}
|
|
@@ -54,7 +54,7 @@ class Page extends Component {
|
|
|
54
54
|
</div>
|
|
55
55
|
|
|
56
56
|
{/* Add a hidden span tag containing chars used in HSPs */}
|
|
57
|
-
<pre className="pre-
|
|
57
|
+
<pre className="m-0 p-0 rounded-none border-0 bg-inherit whitespace-pre-wrap break-keep mt-1 pre-text tracking-wider" ref={this.hspChars} hidden>
|
|
58
58
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ +-
|
|
59
59
|
</pre>
|
|
60
60
|
|
data/public/js/search.js
CHANGED
|
@@ -40,10 +40,13 @@ const root = createRoot(document.getElementById('view'));
|
|
|
40
40
|
root.render(<Page />);
|
|
41
41
|
|
|
42
42
|
document.addEventListener('DOMContentLoaded', () => {
|
|
43
|
-
const closeButton = document.querySelector('.
|
|
43
|
+
const closeButton = document.querySelector('button.advanced-modal-close');
|
|
44
|
+
const modal = document.querySelector('dialog.advanced-modal');
|
|
45
|
+
modal.addEventListener('close', () => { document.body.classList.remove("overflow-hidden") });
|
|
46
|
+
|
|
44
47
|
if (closeButton) {
|
|
45
48
|
closeButton.addEventListener('click', function() {
|
|
46
|
-
|
|
49
|
+
modal.close();
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
});
|
data/public/js/search_button.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { Component, createRef } from 'react';
|
|
2
2
|
import _ from 'underscore';
|
|
3
|
-
import './jquery_world';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* SearchButton widget.
|
|
@@ -13,23 +12,31 @@ export class SearchButton extends Component {
|
|
|
13
12
|
hasQuery: false,
|
|
14
13
|
hasDatabases: false,
|
|
15
14
|
dropdownVisible: false,
|
|
15
|
+
messageTooltip: "",
|
|
16
|
+
titleTooltip: "",
|
|
17
|
+
hideMessageTooltip: false
|
|
16
18
|
};
|
|
17
|
-
this.inputGroup = this.inputGroup.bind(this);
|
|
18
|
-
this.submitButton = this.submitButton.bind(this);
|
|
19
|
-
this.initTooltip = this.initTooltip.bind(this);
|
|
20
|
-
this.showTooltip = this.showTooltip.bind(this);
|
|
21
|
-
this.hideTooltip = this.hideTooltip.bind(this);
|
|
22
19
|
this.changeAlgorithm = this.changeAlgorithm.bind(this);
|
|
23
20
|
this.decorate = this.decorate.bind(this);
|
|
21
|
+
this.setMessageTooltipButton = this.setMessageTooltipButton.bind(this);
|
|
22
|
+
this.setTitleTooltipButton = this.setTitleTooltipButton.bind(this);
|
|
23
|
+
this.renderButton = this.renderButton.bind(this);
|
|
24
|
+
this.inputGroup = this.inputGroup.bind(this);
|
|
25
|
+
this.handleMouseOver = this.handleMouseOver.bind(this)
|
|
26
|
+
this.handeMouseOut = this.handeMouseOut.bind(this)
|
|
24
27
|
this.inputGroupRef = createRef();
|
|
25
|
-
this.submitButtonRef = createRef();
|
|
26
28
|
}
|
|
29
|
+
|
|
27
30
|
componentDidMount() {
|
|
28
|
-
this.
|
|
31
|
+
this.timeout = setTimeout(() => this.setState({ hideMessageTooltip: true}), 3000)
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
shouldComponentUpdate(props, state) {
|
|
32
|
-
return !_.isEqual(state.methods, this.state.methods) ||
|
|
35
|
+
return !_.isEqual(state.methods, this.state.methods) ||
|
|
36
|
+
state.dropdownVisible !== this.state.dropdownVisible ||
|
|
37
|
+
state.messageTooltip !== this.state.messageTooltip ||
|
|
38
|
+
state.titleTooltip !== this.state.titleTooltip ||
|
|
39
|
+
state.hideMessageTooltip !== this.state.hideMessageTooltip;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
componentDidUpdate(_prevProps, prevState) {
|
|
@@ -42,66 +49,11 @@ export class SearchButton extends Component {
|
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
}
|
|
45
|
-
// Internal helpers. //
|
|
46
52
|
|
|
47
|
-
/**
|
|
48
|
-
* Returns jquery wrapped input group.
|
|
49
|
-
*/
|
|
50
53
|
inputGroup() {
|
|
51
54
|
return $(this.inputGroupRef.current);
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
/**
|
|
55
|
-
* Returns jquery wrapped submit button.
|
|
56
|
-
*/
|
|
57
|
-
submitButton() {
|
|
58
|
-
return $(this.submitButtonRef.current);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Initialise tooltip on input group and submit button.
|
|
63
|
-
*/
|
|
64
|
-
initTooltip() {
|
|
65
|
-
this.inputGroup().tooltip({
|
|
66
|
-
trigger: 'manual',
|
|
67
|
-
title: _.bind(function () {
|
|
68
|
-
if (!this.state.hasQuery && !this.state.hasDatabases) {
|
|
69
|
-
return 'You must enter a query sequence and select one or more databases above before you can run a search!';
|
|
70
|
-
} else if (this.state.hasQuery && !this.state.hasDatabases) {
|
|
71
|
-
return 'You must select one or more databases above before you can run a search!';
|
|
72
|
-
} else if (!this.state.hasQuery && this.state.hasDatabases) {
|
|
73
|
-
return 'You must enter a query sequence above before you can run a search!';
|
|
74
|
-
}
|
|
75
|
-
}, this),
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
this.submitButton().tooltip({
|
|
79
|
-
title: _.bind(function () {
|
|
80
|
-
var title = 'Click to BLAST or press Ctrl+Enter.';
|
|
81
|
-
if (this.state.methods.length > 1) {
|
|
82
|
-
title +=
|
|
83
|
-
' Click dropdown button on the right for other' +
|
|
84
|
-
' BLAST algorithms that can be used.';
|
|
85
|
-
}
|
|
86
|
-
return title;
|
|
87
|
-
}, this),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Show tooltip on input group.
|
|
93
|
-
*/
|
|
94
|
-
showTooltip() {
|
|
95
|
-
this.inputGroup()._tooltip('show');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Hide tooltip on input group.
|
|
100
|
-
*/
|
|
101
|
-
hideTooltip() {
|
|
102
|
-
this.inputGroup()._tooltip('hide');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
57
|
/**
|
|
106
58
|
* Change selected algorithm.
|
|
107
59
|
*
|
|
@@ -140,26 +92,49 @@ export class SearchButton extends Component {
|
|
|
140
92
|
dropdownVisible: !prevState.dropdownVisible
|
|
141
93
|
}));
|
|
142
94
|
}
|
|
95
|
+
|
|
96
|
+
setMessageTooltipButton() {
|
|
97
|
+
if (!this.state.hasQuery && !this.state.hasDatabases) {
|
|
98
|
+
this.setState({ messageTooltip: 'You must enter a query sequence and select one or more databases above before you can run a search!' });
|
|
99
|
+
} else if (this.state.hasQuery && !this.state.hasDatabases) {
|
|
100
|
+
this.setState({ messageTooltip: 'You must select one or more databases above before you can run a search!' });
|
|
101
|
+
} else if (!this.state.hasQuery && this.state.hasDatabases) {
|
|
102
|
+
this.setState({ messageTooltip: 'You must enter a query sequence above before you can run a search!' });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
143
105
|
|
|
144
|
-
|
|
145
|
-
var
|
|
146
|
-
|
|
147
|
-
|
|
106
|
+
setTitleTooltipButton() {
|
|
107
|
+
var title = 'Click to BLAST or press Ctrl+Enter.';
|
|
108
|
+
|
|
109
|
+
this.setState({ titleTooltip: title });
|
|
110
|
+
|
|
111
|
+
if (this.state.methods.length > 1) {
|
|
112
|
+
this.setState({
|
|
113
|
+
titleTooltip: title +=
|
|
114
|
+
' Click dropdown button on the right for other' +
|
|
115
|
+
' BLAST algorithms that can be used.'
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
handeMouseOut() {
|
|
121
|
+
clearTimeout(this.timeout);
|
|
122
|
+
this.setState({ hideMessageTooltip: false });
|
|
123
|
+
}
|
|
148
124
|
|
|
125
|
+
handleMouseOver() {
|
|
126
|
+
this.setMessageTooltipButton();
|
|
127
|
+
this.setTitleTooltipButton();
|
|
128
|
+
this.timeout = setTimeout(() => this.setState({ hideMessageTooltip: true}), 3000)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
renderButton(method, multi, methods) {
|
|
149
132
|
return (
|
|
150
|
-
<div
|
|
151
|
-
// className={multi ? 'flex' : 'flex'}
|
|
152
|
-
className="my-4 md:my-2 flex justify-end w-full md:w-auto relative"
|
|
153
|
-
id="methods"
|
|
154
|
-
ref={this.inputGroupRef}
|
|
155
|
-
onMouseOver={this.showTooltip}
|
|
156
|
-
onMouseOut={this.hideTooltip}
|
|
157
|
-
>
|
|
133
|
+
<div className="flex">
|
|
158
134
|
<button
|
|
159
135
|
type="submit"
|
|
160
|
-
className=
|
|
136
|
+
className={`${method ? 'hover:bg-seqorange focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-seqorange ' : 'opacity-50 cursor-not-allowed '}uppercase w-full md:w-auto flex text-xl justify-center py-2 px-16 border border-transparent rounded-md shadow-sm text-white bg-seqblue`}
|
|
161
137
|
id="method"
|
|
162
|
-
ref={this.submitButtonRef}
|
|
163
138
|
name="method"
|
|
164
139
|
value={method}
|
|
165
140
|
disabled={!method}
|
|
@@ -201,6 +176,38 @@ export class SearchButton extends Component {
|
|
|
201
176
|
</div>
|
|
202
177
|
)}
|
|
203
178
|
</div>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
render() {
|
|
183
|
+
var methods = this.state.methods;
|
|
184
|
+
var method = methods[0];
|
|
185
|
+
var multi = methods.length > 1;
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div
|
|
189
|
+
className="my-4 md:my-2 flex justify-end w-full md:w-auto relative"
|
|
190
|
+
id="methods"
|
|
191
|
+
ref={this.inputGroupRef}
|
|
192
|
+
onMouseOver={this.handleMouseOver}
|
|
193
|
+
onMouseOut={this.handeMouseOut}
|
|
194
|
+
>
|
|
195
|
+
<div className="relative flex flex-col items-center group">
|
|
196
|
+
<div className="flex items-center w-full">
|
|
197
|
+
{this.renderButton(method, multi, methods)}
|
|
198
|
+
{ !this.state.dropdownVisible &&
|
|
199
|
+
<div className="absolute hidden bottom-11 items-center flex-col-reverse group-hover:flex w-full">
|
|
200
|
+
<div className="w-0 h-0 border-y-8 border-r-8 border-t-transparent border-b-transparent border-r-black -mt-1 -mr-px tooltip-arrow-rotate"></div>
|
|
201
|
+
<span className="relative z-10 p-2 search-button-text leading-4 text-center text-white whitespace-no-wrap bg-black shadow-lg rounded-md">
|
|
202
|
+
{ !this.state.hasQuery || !this.state.hasDatabases ? (
|
|
203
|
+
this.state.hideMessageTooltip ? <>{this.state.titleTooltip}</> : this.state.messageTooltip
|
|
204
|
+
) : <> {this.state.titleTooltip}</>}
|
|
205
|
+
</span>
|
|
206
|
+
</div>
|
|
207
|
+
}
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
204
211
|
);
|
|
205
212
|
}
|
|
206
213
|
}
|
data/public/js/sequence.js
CHANGED
|
@@ -293,17 +293,9 @@ require = (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { va
|
|
|
293
293
|
//Initialize tooltip
|
|
294
294
|
var tooltip = 'sequenceTip' + this.opt.target;
|
|
295
295
|
jQuery('<div id="' + tooltip + '"></div>')
|
|
296
|
-
.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
'color': '#fff',
|
|
300
|
-
'font-size': '12px',
|
|
301
|
-
'width': 'auto',
|
|
302
|
-
'display': 'none'
|
|
303
|
-
})
|
|
304
|
-
.addClass('tooltip')
|
|
305
|
-
.appendTo('body')
|
|
306
|
-
.hide();
|
|
296
|
+
.appendTo('.fastan-content')
|
|
297
|
+
.addClass('absolute top-0 left-0')
|
|
298
|
+
.show()
|
|
307
299
|
this.opt._tooltip = document.getElementById(tooltip);
|
|
308
300
|
|
|
309
301
|
if ((this.opt.sequence)) {
|
|
@@ -1188,20 +1180,13 @@ require = (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { va
|
|
|
1188
1180
|
if (opt.numLeft) {
|
|
1189
1181
|
$('<pre/>')
|
|
1190
1182
|
.html(indL)
|
|
1191
|
-
.addClass('indL')
|
|
1192
|
-
.css({
|
|
1193
|
-
color: '#aaa',
|
|
1194
|
-
display: 'inline-block'
|
|
1195
|
-
})
|
|
1183
|
+
.addClass('indL hidden sm:inline-block')
|
|
1196
1184
|
.appendTo(this._contentDiv);
|
|
1197
1185
|
}
|
|
1198
1186
|
|
|
1199
1187
|
$('<pre/>')
|
|
1200
1188
|
.html(str)
|
|
1201
|
-
.addClass('seqF')
|
|
1202
|
-
.css({
|
|
1203
|
-
display: 'inline-block'
|
|
1204
|
-
})
|
|
1189
|
+
.addClass('seqF inline-block')
|
|
1205
1190
|
.appendTo(this._contentDiv);
|
|
1206
1191
|
|
|
1207
1192
|
if (opt.numRight) {
|
|
@@ -1328,14 +1313,18 @@ require = (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { va
|
|
|
1328
1313
|
jQuery(target).mouseover(function (e) {
|
|
1329
1314
|
|
|
1330
1315
|
var offset = jQuery(e.target).offset();
|
|
1316
|
+
var containerOffset = jQuery(e.target).closest('.seqF').offset();
|
|
1331
1317
|
|
|
1332
1318
|
if (!jQuery(tipId).is(':visible')) {
|
|
1333
1319
|
jQuery(tipId)
|
|
1334
1320
|
.css({
|
|
1335
1321
|
'background-color': '#000',
|
|
1336
1322
|
'padding': '3px 10px 3px 10px',
|
|
1337
|
-
'top': offset.top + jQuery(e.target).height() + 'px',
|
|
1338
|
-
'left': offset.left + jQuery(e.target).width() + 'px'
|
|
1323
|
+
'top': offset.top - containerOffset.top + jQuery(e.target).height() + 15 + 'px',
|
|
1324
|
+
'left': offset.left - containerOffset.left + jQuery(e.target).width() + 70 + 'px',
|
|
1325
|
+
'color': '#fff',
|
|
1326
|
+
'font-size': '12px',
|
|
1327
|
+
'position': 'absolute'
|
|
1339
1328
|
})
|
|
1340
1329
|
.animate({ opacity: '0.85' }, 10)
|
|
1341
1330
|
.html(cbGetMessageFunction.call(target))
|