From 6409318e53cbcaac8e23712e4bf09299dff0b37c Mon Sep 17 00:00:00 2001 From: Ro Date: Mon, 19 Jun 2023 18:26:17 +0100 Subject: [PATCH] Fix alignments download bug on safari --- public/js/alignment_exporter.js | 24 ++++++--- public/js/exporter.js | 10 ++-- public/js/hit.js | 2 + public/js/report.js | 79 ++++++++++++++++++++++++++++- public/js/sidebar.js | 52 +------------------ public/sequenceserver-report.min.js | 10 ++-- public/sequenceserver-search.min.js | 2 +- 7 files changed, 111 insertions(+), 68 deletions(-) diff --git a/public/js/alignment_exporter.js b/public/js/alignment_exporter.js index 7bdd2dbbe..0e7385d75 100644 --- a/public/js/alignment_exporter.js +++ b/public/js/alignment_exporter.js @@ -1,7 +1,10 @@ import * as Exporter from './exporter'; import _ from 'underscore'; export default class AlignmentExporter { - constructor() {} + constructor() { + this.prepare_alignments_for_export = this.prepare_alignments_for_export.bind(this); + this.export_alignments = this.export_alignments.bind(this); + } wrap_string(str, width) { var idx = 0; @@ -33,17 +36,22 @@ export default class AlignmentExporter { return fasta; } - export_alignments(hsps, filename_prefix) { + get_alignments_download_metadata(hsps, filename_prefix){ var fasta = this.generate_fasta(hsps); - var blob = new Blob([fasta], { type: 'text/fasta' }); - // var filename_prefix = query_def + '_' + subject_def; - // var filename_prefix = query_id + '_' + subject_id; var filename = Exporter.sanitize_filename(filename_prefix) + '.txt'; - Exporter.download_blob(blob, filename); + return {filename, blob}; } - export_alignments_of_all(hsps, name) { - + + prepare_alignments_for_export(hsps, filename_prefix) { + const { filename, blob } = this.get_alignments_download_metadata(hsps, filename_prefix); + const blob_url = Exporter.generate_blob_url(blob, filename); + return blob_url; + } + + export_alignments(hsps, filename_prefix) { + const { filename, blob } = this.get_alignments_download_metadata(hsps, filename_prefix); + Exporter.download_blob(blob, filename); } } diff --git a/public/js/exporter.js b/public/js/exporter.js index 2934c77a6..102422851 100644 --- a/public/js/exporter.js +++ b/public/js/exporter.js @@ -10,13 +10,17 @@ export function download_url(url, filename) { }, 100); } +export function generate_blob_url(blob) { + const url = window.URL.createObjectURL(blob); + return url; +} + export function download_blob(blob, filename) { if (typeof window.navigator.msSaveOrOpenBlob !== 'undefined') { window.navigator.msSaveOrOpenBlob(blob, filename); - return; + }else{ + download_url(generate_blob_url(blob), filename); } - - download_url(window.URL.createObjectURL(blob), filename); } export function sanitize_filename(str) { diff --git a/public/js/hit.js b/public/js/hit.js index 1fc46bfba..f27cc1187 100644 --- a/public/js/hit.js +++ b/public/js/hit.js @@ -95,6 +95,7 @@ export default class extends Component { var aln_exporter = new AlignmentExporter(); aln_exporter.export_alignments(hsps, this.props.query.id + '_' + this.props.hit.id); } + headerJSX() { var meta = `length: ${this.hitLength().toLocaleString()}`; @@ -147,6 +148,7 @@ export default class extends Component { Select diff --git a/public/js/report.js b/public/js/report.js index 3c450d0d2..e84c5bc81 100644 --- a/public/js/report.js +++ b/public/js/report.js @@ -7,6 +7,7 @@ import Circos from './circos'; import { ReportQuery } from './query'; import Hit from './hit'; import HSP from './hsp'; +import AlignmentExporter from './alignment_exporter'; @@ -37,8 +38,12 @@ class Report extends Component { querydb: [], params: [], stats: [], + alignment_blob_url: '', allQueriesLoaded: false }; + this.prepareAlignmentOfSelectedHits = this.prepareAlignmentOfSelectedHits.bind(this); + this.prepareAlignmentOfAllHits = this.prepareAlignmentOfAllHits.bind(this); + this.setStateFromJSON = this.setStateFromJSON.bind(this); } /** * Fetch results. @@ -79,7 +84,8 @@ class Report extends Component { */ setStateFromJSON(responseJSON) { this.lastTimeStamp = Date.now(); - this.setState(responseJSON); + // the callback prepares the download link for all alignments + this.setState(responseJSON, this.prepareAlignmentOfAllHits); } /** * Called as soon as the page has loaded and the user sees the loading spinner. @@ -165,6 +171,7 @@ class Report extends Component { showQueryCrumbs={this.state.queries.length > 1} showHitCrumbs={query.hits.length > 1} veryBig={this.state.veryBig} + onChange={this.prepareAlignmentOfSelectedHits} {...this.props} /> ); @@ -468,6 +475,76 @@ class Report extends Component { } } + prepareAlignmentOfSelectedHits() { + var sequence_ids = $('.hit-links :checkbox:checked').map(function () { + return this.value; + }).get(); + + if(!sequence_ids.length){ + // remove attributes from link if sequence_ids array is empty + $('.download-alignment-of-selected') + .attr('href', '#') + .removeAttr('download'); + return; + + } + if(this.state.alignment_blob_url){ + // always revoke existing url if any because this method will always create a new url + window.URL.revokeObjectURL(this.state.alignment_blob_url); + } + var hsps_arr = []; + var aln_exporter = new AlignmentExporter(); + _.each(this.state.queries, _.bind(function (query) { + _.each(query.hits, function (hit) { + if (_.indexOf(sequence_ids, hit.id) != -1) { + _.each(hit.hsps, function (hsp) { + hsp.hit_id = hit.id; + hsp.query_id = query.id; + hsps_arr.push(hsp); + }); + } + }); + }, this)); + const filename = 'alignment-' + sequence_ids.length + '_hits'; + const blob_url = aln_exporter.prepare_alignments_for_export(hsps_arr, filename); + // set required download attributes for link + $('.download-alignment-of-selected') + .attr('href', blob_url) + .attr('download', filename); + // track new url for future removal + this.setState({alignment_blob_url: blob_url}); + } + + + prepareAlignmentOfAllHits() { + // Get number of hits and array of all hsps. + var num_hits = 0; + var hsps_arr = []; + if(!this.state.queries.length){ + return; + } + this.state.queries.forEach( + (query) => query.hits.forEach( + (hit) => { + num_hits++; + hit.hsps.forEach((hsp) => { + hsp.query_id = query.id; + hsp.hit_id = hit.id; + hsps_arr.push(hsp); + }); + } + ) + ); + + var aln_exporter = new AlignmentExporter(); + var file_name = `alignment-${num_hits}_hits`; + const blob_url = aln_exporter.prepare_alignments_for_export(hsps_arr, file_name); + $('.download-alignment-of-all') + .attr('href', blob_url) + .attr('download', file_name); + return false; + } + render() { return this.isResultAvailable() ? this.resultsJSX() : this.loadingJSX(); } diff --git a/public/js/sidebar.js b/public/js/sidebar.js index 009b52411..ba4bc561b 100644 --- a/public/js/sidebar.js +++ b/public/js/sidebar.js @@ -19,8 +19,6 @@ export default class extends Component { super(props); this.downloadFastaOfAll = this.downloadFastaOfAll.bind(this); this.downloadFastaOfSelected = this.downloadFastaOfSelected.bind(this); - this.downloadAlignmentOfAll = this.downloadAlignmentOfAll.bind(this); - this.downloadAlignmentOfSelected = this.downloadAlignmentOfSelected.bind(this); this.topPanelJSX = this.topPanelJSX.bind(this); this.summaryString = this.summaryString.bind(this); this.indexJSX = this.indexJSX.bind(this); @@ -178,50 +176,6 @@ export default class extends Component { return false; } - downloadAlignmentOfAll() { - // Get number of hits and array of all hsps. - var num_hits = 0; - var hsps_arr = []; - this.props.data.queries.forEach( - (query) => query.hits.forEach( - (hit) => { - num_hits++; - hit.hsps.forEach((hsp) => { - hsp.query_id = query.id; - hsp.hit_id = hit.id; - hsps_arr.push(hsp); - }); - } - ) - ); - - var aln_exporter = new AlignmentExporter(); - var file_name = `alignment-${num_hits}_hits`; - aln_exporter.export_alignments(hsps_arr, file_name); - return false; - } - - downloadAlignmentOfSelected() { - var sequence_ids = $('.hit-links :checkbox:checked').map(function () { - return this.value; - }).get(); - var hsps_arr = []; - var aln_exporter = new AlignmentExporter(); - console.log('check ' + sequence_ids.toString()); - _.each(this.props.data.queries, _.bind(function (query) { - _.each(query.hits, function (hit) { - if (_.indexOf(sequence_ids, hit.id) != -1) { - _.each(hit.hsps, function (hsp) { - hsp.hit_id = hit.id; - hsp.query_id = query.id; - hsps_arr.push(hsp); - }); - } - }); - }, this)); - aln_exporter.export_alignments(hsps_arr, 'alignment-' + sequence_ids.length + '_hits'); - return false; - } @@ -379,14 +333,12 @@ export default class extends Component { }
  • - + Alignment of all hits
  • - + Alignment of selected hit(s)
  • diff --git a/public/sequenceserver-report.min.js b/public/sequenceserver-report.min.js index f6f948041..aec0d4e6d 100644 --- a/public/sequenceserver-report.min.js +++ b/public/sequenceserver-report.min.js @@ -16,7 +16,7 @@ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ AlignmentExporter)\n/* harmony export */ });\n/* harmony import */ var _exporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exporter */ \"./public/js/exporter.js\");\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\n\n\n\nvar AlignmentExporter = /*#__PURE__*/function () {\n function AlignmentExporter() {\n _classCallCheck(this, AlignmentExporter);\n }\n\n _createClass(AlignmentExporter, [{\n key: \"wrap_string\",\n value: function wrap_string(str, width) {\n var idx = 0;\n var wrapped = '';\n\n while (true) {\n wrapped += str.substring(idx, idx + width);\n idx += width;\n\n if (idx < str.length) {\n wrapped += '\\n';\n } else {\n break;\n }\n }\n\n return wrapped;\n }\n }, {\n key: \"generate_fasta\",\n value: function generate_fasta(hsps) {\n var fasta = '';\n\n underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].each(hsps, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].bind(function (hsp) {\n fasta += '>' + hsp.query_id + ':' + hsp.qstart + '-' + hsp.qend + '\\n';\n fasta += hsp.qseq + '\\n';\n fasta += '>' + hsp.query_id + ':' + hsp.qstart + '-' + hsp.qend + '_alignment_' + hsp.hit_id + ':' + hsp.sstart + '-' + hsp.send + '\\n';\n fasta += hsp.midline + '\\n';\n fasta += '>' + hsp.hit_id + ':' + hsp.sstart + '-' + hsp.send + '\\n';\n fasta += hsp.sseq + '\\n';\n }, this));\n\n return fasta;\n }\n }, {\n key: \"export_alignments\",\n value: function export_alignments(hsps, filename_prefix) {\n var fasta = this.generate_fasta(hsps);\n var blob = new Blob([fasta], {\n type: 'text/fasta'\n }); // var filename_prefix = query_def + '_' + subject_def;\n // var filename_prefix = query_id + '_' + subject_id;\n\n var filename = _exporter__WEBPACK_IMPORTED_MODULE_0__.sanitize_filename(filename_prefix) + '.txt';\n _exporter__WEBPACK_IMPORTED_MODULE_0__.download_blob(blob, filename);\n }\n }, {\n key: \"export_alignments_of_all\",\n value: function export_alignments_of_all(hsps, name) {}\n }]);\n\n return AlignmentExporter;\n}();\n\n\n\n//# sourceURL=webpack://SequenceServer/./public/js/alignment_exporter.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ AlignmentExporter)\n/* harmony export */ });\n/* harmony import */ var _exporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exporter */ \"./public/js/exporter.js\");\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\n\n\n\nvar AlignmentExporter = /*#__PURE__*/function () {\n function AlignmentExporter() {\n _classCallCheck(this, AlignmentExporter);\n\n this.prepare_alignments_for_export = this.prepare_alignments_for_export.bind(this);\n this.export_alignments = this.export_alignments.bind(this);\n }\n\n _createClass(AlignmentExporter, [{\n key: \"wrap_string\",\n value: function wrap_string(str, width) {\n var idx = 0;\n var wrapped = '';\n\n while (true) {\n wrapped += str.substring(idx, idx + width);\n idx += width;\n\n if (idx < str.length) {\n wrapped += '\\n';\n } else {\n break;\n }\n }\n\n return wrapped;\n }\n }, {\n key: \"generate_fasta\",\n value: function generate_fasta(hsps) {\n var fasta = '';\n\n underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].each(hsps, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].bind(function (hsp) {\n fasta += '>' + hsp.query_id + ':' + hsp.qstart + '-' + hsp.qend + '\\n';\n fasta += hsp.qseq + '\\n';\n fasta += '>' + hsp.query_id + ':' + hsp.qstart + '-' + hsp.qend + '_alignment_' + hsp.hit_id + ':' + hsp.sstart + '-' + hsp.send + '\\n';\n fasta += hsp.midline + '\\n';\n fasta += '>' + hsp.hit_id + ':' + hsp.sstart + '-' + hsp.send + '\\n';\n fasta += hsp.sseq + '\\n';\n }, this));\n\n return fasta;\n }\n }, {\n key: \"get_alignments_download_metadata\",\n value: function get_alignments_download_metadata(hsps, filename_prefix) {\n var fasta = this.generate_fasta(hsps);\n var blob = new Blob([fasta], {\n type: 'text/fasta'\n });\n var filename = _exporter__WEBPACK_IMPORTED_MODULE_0__.sanitize_filename(filename_prefix) + '.txt';\n return {\n filename: filename,\n blob: blob\n };\n }\n }, {\n key: \"prepare_alignments_for_export\",\n value: function prepare_alignments_for_export(hsps, filename_prefix) {\n var _this$get_alignments_ = this.get_alignments_download_metadata(hsps, filename_prefix),\n filename = _this$get_alignments_.filename,\n blob = _this$get_alignments_.blob;\n\n var blob_url = _exporter__WEBPACK_IMPORTED_MODULE_0__.generate_blob_url(blob, filename);\n return blob_url;\n }\n }, {\n key: \"export_alignments\",\n value: function export_alignments(hsps, filename_prefix) {\n var _this$get_alignments_2 = this.get_alignments_download_metadata(hsps, filename_prefix),\n filename = _this$get_alignments_2.filename,\n blob = _this$get_alignments_2.blob;\n\n _exporter__WEBPACK_IMPORTED_MODULE_0__.download_blob(blob, filename);\n }\n }]);\n\n return AlignmentExporter;\n}();\n\n\n\n//# sourceURL=webpack://SequenceServer/./public/js/alignment_exporter.js?"); /***/ }), @@ -60,7 +60,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"download_blob\": () => (/* binding */ download_blob),\n/* harmony export */ \"download_url\": () => (/* binding */ download_url),\n/* harmony export */ \"sanitize_filename\": () => (/* binding */ sanitize_filename)\n/* harmony export */ });\nfunction download_url(url, filename) {\n var a = d3.select('body').append('a').attr('download', filename).style('display', 'none').attr('href', url);\n a.node().click();\n setTimeout(function () {\n a.remove();\n }, 100);\n}\nfunction download_blob(blob, filename) {\n if (typeof window.navigator.msSaveOrOpenBlob !== 'undefined') {\n window.navigator.msSaveOrOpenBlob(blob, filename);\n return;\n }\n\n download_url(window.URL.createObjectURL(blob), filename);\n}\nfunction sanitize_filename(str) {\n var san = str.replace(/[^a-zA-Z0-9=_\\-]/g, '_'); // Replace runs of underscores with single one.\n\n san = san.replace(/_{2,}/g, '_'); // Remove any leading or trailing underscores.\n\n san = san.replace(/^_/, '').replace(/_$/, '');\n return san;\n}\n\n//# sourceURL=webpack://SequenceServer/./public/js/exporter.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"download_blob\": () => (/* binding */ download_blob),\n/* harmony export */ \"download_url\": () => (/* binding */ download_url),\n/* harmony export */ \"generate_blob_url\": () => (/* binding */ generate_blob_url),\n/* harmony export */ \"sanitize_filename\": () => (/* binding */ sanitize_filename)\n/* harmony export */ });\nfunction download_url(url, filename) {\n var a = d3.select('body').append('a').attr('download', filename).style('display', 'none').attr('href', url);\n a.node().click();\n setTimeout(function () {\n a.remove();\n }, 100);\n}\nfunction generate_blob_url(blob) {\n var url = window.URL.createObjectURL(blob);\n return url;\n}\nfunction download_blob(blob, filename) {\n if (typeof window.navigator.msSaveOrOpenBlob !== 'undefined') {\n window.navigator.msSaveOrOpenBlob(blob, filename);\n } else {\n download_url(generate_blob_url(blob), filename);\n }\n}\nfunction sanitize_filename(str) {\n var san = str.replace(/[^a-zA-Z0-9=_\\-]/g, '_'); // Replace runs of underscores with single one.\n\n san = san.replace(/_{2,}/g, '_'); // Remove any leading or trailing underscores.\n\n san = san.replace(/^_/, '').replace(/_$/, '');\n return san;\n}\n\n//# sourceURL=webpack://SequenceServer/./public/js/exporter.js?"); /***/ }), @@ -82,7 +82,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ _default)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var _kablammo__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./kablammo */ \"./public/js/kablammo.js\");\n/* harmony import */ var _download_fasta__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./download_fasta */ \"./public/js/download_fasta.js\");\n/* harmony import */ var _alignment_exporter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./alignment_exporter */ \"./public/js/alignment_exporter.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n // to download textual alignment\n\n/**\n * Component for each hit. Receives props from Report. Has no state.\n */\n\n\n\n\nvar _default = /*#__PURE__*/function (_Component) {\n _inherits(_default, _Component);\n\n var _super = _createSuper(_default);\n\n function _default(props) {\n var _this;\n\n _classCallCheck(this, _default);\n\n _this = _super.call(this, props);\n _this.accession = _this.accession.bind(_assertThisInitialized(_this));\n _this.sequenceID = _this.sequenceID.bind(_assertThisInitialized(_this));\n _this.hitLength = _this.hitLength.bind(_assertThisInitialized(_this));\n _this.numHSPs = _this.numHSPs.bind(_assertThisInitialized(_this));\n _this.domID = _this.domID.bind(_assertThisInitialized(_this));\n _this.databaseIDs = _this.databaseIDs.bind(_assertThisInitialized(_this));\n _this.showSequenceViewer = _this.showSequenceViewer.bind(_assertThisInitialized(_this));\n _this.viewSequenceLink = _this.viewSequenceLink.bind(_assertThisInitialized(_this));\n _this.downloadFASTA = _this.downloadFASTA.bind(_assertThisInitialized(_this));\n _this.downloadAlignment = _this.downloadAlignment.bind(_assertThisInitialized(_this));\n _this.headerJSX = _this.headerJSX.bind(_assertThisInitialized(_this));\n _this.contentJSX = _this.contentJSX.bind(_assertThisInitialized(_this));\n _this.hitLinks = _this.hitLinks.bind(_assertThisInitialized(_this));\n _this.viewSequenceButton = _this.viewSequenceButton.bind(_assertThisInitialized(_this));\n _this.downloadFASTAButton = _this.downloadFASTAButton.bind(_assertThisInitialized(_this));\n return _this;\n }\n\n _createClass(_default, [{\n key: \"shouldComponentUpdate\",\n value: function shouldComponentUpdate() {\n return !this.props.hit;\n }\n /**\n * Returns accession number of the hit sequence.\n */\n\n }, {\n key: \"accession\",\n value: function accession() {\n return this.props.hit.accession;\n }\n /**\n * Returns id of the hit sequence.\n */\n\n }, {\n key: \"sequenceID\",\n value: function sequenceID() {\n return this.props.hit.id;\n }\n /**\n * Returns length of the hit sequence.\n */\n\n }, {\n key: \"hitLength\",\n value: function hitLength() {\n return this.props.hit.length;\n }\n }, {\n key: \"numHSPs\",\n value: function numHSPs() {\n return this.props.hit.hsps.length;\n } // Internal helpers. //\n\n /**\n * Returns id that will be used for the DOM node corresponding to the hit.\n */\n\n }, {\n key: \"domID\",\n value: function domID() {\n return 'Query_' + this.props.query.number + '_hit_' + this.props.hit.number;\n }\n }, {\n key: \"databaseIDs\",\n value: function databaseIDs() {\n return underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.querydb, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].iteratee('id'));\n }\n }, {\n key: \"showSequenceViewer\",\n value: function showSequenceViewer() {\n this.props.showSequenceModal(this.viewSequenceLink());\n }\n }, {\n key: \"viewSequenceLink\",\n value: function viewSequenceLink() {\n var sequenceIDs = encodeURIComponent(this.sequenceID());\n var databaseIDs = encodeURIComponent(this.databaseIDs());\n return \"get_sequence/?sequence_ids=\".concat(sequenceIDs, \"&database_ids=\").concat(databaseIDs);\n }\n }, {\n key: \"downloadFASTA\",\n value: function downloadFASTA(event) {\n var sequenceIDs = [this.sequenceID()];\n\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(sequenceIDs, this.databaseIDs());\n } // Event-handler for exporting alignments.\n // Calls relevant method on AlignmentExporter defined in alignment_exporter.js.\n\n }, {\n key: \"downloadAlignment\",\n value: function downloadAlignment(event) {\n var hsps = underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.hit.hsps, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].bind(function (hsp) {\n hsp.query_id = this.props.query.id;\n hsp.hit_id = this.props.hit.id;\n return hsp;\n }, this));\n\n var aln_exporter = new _alignment_exporter__WEBPACK_IMPORTED_MODULE_4__[\"default\"]();\n aln_exporter.export_alignments(hsps, this.props.query.id + '_' + this.props.hit.id);\n }\n }, {\n key: \"headerJSX\",\n value: function headerJSX() {\n var meta = \"length: \".concat(this.hitLength().toLocaleString());\n\n if (this.props.showQueryCrumbs && this.props.showHitCrumbs) {\n // Multiper queries, multiple hits\n meta = \"hit \".concat(this.props.hit.number, \" of query \").concat(this.props.query.number, \", \") + meta;\n } else if (this.props.showQueryCrumbs && !this.props.showHitCrumbs) {\n // Multiple queries, single hit\n meta = \"the only hit of query \".concat(this.props.query.number, \", \") + meta;\n } else if (!this.props.showQueryCrumbs && this.props.showHitCrumbs) {\n // Single query, multiple hits\n meta = \"hit \".concat(this.props.hit.number, \", \") + meta;\n }\n\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"div\", {\n className: \"section-header\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"h4\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"i\", {\n className: \"fa fa-minus-square-o\"\n }), \"\\xA0\", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"strong\", {\n children: this.props.hit.id\n }), \"\\xA0\", this.props.hit.title]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"span\", {\n className: \"label label-reset pos-label\",\n children: meta\n })]\n });\n }\n }, {\n key: \"contentJSX\",\n value: function contentJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"div\", {\n className: \"section-content\",\n \"data-parent-hit\": this.domID(),\n children: [this.hitLinks(), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(_kablammo__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n query: this.props.query,\n hit: this.props.hit,\n algorithm: this.props.algorithm,\n showHSPCrumbs: this.numHSPs() > 1 && this.numHSPs() < 27,\n collapsed: this.props.veryBig\n }, 'kablammo' + this.props.query.id)]\n });\n }\n }, {\n key: \"hitLinks\",\n value: function hitLinks() {\n var _this2 = this;\n\n var btns = [];\n\n if (!(this.props.imported_xml || this.props.non_parse_seqids)) {\n btns = btns.concat([this.viewSequenceButton(), this.downloadFASTAButton()]);\n }\n\n btns.push(this.downloadAlignmentButton());\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"div\", {\n className: \"hit-links\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"label\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"input\", {\n type: \"checkbox\",\n id: this.domID() + '_checkbox',\n value: this.sequenceID(),\n onChange: function () {\n this.props.selectHit(this.domID() + '_checkbox');\n }.bind(this),\n \"data-target\": '#' + this.domID()\n }), \" Select\"]\n }), btns.map(function (btn, index) {\n return [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }, \"btn-\".concat(index)), _this2.button(Object.assign(btn, {\n key: index\n }))];\n }), this.props.hit.links.map(function (link, index) {\n return [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }, \"link-\".concat(index)), _this2.a(link, index)];\n })]\n });\n } // Return JSX for view sequence button.\n\n }, {\n key: \"viewSequenceButton\",\n value: function viewSequenceButton() {\n var _this3 = this;\n\n if (this.hitLength() > 10000) {\n return {\n text: 'Sequence',\n icon: 'fa-eye',\n className: 'view-sequence',\n title: 'Sequence too long'\n };\n } else {\n return {\n text: 'Sequence',\n icon: 'fa-eye',\n className: 'view-sequence',\n onClick: function onClick() {\n return _this3.showSequenceViewer();\n }\n };\n }\n }\n }, {\n key: \"downloadFASTAButton\",\n value: function downloadFASTAButton() {\n var _this4 = this;\n\n return {\n text: 'FASTA',\n icon: 'fa-download',\n className: 'download-fa',\n onClick: function onClick() {\n return _this4.downloadFASTA();\n }\n };\n }\n }, {\n key: \"downloadAlignmentButton\",\n value: function downloadAlignmentButton() {\n var _this5 = this;\n\n return {\n text: 'Alignment',\n icon: 'fa-download',\n className: 'download-aln',\n onClick: function onClick() {\n return _this5.downloadAlignment();\n }\n };\n }\n }, {\n key: \"button\",\n value: function button(_ref) {\n var text = _ref.text,\n icon = _ref.icon,\n title = _ref.title,\n className = _ref.className,\n onClick = _ref.onClick,\n key = _ref.key;\n\n if (onClick) {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"button\", {\n className: \"btn-link \".concat(className),\n title: title,\n onClick: onClick,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"i\", {\n className: \"fa \".concat(icon)\n }), \" \", text]\n }, key);\n } else {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"button\", {\n className: \"btn-link view-sequence disabled\",\n title: title,\n disabled: true,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"i\", {\n className: \"fa \".concat(icon)\n }), \" \", text]\n }, key);\n }\n }\n /**\n * Render URL for sequence-viewer.\n */\n\n }, {\n key: \"a\",\n value: function a(link, key) {\n if (!link.title || !link.url) return;\n var className = 'btn btn-link';\n if (link[\"class\"]) className = \"\".concat(className, \" \").concat(link[\"class\"]);\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"a\", {\n href: link.url,\n className: className,\n target: \"_blank\",\n children: [link.icon && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"i\", {\n className: 'fa ' + link.icon\n }), ' ' + link.title + ' ']\n }, \"\".concat(link.url, \"-\").concat(key));\n }\n }, {\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"div\", {\n className: \"hit\",\n id: this.domID(),\n \"data-hit-def\": this.props.hit.id,\n \"data-hit-len\": this.props.hit.length,\n \"data-hit-evalue\": this.props.hit.evalue,\n children: [this.headerJSX(), \" \", this.contentJSX()]\n });\n }\n }]);\n\n return _default;\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\n\n\n\n//# sourceURL=webpack://SequenceServer/./public/js/hit.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ _default)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var _kablammo__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./kablammo */ \"./public/js/kablammo.js\");\n/* harmony import */ var _download_fasta__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./download_fasta */ \"./public/js/download_fasta.js\");\n/* harmony import */ var _alignment_exporter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./alignment_exporter */ \"./public/js/alignment_exporter.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n // to download textual alignment\n\n/**\n * Component for each hit. Receives props from Report. Has no state.\n */\n\n\n\n\nvar _default = /*#__PURE__*/function (_Component) {\n _inherits(_default, _Component);\n\n var _super = _createSuper(_default);\n\n function _default(props) {\n var _this;\n\n _classCallCheck(this, _default);\n\n _this = _super.call(this, props);\n _this.accession = _this.accession.bind(_assertThisInitialized(_this));\n _this.sequenceID = _this.sequenceID.bind(_assertThisInitialized(_this));\n _this.hitLength = _this.hitLength.bind(_assertThisInitialized(_this));\n _this.numHSPs = _this.numHSPs.bind(_assertThisInitialized(_this));\n _this.domID = _this.domID.bind(_assertThisInitialized(_this));\n _this.databaseIDs = _this.databaseIDs.bind(_assertThisInitialized(_this));\n _this.showSequenceViewer = _this.showSequenceViewer.bind(_assertThisInitialized(_this));\n _this.viewSequenceLink = _this.viewSequenceLink.bind(_assertThisInitialized(_this));\n _this.downloadFASTA = _this.downloadFASTA.bind(_assertThisInitialized(_this));\n _this.downloadAlignment = _this.downloadAlignment.bind(_assertThisInitialized(_this));\n _this.headerJSX = _this.headerJSX.bind(_assertThisInitialized(_this));\n _this.contentJSX = _this.contentJSX.bind(_assertThisInitialized(_this));\n _this.hitLinks = _this.hitLinks.bind(_assertThisInitialized(_this));\n _this.viewSequenceButton = _this.viewSequenceButton.bind(_assertThisInitialized(_this));\n _this.downloadFASTAButton = _this.downloadFASTAButton.bind(_assertThisInitialized(_this));\n return _this;\n }\n\n _createClass(_default, [{\n key: \"shouldComponentUpdate\",\n value: function shouldComponentUpdate() {\n return !this.props.hit;\n }\n /**\n * Returns accession number of the hit sequence.\n */\n\n }, {\n key: \"accession\",\n value: function accession() {\n return this.props.hit.accession;\n }\n /**\n * Returns id of the hit sequence.\n */\n\n }, {\n key: \"sequenceID\",\n value: function sequenceID() {\n return this.props.hit.id;\n }\n /**\n * Returns length of the hit sequence.\n */\n\n }, {\n key: \"hitLength\",\n value: function hitLength() {\n return this.props.hit.length;\n }\n }, {\n key: \"numHSPs\",\n value: function numHSPs() {\n return this.props.hit.hsps.length;\n } // Internal helpers. //\n\n /**\n * Returns id that will be used for the DOM node corresponding to the hit.\n */\n\n }, {\n key: \"domID\",\n value: function domID() {\n return 'Query_' + this.props.query.number + '_hit_' + this.props.hit.number;\n }\n }, {\n key: \"databaseIDs\",\n value: function databaseIDs() {\n return underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.querydb, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].iteratee('id'));\n }\n }, {\n key: \"showSequenceViewer\",\n value: function showSequenceViewer() {\n this.props.showSequenceModal(this.viewSequenceLink());\n }\n }, {\n key: \"viewSequenceLink\",\n value: function viewSequenceLink() {\n var sequenceIDs = encodeURIComponent(this.sequenceID());\n var databaseIDs = encodeURIComponent(this.databaseIDs());\n return \"get_sequence/?sequence_ids=\".concat(sequenceIDs, \"&database_ids=\").concat(databaseIDs);\n }\n }, {\n key: \"downloadFASTA\",\n value: function downloadFASTA(event) {\n var sequenceIDs = [this.sequenceID()];\n\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(sequenceIDs, this.databaseIDs());\n } // Event-handler for exporting alignments.\n // Calls relevant method on AlignmentExporter defined in alignment_exporter.js.\n\n }, {\n key: \"downloadAlignment\",\n value: function downloadAlignment(event) {\n var hsps = underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.hit.hsps, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].bind(function (hsp) {\n hsp.query_id = this.props.query.id;\n hsp.hit_id = this.props.hit.id;\n return hsp;\n }, this));\n\n var aln_exporter = new _alignment_exporter__WEBPACK_IMPORTED_MODULE_4__[\"default\"]();\n aln_exporter.export_alignments(hsps, this.props.query.id + '_' + this.props.hit.id);\n }\n }, {\n key: \"headerJSX\",\n value: function headerJSX() {\n var meta = \"length: \".concat(this.hitLength().toLocaleString());\n\n if (this.props.showQueryCrumbs && this.props.showHitCrumbs) {\n // Multiper queries, multiple hits\n meta = \"hit \".concat(this.props.hit.number, \" of query \").concat(this.props.query.number, \", \") + meta;\n } else if (this.props.showQueryCrumbs && !this.props.showHitCrumbs) {\n // Multiple queries, single hit\n meta = \"the only hit of query \".concat(this.props.query.number, \", \") + meta;\n } else if (!this.props.showQueryCrumbs && this.props.showHitCrumbs) {\n // Single query, multiple hits\n meta = \"hit \".concat(this.props.hit.number, \", \") + meta;\n }\n\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"div\", {\n className: \"section-header\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"h4\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"i\", {\n className: \"fa fa-minus-square-o\"\n }), \"\\xA0\", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"strong\", {\n children: this.props.hit.id\n }), \"\\xA0\", this.props.hit.title]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"span\", {\n className: \"label label-reset pos-label\",\n children: meta\n })]\n });\n }\n }, {\n key: \"contentJSX\",\n value: function contentJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"div\", {\n className: \"section-content\",\n \"data-parent-hit\": this.domID(),\n children: [this.hitLinks(), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(_kablammo__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n query: this.props.query,\n hit: this.props.hit,\n algorithm: this.props.algorithm,\n showHSPCrumbs: this.numHSPs() > 1 && this.numHSPs() < 27,\n collapsed: this.props.veryBig\n }, 'kablammo' + this.props.query.id)]\n });\n }\n }, {\n key: \"hitLinks\",\n value: function hitLinks() {\n var _this2 = this;\n\n var btns = [];\n\n if (!(this.props.imported_xml || this.props.non_parse_seqids)) {\n btns = btns.concat([this.viewSequenceButton(), this.downloadFASTAButton()]);\n }\n\n btns.push(this.downloadAlignmentButton());\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"div\", {\n className: \"hit-links\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"label\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"input\", {\n type: \"checkbox\",\n id: this.domID() + '_checkbox',\n value: this.sequenceID(),\n onChange: function () {\n this.props.selectHit(this.domID() + '_checkbox');\n this.props.onChange();\n }.bind(this),\n \"data-target\": '#' + this.domID()\n }), \" Select\"]\n }), btns.map(function (btn, index) {\n return [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }, \"btn-\".concat(index)), _this2.button(Object.assign(btn, {\n key: index\n }))];\n }), this.props.hit.links.map(function (link, index) {\n return [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }, \"link-\".concat(index)), _this2.a(link, index)];\n })]\n });\n } // Return JSX for view sequence button.\n\n }, {\n key: \"viewSequenceButton\",\n value: function viewSequenceButton() {\n var _this3 = this;\n\n if (this.hitLength() > 10000) {\n return {\n text: 'Sequence',\n icon: 'fa-eye',\n className: 'view-sequence',\n title: 'Sequence too long'\n };\n } else {\n return {\n text: 'Sequence',\n icon: 'fa-eye',\n className: 'view-sequence',\n onClick: function onClick() {\n return _this3.showSequenceViewer();\n }\n };\n }\n }\n }, {\n key: \"downloadFASTAButton\",\n value: function downloadFASTAButton() {\n var _this4 = this;\n\n return {\n text: 'FASTA',\n icon: 'fa-download',\n className: 'download-fa',\n onClick: function onClick() {\n return _this4.downloadFASTA();\n }\n };\n }\n }, {\n key: \"downloadAlignmentButton\",\n value: function downloadAlignmentButton() {\n var _this5 = this;\n\n return {\n text: 'Alignment',\n icon: 'fa-download',\n className: 'download-aln',\n onClick: function onClick() {\n return _this5.downloadAlignment();\n }\n };\n }\n }, {\n key: \"button\",\n value: function button(_ref) {\n var text = _ref.text,\n icon = _ref.icon,\n title = _ref.title,\n className = _ref.className,\n onClick = _ref.onClick,\n key = _ref.key;\n\n if (onClick) {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"button\", {\n className: \"btn-link \".concat(className),\n title: title,\n onClick: onClick,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"i\", {\n className: \"fa \".concat(icon)\n }), \" \", text]\n }, key);\n } else {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"button\", {\n className: \"btn-link view-sequence disabled\",\n title: title,\n disabled: true,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"i\", {\n className: \"fa \".concat(icon)\n }), \" \", text]\n }, key);\n }\n }\n /**\n * Render URL for sequence-viewer.\n */\n\n }, {\n key: \"a\",\n value: function a(link, key) {\n if (!link.title || !link.url) return;\n var className = 'btn btn-link';\n if (link[\"class\"]) className = \"\".concat(className, \" \").concat(link[\"class\"]);\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"a\", {\n href: link.url,\n className: className,\n target: \"_blank\",\n children: [link.icon && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsx)(\"i\", {\n className: 'fa ' + link.icon\n }), ' ' + link.title + ' ']\n }, \"\".concat(link.url, \"-\").concat(key));\n }\n }, {\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxs)(\"div\", {\n className: \"hit\",\n id: this.domID(),\n \"data-hit-def\": this.props.hit.id,\n \"data-hit-len\": this.props.hit.length,\n \"data-hit-evalue\": this.props.hit.evalue,\n children: [this.headerJSX(), \" \", this.contentJSX()]\n });\n }\n }]);\n\n return _default;\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\n\n\n\n//# sourceURL=webpack://SequenceServer/./public/js/hit.js?"); /***/ }), @@ -159,7 +159,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _jquery_world__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./jquery_world */ \"./public/js/jquery_world.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var _sidebar__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./sidebar */ \"./public/js/sidebar.js\");\n/* harmony import */ var _circos__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./circos */ \"./public/js/circos.js\");\n/* harmony import */ var _query__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./query */ \"./public/js/query.js\");\n/* harmony import */ var _hit__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./hit */ \"./public/js/hit.js\");\n/* harmony import */ var _hsp__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./hsp */ \"./public/js/hsp.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\n\nfunction ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n // for custom $.tooltip function\n\n\n\n\n\n\n\n\n/**\n * Renders entire report.\n *\n * Composed of Query and Sidebar components.\n */\n\n\n\n\nvar Report = /*#__PURE__*/function (_Component) {\n _inherits(Report, _Component);\n\n var _super = _createSuper(Report);\n\n function Report(props) {\n var _this;\n\n _classCallCheck(this, Report);\n\n _this = _super.call(this, props); // Properties below are internal state used to render results in small\n // slices (see updateState).\n\n _this.numUpdates = 0;\n _this.nextQuery = 0;\n _this.nextHit = 0;\n _this.nextHSP = 0;\n _this.maxHSPs = 3; // max HSPs to render in a cycle\n\n _this.state = {\n search_id: '',\n seqserv_version: '',\n program: '',\n program_version: '',\n submitted_at: '',\n queries: [],\n results: [],\n querydb: [],\n params: [],\n stats: [],\n allQueriesLoaded: false\n };\n return _this;\n }\n /**\n * Fetch results.\n */\n\n\n _createClass(Report, [{\n key: \"fetchResults\",\n value: function fetchResults() {\n var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];\n var component = this;\n\n function poll() {\n $.getJSON(location.pathname + '.json').complete(function (jqXHR) {\n switch (jqXHR.status) {\n case 202:\n var interval;\n\n if (intervals.length === 1) {\n interval = intervals[0];\n } else {\n interval = intervals.shift();\n }\n\n setTimeout(poll, interval);\n break;\n\n case 200:\n component.setStateFromJSON(jqXHR.responseJSON);\n break;\n\n case 404:\n case 400:\n case 500:\n component.props.showErrorModal(jqXHR.responseJSON);\n break;\n }\n });\n }\n\n poll();\n }\n /**\n * Calls setState after any required modification to responseJSON.\n */\n\n }, {\n key: \"setStateFromJSON\",\n value: function setStateFromJSON(responseJSON) {\n this.lastTimeStamp = Date.now();\n this.setState(responseJSON);\n }\n /**\n * Called as soon as the page has loaded and the user sees the loading spinner.\n * We use this opportunity to setup services that make use of delegated events\n * bound to the window, document, or body.\n */\n\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.fetchResults(); // This sets up an event handler which enables users to select text from\n // hit header without collapsing the hit.\n\n this.preventCollapseOnSelection();\n this.toggleTable();\n }\n /**\n * Called for the first time after as BLAST results have been retrieved from\n * the server and added to this.state by fetchResults. Only summary overview\n * and circos would have been rendered at this point. At this stage we kick\n * start iteratively adding 1 HSP to the page every 25 milli-seconds.\n */\n\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n var _this2 = this;\n\n // Log to console how long the last update take?\n console.log((Date.now() - this.lastTimeStamp) / 1000); // Lock sidebar in its position on the first update.\n\n if (this.nextQuery == 0 && this.nextHit == 0 && this.nextHSP == 0) {\n this.affixSidebar();\n } // Queue next update if we have not rendered all results yet.\n\n\n if (this.nextQuery < this.state.queries.length) {\n // setTimeout is used to clear call stack and space out\n // the updates giving the browser a chance to respond\n // to user interactions.\n setTimeout(function () {\n return _this2.updateState();\n }, 25);\n } else {\n this.componentFinishedUpdating();\n }\n }\n /**\n * Push next slice of results to React for rendering.\n */\n\n }, {\n key: \"updateState\",\n value: function updateState() {\n var results = [];\n var numHSPsProcessed = 0;\n\n while (this.nextQuery < this.state.queries.length) {\n var query = this.state.queries[this.nextQuery]; // We may see a query multiple times during rendering because only\n // 3 hsps or are rendered in each cycle, but we want to create the\n // corresponding Query component only the first time we see it.\n\n if (this.nextHit == 0 && this.nextHSP == 0) {\n results.push( /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_query__WEBPACK_IMPORTED_MODULE_5__.ReportQuery, {\n query: query,\n program: this.state.program,\n querydb: this.state.querydb,\n showQueryCrumbs: this.state.queries.length > 1,\n non_parse_seqids: this.state.non_parse_seqids,\n imported_xml: this.state.imported_xml,\n veryBig: this.state.veryBig\n }, 'Query_' + query.number));\n }\n\n while (this.nextHit < query.hits.length) {\n var hit = query.hits[this.nextHit]; // We may see a hit multiple times during rendering because only\n // 10 hsps are rendered in each cycle, but we want to create the\n // corresponding Hit component only the first time we see it.\n\n if (this.nextHSP == 0) {\n results.push( /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_hit__WEBPACK_IMPORTED_MODULE_6__[\"default\"], _objectSpread({\n query: query,\n hit: hit,\n algorithm: this.state.program,\n querydb: this.state.querydb,\n selectHit: this.selectHit,\n imported_xml: this.state.imported_xml,\n non_parse_seqids: this.state.non_parse_seqids,\n showQueryCrumbs: this.state.queries.length > 1,\n showHitCrumbs: query.hits.length > 1,\n veryBig: this.state.veryBig\n }, this.props), 'Query_' + query.number + '_Hit_' + hit.number));\n }\n\n while (this.nextHSP < hit.hsps.length) {\n // Get nextHSP and increment the counter.\n var hsp = hit.hsps[this.nextHSP++];\n results.push( /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_hsp__WEBPACK_IMPORTED_MODULE_7__[\"default\"], _objectSpread({\n query: query,\n hit: hit,\n hsp: hsp,\n algorithm: this.state.program,\n showHSPNumbers: hit.hsps.length > 1\n }, this.props), 'Query_' + query.number + '_Hit_' + hit.number + '_HSP_' + hsp.number));\n numHSPsProcessed++;\n if (numHSPsProcessed == this.maxHSPs) break;\n } // Are we here because we have iterated over all hsps of a hit,\n // or because of the break clause in the inner loop?\n\n\n if (this.nextHSP == hit.hsps.length) {\n this.nextHit = this.nextHit + 1;\n this.nextHSP = 0;\n }\n\n if (numHSPsProcessed == this.maxHSPs) break;\n } // Are we here because we have iterated over all hits of a query,\n // or because of the break clause in the inner loop?\n\n\n if (this.nextHit == query.hits.length) {\n this.nextQuery = this.nextQuery + 1;\n this.nextHit = 0;\n }\n\n if (numHSPsProcessed == this.maxHSPs) break;\n } // Push the components to react for rendering.\n\n\n this.numUpdates++;\n this.lastTimeStamp = Date.now();\n this.setState({\n results: this.state.results.concat(results),\n veryBig: this.numUpdates >= 250\n });\n }\n /**\n * Called after all results have been rendered.\n */\n\n }, {\n key: \"componentFinishedUpdating\",\n value: function componentFinishedUpdating() {\n if (this.state.allQueriesLoaded) return;\n this.shouldShowIndex() && this.setupScrollSpy();\n this.setState({\n allQueriesLoaded: true\n });\n }\n /**\n * Returns loading message\n */\n\n }, {\n key: \"loadingJSX\",\n value: function loadingJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"row\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"col-md-6 col-md-offset-3 text-center\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"h1\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"i\", {\n className: \"fa fa-cog fa-spin\"\n }), \"\\xA0 BLAST-ing\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"p\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"br\", {}), \"This can take some time depending on the size of your query and database(s). The page will update automatically when BLAST is done.\", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"br\", {}), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"br\", {}), \"You can bookmark the page and come back to it later or share the link with someone.\"]\n })]\n })\n });\n }\n /**\n * Return results JSX.\n */\n\n }, {\n key: \"resultsJSX\",\n value: function resultsJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"row\",\n id: \"results\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"col-md-3 hidden-sm hidden-xs\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_sidebar__WEBPACK_IMPORTED_MODULE_3__[\"default\"], {\n data: this.state,\n atLeastOneHit: this.atLeastOneHit(),\n shouldShowIndex: this.shouldShowIndex(),\n allQueriesLoaded: this.state.allQueriesLoaded\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"col-md-9\",\n children: [this.overviewJSX(), this.circosJSX(), this.state.results]\n })]\n });\n }\n /**\n * Renders report overview.\n */\n\n }, {\n key: \"overviewJSX\",\n value: function overviewJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"overview\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"p\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"strong\", {\n children: [\"SequenceServer \", this.state.seqserv_version]\n }), \" using\", ' ', /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"strong\", {\n children: this.state.program_version\n }), this.state.submitted_at && \", query submitted on \".concat(this.state.submitted_at)]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"p\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"strong\", {\n children: \" Databases: \"\n }), this.state.querydb.map(function (db) {\n return db.title;\n }).join(', '), ' ', \"(\", this.state.stats.nsequences, \" sequences,\\xA0\", this.state.stats.ncharacters, \" characters)\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"p\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"strong\", {\n children: \"Parameters: \"\n }), ' ', underscore__WEBPACK_IMPORTED_MODULE_2__[\"default\"].map(this.state.params, function (val, key) {\n return key + ' ' + val;\n }).join(', ')]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"p\", {\n children: [\"Please cite:\", ' ', /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"a\", {\n href: \"https://doi.org/10.1093/molbev/msz185\",\n children: \"https://doi.org/10.1093/molbev/msz185\"\n })]\n })]\n });\n }\n /**\n * Return JSX for circos if we have at least one hit.\n */\n\n }, {\n key: \"circosJSX\",\n value: function circosJSX() {\n return this.atLeastTwoHits() ? /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_circos__WEBPACK_IMPORTED_MODULE_4__[\"default\"], {\n queries: this.state.queries,\n program: this.state.program,\n collapsed: \"true\"\n }) : /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"span\", {});\n } // Controller //\n\n /**\n * Returns true if results have been fetched.\n *\n * A holding message is shown till results are fetched.\n */\n\n }, {\n key: \"isResultAvailable\",\n value: function isResultAvailable() {\n return this.state.queries.length >= 1;\n }\n /**\n * Returns true if we have at least one hit.\n */\n\n }, {\n key: \"atLeastOneHit\",\n value: function atLeastOneHit() {\n return this.state.queries.some(function (query) {\n return query.hits.length > 0;\n });\n }\n /**\n * Does the report have at least two hits? This is used to determine\n * whether Circos should be enabled or not.\n */\n\n }, {\n key: \"atLeastTwoHits\",\n value: function atLeastTwoHits() {\n var hit_num = 0;\n return this.state.queries.some(function (query) {\n hit_num += query.hits.length;\n return hit_num > 1;\n });\n }\n /**\n * Returns true if index should be shown in the sidebar. Index is shown\n * only for 2 and 8 queries.\n */\n\n }, {\n key: \"shouldShowIndex\",\n value: function shouldShowIndex() {\n var num_queries = this.state.queries.length;\n return num_queries >= 2 && num_queries <= 12;\n }\n /**\n * Prevents folding of hits during text-selection.\n */\n\n }, {\n key: \"preventCollapseOnSelection\",\n value: function preventCollapseOnSelection() {\n $('body').on('mousedown', '.hit > .section-header > h4', function (event) {\n var $this = $(this);\n $this.on('mouseup mousemove', function handler(event) {\n if (event.type === 'mouseup') {\n // user wants to toggle\n var hitID = $this.parents('.hit').attr('id');\n $(\"div[data-parent-hit=\".concat(hitID, \"]\")).toggle();\n $this.find('i').toggleClass('fa-minus-square-o fa-plus-square-o');\n } else {\n // user wants to select\n $this.attr('data-toggle', '');\n }\n\n $this.off('mouseup mousemove', handler);\n });\n });\n }\n /* Handling the fa icon when Hit Table is collapsed */\n\n }, {\n key: \"toggleTable\",\n value: function toggleTable() {\n $('body').on('mousedown', '.resultn > .section-content > .table-hit-overview > .caption', function (event) {\n var $this = $(this);\n $this.on('mouseup mousemove', function handler(event) {\n $this.find('i').toggleClass('fa-minus-square-o fa-plus-square-o');\n $this.off('mouseup mousemove', handler);\n });\n });\n }\n /**\n * Affixes the sidebar.\n */\n\n }, {\n key: \"affixSidebar\",\n value: function affixSidebar() {\n var $sidebar = $('.sidebar');\n var sidebarOffset = $sidebar.offset();\n\n if (sidebarOffset) {\n $sidebar.affix({\n offset: {\n top: sidebarOffset.top\n }\n });\n }\n }\n /**\n * For the query in viewport, highlights corresponding entry in the index.\n */\n\n }, {\n key: \"setupScrollSpy\",\n value: function setupScrollSpy() {\n $('body').scrollspy({\n target: '.sidebar'\n });\n }\n /**\n * Event-handler when hit is selected\n * Adds glow to hit component.\n * Updates number of Fasta that can be downloaded\n */\n\n }, {\n key: \"selectHit\",\n value: function selectHit(id) {\n var checkbox = $('#' + id);\n var num_checked = $('.hit-links :checkbox:checked').length;\n\n if (!checkbox || !checkbox.val()) {\n return;\n }\n\n var $hit = $(checkbox.data('target')); // Highlight selected hit and enable 'Download FASTA/Alignment of\n // selected' links.\n\n if (checkbox.is(':checked')) {\n $hit.addClass('glow');\n $hit.next('.hsp').addClass('glow');\n $('.download-fasta-of-selected').enable();\n $('.download-alignment-of-selected').enable();\n } else {\n $hit.removeClass('glow');\n $hit.next('.hsp').removeClass('glow');\n }\n\n var $a = $('.download-fasta-of-selected');\n var $b = $('.download-alignment-of-selected');\n\n if (num_checked >= 1) {\n $a.find('.text-bold').html(num_checked);\n $b.find('.text-bold').html(num_checked);\n }\n\n if (num_checked == 0) {\n $a.addClass('disabled').find('.text-bold').html('');\n $b.addClass('disabled').find('.text-bold').html('');\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n return this.isResultAvailable() ? this.resultsJSX() : this.loadingJSX();\n }\n }]);\n\n return Report;\n}(react__WEBPACK_IMPORTED_MODULE_1__.Component);\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Report);\n\n//# sourceURL=webpack://SequenceServer/./public/js/report.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _jquery_world__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./jquery_world */ \"./public/js/jquery_world.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var _sidebar__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./sidebar */ \"./public/js/sidebar.js\");\n/* harmony import */ var _circos__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./circos */ \"./public/js/circos.js\");\n/* harmony import */ var _query__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./query */ \"./public/js/query.js\");\n/* harmony import */ var _hit__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./hit */ \"./public/js/hit.js\");\n/* harmony import */ var _hsp__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./hsp */ \"./public/js/hsp.js\");\n/* harmony import */ var _alignment_exporter__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./alignment_exporter */ \"./public/js/alignment_exporter.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\n\nfunction ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n // for custom $.tooltip function\n\n\n\n\n\n\n\n\n\n/**\n * Renders entire report.\n *\n * Composed of Query and Sidebar components.\n */\n\n\n\n\nvar Report = /*#__PURE__*/function (_Component) {\n _inherits(Report, _Component);\n\n var _super = _createSuper(Report);\n\n function Report(props) {\n var _this;\n\n _classCallCheck(this, Report);\n\n _this = _super.call(this, props); // Properties below are internal state used to render results in small\n // slices (see updateState).\n\n _this.numUpdates = 0;\n _this.nextQuery = 0;\n _this.nextHit = 0;\n _this.nextHSP = 0;\n _this.maxHSPs = 3; // max HSPs to render in a cycle\n\n _this.state = {\n search_id: '',\n seqserv_version: '',\n program: '',\n program_version: '',\n submitted_at: '',\n queries: [],\n results: [],\n querydb: [],\n params: [],\n stats: [],\n alignment_blob_url: '',\n allQueriesLoaded: false\n };\n _this.prepareAlignmentOfSelectedHits = _this.prepareAlignmentOfSelectedHits.bind(_assertThisInitialized(_this));\n _this.prepareAlignmentOfAllHits = _this.prepareAlignmentOfAllHits.bind(_assertThisInitialized(_this));\n _this.setStateFromJSON = _this.setStateFromJSON.bind(_assertThisInitialized(_this));\n return _this;\n }\n /**\n * Fetch results.\n */\n\n\n _createClass(Report, [{\n key: \"fetchResults\",\n value: function fetchResults() {\n var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];\n var component = this;\n\n function poll() {\n $.getJSON(location.pathname + '.json').complete(function (jqXHR) {\n switch (jqXHR.status) {\n case 202:\n var interval;\n\n if (intervals.length === 1) {\n interval = intervals[0];\n } else {\n interval = intervals.shift();\n }\n\n setTimeout(poll, interval);\n break;\n\n case 200:\n component.setStateFromJSON(jqXHR.responseJSON);\n break;\n\n case 404:\n case 400:\n case 500:\n component.props.showErrorModal(jqXHR.responseJSON);\n break;\n }\n });\n }\n\n poll();\n }\n /**\n * Calls setState after any required modification to responseJSON.\n */\n\n }, {\n key: \"setStateFromJSON\",\n value: function setStateFromJSON(responseJSON) {\n this.lastTimeStamp = Date.now(); // the callback prepares the download link for all alignments\n\n this.setState(responseJSON, this.prepareAlignmentOfAllHits);\n }\n /**\n * Called as soon as the page has loaded and the user sees the loading spinner.\n * We use this opportunity to setup services that make use of delegated events\n * bound to the window, document, or body.\n */\n\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.fetchResults(); // This sets up an event handler which enables users to select text from\n // hit header without collapsing the hit.\n\n this.preventCollapseOnSelection();\n this.toggleTable();\n }\n /**\n * Called for the first time after as BLAST results have been retrieved from\n * the server and added to this.state by fetchResults. Only summary overview\n * and circos would have been rendered at this point. At this stage we kick\n * start iteratively adding 1 HSP to the page every 25 milli-seconds.\n */\n\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n var _this2 = this;\n\n // Log to console how long the last update take?\n console.log((Date.now() - this.lastTimeStamp) / 1000); // Lock sidebar in its position on the first update.\n\n if (this.nextQuery == 0 && this.nextHit == 0 && this.nextHSP == 0) {\n this.affixSidebar();\n } // Queue next update if we have not rendered all results yet.\n\n\n if (this.nextQuery < this.state.queries.length) {\n // setTimeout is used to clear call stack and space out\n // the updates giving the browser a chance to respond\n // to user interactions.\n setTimeout(function () {\n return _this2.updateState();\n }, 25);\n } else {\n this.componentFinishedUpdating();\n }\n }\n /**\n * Push next slice of results to React for rendering.\n */\n\n }, {\n key: \"updateState\",\n value: function updateState() {\n var results = [];\n var numHSPsProcessed = 0;\n\n while (this.nextQuery < this.state.queries.length) {\n var query = this.state.queries[this.nextQuery]; // We may see a query multiple times during rendering because only\n // 3 hsps or are rendered in each cycle, but we want to create the\n // corresponding Query component only the first time we see it.\n\n if (this.nextHit == 0 && this.nextHSP == 0) {\n results.push( /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(_query__WEBPACK_IMPORTED_MODULE_5__.ReportQuery, {\n query: query,\n program: this.state.program,\n querydb: this.state.querydb,\n showQueryCrumbs: this.state.queries.length > 1,\n non_parse_seqids: this.state.non_parse_seqids,\n imported_xml: this.state.imported_xml,\n veryBig: this.state.veryBig\n }, 'Query_' + query.number));\n }\n\n while (this.nextHit < query.hits.length) {\n var hit = query.hits[this.nextHit]; // We may see a hit multiple times during rendering because only\n // 10 hsps are rendered in each cycle, but we want to create the\n // corresponding Hit component only the first time we see it.\n\n if (this.nextHSP == 0) {\n results.push( /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(_hit__WEBPACK_IMPORTED_MODULE_6__[\"default\"], _objectSpread({\n query: query,\n hit: hit,\n algorithm: this.state.program,\n querydb: this.state.querydb,\n selectHit: this.selectHit,\n imported_xml: this.state.imported_xml,\n non_parse_seqids: this.state.non_parse_seqids,\n showQueryCrumbs: this.state.queries.length > 1,\n showHitCrumbs: query.hits.length > 1,\n veryBig: this.state.veryBig,\n onChange: this.prepareAlignmentOfSelectedHits\n }, this.props), 'Query_' + query.number + '_Hit_' + hit.number));\n }\n\n while (this.nextHSP < hit.hsps.length) {\n // Get nextHSP and increment the counter.\n var hsp = hit.hsps[this.nextHSP++];\n results.push( /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(_hsp__WEBPACK_IMPORTED_MODULE_7__[\"default\"], _objectSpread({\n query: query,\n hit: hit,\n hsp: hsp,\n algorithm: this.state.program,\n showHSPNumbers: hit.hsps.length > 1\n }, this.props), 'Query_' + query.number + '_Hit_' + hit.number + '_HSP_' + hsp.number));\n numHSPsProcessed++;\n if (numHSPsProcessed == this.maxHSPs) break;\n } // Are we here because we have iterated over all hsps of a hit,\n // or because of the break clause in the inner loop?\n\n\n if (this.nextHSP == hit.hsps.length) {\n this.nextHit = this.nextHit + 1;\n this.nextHSP = 0;\n }\n\n if (numHSPsProcessed == this.maxHSPs) break;\n } // Are we here because we have iterated over all hits of a query,\n // or because of the break clause in the inner loop?\n\n\n if (this.nextHit == query.hits.length) {\n this.nextQuery = this.nextQuery + 1;\n this.nextHit = 0;\n }\n\n if (numHSPsProcessed == this.maxHSPs) break;\n } // Push the components to react for rendering.\n\n\n this.numUpdates++;\n this.lastTimeStamp = Date.now();\n this.setState({\n results: this.state.results.concat(results),\n veryBig: this.numUpdates >= 250\n });\n }\n /**\n * Called after all results have been rendered.\n */\n\n }, {\n key: \"componentFinishedUpdating\",\n value: function componentFinishedUpdating() {\n if (this.state.allQueriesLoaded) return;\n this.shouldShowIndex() && this.setupScrollSpy();\n this.setState({\n allQueriesLoaded: true\n });\n }\n /**\n * Returns loading message\n */\n\n }, {\n key: \"loadingJSX\",\n value: function loadingJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"div\", {\n className: \"row\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"div\", {\n className: \"col-md-6 col-md-offset-3 text-center\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"h1\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"i\", {\n className: \"fa fa-cog fa-spin\"\n }), \"\\xA0 BLAST-ing\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"p\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"br\", {}), \"This can take some time depending on the size of your query and database(s). The page will update automatically when BLAST is done.\", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"br\", {}), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"br\", {}), \"You can bookmark the page and come back to it later or share the link with someone.\"]\n })]\n })\n });\n }\n /**\n * Return results JSX.\n */\n\n }, {\n key: \"resultsJSX\",\n value: function resultsJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"div\", {\n className: \"row\",\n id: \"results\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"div\", {\n className: \"col-md-3 hidden-sm hidden-xs\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(_sidebar__WEBPACK_IMPORTED_MODULE_3__[\"default\"], {\n data: this.state,\n atLeastOneHit: this.atLeastOneHit(),\n shouldShowIndex: this.shouldShowIndex(),\n allQueriesLoaded: this.state.allQueriesLoaded\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"div\", {\n className: \"col-md-9\",\n children: [this.overviewJSX(), this.circosJSX(), this.state.results]\n })]\n });\n }\n /**\n * Renders report overview.\n */\n\n }, {\n key: \"overviewJSX\",\n value: function overviewJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"div\", {\n className: \"overview\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"p\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"strong\", {\n children: [\"SequenceServer \", this.state.seqserv_version]\n }), \" using\", ' ', /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"strong\", {\n children: this.state.program_version\n }), this.state.submitted_at && \", query submitted on \".concat(this.state.submitted_at)]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"p\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"strong\", {\n children: \" Databases: \"\n }), this.state.querydb.map(function (db) {\n return db.title;\n }).join(', '), ' ', \"(\", this.state.stats.nsequences, \" sequences,\\xA0\", this.state.stats.ncharacters, \" characters)\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"p\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"strong\", {\n children: \"Parameters: \"\n }), ' ', underscore__WEBPACK_IMPORTED_MODULE_2__[\"default\"].map(this.state.params, function (val, key) {\n return key + ' ' + val;\n }).join(', ')]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsxs)(\"p\", {\n children: [\"Please cite:\", ' ', /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"a\", {\n href: \"https://doi.org/10.1093/molbev/msz185\",\n children: \"https://doi.org/10.1093/molbev/msz185\"\n })]\n })]\n });\n }\n /**\n * Return JSX for circos if we have at least one hit.\n */\n\n }, {\n key: \"circosJSX\",\n value: function circosJSX() {\n return this.atLeastTwoHits() ? /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(_circos__WEBPACK_IMPORTED_MODULE_4__[\"default\"], {\n queries: this.state.queries,\n program: this.state.program,\n collapsed: \"true\"\n }) : /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_9__.jsx)(\"span\", {});\n } // Controller //\n\n /**\n * Returns true if results have been fetched.\n *\n * A holding message is shown till results are fetched.\n */\n\n }, {\n key: \"isResultAvailable\",\n value: function isResultAvailable() {\n return this.state.queries.length >= 1;\n }\n /**\n * Returns true if we have at least one hit.\n */\n\n }, {\n key: \"atLeastOneHit\",\n value: function atLeastOneHit() {\n return this.state.queries.some(function (query) {\n return query.hits.length > 0;\n });\n }\n /**\n * Does the report have at least two hits? This is used to determine\n * whether Circos should be enabled or not.\n */\n\n }, {\n key: \"atLeastTwoHits\",\n value: function atLeastTwoHits() {\n var hit_num = 0;\n return this.state.queries.some(function (query) {\n hit_num += query.hits.length;\n return hit_num > 1;\n });\n }\n /**\n * Returns true if index should be shown in the sidebar. Index is shown\n * only for 2 and 8 queries.\n */\n\n }, {\n key: \"shouldShowIndex\",\n value: function shouldShowIndex() {\n var num_queries = this.state.queries.length;\n return num_queries >= 2 && num_queries <= 12;\n }\n /**\n * Prevents folding of hits during text-selection.\n */\n\n }, {\n key: \"preventCollapseOnSelection\",\n value: function preventCollapseOnSelection() {\n $('body').on('mousedown', '.hit > .section-header > h4', function (event) {\n var $this = $(this);\n $this.on('mouseup mousemove', function handler(event) {\n if (event.type === 'mouseup') {\n // user wants to toggle\n var hitID = $this.parents('.hit').attr('id');\n $(\"div[data-parent-hit=\".concat(hitID, \"]\")).toggle();\n $this.find('i').toggleClass('fa-minus-square-o fa-plus-square-o');\n } else {\n // user wants to select\n $this.attr('data-toggle', '');\n }\n\n $this.off('mouseup mousemove', handler);\n });\n });\n }\n /* Handling the fa icon when Hit Table is collapsed */\n\n }, {\n key: \"toggleTable\",\n value: function toggleTable() {\n $('body').on('mousedown', '.resultn > .section-content > .table-hit-overview > .caption', function (event) {\n var $this = $(this);\n $this.on('mouseup mousemove', function handler(event) {\n $this.find('i').toggleClass('fa-minus-square-o fa-plus-square-o');\n $this.off('mouseup mousemove', handler);\n });\n });\n }\n /**\n * Affixes the sidebar.\n */\n\n }, {\n key: \"affixSidebar\",\n value: function affixSidebar() {\n var $sidebar = $('.sidebar');\n var sidebarOffset = $sidebar.offset();\n\n if (sidebarOffset) {\n $sidebar.affix({\n offset: {\n top: sidebarOffset.top\n }\n });\n }\n }\n /**\n * For the query in viewport, highlights corresponding entry in the index.\n */\n\n }, {\n key: \"setupScrollSpy\",\n value: function setupScrollSpy() {\n $('body').scrollspy({\n target: '.sidebar'\n });\n }\n /**\n * Event-handler when hit is selected\n * Adds glow to hit component.\n * Updates number of Fasta that can be downloaded\n */\n\n }, {\n key: \"selectHit\",\n value: function selectHit(id) {\n var checkbox = $('#' + id);\n var num_checked = $('.hit-links :checkbox:checked').length;\n\n if (!checkbox || !checkbox.val()) {\n return;\n }\n\n var $hit = $(checkbox.data('target')); // Highlight selected hit and enable 'Download FASTA/Alignment of\n // selected' links.\n\n if (checkbox.is(':checked')) {\n $hit.addClass('glow');\n $hit.next('.hsp').addClass('glow');\n $('.download-fasta-of-selected').enable();\n $('.download-alignment-of-selected').enable();\n } else {\n $hit.removeClass('glow');\n $hit.next('.hsp').removeClass('glow');\n }\n\n var $a = $('.download-fasta-of-selected');\n var $b = $('.download-alignment-of-selected');\n\n if (num_checked >= 1) {\n $a.find('.text-bold').html(num_checked);\n $b.find('.text-bold').html(num_checked);\n }\n\n if (num_checked == 0) {\n $a.addClass('disabled').find('.text-bold').html('');\n $b.addClass('disabled').find('.text-bold').html('');\n }\n }\n }, {\n key: \"prepareAlignmentOfSelectedHits\",\n value: function prepareAlignmentOfSelectedHits() {\n var sequence_ids = $('.hit-links :checkbox:checked').map(function () {\n return this.value;\n }).get();\n\n if (!sequence_ids.length) {\n // remove attributes from link if sequence_ids array is empty\n $('.download-alignment-of-selected').attr('href', '#').removeAttr('download');\n return;\n }\n\n if (this.state.alignment_blob_url) {\n // always revoke existing url if any because this method will always create a new url\n window.URL.revokeObjectURL(this.state.alignment_blob_url);\n }\n\n var hsps_arr = [];\n var aln_exporter = new _alignment_exporter__WEBPACK_IMPORTED_MODULE_8__[\"default\"]();\n\n underscore__WEBPACK_IMPORTED_MODULE_2__[\"default\"].each(this.state.queries, underscore__WEBPACK_IMPORTED_MODULE_2__[\"default\"].bind(function (query) {\n underscore__WEBPACK_IMPORTED_MODULE_2__[\"default\"].each(query.hits, function (hit) {\n if (underscore__WEBPACK_IMPORTED_MODULE_2__[\"default\"].indexOf(sequence_ids, hit.id) != -1) {\n underscore__WEBPACK_IMPORTED_MODULE_2__[\"default\"].each(hit.hsps, function (hsp) {\n hsp.hit_id = hit.id;\n hsp.query_id = query.id;\n hsps_arr.push(hsp);\n });\n }\n });\n }, this));\n\n var filename = 'alignment-' + sequence_ids.length + '_hits';\n var blob_url = aln_exporter.prepare_alignments_for_export(hsps_arr, filename); // set required download attributes for link\n\n $('.download-alignment-of-selected').attr('href', blob_url).attr('download', filename); // track new url for future removal\n\n this.setState({\n alignment_blob_url: blob_url\n });\n }\n }, {\n key: \"prepareAlignmentOfAllHits\",\n value: function prepareAlignmentOfAllHits() {\n // Get number of hits and array of all hsps.\n var num_hits = 0;\n var hsps_arr = [];\n\n if (!this.state.queries.length) {\n return;\n }\n\n this.state.queries.forEach(function (query) {\n return query.hits.forEach(function (hit) {\n num_hits++;\n hit.hsps.forEach(function (hsp) {\n hsp.query_id = query.id;\n hsp.hit_id = hit.id;\n hsps_arr.push(hsp);\n });\n });\n });\n var aln_exporter = new _alignment_exporter__WEBPACK_IMPORTED_MODULE_8__[\"default\"]();\n var file_name = \"alignment-\".concat(num_hits, \"_hits\");\n var blob_url = aln_exporter.prepare_alignments_for_export(hsps_arr, file_name);\n $('.download-alignment-of-all').attr('href', blob_url).attr('download', file_name);\n return false;\n }\n }, {\n key: \"render\",\n value: function render() {\n return this.isResultAvailable() ? this.resultsJSX() : this.loadingJSX();\n }\n }]);\n\n return Report;\n}(react__WEBPACK_IMPORTED_MODULE_1__.Component);\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Report);\n\n//# sourceURL=webpack://SequenceServer/./public/js/report.js?"); /***/ }), @@ -203,7 +203,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ _default)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var _download_fasta__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./download_fasta */ \"./public/js/download_fasta.js\");\n/* harmony import */ var _alignment_exporter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./alignment_exporter */ \"./public/js/alignment_exporter.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n // to download textual alignment\n\n/**\n * checks whether code is being run by jest\n */\n// eslint-disable-next-line no-undef\n\n\n\n\nvar isTestMode = function isTestMode() {\n return ({}).JEST_WORKER_ID !== undefined || \"development\" === 'test';\n};\n/**\n * Renders links for downloading hit information in different formats.\n * Renders links for navigating to each query.\n */\n\n\nvar _default = /*#__PURE__*/function (_Component) {\n _inherits(_default, _Component);\n\n var _super = _createSuper(_default);\n\n function _default(props) {\n var _this;\n\n _classCallCheck(this, _default);\n\n _this = _super.call(this, props);\n _this.downloadFastaOfAll = _this.downloadFastaOfAll.bind(_assertThisInitialized(_this));\n _this.downloadFastaOfSelected = _this.downloadFastaOfSelected.bind(_assertThisInitialized(_this));\n _this.downloadAlignmentOfAll = _this.downloadAlignmentOfAll.bind(_assertThisInitialized(_this));\n _this.downloadAlignmentOfSelected = _this.downloadAlignmentOfSelected.bind(_assertThisInitialized(_this));\n _this.topPanelJSX = _this.topPanelJSX.bind(_assertThisInitialized(_this));\n _this.summaryString = _this.summaryString.bind(_assertThisInitialized(_this));\n _this.indexJSX = _this.indexJSX.bind(_assertThisInitialized(_this));\n _this.downloadsPanelJSX = _this.downloadsPanelJSX.bind(_assertThisInitialized(_this));\n _this.handleQueryIndexChange = _this.handleQueryIndexChange.bind(_assertThisInitialized(_this));\n _this.isElementInViewPort = _this.isElementInViewPort.bind(_assertThisInitialized(_this));\n _this.setVisibleQueryIndex = _this.setVisibleQueryIndex.bind(_assertThisInitialized(_this));\n _this.debounceScrolling = _this.debounceScrolling.bind(_assertThisInitialized(_this));\n _this.scrollListener = _this.scrollListener.bind(_assertThisInitialized(_this));\n _this.copyURL = _this.copyURL.bind(_assertThisInitialized(_this));\n _this.mailtoLink = _this.mailtoLink.bind(_assertThisInitialized(_this));\n _this.sharingPanelJSX = _this.sharingPanelJSX.bind(_assertThisInitialized(_this));\n _this.timeout = null;\n _this.queryElems = [];\n _this.state = {\n queryIndex: 1\n };\n return _this;\n }\n\n _createClass(_default, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n /**\n * Fixes tooltips in the sidebar, allows tooltip display on click\n */\n $(function () {\n $('.sidebar [data-toggle=\"tooltip\"]').tooltip({\n placement: 'right'\n });\n $('#copyURL').tooltip({\n title: 'Copied!',\n trigger: 'click',\n placement: 'right',\n delay: 0\n });\n }); //keep track of the current queryIndex so it doesn't get lost on page reload\n\n var urlMatch = window.location.href.match(/#Query_(\\d+)/);\n\n if (urlMatch && urlMatch.length > 1) {\n var queryNumber = +urlMatch[1];\n var index = this.props.data.queries.findIndex(function (query) {\n return query.number === queryNumber;\n });\n this.setState({\n queryIndex: index + 1\n });\n }\n\n window.addEventListener('scroll', this.scrollListener);\n $('a[href^=\"#Query_\"]').on('click', this.animateAnchorElements);\n }\n }, {\n key: \"componentWillUnmount\",\n value: function componentWillUnmount() {\n window.removeEventListener('scroll', this.scrollListener);\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate(prevProps) {\n if (this.props.allQueriesLoaded && !prevProps.allQueriesLoaded) {\n /**\n * storing all query elements in this variable once they all become available so we don't have to fetch them all over again\n */\n this.queryElems = Array.from(document.querySelectorAll('.resultn'));\n }\n }\n /**\n * to avoid unnecessary computations, we debounce the scroll listener so it only fires after user has stopped scrolling for some milliseconds\n */\n\n }, {\n key: \"scrollListener\",\n value: function scrollListener() {\n this.debounceScrolling(this.setVisibleQueryIndex, 500);\n }\n }, {\n key: \"debounceScrolling\",\n value: function debounceScrolling(callback, timer) {\n if (this.timeout) {\n clearTimeout(this.timeout);\n }\n\n this.timeout = setTimeout(callback, timer);\n }\n /**\n * This method makes the page aware of what query is visible so that clicking previous / next button at any point \n * navigates to the proper query\n */\n\n }, {\n key: \"setVisibleQueryIndex\",\n value: function setVisibleQueryIndex() {\n var queryElems = this.queryElems.length ? this.queryElems : Array.from(document.querySelectorAll('.resultn'));\n var hits = Array.from(document.querySelectorAll('.hit[id^=Query_]')); // get the first visible element and marks it as the current query\n\n var topmostEl = queryElems.find(this.isElementInViewPort) || hits.find(this.isElementInViewPort);\n\n if (topmostEl) {\n var queryIndex = Number(topmostEl.id.match(/Query_(\\d+)/)[1]);\n var hash = \"#Query_\".concat(queryIndex); // if we can guarantee that the browser can handle change in url hash without the page jumping,\n // then we update the url hash after scroll. else, hash is only updated on click of next or prev button\n\n if (window.history.pushState) {\n window.history.pushState(null, null, hash);\n }\n\n this.setState({\n queryIndex: queryIndex\n });\n }\n }\n }, {\n key: \"animateAnchorElements\",\n value: function animateAnchorElements(e) {\n // allow normal behavior in test mode to prevent warnings or errors from jquery\n if (isTestMode()) return;\n e.preventDefault();\n $('html, body').animate({\n scrollTop: $(this.hash).offset().top\n }, 300);\n\n if (window.history.pushState) {\n window.history.pushState(null, null, this.hash);\n } else {\n window.location.hash = this.hash;\n }\n }\n }, {\n key: \"isElementInViewPort\",\n value: function isElementInViewPort(elem) {\n var _elem$getBoundingClie = elem.getBoundingClientRect(),\n top = _elem$getBoundingClie.top,\n left = _elem$getBoundingClie.left,\n right = _elem$getBoundingClie.right,\n bottom = _elem$getBoundingClie.bottom;\n\n return top >= 0 && left >= 0 && bottom <= (window.innerHeight || document.documentElement.clientHeight) && right <= (window.innerWidth || document.documentElement.clientWidth);\n }\n /**\n * Clear sessionStorage - useful to initiate a new search in the same tab.\n * Passing sessionStorage.clear directly as onclick callback didn't work\n * (on macOS Chrome).\n */\n\n }, {\n key: \"clearSession\",\n value: function clearSession() {\n sessionStorage.clear();\n }\n /**\n * \n * handle next and previous query button clicks\n */\n\n }, {\n key: \"handleQueryIndexChange\",\n value: function handleQueryIndexChange(nextQuery) {\n if (nextQuery < 1 || nextQuery > this.props.data.queries.length) return;\n var anchorEl = document.createElement('a'); //indexing at [nextQuery - 1] because array is 0-indexed\n\n anchorEl.setAttribute('href', '#Query_' + this.props.data.queries[nextQuery - 1].number);\n anchorEl.setAttribute('hidden', true);\n document.body.appendChild(anchorEl); // add smooth scrolling animation with jquery\n\n $(anchorEl).on('click', this.animateAnchorElements);\n anchorEl.click();\n document.body.removeChild(anchorEl);\n this.setState({\n queryIndex: nextQuery\n });\n }\n /**\n * Event-handler for downloading fasta of all hits.\n */\n\n }, {\n key: \"downloadFastaOfAll\",\n value: function downloadFastaOfAll() {\n var sequence_ids = [];\n this.props.data.queries.forEach(function (query) {\n return query.hits.forEach(function (hit) {\n return sequence_ids.push(hit.id);\n });\n });\n var database_ids = this.props.data.querydb.map(function (querydb) {\n return querydb.id;\n });\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(sequence_ids, database_ids);\n return false;\n }\n /**\n * Handles downloading fasta of selected hits.\n */\n\n }, {\n key: \"downloadFastaOfSelected\",\n value: function downloadFastaOfSelected() {\n var sequence_ids = $('.hit-links :checkbox:checked').map(function () {\n return this.value;\n }).get();\n\n var database_ids = underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.data.querydb, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].iteratee('id'));\n\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(sequence_ids, database_ids);\n return false;\n }\n }, {\n key: \"downloadAlignmentOfAll\",\n value: function downloadAlignmentOfAll() {\n // Get number of hits and array of all hsps.\n var num_hits = 0;\n var hsps_arr = [];\n this.props.data.queries.forEach(function (query) {\n return query.hits.forEach(function (hit) {\n num_hits++;\n hit.hsps.forEach(function (hsp) {\n hsp.query_id = query.id;\n hsp.hit_id = hit.id;\n hsps_arr.push(hsp);\n });\n });\n });\n var aln_exporter = new _alignment_exporter__WEBPACK_IMPORTED_MODULE_3__[\"default\"]();\n var file_name = \"alignment-\".concat(num_hits, \"_hits\");\n aln_exporter.export_alignments(hsps_arr, file_name);\n return false;\n }\n }, {\n key: \"downloadAlignmentOfSelected\",\n value: function downloadAlignmentOfSelected() {\n var sequence_ids = $('.hit-links :checkbox:checked').map(function () {\n return this.value;\n }).get();\n var hsps_arr = [];\n var aln_exporter = new _alignment_exporter__WEBPACK_IMPORTED_MODULE_3__[\"default\"]();\n console.log('check ' + sequence_ids.toString());\n\n underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].each(this.props.data.queries, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].bind(function (query) {\n underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].each(query.hits, function (hit) {\n if (underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].indexOf(sequence_ids, hit.id) != -1) {\n underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].each(hit.hsps, function (hsp) {\n hsp.hit_id = hit.id;\n hsp.query_id = query.id;\n hsps_arr.push(hsp);\n });\n }\n });\n }, this));\n\n aln_exporter.export_alignments(hsps_arr, 'alignment-' + sequence_ids.length + '_hits');\n return false;\n }\n /**\n * Handles copying the URL into the user's clipboard. Modified from: https://stackoverflow.com/a/49618964/18117380\n * Hides the 'Copied!' tooltip after 3 seconds\n */\n\n }, {\n key: \"copyURL\",\n value: function copyURL() {\n var element = document.createElement('input');\n var url = window.location.href;\n document.body.appendChild(element);\n element.value = url;\n element.select();\n document.execCommand('copy');\n document.body.removeChild(element);\n setTimeout(function () {\n $('#copyURL')._tooltip('destroy');\n }, 3000);\n }\n /**\n * Returns a mailto message with at most 15 databases used\n */\n\n }, {\n key: \"mailtoLink\",\n value: function mailtoLink() {\n // Iterates over the databases used and appends the first 15 to an array with string formatting\n var dbsArr = [];\n var i = 0;\n\n while (this.props.data.querydb[i] && i < 15) {\n dbsArr.push(' ' + this.props.data.querydb[i].title);\n i += 1;\n } // returns the mailto message\n\n\n var mailto = \"mailto:?subject=SequenceServer \".concat(this.props.data.program.toUpperCase(), \" analysis results &body=Hello,\\n\\n Here is a link to my recent \").concat(this.props.data.program.toUpperCase(), \" analysis of \").concat(this.props.data.queries.length, \" sequences.\\n \\xA0\\xA0\\xA0\\xA0\").concat(window.location.href, \"\\n\\n The following databases were used (up to 15 are shown):\\n \\xA0\\xA0\\xA0\\xA0\").concat(dbsArr, \"\\n\\n The link will work if you have access to that particular SequenceServer instance.\\n\\n Thank you for using SequenceServer, and please remember to cite our paper.\\n\\n Best regards,\\n\\n https://sequenceserver.com\");\n var message = encodeURI(mailto).replace(/(%20){2,}/g, '');\n return message;\n }\n }, {\n key: \"topPanelJSX\",\n value: function topPanelJSX() {\n var path = location.pathname.split('/'); // Get job id.\n\n var job_id = path.pop(); // Deriving rootURL this way is required for subURI deployments\n // - we cannot just send to '/'.\n\n var rootURL = path.join('/');\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n className: \"sidebar-top-panel\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"h4\", {\n children: this.summaryString()\n })\n }), this.props.data.queries.length > 12 && this.queryIndexButtons(), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n href: \"\".concat(rootURL, \"/?job_id=\").concat(job_id),\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"i\", {\n className: \"fa fa-pencil\"\n }), \" Edit search\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n href: \"\".concat(rootURL, \"/\"),\n onClick: this.clearSession,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"i\", {\n className: \"fa fa-file-o\"\n }), \" New search\"]\n })]\n }), this.props.shouldShowIndex && this.indexJSX()]\n });\n }\n }, {\n key: \"summaryString\",\n value: function summaryString() {\n var program = this.props.data.program;\n var numqueries = this.props.data.queries.length;\n var numquerydb = this.props.data.querydb.length;\n return program.toUpperCase() + ': ' + numqueries + ' ' + (numqueries > 1 ? 'queries' : 'query') + ', ' + numquerydb + ' ' + (numquerydb > 1 ? 'databases' : 'database');\n }\n }, {\n key: \"queryIndexButtons\",\n value: function queryIndexButtons() {\n var _this2 = this;\n\n var buttonStyle = {\n outline: 'none',\n border: 'none',\n background: 'none'\n };\n var buttonClasses = 'btn-link nowrap-ellipsis hover-bold';\n\n var handlePreviousBtnClick = function handlePreviousBtnClick() {\n return _this2.handleQueryIndexChange(_this2.state.queryIndex - 1);\n };\n\n var handleNextBtnClick = function handleNextBtnClick() {\n return _this2.handleQueryIndexChange(_this2.state.queryIndex + 1);\n }; // eslint-disable-next-line no-unused-vars\n\n\n var NavButton = function NavButton(_ref) {\n var text = _ref.text,\n onClick = _ref.onClick;\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"button\", {\n className: buttonClasses,\n onClick: onClick,\n style: buttonStyle,\n children: text\n });\n };\n\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n style: {\n display: 'flex',\n width: '100%',\n margin: '7px 0'\n },\n children: [this.state.queryIndex > 1 && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(NavButton, {\n text: \"Previous Query\",\n onClick: handlePreviousBtnClick\n }), this.state.queryIndex > 1 && this.state.queryIndex < this.props.data.queries.length && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }), this.state.queryIndex < this.props.data.queries.length && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(NavButton, {\n onClick: handleNextBtnClick,\n text: \"Next Query\"\n })]\n });\n }\n }, {\n key: \"indexJSX\",\n value: function indexJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"ul\", {\n className: \"nav hover-reset active-bold\",\n children: [\" \", underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.data.queries, function (query) {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n className: \"btn-link nowrap-ellipsis hover-bold\",\n title: 'Query= ' + query.id + ' ' + query.title,\n href: '#Query_' + query.number,\n children: 'Query= ' + query.id\n })\n }, 'Side_bar_' + query.id);\n })]\n });\n }\n }, {\n key: \"downloadsPanelJSX\",\n value: function downloadsPanelJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n className: \"downloads\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"h4\", {\n children: \"Download FASTA, XML, TSV\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"ul\", {\n className: \"nav\",\n children: [!(this.props.data.imported_xml || this.props.data.non_parse_seqids) && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n href: \"#\",\n className: \"btn-link download-fasta-of-all \".concat(!this.props.atLeastOneHit && 'disabled'),\n onClick: this.downloadFastaOfAll,\n children: \"FASTA of all hits\"\n })\n }), !(this.props.data.imported_xml || this.props.data.non_parse_seqids) && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n href: \"#\",\n className: \"btn-link download-fasta-of-selected disabled\",\n onClick: this.downloadFastaOfSelected,\n children: [\"FASTA of \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"span\", {\n className: \"text-bold\"\n }), \" selected hit(s)\"]\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n href: \"#\",\n className: \"btn-link download-alignment-of-all \".concat(!this.props.atLeastOneHit && 'disabled'),\n onClick: this.downloadAlignmentOfAll,\n children: \"Alignment of all hits\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n href: \"#\",\n className: \"btn-link download-alignment-of-selected disabled\",\n onClick: this.downloadAlignmentOfSelected,\n children: [\"Alignment of \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"span\", {\n className: \"text-bold\"\n }), \" selected hit(s)\"]\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"15 columns: query and subject ID; scientific name, alignment length, mismatches, gaps, identity, start and end coordinates, e value, bitscore, query coverage per subject and per HSP.\",\n href: 'download/' + this.props.data.search_id + '.std_tsv',\n children: \"Standard tabular report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"44 columns: query and subject ID, GI, accessions, and length; alignment details; taxonomy details of subject sequence(s) and query coverage per subject and per HSP.\",\n href: 'download/' + this.props.data.search_id + '.full_tsv',\n children: \"Full tabular report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"Results in XML format.\",\n href: 'download/' + this.props.data.search_id + '.xml',\n children: \"Full XML report\"\n })\n })]\n })]\n });\n }\n }, {\n key: \"sharingPanelJSX\",\n value: function sharingPanelJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n className: \"sharing-panel\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"h4\", {\n children: \"Share results\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"ul\", {\n className: \"nav\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n id: \"copyURL\",\n className: \"btn-link copy-URL cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n onClick: this.copyURL,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"i\", {\n className: \"fa fa-copy\"\n }), \" Copy URL to clipboard\"]\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n id: \"sendEmail\",\n className: \"btn-link email-URL cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n title: \"Send by email\",\n href: this.mailtoLink(),\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"i\", {\n className: \"fa fa-envelope\"\n }), \" Send by email\"]\n })\n })]\n })]\n });\n }\n }, {\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n className: \"sidebar\",\n children: [this.topPanelJSX(), this.downloadsPanelJSX(), this.sharingPanelJSX()]\n });\n }\n }]);\n\n return _default;\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\n\n\n\n//# sourceURL=webpack://SequenceServer/./public/js/sidebar.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ _default)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var _download_fasta__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./download_fasta */ \"./public/js/download_fasta.js\");\n/* harmony import */ var _alignment_exporter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./alignment_exporter */ \"./public/js/alignment_exporter.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n // to download textual alignment\n\n/**\n * checks whether code is being run by jest\n */\n// eslint-disable-next-line no-undef\n\n\n\n\nvar isTestMode = function isTestMode() {\n return ({}).JEST_WORKER_ID !== undefined || \"development\" === 'test';\n};\n/**\n * Renders links for downloading hit information in different formats.\n * Renders links for navigating to each query.\n */\n\n\nvar _default = /*#__PURE__*/function (_Component) {\n _inherits(_default, _Component);\n\n var _super = _createSuper(_default);\n\n function _default(props) {\n var _this;\n\n _classCallCheck(this, _default);\n\n _this = _super.call(this, props);\n _this.downloadFastaOfAll = _this.downloadFastaOfAll.bind(_assertThisInitialized(_this));\n _this.downloadFastaOfSelected = _this.downloadFastaOfSelected.bind(_assertThisInitialized(_this));\n _this.topPanelJSX = _this.topPanelJSX.bind(_assertThisInitialized(_this));\n _this.summaryString = _this.summaryString.bind(_assertThisInitialized(_this));\n _this.indexJSX = _this.indexJSX.bind(_assertThisInitialized(_this));\n _this.downloadsPanelJSX = _this.downloadsPanelJSX.bind(_assertThisInitialized(_this));\n _this.handleQueryIndexChange = _this.handleQueryIndexChange.bind(_assertThisInitialized(_this));\n _this.isElementInViewPort = _this.isElementInViewPort.bind(_assertThisInitialized(_this));\n _this.setVisibleQueryIndex = _this.setVisibleQueryIndex.bind(_assertThisInitialized(_this));\n _this.debounceScrolling = _this.debounceScrolling.bind(_assertThisInitialized(_this));\n _this.scrollListener = _this.scrollListener.bind(_assertThisInitialized(_this));\n _this.copyURL = _this.copyURL.bind(_assertThisInitialized(_this));\n _this.mailtoLink = _this.mailtoLink.bind(_assertThisInitialized(_this));\n _this.sharingPanelJSX = _this.sharingPanelJSX.bind(_assertThisInitialized(_this));\n _this.timeout = null;\n _this.queryElems = [];\n _this.state = {\n queryIndex: 1\n };\n return _this;\n }\n\n _createClass(_default, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n /**\n * Fixes tooltips in the sidebar, allows tooltip display on click\n */\n $(function () {\n $('.sidebar [data-toggle=\"tooltip\"]').tooltip({\n placement: 'right'\n });\n $('#copyURL').tooltip({\n title: 'Copied!',\n trigger: 'click',\n placement: 'right',\n delay: 0\n });\n }); //keep track of the current queryIndex so it doesn't get lost on page reload\n\n var urlMatch = window.location.href.match(/#Query_(\\d+)/);\n\n if (urlMatch && urlMatch.length > 1) {\n var queryNumber = +urlMatch[1];\n var index = this.props.data.queries.findIndex(function (query) {\n return query.number === queryNumber;\n });\n this.setState({\n queryIndex: index + 1\n });\n }\n\n window.addEventListener('scroll', this.scrollListener);\n $('a[href^=\"#Query_\"]').on('click', this.animateAnchorElements);\n }\n }, {\n key: \"componentWillUnmount\",\n value: function componentWillUnmount() {\n window.removeEventListener('scroll', this.scrollListener);\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate(prevProps) {\n if (this.props.allQueriesLoaded && !prevProps.allQueriesLoaded) {\n /**\n * storing all query elements in this variable once they all become available so we don't have to fetch them all over again\n */\n this.queryElems = Array.from(document.querySelectorAll('.resultn'));\n }\n }\n /**\n * to avoid unnecessary computations, we debounce the scroll listener so it only fires after user has stopped scrolling for some milliseconds\n */\n\n }, {\n key: \"scrollListener\",\n value: function scrollListener() {\n this.debounceScrolling(this.setVisibleQueryIndex, 500);\n }\n }, {\n key: \"debounceScrolling\",\n value: function debounceScrolling(callback, timer) {\n if (this.timeout) {\n clearTimeout(this.timeout);\n }\n\n this.timeout = setTimeout(callback, timer);\n }\n /**\n * This method makes the page aware of what query is visible so that clicking previous / next button at any point \n * navigates to the proper query\n */\n\n }, {\n key: \"setVisibleQueryIndex\",\n value: function setVisibleQueryIndex() {\n var queryElems = this.queryElems.length ? this.queryElems : Array.from(document.querySelectorAll('.resultn'));\n var hits = Array.from(document.querySelectorAll('.hit[id^=Query_]')); // get the first visible element and marks it as the current query\n\n var topmostEl = queryElems.find(this.isElementInViewPort) || hits.find(this.isElementInViewPort);\n\n if (topmostEl) {\n var queryIndex = Number(topmostEl.id.match(/Query_(\\d+)/)[1]);\n var hash = \"#Query_\".concat(queryIndex); // if we can guarantee that the browser can handle change in url hash without the page jumping,\n // then we update the url hash after scroll. else, hash is only updated on click of next or prev button\n\n if (window.history.pushState) {\n window.history.pushState(null, null, hash);\n }\n\n this.setState({\n queryIndex: queryIndex\n });\n }\n }\n }, {\n key: \"animateAnchorElements\",\n value: function animateAnchorElements(e) {\n // allow normal behavior in test mode to prevent warnings or errors from jquery\n if (isTestMode()) return;\n e.preventDefault();\n $('html, body').animate({\n scrollTop: $(this.hash).offset().top\n }, 300);\n\n if (window.history.pushState) {\n window.history.pushState(null, null, this.hash);\n } else {\n window.location.hash = this.hash;\n }\n }\n }, {\n key: \"isElementInViewPort\",\n value: function isElementInViewPort(elem) {\n var _elem$getBoundingClie = elem.getBoundingClientRect(),\n top = _elem$getBoundingClie.top,\n left = _elem$getBoundingClie.left,\n right = _elem$getBoundingClie.right,\n bottom = _elem$getBoundingClie.bottom;\n\n return top >= 0 && left >= 0 && bottom <= (window.innerHeight || document.documentElement.clientHeight) && right <= (window.innerWidth || document.documentElement.clientWidth);\n }\n /**\n * Clear sessionStorage - useful to initiate a new search in the same tab.\n * Passing sessionStorage.clear directly as onclick callback didn't work\n * (on macOS Chrome).\n */\n\n }, {\n key: \"clearSession\",\n value: function clearSession() {\n sessionStorage.clear();\n }\n /**\n * \n * handle next and previous query button clicks\n */\n\n }, {\n key: \"handleQueryIndexChange\",\n value: function handleQueryIndexChange(nextQuery) {\n if (nextQuery < 1 || nextQuery > this.props.data.queries.length) return;\n var anchorEl = document.createElement('a'); //indexing at [nextQuery - 1] because array is 0-indexed\n\n anchorEl.setAttribute('href', '#Query_' + this.props.data.queries[nextQuery - 1].number);\n anchorEl.setAttribute('hidden', true);\n document.body.appendChild(anchorEl); // add smooth scrolling animation with jquery\n\n $(anchorEl).on('click', this.animateAnchorElements);\n anchorEl.click();\n document.body.removeChild(anchorEl);\n this.setState({\n queryIndex: nextQuery\n });\n }\n /**\n * Event-handler for downloading fasta of all hits.\n */\n\n }, {\n key: \"downloadFastaOfAll\",\n value: function downloadFastaOfAll() {\n var sequence_ids = [];\n this.props.data.queries.forEach(function (query) {\n return query.hits.forEach(function (hit) {\n return sequence_ids.push(hit.id);\n });\n });\n var database_ids = this.props.data.querydb.map(function (querydb) {\n return querydb.id;\n });\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(sequence_ids, database_ids);\n return false;\n }\n /**\n * Handles downloading fasta of selected hits.\n */\n\n }, {\n key: \"downloadFastaOfSelected\",\n value: function downloadFastaOfSelected() {\n var sequence_ids = $('.hit-links :checkbox:checked').map(function () {\n return this.value;\n }).get();\n\n var database_ids = underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.data.querydb, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].iteratee('id'));\n\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(sequence_ids, database_ids);\n return false;\n }\n /**\n * Handles copying the URL into the user's clipboard. Modified from: https://stackoverflow.com/a/49618964/18117380\n * Hides the 'Copied!' tooltip after 3 seconds\n */\n\n }, {\n key: \"copyURL\",\n value: function copyURL() {\n var element = document.createElement('input');\n var url = window.location.href;\n document.body.appendChild(element);\n element.value = url;\n element.select();\n document.execCommand('copy');\n document.body.removeChild(element);\n setTimeout(function () {\n $('#copyURL')._tooltip('destroy');\n }, 3000);\n }\n /**\n * Returns a mailto message with at most 15 databases used\n */\n\n }, {\n key: \"mailtoLink\",\n value: function mailtoLink() {\n // Iterates over the databases used and appends the first 15 to an array with string formatting\n var dbsArr = [];\n var i = 0;\n\n while (this.props.data.querydb[i] && i < 15) {\n dbsArr.push(' ' + this.props.data.querydb[i].title);\n i += 1;\n } // returns the mailto message\n\n\n var mailto = \"mailto:?subject=SequenceServer \".concat(this.props.data.program.toUpperCase(), \" analysis results &body=Hello,\\n\\n Here is a link to my recent \").concat(this.props.data.program.toUpperCase(), \" analysis of \").concat(this.props.data.queries.length, \" sequences.\\n \\xA0\\xA0\\xA0\\xA0\").concat(window.location.href, \"\\n\\n The following databases were used (up to 15 are shown):\\n \\xA0\\xA0\\xA0\\xA0\").concat(dbsArr, \"\\n\\n The link will work if you have access to that particular SequenceServer instance.\\n\\n Thank you for using SequenceServer, and please remember to cite our paper.\\n\\n Best regards,\\n\\n https://sequenceserver.com\");\n var message = encodeURI(mailto).replace(/(%20){2,}/g, '');\n return message;\n }\n }, {\n key: \"topPanelJSX\",\n value: function topPanelJSX() {\n var path = location.pathname.split('/'); // Get job id.\n\n var job_id = path.pop(); // Deriving rootURL this way is required for subURI deployments\n // - we cannot just send to '/'.\n\n var rootURL = path.join('/');\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n className: \"sidebar-top-panel\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"h4\", {\n children: this.summaryString()\n })\n }), this.props.data.queries.length > 12 && this.queryIndexButtons(), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n href: \"\".concat(rootURL, \"/?job_id=\").concat(job_id),\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"i\", {\n className: \"fa fa-pencil\"\n }), \" Edit search\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n href: \"\".concat(rootURL, \"/\"),\n onClick: this.clearSession,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"i\", {\n className: \"fa fa-file-o\"\n }), \" New search\"]\n })]\n }), this.props.shouldShowIndex && this.indexJSX()]\n });\n }\n }, {\n key: \"summaryString\",\n value: function summaryString() {\n var program = this.props.data.program;\n var numqueries = this.props.data.queries.length;\n var numquerydb = this.props.data.querydb.length;\n return program.toUpperCase() + ': ' + numqueries + ' ' + (numqueries > 1 ? 'queries' : 'query') + ', ' + numquerydb + ' ' + (numquerydb > 1 ? 'databases' : 'database');\n }\n }, {\n key: \"queryIndexButtons\",\n value: function queryIndexButtons() {\n var _this2 = this;\n\n var buttonStyle = {\n outline: 'none',\n border: 'none',\n background: 'none'\n };\n var buttonClasses = 'btn-link nowrap-ellipsis hover-bold';\n\n var handlePreviousBtnClick = function handlePreviousBtnClick() {\n return _this2.handleQueryIndexChange(_this2.state.queryIndex - 1);\n };\n\n var handleNextBtnClick = function handleNextBtnClick() {\n return _this2.handleQueryIndexChange(_this2.state.queryIndex + 1);\n }; // eslint-disable-next-line no-unused-vars\n\n\n var NavButton = function NavButton(_ref) {\n var text = _ref.text,\n onClick = _ref.onClick;\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"button\", {\n className: buttonClasses,\n onClick: onClick,\n style: buttonStyle,\n children: text\n });\n };\n\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n style: {\n display: 'flex',\n width: '100%',\n margin: '7px 0'\n },\n children: [this.state.queryIndex > 1 && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(NavButton, {\n text: \"Previous Query\",\n onClick: handlePreviousBtnClick\n }), this.state.queryIndex > 1 && this.state.queryIndex < this.props.data.queries.length && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }), this.state.queryIndex < this.props.data.queries.length && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(NavButton, {\n onClick: handleNextBtnClick,\n text: \"Next Query\"\n })]\n });\n }\n }, {\n key: \"indexJSX\",\n value: function indexJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"ul\", {\n className: \"nav hover-reset active-bold\",\n children: [\" \", underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.data.queries, function (query) {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n className: \"btn-link nowrap-ellipsis hover-bold\",\n title: 'Query= ' + query.id + ' ' + query.title,\n href: '#Query_' + query.number,\n children: 'Query= ' + query.id\n })\n }, 'Side_bar_' + query.id);\n })]\n });\n }\n }, {\n key: \"downloadsPanelJSX\",\n value: function downloadsPanelJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n className: \"downloads\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"h4\", {\n children: \"Download FASTA, XML, TSV\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"ul\", {\n className: \"nav\",\n children: [!(this.props.data.imported_xml || this.props.data.non_parse_seqids) && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n href: \"#\",\n className: \"btn-link download-fasta-of-all \".concat(!this.props.atLeastOneHit && 'disabled'),\n onClick: this.downloadFastaOfAll,\n children: \"FASTA of all hits\"\n })\n }), !(this.props.data.imported_xml || this.props.data.non_parse_seqids) && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n href: \"#\",\n className: \"btn-link download-fasta-of-selected disabled\",\n onClick: this.downloadFastaOfSelected,\n children: [\"FASTA of \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"span\", {\n className: \"text-bold\"\n }), \" selected hit(s)\"]\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n href: \"#\",\n className: \"btn-link download-alignment-of-all \".concat(!this.props.atLeastOneHit && 'disabled'),\n children: \"Alignment of all hits\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n href: \"#\",\n className: \"btn-link download-alignment-of-selected disabled\",\n children: [\"Alignment of \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"span\", {\n className: \"text-bold\"\n }), \" selected hit(s)\"]\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"15 columns: query and subject ID; scientific name, alignment length, mismatches, gaps, identity, start and end coordinates, e value, bitscore, query coverage per subject and per HSP.\",\n href: 'download/' + this.props.data.search_id + '.std_tsv',\n children: \"Standard tabular report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"44 columns: query and subject ID, GI, accessions, and length; alignment details; taxonomy details of subject sequence(s) and query coverage per subject and per HSP.\",\n href: 'download/' + this.props.data.search_id + '.full_tsv',\n children: \"Full tabular report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"Results in XML format.\",\n href: 'download/' + this.props.data.search_id + '.xml',\n children: \"Full XML report\"\n })\n })]\n })]\n });\n }\n }, {\n key: \"sharingPanelJSX\",\n value: function sharingPanelJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n className: \"sharing-panel\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"h4\", {\n children: \"Share results\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"ul\", {\n className: \"nav\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n id: \"copyURL\",\n className: \"btn-link copy-URL cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n onClick: this.copyURL,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"i\", {\n className: \"fa fa-copy\"\n }), \" Copy URL to clipboard\"]\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"a\", {\n id: \"sendEmail\",\n className: \"btn-link email-URL cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n title: \"Send by email\",\n href: this.mailtoLink(),\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsx)(\"i\", {\n className: \"fa fa-envelope\"\n }), \" Send by email\"]\n })\n })]\n })]\n });\n }\n }, {\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxs)(\"div\", {\n className: \"sidebar\",\n children: [this.topPanelJSX(), this.downloadsPanelJSX(), this.sharingPanelJSX()]\n });\n }\n }]);\n\n return _default;\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\n\n\n\n//# sourceURL=webpack://SequenceServer/./public/js/sidebar.js?"); /***/ }), diff --git a/public/sequenceserver-search.min.js b/public/sequenceserver-search.min.js index a005f1569..8d6f874e2 100644 --- a/public/sequenceserver-search.min.js +++ b/public/sequenceserver-search.min.js @@ -49,7 +49,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"download_blob\": () => (/* binding */ download_blob),\n/* harmony export */ \"download_url\": () => (/* binding */ download_url),\n/* harmony export */ \"sanitize_filename\": () => (/* binding */ sanitize_filename)\n/* harmony export */ });\nfunction download_url(url, filename) {\n var a = d3.select('body').append('a').attr('download', filename).style('display', 'none').attr('href', url);\n a.node().click();\n setTimeout(function () {\n a.remove();\n }, 100);\n}\nfunction download_blob(blob, filename) {\n if (typeof window.navigator.msSaveOrOpenBlob !== 'undefined') {\n window.navigator.msSaveOrOpenBlob(blob, filename);\n return;\n }\n\n download_url(window.URL.createObjectURL(blob), filename);\n}\nfunction sanitize_filename(str) {\n var san = str.replace(/[^a-zA-Z0-9=_\\-]/g, '_'); // Replace runs of underscores with single one.\n\n san = san.replace(/_{2,}/g, '_'); // Remove any leading or trailing underscores.\n\n san = san.replace(/^_/, '').replace(/_$/, '');\n return san;\n}\n\n//# sourceURL=webpack://SequenceServer/./public/js/exporter.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"download_blob\": () => (/* binding */ download_blob),\n/* harmony export */ \"download_url\": () => (/* binding */ download_url),\n/* harmony export */ \"generate_blob_url\": () => (/* binding */ generate_blob_url),\n/* harmony export */ \"sanitize_filename\": () => (/* binding */ sanitize_filename)\n/* harmony export */ });\nfunction download_url(url, filename) {\n var a = d3.select('body').append('a').attr('download', filename).style('display', 'none').attr('href', url);\n a.node().click();\n setTimeout(function () {\n a.remove();\n }, 100);\n}\nfunction generate_blob_url(blob) {\n var url = window.URL.createObjectURL(blob);\n return url;\n}\nfunction download_blob(blob, filename) {\n if (typeof window.navigator.msSaveOrOpenBlob !== 'undefined') {\n window.navigator.msSaveOrOpenBlob(blob, filename);\n } else {\n download_url(generate_blob_url(blob), filename);\n }\n}\nfunction sanitize_filename(str) {\n var san = str.replace(/[^a-zA-Z0-9=_\\-]/g, '_'); // Replace runs of underscores with single one.\n\n san = san.replace(/_{2,}/g, '_'); // Remove any leading or trailing underscores.\n\n san = san.replace(/^_/, '').replace(/_$/, '');\n return san;\n}\n\n//# sourceURL=webpack://SequenceServer/./public/js/exporter.js?"); /***/ }),