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/grapher.js
CHANGED
|
@@ -1,131 +1,133 @@
|
|
|
1
1
|
import _ from 'underscore';
|
|
2
|
-
import React, { createRef } from 'react';
|
|
2
|
+
import React, { createRef, useState, useEffect, useCallback } from 'react';
|
|
3
3
|
|
|
4
4
|
import './svgExporter'; // create handlers for SVG and PNG download buttons
|
|
5
5
|
import CollapsePreferences from './collapse_preferences';
|
|
6
|
-
|
|
7
|
-
// Each instance of Grapher is added to this object once the component has been
|
|
8
|
-
// mounted. This is so that grapher can be iterated over and redrawn on window
|
|
9
|
-
// resize event.
|
|
10
|
-
var Graphers = {};
|
|
6
|
+
import useDetectPrint from "react-detect-print";
|
|
11
7
|
|
|
12
8
|
// Grapher is a function that takes a Graph class and returns a React component.
|
|
13
9
|
// This React component provides HTML boilerplate to add heading, to make the
|
|
14
10
|
// graphs collapsible, to redraw graphs when window is resized, and SVG and PNG
|
|
15
11
|
// export buttons and functionality.
|
|
16
12
|
export default function Grapher(Graph) {
|
|
17
|
-
return
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
13
|
+
return function Component(props) {
|
|
14
|
+
const alwaysShowName = Graph.alwaysShowName === undefined ? false : Graph.alwaysShowName();
|
|
15
|
+
const printing = useDetectPrint();
|
|
16
|
+
const name = Graph.name(props);
|
|
17
|
+
const [width, setWidth] = useState(window.innerWidth);
|
|
18
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
19
|
+
const svgContainerRef = createRef();
|
|
20
|
+
let graph = null;
|
|
26
21
|
|
|
27
|
-
graphId()
|
|
28
|
-
return Graph.graphId(this.props);
|
|
29
|
-
}
|
|
22
|
+
const graphId = () => Graph.graphId(props)
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
24
|
+
const graphLinksJSX = () => {
|
|
25
|
+
return (
|
|
26
|
+
<div className="hit-links float-right text-right text-blue-300 h-4 print:hidden">
|
|
27
|
+
<a href="#" className="btn-link text-sm text-seqblue hover:text-seqorange cursor-pointer export-to-svg">
|
|
28
|
+
<i className="fa fa-download" /> SVG
|
|
29
|
+
</a>
|
|
30
|
+
<span className="line px-1">|</span>
|
|
31
|
+
<a href="#" className="btn-link text-sm text-seqblue hover:text-seqorange cursor-pointer export-to-png">
|
|
32
|
+
<i className="fa fa-download" /> PNG
|
|
33
|
+
</a>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
44
36
|
}
|
|
45
37
|
|
|
46
|
-
header() {
|
|
38
|
+
const header = () => {
|
|
47
39
|
if(Graph.canCollapse()) {
|
|
48
|
-
return <div className="grapher-header">
|
|
40
|
+
return <div className="grapher-header pr-px">
|
|
49
41
|
<h4
|
|
50
|
-
className="caption"
|
|
51
|
-
onClick={() =>
|
|
42
|
+
className="inline-block pl-px m-0 caption cursor-pointer text-sm"
|
|
43
|
+
onClick={() => collapsePreferences.toggleCollapse()}
|
|
52
44
|
>
|
|
53
|
-
{
|
|
54
|
-
|
|
45
|
+
{collapsePreferences.renderCollapseIcon()}
|
|
46
|
+
<span className="print:hidden"> </span>{Graph.name(props)}
|
|
55
47
|
</h4>
|
|
56
|
-
{!
|
|
48
|
+
{!collapsed && graphLinksJSX()}
|
|
49
|
+
</div>;
|
|
50
|
+
} else if (alwaysShowName) {
|
|
51
|
+
return <div className="grapher-histogram-header" style={{ position: 'relative' }}>
|
|
52
|
+
<h4 className="caption"> {Graph.name(props)}</h4>
|
|
53
|
+
<div className="pull-right" style={{ position: 'absolute', top: 0, right: 0 }}>
|
|
54
|
+
{!collapsed && graphLinksJSX()}
|
|
55
|
+
</div>
|
|
57
56
|
</div>;
|
|
58
57
|
} else {
|
|
59
|
-
return <div className="
|
|
60
|
-
{!
|
|
58
|
+
return <div className="pr-px">
|
|
59
|
+
{!collapsed && graphLinksJSX()}
|
|
61
60
|
</div>;
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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';
|
|
64
|
+
const svgContainerJSX = () => {
|
|
65
|
+
var cssClasses = Graph.className() + ' svg-container hidden';
|
|
66
|
+
if (!collapsed) cssClasses += ' !block';
|
|
82
67
|
return (
|
|
83
68
|
<div
|
|
84
|
-
ref={
|
|
85
|
-
id={
|
|
69
|
+
ref={svgContainerRef}
|
|
70
|
+
id={graphId()}
|
|
86
71
|
className={cssClasses}
|
|
87
72
|
></div>
|
|
88
73
|
);
|
|
89
74
|
}
|
|
90
75
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// Draw visualisation for the first time. Visualisations are
|
|
95
|
-
// redrawn when browser window is resized.
|
|
96
|
-
this.draw();
|
|
76
|
+
const svgContainer = () => {
|
|
77
|
+
return $(svgContainerRef.current);
|
|
97
78
|
}
|
|
98
79
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
svgContainer() {
|
|
104
|
-
return $(this.svgContainerRef.current);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
draw() {
|
|
80
|
+
const draw = (printing = false) => {
|
|
81
|
+
let graphWidth = 'auto';
|
|
82
|
+
if (printing) graphWidth = '900';
|
|
108
83
|
// Clean slate.
|
|
109
|
-
|
|
110
|
-
|
|
84
|
+
svgContainer().empty();
|
|
85
|
+
graph = null;
|
|
111
86
|
|
|
112
87
|
// Draw if uncollapsed.
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
88
|
+
if (collapsed) return;
|
|
89
|
+
|
|
90
|
+
svgContainer().width(graphWidth);
|
|
91
|
+
graph = new Graph(svgContainer(), props);
|
|
92
|
+
svgContainer()
|
|
118
93
|
.find('svg')
|
|
119
|
-
.attr('data-name', Graph.dataName(
|
|
94
|
+
.attr('data-name', Graph.dataName(props));
|
|
120
95
|
}
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
96
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
// Attach a debounced listener to handle window resize events
|
|
99
|
+
// Updates the width state with the current window width, throttled to run at most once every 125ms
|
|
100
|
+
const handleResize = _.debounce(() => setWidth(window.innerWidth), 125);
|
|
101
|
+
window.addEventListener("resize", handleResize);
|
|
102
|
+
|
|
103
|
+
const isCollapsed = collapsePreferences.preferenceStoredAsCollapsed();
|
|
104
|
+
setCollapsed(Graph.canCollapse() && (props.collapsed || isCollapsed))
|
|
105
|
+
draw();
|
|
106
|
+
|
|
107
|
+
return () => window.removeEventListener("resize", handleResize)
|
|
108
|
+
}, [])
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
draw(printing);
|
|
112
|
+
}, [printing, width])
|
|
113
|
+
|
|
114
|
+
const setState = (state) => {
|
|
115
|
+
setCollapsed(state.collapsed)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const collapsePreferences = new CollapsePreferences({name: name, state: { collapsed: collapsed }, setState: setState});
|
|
119
|
+
|
|
120
|
+
if (Graph.name(props) === null) {
|
|
121
|
+
return(null);
|
|
122
|
+
} else {
|
|
123
|
+
const printCss = collapsed ? 'print:hidden' : '';
|
|
124
|
+
const cssClasses = Graph.className() + ' grapher' + printCss;
|
|
125
|
+
return (
|
|
126
|
+
<div className={cssClasses}>
|
|
127
|
+
{header()}
|
|
128
|
+
{svgContainerJSX()}
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
data/public/js/hit.js
CHANGED
|
@@ -99,33 +99,35 @@ export default class extends Component {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
headerJSX() {
|
|
102
|
-
var meta = `
|
|
102
|
+
var meta = `Length: ${this.hitLength().toLocaleString()}`;
|
|
103
103
|
|
|
104
104
|
if (this.props.showQueryCrumbs && this.props.showHitCrumbs) {
|
|
105
105
|
// Multiper queries, multiple hits
|
|
106
|
-
meta = `hit ${this.props.hit.number} of query ${this.props.query.number}
|
|
106
|
+
meta = `hit ${this.props.hit.number} of query ${this.props.query.number}. ${meta}`;
|
|
107
107
|
}
|
|
108
108
|
else if (this.props.showQueryCrumbs && !this.props.showHitCrumbs) {
|
|
109
109
|
// Multiple queries, single hit
|
|
110
|
-
meta = `the only hit of query ${this.props.query.number}
|
|
110
|
+
meta = `the only hit of query ${this.props.query.number}. ${meta}`;
|
|
111
111
|
}
|
|
112
112
|
else if (!this.props.showQueryCrumbs && this.props.showHitCrumbs) {
|
|
113
113
|
// Single query, multiple hits
|
|
114
|
-
meta = `hit ${this.props.hit.number}
|
|
114
|
+
meta = `hit ${this.props.hit.number}. ${meta}`;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
return <div className="section-header">
|
|
118
|
-
<h4>
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
return <div className="section-header border-b border-seqorange flex flex-col sm:flex-row sm:justify-between w-full">
|
|
118
|
+
<h4 className="text-sm cursor-pointer flex flex-col sm:flex-row items-start sm:items-start" data-parent-id={`#${this.domID()}`}>
|
|
119
|
+
<div className="flex items-start">
|
|
120
|
+
<i className="fa-regular fa-square-minus print:!hidden"></i>
|
|
121
|
+
<strong className="cursor-text ml-1 print:ml-0"> {this.props.hit.id}</strong>
|
|
122
|
+
</div>
|
|
123
|
+
<span className="ml-1">{this.props.hit.title}</span>
|
|
122
124
|
</h4>
|
|
123
|
-
<span className="label
|
|
125
|
+
<span className="label first-letter:capitalize text-sm font-normal text-inherit cursor-text">{meta}</span>
|
|
124
126
|
</div>;
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
contentJSX() {
|
|
128
|
-
return <div className="
|
|
130
|
+
return <div className="pt-0 px-0 pb-px" data-parent-hit={this.domID()}>
|
|
129
131
|
{this.hitLinks()}
|
|
130
132
|
<HSPOverview key={'kablammo' + this.props.query.id} query={this.props.query}
|
|
131
133
|
hit={this.props.hit} algorithm={this.props.algorithm}
|
|
@@ -149,8 +151,8 @@ export default class extends Component {
|
|
|
149
151
|
});
|
|
150
152
|
|
|
151
153
|
return (
|
|
152
|
-
<div className="hit-links">
|
|
153
|
-
<label>
|
|
154
|
+
<div className="hit-links h-4 print:hidden">
|
|
155
|
+
<label className="text-sm text-seqblue hover:seqorange cursor-pointer mb-0">
|
|
154
156
|
<input type="checkbox" id={this.domID() + '_checkbox'}
|
|
155
157
|
value={this.sequenceID()} onChange={function () {
|
|
156
158
|
this.props.selectHit(this.domID() + '_checkbox');
|
|
@@ -160,12 +162,12 @@ export default class extends Component {
|
|
|
160
162
|
</label>
|
|
161
163
|
{
|
|
162
164
|
btns.map((btn, index) => {
|
|
163
|
-
return [<span className="
|
|
165
|
+
return [<span className="text-seqorange mt-0 mr-1 ml-0 mb-1 px-1" key={`btn-${index}`}>|</span>, this.button(Object.assign(btn, { key: index }))];
|
|
164
166
|
})
|
|
165
167
|
}
|
|
166
168
|
{
|
|
167
169
|
this.props.hit.links.map((link, index) => {
|
|
168
|
-
return [<span className="
|
|
170
|
+
return [<span className="text-seqorange mt-0 mr-1 ml-0 mb-1 px-1" key={`link-${index}`}>|</span>, this.a(link, index)];
|
|
169
171
|
})
|
|
170
172
|
}
|
|
171
173
|
</div>
|
|
@@ -213,12 +215,12 @@ export default class extends Component {
|
|
|
213
215
|
|
|
214
216
|
button({ text, icon, title, className, onClick, key }) {
|
|
215
217
|
if (onClick) {
|
|
216
|
-
return <button key={key} className={`btn-link ${className}`}
|
|
218
|
+
return <button key={key} className={`btn-link text-sm text-seqblue hover:text-seqorange cursor-pointer ${className}`}
|
|
217
219
|
title={title} onClick={onClick}><i className={`fa ${icon}`}></i> {text}
|
|
218
220
|
</button>;
|
|
219
221
|
}
|
|
220
222
|
else {
|
|
221
|
-
return <button key={key} className="
|
|
223
|
+
return <button key={key} className="text-sm view-sequence disabled"
|
|
222
224
|
title={title} disabled={true}>
|
|
223
225
|
<i className={`fa ${icon}`}></i> {text}
|
|
224
226
|
</button>;
|
|
@@ -231,7 +233,7 @@ export default class extends Component {
|
|
|
231
233
|
a(link, key) {
|
|
232
234
|
if (!link.title || !link.url) return;
|
|
233
235
|
|
|
234
|
-
let className = 'btn
|
|
236
|
+
let className = 'btn-link text-sm text-seqblue hover:text-seqorange cursor-pointer';
|
|
235
237
|
if (link.class) className = `${className} ${link.class}`;
|
|
236
238
|
return <a href={link.url} key={`${link.url}-${key}`} className={className} target='_blank'>
|
|
237
239
|
{link.icon && <i className={'fa ' + link.icon}></i>}
|
|
@@ -240,7 +242,7 @@ export default class extends Component {
|
|
|
240
242
|
}
|
|
241
243
|
render() {
|
|
242
244
|
return (
|
|
243
|
-
<div className="hit" id={this.domID()} data-hit-def={this.props.hit.id}
|
|
245
|
+
<div className="hit mt-1 border-l-2 border-transparent pl-1 -ml-1" id={this.domID()} data-hit-def={this.props.hit.id}
|
|
244
246
|
data-hit-len={this.props.hit.length} data-hit-evalue={this.props.hit.evalue}>
|
|
245
247
|
{this.headerJSX()} {this.contentJSX()}
|
|
246
248
|
</div>
|
data/public/js/hits.js
CHANGED
|
@@ -15,30 +15,38 @@ class Hits extends Component {
|
|
|
15
15
|
this.nextQuery = 0;
|
|
16
16
|
this.nextHit = 0;
|
|
17
17
|
this.nextHSP = 0;
|
|
18
|
-
this.maxHSPs =
|
|
18
|
+
this.maxHSPs = 10; // max HSPs to render in a cycle
|
|
19
19
|
this.state = props.state;
|
|
20
|
+
this.state.pluginResults = [];
|
|
20
21
|
this.prepareAlignmentOfSelectedHits = this.prepareAlignmentOfSelectedHits.bind(this);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
componentDidMount() {
|
|
25
|
+
this.props.plugins.init(this.onPluginResultsFetched.bind(this));
|
|
24
26
|
this.componentDidUpdate(this.props, this.state);
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
onPluginResultsFetched(pluginResults) {
|
|
30
|
+
this.setState({ pluginResults: pluginResults });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
replacePluginResults(pluginResults) {
|
|
34
|
+
if (!pluginResults) return;
|
|
35
|
+
|
|
36
|
+
const updatedResults = this.props.plugins.replacePluginResults(this.state.results, pluginResults);
|
|
37
|
+
this.setState({ results: updatedResults, pluginResults: pluginResults });
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
/**
|
|
28
41
|
* Called for the first time after as BLAST results have been retrieved from
|
|
29
42
|
* the server and added to this.state by fetchResults. Only summary overview
|
|
30
43
|
* and circos would have been rendered at this point. At this stage we kick
|
|
31
44
|
* start iteratively adding 1 HSP to the page every 25 milli-seconds.
|
|
32
45
|
*/
|
|
33
|
-
componentDidUpdate(
|
|
46
|
+
componentDidUpdate(_prevProps, prevState) {
|
|
34
47
|
// Log to console how long the last update take?
|
|
35
48
|
// console.log((Date.now() - this.lastTimeStamp) / 1000);
|
|
36
49
|
|
|
37
|
-
// Lock sidebar in its position on the first update.
|
|
38
|
-
if (this.nextQuery == 0 && this.nextHit == 0 && this.nextHSP == 0) {
|
|
39
|
-
this.affixSidebar();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
50
|
// Queue next update if we have not rendered all results yet.
|
|
43
51
|
if (this.nextQuery < this.state.queries.length) {
|
|
44
52
|
// setTimeout is used to clear call stack and space out
|
|
@@ -49,7 +57,13 @@ class Hits extends Component {
|
|
|
49
57
|
this.props.componentFinishedUpdating();
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
this.
|
|
60
|
+
if (this.state.pluginResults.length > 0 && prevState.pluginResults.length == 0) {
|
|
61
|
+
this.replacePluginResults(this.state.pluginResults);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
isFirstUpdate() {
|
|
66
|
+
return this.nextQuery == 0 && this.nextHit == 0 && this.nextHSP == 0;
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
/* eslint complexity: ["error", 6] */
|
|
@@ -74,15 +88,15 @@ class Hits extends Component {
|
|
|
74
88
|
var query = this.state.queries[this.nextQuery];
|
|
75
89
|
|
|
76
90
|
// We may see a query multiple times during rendering because only
|
|
77
|
-
//
|
|
91
|
+
// 10 hsps are rendered in each cycle, but we want to create the
|
|
78
92
|
// corresponding Query component only the first time we see it.
|
|
79
93
|
if (this.nextHit == 0 && this.nextHSP == 0) {
|
|
80
94
|
results.items.push(this.renderReportQuery(query));
|
|
81
|
-
results.items.push(
|
|
95
|
+
results.items.push(this.props.plugins.queryResult(query, this.state.pluginResults));
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
this.processHits(results, query);
|
|
85
|
-
this.
|
|
99
|
+
this.iterateLoops(['nextQuery', 'nextHit'], query.hits.length);
|
|
86
100
|
if (results.numHSPsProcessed == this.maxHSPs) break;
|
|
87
101
|
}
|
|
88
102
|
}
|
|
@@ -91,12 +105,12 @@ class Hits extends Component {
|
|
|
91
105
|
while (this.nextHit < query.hits.length) {
|
|
92
106
|
var hit = query.hits[this.nextHit];
|
|
93
107
|
// We may see a hit multiple times during rendering because only
|
|
94
|
-
//
|
|
108
|
+
// 3 hsps are rendered in each cycle, but we want to create the
|
|
95
109
|
// corresponding Hit component only the first time we see it.
|
|
96
110
|
if (this.nextHSP == 0) results.items.push(this.renderHit(query, hit));
|
|
97
111
|
|
|
98
112
|
this.processHSPS(results, query, hit);
|
|
99
|
-
this.
|
|
113
|
+
this.iterateLoops(['nextHit', 'nextHSP'], hit.hsps.length);
|
|
100
114
|
if (results.numHSPsProcessed == this.maxHSPs) break;
|
|
101
115
|
}
|
|
102
116
|
}
|
|
@@ -116,7 +130,7 @@ class Hits extends Component {
|
|
|
116
130
|
/*
|
|
117
131
|
* this function check if 2nd argument is reach end of it
|
|
118
132
|
*/
|
|
119
|
-
|
|
133
|
+
iterateLoops(args, length) {
|
|
120
134
|
if (this[args[1]] != length) return;
|
|
121
135
|
|
|
122
136
|
this[args[0]]++;
|
|
@@ -179,25 +193,10 @@ class Hits extends Component {
|
|
|
179
193
|
);
|
|
180
194
|
}
|
|
181
195
|
|
|
182
|
-
/**
|
|
183
|
-
* Affixes the sidebar.
|
|
184
|
-
*/
|
|
185
|
-
affixSidebar() {
|
|
186
|
-
var $sidebar = $('.sidebar');
|
|
187
|
-
var sidebarOffset = $sidebar.offset();
|
|
188
|
-
if (sidebarOffset) {
|
|
189
|
-
$sidebar.affix({
|
|
190
|
-
offset: {
|
|
191
|
-
top: sidebarOffset.top,
|
|
192
|
-
},
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
196
|
/* eslint complexity: ["error", 6] */
|
|
198
197
|
/* -----------------------------------
|
|
199
198
|
* Event-handler when hit is selected
|
|
200
|
-
* Adds
|
|
199
|
+
* Adds border-seqorange to hit component.
|
|
201
200
|
* Updates number of Fasta that can be downloaded
|
|
202
201
|
*/
|
|
203
202
|
selectHit(id) {
|
|
@@ -211,13 +210,17 @@ class Hits extends Component {
|
|
|
211
210
|
// Highlight selected hit and enable 'Download FASTA/Alignment of
|
|
212
211
|
// selected' links.
|
|
213
212
|
if (checkbox.is(':checked')) {
|
|
214
|
-
$hit.addClass('
|
|
215
|
-
$hit.next('.hsp').addClass('
|
|
213
|
+
$hit.addClass('border-seqorange');
|
|
214
|
+
$hit.next('.hsp').addClass('border-seqorange');
|
|
215
|
+
$hit.removeClass('border-transparent');
|
|
216
|
+
$hit.next('.hsp').removeClass('border-transparent');
|
|
216
217
|
$('.download-fasta-of-selected').enable();
|
|
217
218
|
$('.download-alignment-of-selected').enable();
|
|
218
219
|
} else {
|
|
219
|
-
$hit.removeClass('
|
|
220
|
-
$hit.next('.hsp').removeClass('
|
|
220
|
+
$hit.removeClass('border-seqorange');
|
|
221
|
+
$hit.next('.hsp').removeClass('border-seqorange');
|
|
222
|
+
$hit.addClass('border-transparent');
|
|
223
|
+
$hit.next('.hsp').addClass('border-transparent');
|
|
221
224
|
$('.download-fasta-of-selected').attr('href', '#').removeAttr('download');
|
|
222
225
|
}
|
|
223
226
|
|
|
@@ -225,13 +228,17 @@ class Hits extends Component {
|
|
|
225
228
|
var $b = $('.download-alignment-of-selected');
|
|
226
229
|
|
|
227
230
|
if (num_checked >= 1) {
|
|
228
|
-
$a.find('.
|
|
229
|
-
$b.find('.
|
|
231
|
+
$a.find('.font-bold').html(num_checked);
|
|
232
|
+
$b.find('.font-bold').html(num_checked);
|
|
233
|
+
$a.find('.font-bold').addClass('px-1').removeClass('px-0.5');
|
|
234
|
+
$b.find('.font-bold').addClass('px-1').removeClass('px-0.5');
|
|
230
235
|
}
|
|
231
236
|
|
|
232
237
|
if (num_checked == 0) {
|
|
233
|
-
$a.addClass('disabled').find('.
|
|
234
|
-
$b.addClass('disabled').find('.
|
|
238
|
+
$a.addClass('disabled').find('.font-bold').html('');
|
|
239
|
+
$b.addClass('disabled').find('.font-bold').html('');
|
|
240
|
+
$a.find('.font-bold').removeClass('px-1').addClass('px-0.5');
|
|
241
|
+
$b.find('.font-bold').removeClass('px-1').addClass('px-0.5');
|
|
235
242
|
}
|
|
236
243
|
}
|
|
237
244
|
|
data/public/js/hits_overview.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import d3 from 'd3';
|
|
1
|
+
import * as d3 from 'd3';
|
|
2
2
|
import _ from 'underscore';
|
|
3
3
|
import Grapher from 'grapher';
|
|
4
4
|
import * as Helpers from './visualisation_helpers';
|
|
@@ -58,13 +58,6 @@ class Graph {
|
|
|
58
58
|
return hits;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
setupTooltip() {
|
|
62
|
-
this.svg_container.find('[data-toggle="tooltip"]').tooltip({
|
|
63
|
-
'placement': 'top', 'container': 'body', 'html': 'true',
|
|
64
|
-
'delay': 0, 'white-space': 'nowrap'
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
61
|
setupClick($graphDiv) {
|
|
69
62
|
$('a', $graphDiv).click(function (evt) {
|
|
70
63
|
evt.preventDefault();
|
|
@@ -87,7 +80,7 @@ class Graph {
|
|
|
87
80
|
$graphDiv
|
|
88
81
|
.append(
|
|
89
82
|
$('<button/>')
|
|
90
|
-
.addClass('btn btn-link more')
|
|
83
|
+
.addClass('btn btn-link text-sm text-seqblue hover:text-seqorange cursor-pointer more')
|
|
91
84
|
.attr('type', 'button')
|
|
92
85
|
.attr('data-parent-query', $queryDiv.attr('id'))
|
|
93
86
|
.html('View More ')
|
|
@@ -97,7 +90,7 @@ class Graph {
|
|
|
97
90
|
.addClass('fa fa-angle-double-down')
|
|
98
91
|
),
|
|
99
92
|
$('<button/>')
|
|
100
|
-
.addClass('btn btn-link less')
|
|
93
|
+
.addClass('btn btn-link text-sm text-seqblue hover:text-seqorange cursor-pointer less')
|
|
101
94
|
.attr('type', 'button')
|
|
102
95
|
.attr('data-parent-query', $queryDiv.attr('id'))
|
|
103
96
|
.html('View Less ')
|
|
@@ -143,7 +136,6 @@ class Graph {
|
|
|
143
136
|
countHits();
|
|
144
137
|
this.graphIt($queryDiv, $graphDiv, shownHits, MIN_HITS_TO_SHOW, opts, hits);
|
|
145
138
|
initButtons();
|
|
146
|
-
this.setupTooltip();
|
|
147
139
|
e.stopPropagation();
|
|
148
140
|
},this));
|
|
149
141
|
|
|
@@ -161,7 +153,6 @@ class Graph {
|
|
|
161
153
|
this.graphIt($queryDiv, $graphDiv, shownHits, MIN_HITS_TO_SHOW - shownHits, opts, hits);
|
|
162
154
|
initButtons();
|
|
163
155
|
}
|
|
164
|
-
this.setupTooltip();
|
|
165
156
|
e.stopPropagation();
|
|
166
157
|
},this));
|
|
167
158
|
}
|
|
@@ -262,9 +253,7 @@ class Graph {
|
|
|
262
253
|
.append('g')
|
|
263
254
|
.attr('transform', 'translate(' + options.margin / 2 + ', ' + (1.5 * options.margin) + ')');
|
|
264
255
|
|
|
265
|
-
var x = d3.
|
|
266
|
-
.linear()
|
|
267
|
-
.range([0, width - options.margin]);
|
|
256
|
+
var x = d3.scaleLinear().range([0, width - options.margin]);
|
|
268
257
|
|
|
269
258
|
x.domain([1, queryLen]);
|
|
270
259
|
|
|
@@ -274,10 +263,7 @@ class Graph {
|
|
|
274
263
|
var _tValues = x.ticks(11);
|
|
275
264
|
_tValues.pop();
|
|
276
265
|
|
|
277
|
-
var xAxis = d3.
|
|
278
|
-
.axis()
|
|
279
|
-
.scale(x)
|
|
280
|
-
.orient('top')
|
|
266
|
+
var xAxis = d3.axisBottom(x)
|
|
281
267
|
.tickValues(_tValues.concat([1, queryLen]))
|
|
282
268
|
.tickFormat(formatter);
|
|
283
269
|
|
|
@@ -294,16 +280,14 @@ class Graph {
|
|
|
294
280
|
.attr('y','2px')
|
|
295
281
|
.attr('transform','rotate(-90)');
|
|
296
282
|
|
|
297
|
-
var y = d3.
|
|
298
|
-
.
|
|
299
|
-
.rangeBands([0, height - 3 * options.margin - 2 * options.legend], 0.3);
|
|
283
|
+
var y = d3.scaleBand()
|
|
284
|
+
.range([0, height - 3 * options.margin - 2 * options.legend], 0.3);
|
|
300
285
|
|
|
301
286
|
y.domain(hits.map(function (d) {
|
|
302
287
|
return d.hitId;
|
|
303
288
|
}));
|
|
304
289
|
|
|
305
|
-
var gradScale = d3.
|
|
306
|
-
.log()
|
|
290
|
+
var gradScale = d3.scaleLog()
|
|
307
291
|
.domain([
|
|
308
292
|
d3.min([1e-5, d3.min(hits.map(function (d) {
|
|
309
293
|
if (parseFloat(d.hitEvalue) === 0.0) return undefined;
|
|
@@ -369,7 +353,6 @@ class Graph {
|
|
|
369
353
|
d3.select(this)
|
|
370
354
|
.attr('xlink:href', '#' + q_i + '_hit_' + (i+1))
|
|
371
355
|
.append('rect')
|
|
372
|
-
.attr('data-toggle', 'tooltip')
|
|
373
356
|
.attr('title', alt_tooltip)
|
|
374
357
|
.attr('class','bar')
|
|
375
358
|
.attr('x', function (d) {
|
|
@@ -393,8 +376,6 @@ class Graph {
|
|
|
393
376
|
if (index === 0) {
|
|
394
377
|
this.graphControls($queryDiv, $graphDiv, true, opts, inhits);
|
|
395
378
|
}
|
|
396
|
-
// Refresh tooltip each time graph is redrawn.
|
|
397
|
-
this.setupTooltip();
|
|
398
379
|
// Ensure clicking on 'rect' takes user to the relevant hit on all
|
|
399
380
|
// browsers.
|
|
400
381
|
this.setupClick($graphDiv);
|