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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequenceserver/api_errors.rb +18 -0
  3. data/lib/sequenceserver/blast/hit.rb +1 -2
  4. data/lib/sequenceserver/blast/job.rb +15 -5
  5. data/lib/sequenceserver/blast/report.rb +5 -1
  6. data/lib/sequenceserver/database.rb +5 -1
  7. data/lib/sequenceserver/job.rb +26 -10
  8. data/lib/sequenceserver/makeblastdb.rb +2 -1
  9. data/lib/sequenceserver/routes.rb +37 -12
  10. data/lib/sequenceserver/version.rb +1 -1
  11. data/lib/sequenceserver/zip_file_generator.rb +1 -1
  12. data/public/404.html +8 -16
  13. data/public/css/app.css +794 -111
  14. data/public/css/app.min.css +1 -1
  15. data/public/css/sequenceserver.min.css +1 -8
  16. data/public/js/circos.js +103 -96
  17. data/public/{packages/circosJS@1.7.0.js → js/circosjs.js} +156 -778
  18. data/public/js/cloud_share_modal.js +55 -58
  19. data/public/js/collapse_preferences.js +13 -15
  20. data/public/js/databases.js +4 -3
  21. data/public/js/databases_tree.js +51 -30
  22. data/public/js/error_modal.js +35 -22
  23. data/public/js/exporter.js +2 -0
  24. data/public/js/form.js +43 -7
  25. data/public/js/grapher.js +94 -92
  26. data/public/js/hit.js +21 -19
  27. data/public/js/hits.js +45 -38
  28. data/public/js/hits_overview.js +8 -27
  29. data/public/js/hsp.js +140 -144
  30. data/public/js/jquery_world.js +24 -15
  31. data/public/js/kablammo.js +14 -10
  32. data/public/js/length_distribution.js +23 -37
  33. data/public/js/null_plugins/report_plugins.js +3 -5
  34. data/public/js/options.js +17 -19
  35. data/public/js/query.js +36 -35
  36. data/public/js/report/alignment_results.js +18 -0
  37. data/public/js/report/graphical_overview.js +38 -0
  38. data/public/js/report/report_header.js +28 -0
  39. data/public/js/report/run_summary.js +43 -0
  40. data/public/js/report.js +62 -102
  41. data/public/js/report_root.js +3 -3
  42. data/public/js/search.js +5 -2
  43. data/public/js/search_button.js +85 -78
  44. data/public/js/sequence.js +11 -22
  45. data/public/js/sequence_modal.js +38 -37
  46. data/public/js/share_url.js +5 -5
  47. data/public/js/sidebar.js +148 -86
  48. data/public/js/svgExporter.js +8 -3
  49. data/public/js/tests/form.spec.js +1 -1
  50. data/public/js/tests/visualisation_helpers.spec.js +28 -0
  51. data/public/js/visualisation_helpers.js +24 -27
  52. data/public/sequenceserver-report.min.js +1 -1
  53. data/public/sequenceserver-report.min.js.LICENSE.txt +1 -88
  54. data/public/sequenceserver-report.min.js.map +1 -1
  55. data/public/sequenceserver-search.min.js +1 -1
  56. data/public/sequenceserver-search.min.js.LICENSE.txt +0 -30
  57. data/public/sequenceserver-search.min.js.map +1 -1
  58. data/views/error.erb +1 -1
  59. data/views/layout.erb +123 -130
  60. data/views/report.erb +3 -2
  61. data/views/search.erb +10 -14
  62. metadata +13 -26
  63. data/public/css/fonts.css +0 -31
  64. data/public/css/grapher.css +0 -670
  65. data/public/css/sequenceserver.css +0 -509
  66. data/public/vendor/github/mbostock/d3@3.5.6/d3.js +0 -9506
  67. data/public/vendor/github/mbostock/d3@3.5.6.js +0 -1
  68. 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 class extends React.Component {
18
- constructor(props) {
19
- super(props);
20
- this.name = Graph.name(this.props);
21
- this.collapsePreferences = new CollapsePreferences(this);
22
- let isCollapsed = this.collapsePreferences.preferenceStoredAsCollapsed();
23
- this.state = { collapsed: Graph.canCollapse() && (this.props.collapsed || isCollapsed) };
24
- this.svgContainerRef = createRef();
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
- render() {
32
- // Do not render when Graph.name() is null
33
- if (Graph.name(this.props) === null) {
34
- return null;
35
- } else {
36
- var cssClasses = Graph.className() + ' grapher';
37
- return (
38
- <div className={cssClasses}>
39
- {this.header()}
40
- {this.svgContainerJSX()}
41
- </div>
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={() => this.collapsePreferences.toggleCollapse()}
42
+ className="inline-block pl-px m-0 caption cursor-pointer text-sm"
43
+ onClick={() => collapsePreferences.toggleCollapse()}
52
44
  >
53
- {this.collapsePreferences.renderCollapseIcon()}
54
- &nbsp;{Graph.name(this.props)}
45
+ {collapsePreferences.renderCollapseIcon()}
46
+ <span className="print:hidden">&nbsp;</span>{Graph.name(props)}
55
47
  </h4>
56
- {!this.state.collapsed && this.graphLinksJSX()}
48
+ {!collapsed && graphLinksJSX()}
49
+ </div>;
50
+ } else if (alwaysShowName) {
51
+ return <div className="grapher-histogram-header" style={{ position: 'relative' }}>
52
+ <h4 className="caption">&nbsp;{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="grapher-header">
60
- {!this.state.collapsed && this.graphLinksJSX()}
58
+ return <div className="pr-px">
59
+ {!collapsed && graphLinksJSX()}
61
60
  </div>;
62
61
  }
63
62
  }
64
63
 
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';
64
+ const svgContainerJSX = () => {
65
+ var cssClasses = Graph.className() + ' svg-container hidden';
66
+ if (!collapsed) cssClasses += ' !block';
82
67
  return (
83
68
  <div
84
- ref={this.svgContainerRef}
85
- id={this.graphId()}
69
+ ref={svgContainerRef}
70
+ id={graphId()}
86
71
  className={cssClasses}
87
72
  ></div>
88
73
  );
89
74
  }
90
75
 
91
- componentDidMount() {
92
- Graphers[this.graphId()] = this;
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
- 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() {
80
+ const draw = (printing = false) => {
81
+ let graphWidth = 'auto';
82
+ if (printing) graphWidth = '900';
108
83
  // Clean slate.
109
- this.svgContainer().empty();
110
- this.graph = null;
84
+ svgContainer().empty();
85
+ graph = null;
111
86
 
112
87
  // Draw if uncollapsed.
113
- if (this.state.collapsed) {
114
- return;
115
- }
116
- this.graph = new Graph(this.svgContainer(), this.props);
117
- this.svgContainer()
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(this.props));
94
+ .attr('data-name', Graph.dataName(props));
120
95
  }
121
- };
122
- }
123
96
 
124
- // Redraw if window resized.
125
- $(window).resize(
126
- _.debounce(function () {
127
- _.each(Graphers, (grapher) => {
128
- grapher.draw();
129
- });
130
- }, 125)
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 = `length: ${this.hitLength().toLocaleString()}`;
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}, ` + meta;
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}, ` + meta;
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}, ` + meta;
114
+ meta = `hit ${this.props.hit.number}. ${meta}`;
115
115
  }
116
116
 
117
- return <div className="section-header">
118
- <h4>
119
- <i className="fa fa-minus-square-o"></i>&nbsp;
120
- <strong>{this.props.hit.id}</strong>&nbsp;
121
- {this.props.hit.title}
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 label-reset pos-label">{meta}</span>
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="section-content" data-parent-hit={this.domID()}>
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="line" key={`btn-${index}`}>|</span>, this.button(Object.assign(btn, { key: index }))];
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="line" key={`link-${index}`}>|</span>, this.a(link, index)];
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="btn-link view-sequence disabled"
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 btn-link';
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 = 3; // max HSPs to render in a cycle
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(prevProps, prevState) {
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.props.plugins.componentDidUpdate(prevProps, prevState);
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
- // 3 hsps are rendered in each cycle, but we want to create the
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(...this.props.plugins.queryResults(query));
95
+ results.items.push(this.props.plugins.queryResult(query, this.state.pluginResults));
82
96
  }
83
97
 
84
98
  this.processHits(results, query);
85
- this.itterateLoops(['nextQuery', 'nextHit'], query.hits.length);
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
- // 10 hsps are rendered in each cycle, but we want to create the
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.itterateLoops(['nextHit', 'nextHSP'], hit.hsps.length);
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
- itterateLoops(args, length) {
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 glow to hit component.
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('glow');
215
- $hit.next('.hsp').addClass('glow');
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('glow');
220
- $hit.next('.hsp').removeClass('glow');
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('.text-bold').html(num_checked);
229
- $b.find('.text-bold').html(num_checked);
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('.text-bold').html('');
234
- $b.addClass('disabled').find('.text-bold').html('');
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
 
@@ -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&nbsp;')
@@ -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&nbsp;')
@@ -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.scale
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.svg
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.scale
298
- .ordinal()
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.scale
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);