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
@@ -15,6 +15,7 @@ export default class CloudShareModal extends React.Component {
15
15
  email: '',
16
16
  agreeToTos: false,
17
17
  shareableurl: '',
18
+ isModalVisible: false,
18
19
  };
19
20
  this.modalRef = createRef();
20
21
  }
@@ -77,11 +78,19 @@ export default class CloudShareModal extends React.Component {
77
78
  return match ? match[1] : match;
78
79
  }
79
80
 
81
+ show = () => {
82
+ this.modalRef.current?.showModal();
83
+ }
84
+
85
+ hide = () => {
86
+ this.modalRef.current?.close();
87
+ }
88
+
80
89
  renderLoading() {
81
90
  return (
82
- <div className="text-center">
91
+ <div className="text-center pt-3">
83
92
  <i className="fa fa-spinner fa-3x fa-spin"></i>
84
- <p>Uploading the job to SequenceServer Cloud, please wait...</p>
93
+ <p className="my-3">Uploading the job to SequenceServer Cloud, please wait...</p>
85
94
  </div>
86
95
  );
87
96
  }
@@ -100,8 +109,8 @@ export default class CloudShareModal extends React.Component {
100
109
  {
101
110
  errorMessages.map((errorMessage, index) => (
102
111
  <div key={`fastan-${index}`} className="fastan">
103
- <div className="section-content">
104
- <div className="modal-error">{errorMessage}</div>
112
+ <div className="pt-0 px-0 pb-px">
113
+ <div className="text-danger text-lg border border-danger p-2 my-2">{errorMessage}</div>
105
114
  </div>
106
115
  </div>
107
116
  ))
@@ -117,46 +126,52 @@ export default class CloudShareModal extends React.Component {
117
126
 
118
127
  return(
119
128
  <form onSubmit={this.handleSubmit}>
120
- <div className="form-group">
121
- <label htmlFor="emailInput">Your Email Address</label>
129
+ <div className="px-6 py-4 text-sm">
130
+ <label htmlFor="emailInput" className="text-seqblue hover:text-orange cursor-pointer mb-0">Your Email Address</label>
122
131
  <input
123
132
  type="email"
124
133
  id="emailInput"
125
- className="form-control"
134
+ className="block w-full rounded-md border-0 py-1.5 px-3 mb-3 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-seqblue sm:text-sm sm:leading-6"
126
135
  placeholder="person@example.com"
127
136
  name="email"
128
137
  value={email}
129
138
  required="required"
130
139
  onChange={this.handleChange}
131
140
  />
141
+ <p className="mb-3">
142
+ By submitting this form you agree to upload this SequenceServer result set to <a href="https://sequenceserver.com/cloud/" target="_bank" className="text-seqblue hover:text-seqorange">SenquenceServer Cloud</a>
143
+ , where it will become available on the internet to everyone with the link. You also agree that your email address will be stored on SequenceServer databases as proof of authentication for support and similar purposes.
144
+ </p>
145
+ <div className="form-check">
146
+ <input
147
+ type="checkbox"
148
+ id="tosCheckbox"
149
+ className="form-check-input"
150
+ name="agreeToTos"
151
+ checked={agreeToTos}
152
+ onChange={this.handleChange}
153
+ />
154
+ <label htmlFor="tosCheckbox" className="pl-2">
155
+ &nbsp;I agree to the <b><a href="https://sequenceserver.com/cloud/terms_and_conditions" target="_blank" className="text-seqblue hover:text-seqorange">Terms and Conditions of Service</a></b>
156
+ </label>
157
+ </div>
132
158
  </div>
133
- <p>
134
- By submitting this form you agree to upload this SequenceServer result set to <a href="https://sequenceserver.com/cloud/" target="_bank">SenquenceServer Cloud</a>
135
- , where it will become available on the internet to everyone with the link. You also agree that your email address will be stored on SequenceServer databases as proof of authentication for support and similar purposes.
136
- </p>
137
-
138
- <div className="form-group form-check">
139
- <input
140
- type="checkbox"
141
- id="tosCheckbox"
142
- className="form-check-input"
143
- name="agreeToTos"
144
- checked={agreeToTos}
145
- onChange={this.handleChange}
146
- />
147
- <label htmlFor="tosCheckbox" className="form-check-label">
148
- &nbsp;I agree to the <b><a href="https://sequenceserver.com/cloud/terms_and_conditions" target="_blank">Terms and Conditions of Service</a></b>
149
- </label>
159
+ <div className="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
160
+ <button
161
+ type="submit"
162
+ style={{ backgroundColor: isSubmitDisabled ? '#C74F13' : '#1B557A' }}
163
+ className='border-seqblue py-2 px-3 rounded-md text-white'
164
+ disabled={isSubmitDisabled}
165
+ >
166
+ Submit
167
+ </button>
150
168
  </div>
151
- <button type="submit" className="btn btn-primary" disabled={isSubmitDisabled}>
152
- Submit
153
- </button>
154
169
  </form>
155
170
  )
156
171
  }
157
172
 
158
173
  render() {
159
- const { formState } = this.state;
174
+ const { formState, isModalVisible } = this.state;
160
175
 
161
176
  let content;
162
177
  switch (formState) {
@@ -176,41 +191,23 @@ export default class CloudShareModal extends React.Component {
176
191
  }
177
192
 
178
193
  return (
179
- <div className="modal cloud-share" ref={this.modalRef} tabIndex="-1">
180
- <div className="modal-dialog">
181
- <div className="modal-content">
182
- <div className="modal-header">
183
- <h3>Share to SequenceServer Cloud</h3>
194
+ <div className="relative">
195
+ <dialog ref={this.modalRef} className="fixed p-4 w-full max-w-2xl bg-transparent focus:outline-none">
196
+ <div className="relative flex flex-col rounded-lg bg-white shadow">
197
+ <div className="flex items-start justify-between rounded-t border-b p-5">
198
+ <h3 className="text-xl font-medium text-gray-900">
199
+ Share to SequenceServer Cloud
200
+ </h3>
201
+ <button className="ml-auto inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:bg-gray-200" onClick={this.hide}>
202
+ <i className="fa-solid fa-xmark hover:text-black"></i>
203
+ </button>
184
204
  </div>
185
- <div className="modal-body">
205
+ <div className="dialog-content">
186
206
  {content}
187
207
  </div>
188
208
  </div>
189
- </div>
209
+ </dialog>
190
210
  </div>
191
211
  );
192
212
  }
193
-
194
- /*
195
- * Returns jQuery reference to the main modal container.
196
- */
197
- modal() {
198
- return $(this.modalRef.current);
199
- }
200
-
201
- /**
202
- * Shows share dialogue.
203
- */
204
- show() {
205
- this.setState({ requestCompleted: false }, () => {
206
- this.modal().modal("show");
207
- });
208
- }
209
-
210
- /**
211
- * Hide share dialogue.
212
- */
213
- hide() {
214
- this.modal().modal("hide");
215
- }
216
213
  }
@@ -1,26 +1,24 @@
1
1
  export default class CollapsePreferences {
2
- constructor(component) {
2
+ constructor(component, defaultCollapsed = false) {
3
3
  this.component = component;
4
- this.collapsePreferences = JSON.parse(localStorage.getItem('collapsePreferences')) || [];
4
+ this.defaultCollapsed = defaultCollapsed;
5
+ this.storageKey = 'sequenceServerCollapseStates';
6
+ this.collapseStates = JSON.parse(localStorage.getItem(this.storageKey)) || {};
5
7
  }
6
8
 
7
9
  toggleCollapse() {
8
10
  let currentlyCollapsed = this.component.state.collapsed;
9
-
10
11
  this.component.setState({ collapsed: !currentlyCollapsed });
11
12
 
12
- let collapsePreferences = JSON.parse(localStorage.getItem('collapsePreferences')) || [];
13
-
14
- if (currentlyCollapsed) {
15
- localStorage.setItem('collapsePreferences', JSON.stringify(collapsePreferences.filter((name) => name !== this.component.name)));
16
- } else {
17
- let uniqueCollapsePreferences = [... new Set(collapsePreferences.concat([this.component.name]))];
18
- localStorage.setItem('collapsePreferences', JSON.stringify(uniqueCollapsePreferences));
19
- }
13
+ this.collapseStates[this.component.name] = !currentlyCollapsed;
14
+ localStorage.setItem(this.storageKey, JSON.stringify(this.collapseStates));
20
15
  }
21
16
 
22
17
  preferenceStoredAsCollapsed() {
23
- return this.collapsePreferences.includes(this.component.name);
18
+ if (this.component.name in this.collapseStates) {
19
+ return this.collapseStates[this.component.name];
20
+ }
21
+ return this.defaultCollapsed;
24
22
  }
25
23
 
26
24
  renderCollapseIcon() {
@@ -28,10 +26,10 @@ export default class CollapsePreferences {
28
26
  }
29
27
 
30
28
  minusIcon() {
31
- return <i className="fa fa-minus-square-o"></i>;
29
+ return <i className="print:!hidden fa-regular fa-square-minus"></i>;
32
30
  }
33
31
 
34
32
  plusIcon() {
35
- return <i className="fa fa-plus-square-o"></i>;
33
+ return <i className="print:!hidden fa-regular fa-square-plus"></i>;
36
34
  }
37
- }
35
+ }
@@ -76,7 +76,7 @@ export class Databases extends Component {
76
76
 
77
77
  // Toggle button.
78
78
  var toggleState = '[Select all]';
79
- var toggleClass = 'px-2 text-sm';
79
+ var toggleClass = 'px-2 text-base md:text-lg';
80
80
  var toggleShown = this.databases(category).length > 1;
81
81
  var toggleDisabled = this.state.type && this.state.type !== category;
82
82
  if (toggleShown && toggleDisabled) {
@@ -94,7 +94,7 @@ export class Databases extends Component {
94
94
  <div className={columnClass} key={'DB_' + category}>
95
95
  <div>
96
96
  <div className="border-b border-seqorange mb-2">
97
- <h4 style={{ display: 'inline' }} className="font-medium">
97
+ <h4 className="font-medium inline text-base md:text-lg">
98
98
  {panelTitle}
99
99
  </h4>
100
100
  <button
@@ -113,7 +113,7 @@ export class Databases extends Component {
113
113
  this.databases(category),
114
114
  _.bind(function (database, index) {
115
115
  return (
116
- <li key={'DB_' + category + index}>
116
+ <li key={'DB_' + category + index} className="text-base md:text-lg leading-tight md:leading-tight">
117
117
  {this.renderDatabase(database)}
118
118
  </li>
119
119
  );
@@ -152,6 +152,7 @@ export class Databases extends Component {
152
152
  data-type={database.type}
153
153
  disabled={isDisabled}
154
154
  checked={isChecked}
155
+ className="checkbox-database"
155
156
  onChange={_.bind(function () {
156
157
  this.handleDatabaseSelectionClick(database);
157
158
  }, this)}
@@ -16,29 +16,37 @@ export default class extends Databases {
16
16
  var tree_id = '#' + category + '_database_tree';
17
17
  // hack that is needed to sync the selected tree db with the hidden main db
18
18
  window.jstree_node_change_timeout = null;
19
+ window.jstree_rapid_timeout = null
20
+ const _this = this;
19
21
 
20
- // when a tree database gets selected
21
- $(tree_id).on('select_node.jstree deselect_node.jstree', function (e, data) {
22
- if (window.jstree_node_change_timeout) {
23
- clearTimeout(window.jstree_node_change_timeout);
24
- window.jstree_node_change_timeout = null;
25
- }
22
+ $(tree_id).on('select_node.jstree deselect_node.jstree', function (_, _data) {
23
+ if (window.jstree_node_change_timeout) clearTimeout(window.jstree_node_change_timeout);
24
+ if (window.jstree_rapid_timeout) clearTimeout(window.jstree_rapid_timeout);
25
+
26
+ // Set a timeout to handle jsTree node changes with some delay to avoid rapid re-triggering
27
+ window.jstree_rapid_timeout = setTimeout(function () {
28
+ // Set another timeout to handle the retrieval and processing of selected nodes
29
+ window.jstree_node_change_timeout = setTimeout(function () {
30
+ // Filter out the current category to get a list of other categories
31
+ const otherCategories = _this.categories().filter(item => item !== category);
32
+
33
+ // Uncheck all nodes in the trees of the other categories
34
+ otherCategories.forEach((value) => $(`#${value}_database_tree`).jstree('uncheck_all'));
35
+ // Get all selected nodes from the current tree.
36
+ // Note: This will also include folders. To filter only database nodes, ensure the ID length is 32.
37
+ // These IDs correspond to specific elements in the hidden main form.
38
+ const selected = $(tree_id).jstree('get_selected').filter(selected => selected.length === 32);
39
+
40
+ // Find database entries in the current category whose IDs match the selected nodes
41
+ const foundDatabases = _this.databases(category).filter(db => selected.includes(db.id));
26
42
 
27
- window.jstree_node_change_timeout = setTimeout(function () {
28
- // uncheck all input
29
- $('div#database_list input[type="checkbox"]:checked').click();
30
- setTimeout(function () {
31
- // get all selected tree dbs. Also includes folders. Therefore, the id must have a length of 32
32
- // this id is used to find the corresponding element from the hidden main form
33
- var selected = $(tree_id).jstree('get_selected').filter(selected => selected.length == 32);
34
- $.each(selected, function (index, value) {
35
- // select hidden element to trigger original sequenceserver behavior, like blast algorithm, ...
36
- $('input[value="' + value + '"]').click();
37
- });
38
- }, 100);
39
- }, 100);
43
+ // Update the selected databases in the view model with the found databases
44
+ _this.selectDatabases(foundDatabases);
45
+ }, 100); // Delay to ensure jsTree updates are processed
46
+ }, 100); // Delay to prevent rapid changes causing multiple triggers
40
47
  });
41
48
 
49
+
42
50
  $(tree_id).jstree({
43
51
  'core': {
44
52
  'data': this.props.tree[category]
@@ -50,6 +58,10 @@ export default class extends Databases {
50
58
  });
51
59
  }
52
60
 
61
+ selectDatabases(databases) {
62
+ this.setState({ currentlySelectedDatabases: databases });
63
+ }
64
+
53
65
  handleTreeSearch(category, tree_id, search_id) {
54
66
  var search_for = $('#' + search_id).val();
55
67
  $('#' + tree_id).jstree(true).search(search_for);
@@ -59,15 +71,18 @@ export default class extends Databases {
59
71
  // Panel name and column width.
60
72
  var panelTitle = category[0].toUpperCase() +
61
73
  category.substring(1).toLowerCase() + ' databases';
62
- var columnClass = this.categories().length === 1 ? 'col-md-12' :
63
- 'col-md-6';
74
+ var columnClass = this.categories().length === 1 ? 'col-span-2' : '';
64
75
 
65
76
  // Toggle button.
66
77
  var toggleState = '[Select all]';
67
- var toggleClass = 'btn-link';
78
+ var toggleClass = 'px-2 text-base md:text-lg hidden';
68
79
  var toggleShown = this.databases(category).length > 1;
69
80
  var toggleDisabled = this.state.type && this.state.type !== category;
70
- if (toggleShown && toggleDisabled) toggleClass += ' disabled';
81
+ if (toggleShown && toggleDisabled) {
82
+ toggleClass += ' text-gray-400';
83
+ } else {
84
+ toggleClass += ' text-seqblue';
85
+ }
71
86
  if (!toggleShown) toggleClass += ' hidden';
72
87
  if (this.nselected() === this.databases(category).length) {
73
88
  toggleState = '[Deselect all]';
@@ -76,22 +91,28 @@ export default class extends Databases {
76
91
  // JSX.
77
92
  return (
78
93
  <div className={columnClass} key={'DB_' + category}>
79
- <div className='panel panel-default' id='database_list'>
80
- <div className='panel-heading'>
81
- <h4 style={{ display: 'inline' }}>{panelTitle}</h4> &nbsp;&nbsp;
94
+ <div>
95
+ <div className="border-b border-seqorange mb-2" id="database_list">
96
+ <h4 className="font-medium text-base md:text-lg">{panelTitle}</h4>
82
97
  {
83
98
  this.renderDatabaseSearch(category)
84
99
  }
85
- <button type='button' className={toggleClass} disabled={toggleDisabled} style={{ display: 'none' }}
86
- onClick={function () { this.handleToggle(toggleState, category); }.bind(this)}>
100
+ <button
101
+ type="button"
102
+ className={toggleClass}
103
+ disabled={toggleDisabled}
104
+ onClick={function () {
105
+ this.handleToggle(toggleState, category);
106
+ }.bind(this)}
107
+ >
87
108
  {toggleState}
88
109
  </button>
89
110
  </div>
90
- <ul className={'list-group databases ' + category} style={{ display: 'none' }}>
111
+ <ul className={'databases hidden text-lg ' + category}>
91
112
  {
92
113
  _.map(this.databases(category), _.bind(function (database, index) {
93
114
  return (
94
- <li className='list-group-item' key={'DB_' + category + index}>
115
+ <li key={'DB_' + category + index}>
95
116
  {this.renderDatabase(database)}
96
117
  </li>
97
118
  );
@@ -2,7 +2,7 @@ import React, { createRef } from 'react';
2
2
 
3
3
  /**
4
4
  * Takes errorData object with title, message, and more_info keys as props. The
5
- * component displays a bootstrap modal when mounted. errorData.title is used
5
+ * component displays a tailwind modal when mounted. errorData.title is used
6
6
  * to set modal title. errorData.message is inserted as HTML text in modal
7
7
  * body. And errorData.more_info is shown using a pre tag in modal body.
8
8
  *
@@ -13,46 +13,59 @@ export default class ErrorModal extends React.Component {
13
13
 
14
14
  constructor(props) {
15
15
  super(props);
16
- this.state = { errorData: {} };
17
- this.modal = createRef();
16
+ this.state = {
17
+ errorData: {},
18
+ isModalVisible: false
19
+ };
20
+ this.modalRef = createRef();
18
21
  }
19
22
 
20
- // HTML for Bootstrap modal.
21
23
  render() {
24
+ const { isModalVisible, errorData } = this.state;
25
+
22
26
  return (
23
- <div id="error" ref={this.modal} className="modal fade"
24
- data-keyboard="false" data-backdrop="static">
25
- <div className="modal-dialog modal-lg">
26
- <div className="modal-content">
27
- <div className="modal-header">
28
- <h3>{this.state.errorData.title}</h3>
27
+ <div className="relative">
28
+ <dialog ref={this.modalRef} className="fixed p-4 w-full max-w-2xl bg-transparent focus:outline-none">
29
+ <div className="relative flex flex-col rounded-lg bg-white shadow">
30
+ <div className="flex items-start justify-between rounded-t border-b p-5">
31
+ <h3 className="text-xl font-medium text-gray-900">
32
+ {errorData.title}
33
+ </h3>
34
+ <button className="ml-auto inline-flex items-center rounded-lg bg-transparent p-1.5 text-gray-400 hover:bg-gray-200" onClick={this.hide}>
35
+ <i className="fa-solid fa-xmark hover:text-black"></i>
36
+ </button>
29
37
  </div>
30
-
31
- <div className="modal-body">
32
- <p dangerouslySetInnerHTML={{ __html: this.state.errorData.message}}></p>
33
-
38
+ <div className="pt-2 px-6 pb-6 mt-2 text-sm">
39
+ <p dangerouslySetInnerHTML={{ __html: errorData.message }} className="mb-4"></p>
34
40
  {
35
- this.state.errorData.more_info &&
36
- <pre className="pre-scrollable">
37
- {this.state.errorData.more_info}
38
- </pre>
41
+ errorData.more_info &&
42
+ <pre className="p-2 bg-slate-200 overflow-auto max-h-56">{errorData.more_info}</pre>
39
43
  }
40
44
  </div>
41
45
  </div>
42
- </div>
46
+ </dialog>
43
47
  </div>
44
48
  );
45
49
  }
50
+
46
51
  /**
47
52
  * Shows error viewer.
48
53
  */
49
- show (errorData, beforeShow) {
50
- this.setState({errorData: errorData});
54
+ show = (errorData, beforeShow) => {
55
+ this.setState({ errorData: errorData });
56
+
51
57
  // Caller can specify an amount of time to wait for before showing the
52
58
  // modal. This is helpful if the caller wants to finish some work
53
59
  // before showing error modal.
54
60
  setTimeout(() => {
55
- $(this.modal.current).modal('show');
61
+ this.modalRef.current?.showModal();
56
62
  }, beforeShow || 0);
57
63
  }
64
+
65
+ /**
66
+ * Hide dialogue.
67
+ */
68
+ hide = () => {
69
+ this.modalRef.current?.close();
70
+ }
58
71
  }
@@ -1,3 +1,5 @@
1
+ var d3 = require('d3');
2
+
1
3
  export function download_url(url, filename) {
2
4
  var a = d3.select('body').append('a')
3
5
  .attr('download', filename)
data/public/js/form.js CHANGED
@@ -60,7 +60,12 @@ export class Form extends Component {
60
60
  databases: data['database'],
61
61
  preSelectedDbs: data['preSelectedDbs'],
62
62
  preDefinedOpts: data['options'],
63
- blastTaskMap: data['blastTaskMap']
63
+ blastTaskMap: data['blastTaskMap'],
64
+ currentlySelectedDbs: data['preSelectedDbs'] || [],
65
+ }, () => {
66
+ this.handleDatabaseSelectionChanged(this.state.currentlySelectedDbs);
67
+ this.handleFirstSelectionDB();
68
+ this.setBlastMethods();
64
69
  });
65
70
 
66
71
  /* Pre-populate the form with server sent query sequences
@@ -70,6 +75,11 @@ export class Form extends Component {
70
75
  this.query.current.value(data['query']);
71
76
  }
72
77
 
78
+ // DB selection
79
+ setTimeout(() => {
80
+ this.preselectDatabases(this.state.preSelectedDbs);
81
+ }, 0);
82
+
73
83
  setTimeout(function () {
74
84
  $('.jstree_div').click();
75
85
  }, 1000);
@@ -83,11 +93,32 @@ export class Form extends Component {
83
93
  $button.trigger('click');
84
94
  }
85
95
  });
96
+ }
86
97
 
87
- // show overlay to create visual feedback on button click
88
- $('#method').on('click', () => {
89
- $('#overlay').css('display', 'block');
90
- });
98
+ handleFirstSelectionDB() {
99
+ const firstSelectedDb = this.state.preSelectedDbs != null ? this.state.preSelectedDbs[0].type : null;
100
+ if (firstSelectedDb) {
101
+ this.handleDatabaseTypeChanged(firstSelectedDb);
102
+ }
103
+ }
104
+
105
+ setBlastMethods() {
106
+ const methods = this.determineBlastMethods();
107
+ if (methods.length > 0) {
108
+ this.setState({ blastMethod: methods[0] });
109
+ }
110
+ }
111
+
112
+ preselectDatabases(preSelectedDbs) {
113
+ if (preSelectedDbs) {
114
+ preSelectedDbs.forEach(db => {
115
+ const matchCheckbox = this.formRef.current.querySelector(`input.checkbox-database[value="${db.id}"]`);
116
+ if (matchCheckbox) {
117
+ matchCheckbox.click();
118
+ matchCheckbox.checked = true;
119
+ }
120
+ });
121
+ }
91
122
  }
92
123
 
93
124
  useTreeWidget() {
@@ -97,14 +128,18 @@ export class Form extends Component {
97
128
  handleFormSubmission(evt) {
98
129
  evt.preventDefault();
99
130
  const form = this.formRef.current;
131
+
132
+ document.getElementById('overlay').style.display = 'block';
133
+
100
134
  const formData = new FormData(form);
101
135
  formData.append('method', this.button.current.state.methods[0]);
136
+
102
137
  fetch(window.location.href, {
103
138
  method: 'POST',
104
139
  body: formData
105
140
  }).then(res => {
106
141
  //remove overlay when form is submitted
107
- $('#overlay').css('display', 'none');
142
+ document.getElementById('overlay').style.display = 'none';
108
143
  // redirect
109
144
  if (res.redirected && res.url) {
110
145
  // setTimeout is needed here as a workaround because safari doesnt allow async calling of window.open
@@ -223,6 +258,7 @@ export class Form extends Component {
223
258
  }
224
259
 
225
260
  <Options blastMethod={this.state.blastMethod} predefinedOptions={this.state.preDefinedOpts[this.state.blastMethod] || {}} blastTasks={(this.state.blastTaskMap || {})[this.state.blastMethod]} />
261
+
226
262
  </div>
227
263
 
228
264
  <div className="py-6"></div> {/* add a spacer so that the sticky action bar does not hide any contents */}
@@ -233,7 +269,7 @@ export class Form extends Component {
233
269
  currentBlastMethod={this.state.blastMethod}
234
270
  />
235
271
 
236
- <label className="block my-4 md:my-2">
272
+ <label className="text-seqblue hover:seqorange cursor-pointer block my-4 md:my-2">
237
273
  <input type="checkbox" id="toggleNewTab" /> Open results in new tab
238
274
  </label>
239
275
  <SearchButton ref={this.button} onAlgoChanged={this.handleAlgoChanged} />