sequenceserver 3.1.2 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/lib/sequenceserver/blast/tasks.rb +1 -1
 - data/lib/sequenceserver/routes.rb +1 -1
 - data/lib/sequenceserver/version.rb +1 -1
 - data/lib/sequenceserver.rb +1 -1
 - data/public/css/app.min.css +1 -1
 - data/public/css/sequenceserver.css +0 -18
 - data/public/css/sequenceserver.min.css +2 -2
 - data/public/js/alignment_exporter.js +16 -28
 - data/public/js/cloud_share_modal.js +42 -42
 - data/public/js/form.js +12 -10
 - data/public/js/grapher.js +4 -4
 - data/public/js/hit.js +3 -3
 - data/public/js/hits.js +276 -0
 - data/public/js/jquery_world.js +1 -1
 - data/public/js/mailto.js +1 -3
 - data/public/js/null_plugins/report_plugins.js +1 -0
 - data/public/js/options.js +2 -6
 - data/public/js/query.js +1 -1
 - data/public/js/report.js +68 -252
 - data/public/js/report_root.js +7 -5
 - data/public/js/search.js +28 -11
 - data/public/js/sequence.js +158 -158
 - data/public/js/sequence_modal.js +28 -36
 - data/public/js/sidebar.js +7 -6
 - data/public/js/tests/alignment_exporter.spec.js +38 -0
 - data/public/js/tests/cloud_share_modal.spec.js +75 -0
 - data/public/js/tests/report.spec.js +37 -15
 - data/public/packages/jquery-ui@1.13.3.js +19070 -0
 - data/public/sequenceserver-report.min.js +3 -2481
 - data/public/sequenceserver-report.min.js.LICENSE.txt +300 -0
 - data/public/sequenceserver-report.min.js.map +1 -0
 - data/public/sequenceserver-search.min.js +3 -2382
 - data/public/sequenceserver-search.min.js.LICENSE.txt +292 -0
 - data/public/sequenceserver-search.min.js.map +1 -0
 - data/views/layout.erb +3 -7
 - data/views/search.erb +1 -1
 - data/views/search_layout.erb +1 -1
 - metadata +11 -5
 - data/public/config.js +0 -147
 - data/public/packages/jquery-ui@1.11.4.js +0 -16624
 
| 
         @@ -1,5 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import * as Exporter from './exporter';
         
     | 
| 
       2 
2 
     | 
    
         
             
            import _ from 'underscore';
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
       3 
4 
     | 
    
         
             
            export default class AlignmentExporter {
         
     | 
| 
       4 
5 
     | 
    
         
             
                constructor() {
         
     | 
| 
       5 
6 
     | 
    
         
             
                    this.prepare_alignments_for_export = this.prepare_alignments_for_export.bind(this);
         
     | 
| 
         @@ -7,49 +8,36 @@ export default class AlignmentExporter { 
     | 
|
| 
       7 
8 
     | 
    
         
             
                }
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
                wrap_string(str, width) {
         
     | 
| 
       10 
     | 
    
         
            -
                     
     | 
| 
       11 
     | 
    
         
            -
                    var wrapped = '';
         
     | 
| 
       12 
     | 
    
         
            -
                    while(true) {
         
     | 
| 
       13 
     | 
    
         
            -
                        wrapped += str.substring(idx, idx + width);
         
     | 
| 
       14 
     | 
    
         
            -
                        idx += width;
         
     | 
| 
       15 
     | 
    
         
            -
                        if(idx < str.length) {
         
     | 
| 
       16 
     | 
    
         
            -
                            wrapped += '\n';
         
     | 
| 
       17 
     | 
    
         
            -
                        } else {
         
     | 
| 
       18 
     | 
    
         
            -
                            break;
         
     | 
| 
       19 
     | 
    
         
            -
                        }
         
     | 
| 
       20 
     | 
    
         
            -
                    }
         
     | 
| 
       21 
     | 
    
         
            -
                    return wrapped;
         
     | 
| 
      
 11 
     | 
    
         
            +
                    return str.match(new RegExp(`.{1,${width}}`, 'g')).join('\n');
         
     | 
| 
       22 
12 
     | 
    
         
             
                }
         
     | 
| 
       23 
13 
     | 
    
         | 
| 
       24 
14 
     | 
    
         
             
                generate_fasta(hsps) {
         
     | 
| 
      
 15 
     | 
    
         
            +
                    let fasta = '';
         
     | 
| 
       25 
16 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
                     
     | 
| 
      
 17 
     | 
    
         
            +
                    hsps.map(hsp => {
         
     | 
| 
      
 18 
     | 
    
         
            +
                        fasta += `>${hsp.query_id}:${hsp.qstart}-${hsp.qend}\n`;
         
     | 
| 
      
 19 
     | 
    
         
            +
                        fasta += `${hsp.qseq}\n`;
         
     | 
| 
      
 20 
     | 
    
         
            +
                        fasta += `>${hsp.query_id}:${hsp.qstart}-${hsp.qend}_alignment_${hsp.hit_id}:${hsp.sstart}-${hsp.send}\n`;
         
     | 
| 
      
 21 
     | 
    
         
            +
                        fasta += `${hsp.midline}\n`;
         
     | 
| 
      
 22 
     | 
    
         
            +
                        fasta += `>${hsp.hit_id}:${hsp.sstart}-${hsp.send}\n`;
         
     | 
| 
      
 23 
     | 
    
         
            +
                        fasta += `${hsp.sseq}\n`;
         
     | 
| 
      
 24 
     | 
    
         
            +
                    });
         
     | 
| 
       27 
25 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                    _.each(hsps, _.bind(function (hsp) {
         
     | 
| 
       29 
     | 
    
         
            -
                        fasta += '>'+hsp.query_id+':'+hsp.qstart+'-'+hsp.qend+'\n';
         
     | 
| 
       30 
     | 
    
         
            -
                        fasta += hsp.qseq+'\n';
         
     | 
| 
       31 
     | 
    
         
            -
                        fasta += '>'+hsp.query_id+':'+hsp.qstart+'-'+hsp.qend+'_alignment_'+hsp.hit_id+':'+hsp.sstart+'-'+hsp.send+'\n';
         
     | 
| 
       32 
     | 
    
         
            -
                        fasta += hsp.midline+'\n';
         
     | 
| 
       33 
     | 
    
         
            -
                        fasta += '>'+hsp.hit_id+':'+hsp.sstart+'-'+hsp.send+'\n';
         
     | 
| 
       34 
     | 
    
         
            -
                        fasta += hsp.sseq+'\n';
         
     | 
| 
       35 
     | 
    
         
            -
                    }, this));
         
     | 
| 
       36 
26 
     | 
    
         
             
                    return fasta;
         
     | 
| 
       37 
27 
     | 
    
         
             
                }
         
     | 
| 
       38 
28 
     | 
    
         | 
| 
       39 
29 
     | 
    
         
             
                get_alignments_download_metadata(hsps, filename_prefix){
         
     | 
| 
       40 
     | 
    
         
            -
                     
     | 
| 
       41 
     | 
    
         
            -
                     
     | 
| 
       42 
     | 
    
         
            -
                     
     | 
| 
      
 30 
     | 
    
         
            +
                    const fasta = this.generate_fasta(hsps);
         
     | 
| 
      
 31 
     | 
    
         
            +
                    const blob = new Blob([fasta], { type: 'text/fasta' });
         
     | 
| 
      
 32 
     | 
    
         
            +
                    const filename = Exporter.sanitize_filename(filename_prefix) + '.txt';
         
     | 
| 
       43 
33 
     | 
    
         
             
                    return {filename, blob};
         
     | 
| 
       44 
34 
     | 
    
         
             
                }
         
     | 
| 
       45 
35 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
                
         
     | 
| 
       47 
36 
     | 
    
         
             
                prepare_alignments_for_export(hsps, filename_prefix) {
         
     | 
| 
       48 
37 
     | 
    
         
             
                    const { filename, blob } = this.get_alignments_download_metadata(hsps, filename_prefix);
         
     | 
| 
       49 
     | 
    
         
            -
                     
     | 
| 
       50 
     | 
    
         
            -
                    return blob_url;
         
     | 
| 
      
 38 
     | 
    
         
            +
                    return Exporter.generate_blob_url(blob, filename);
         
     | 
| 
       51 
39 
     | 
    
         
             
                }
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
       53 
41 
     | 
    
         
             
                export_alignments(hsps, filename_prefix) {
         
     | 
| 
       54 
42 
     | 
    
         
             
                    const { filename, blob } = this.get_alignments_download_metadata(hsps, filename_prefix);
         
     | 
| 
       55 
43 
     | 
    
         
             
                    Exporter.download_blob(blob, filename);
         
     | 
| 
         @@ -28,13 +28,11 @@ export default class CloudShareModal extends React.Component { 
     | 
|
| 
       28 
28 
     | 
    
         
             
                this.setState({ [name]: inputValue });
         
     | 
| 
       29 
29 
     | 
    
         
             
              }
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
              handleSubmit = (e) => {
         
     | 
| 
      
 31 
     | 
    
         
            +
              handleSubmit = async (e) => {
         
     | 
| 
       32 
32 
     | 
    
         
             
                e.preventDefault();
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                const { email } = this.state;
         
     | 
| 
       35 
     | 
    
         
            -
                const  
     | 
| 
       36 
     | 
    
         
            -
                const match = window.location.pathname.match(regex);
         
     | 
| 
       37 
     | 
    
         
            -
                const jobId = match[1];
         
     | 
| 
      
 35 
     | 
    
         
            +
                const jobId = this.getJobIdFromPath();
         
     | 
| 
       38 
36 
     | 
    
         | 
| 
       39 
37 
     | 
    
         
             
                this.setState({ formState: 'loading' });
         
     | 
| 
       40 
38 
     | 
    
         | 
| 
         @@ -43,33 +41,40 @@ export default class CloudShareModal extends React.Component { 
     | 
|
| 
       43 
41 
     | 
    
         
             
                  sender_email: email
         
     | 
| 
       44 
42 
     | 
    
         
             
                };
         
     | 
| 
       45 
43 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
                 
     | 
| 
       47 
     | 
    
         
            -
                   
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                     
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                  .then(response => response.json())
         
     | 
| 
       54 
     | 
    
         
            -
                  .then(data => {
         
     | 
| 
       55 
     | 
    
         
            -
                    if (data.shareable_url) {
         
     | 
| 
       56 
     | 
    
         
            -
                      // Successful response
         
     | 
| 
       57 
     | 
    
         
            -
                      this.setState({ formState: 'results', shareableurl: data.shareable_url });
         
     | 
| 
       58 
     | 
    
         
            -
                    } else if (data.errors) {
         
     | 
| 
       59 
     | 
    
         
            -
                      // Error response with specific error messages
         
     | 
| 
       60 
     | 
    
         
            -
                      const errorMessages = data.errors;
         
     | 
| 
       61 
     | 
    
         
            -
                      this.setState({ formState: 'error', errorMessages });
         
     | 
| 
       62 
     | 
    
         
            -
                    } else {
         
     | 
| 
       63 
     | 
    
         
            -
                      // Generic error message
         
     | 
| 
       64 
     | 
    
         
            -
                      throw new Error('Unknown error submitting form');
         
     | 
| 
       65 
     | 
    
         
            -
                    }
         
     | 
| 
       66 
     | 
    
         
            -
                  })
         
     | 
| 
       67 
     | 
    
         
            -
                  .catch(error => {
         
     | 
| 
       68 
     | 
    
         
            -
                    this.setState({
         
     | 
| 
       69 
     | 
    
         
            -
                      formState: 'error',
         
     | 
| 
       70 
     | 
    
         
            -
                      errorMessages: [error.message]
         
     | 
| 
       71 
     | 
    
         
            -
                    });
         
     | 
| 
      
 44 
     | 
    
         
            +
                try {
         
     | 
| 
      
 45 
     | 
    
         
            +
                  const response = await fetch('/cloud_share', {
         
     | 
| 
      
 46 
     | 
    
         
            +
                    method: 'POST',
         
     | 
| 
      
 47 
     | 
    
         
            +
                    headers: {
         
     | 
| 
      
 48 
     | 
    
         
            +
                      'Content-Type': 'application/json'
         
     | 
| 
      
 49 
     | 
    
         
            +
                    },
         
     | 
| 
      
 50 
     | 
    
         
            +
                    body: JSON.stringify(requestData)
         
     | 
| 
       72 
51 
     | 
    
         
             
                  });
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  if (!response.ok) {
         
     | 
| 
      
 54 
     | 
    
         
            +
                    throw new Error('Network response was not ok');
         
     | 
| 
      
 55 
     | 
    
         
            +
                  }
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  const data = await response.json();
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  if (data.shareable_url) {
         
     | 
| 
      
 60 
     | 
    
         
            +
                    this.setState({ formState: 'results', shareableurl: data.shareable_url });
         
     | 
| 
      
 61 
     | 
    
         
            +
                  } else if (data.errors) {
         
     | 
| 
      
 62 
     | 
    
         
            +
                    this.setState({ formState: 'error', errorMessages: data.errors });
         
     | 
| 
      
 63 
     | 
    
         
            +
                  } else {
         
     | 
| 
      
 64 
     | 
    
         
            +
                    throw new Error('Unknown error submitting form');
         
     | 
| 
      
 65 
     | 
    
         
            +
                  }
         
     | 
| 
      
 66 
     | 
    
         
            +
                } catch (error) {
         
     | 
| 
      
 67 
     | 
    
         
            +
                  this.setState({
         
     | 
| 
      
 68 
     | 
    
         
            +
                    formState: 'error',
         
     | 
| 
      
 69 
     | 
    
         
            +
                    errorMessages: [error.message]
         
     | 
| 
      
 70 
     | 
    
         
            +
                  });
         
     | 
| 
      
 71 
     | 
    
         
            +
                }
         
     | 
| 
      
 72 
     | 
    
         
            +
              }
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
              getJobIdFromPath = () => {
         
     | 
| 
      
 75 
     | 
    
         
            +
                const regex = /\/([^/]+)(?:\/|#|\?|$)/;
         
     | 
| 
      
 76 
     | 
    
         
            +
                const match = window.location.pathname.match(regex);
         
     | 
| 
      
 77 
     | 
    
         
            +
                return match ? match[1] : match;
         
     | 
| 
       73 
78 
     | 
    
         
             
              }
         
     | 
| 
       74 
79 
     | 
    
         | 
| 
       75 
80 
     | 
    
         
             
              renderLoading() {
         
     | 
| 
         @@ -93,18 +98,13 @@ export default class CloudShareModal extends React.Component { 
     | 
|
| 
       93 
98 
     | 
    
         
             
                return (
         
     | 
| 
       94 
99 
     | 
    
         
             
                  <>
         
     | 
| 
       95 
100 
     | 
    
         
             
                    {
         
     | 
| 
       96 
     | 
    
         
            -
                       
     | 
| 
       97 
     | 
    
         
            -
                         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
                              </div>
         
     | 
| 
       104 
     | 
    
         
            -
                            </div>
         
     | 
| 
       105 
     | 
    
         
            -
                          );
         
     | 
| 
       106 
     | 
    
         
            -
                        }, this)
         
     | 
| 
       107 
     | 
    
         
            -
                      )
         
     | 
| 
      
 101 
     | 
    
         
            +
                      errorMessages.map((errorMessage, index) => (
         
     | 
| 
      
 102 
     | 
    
         
            +
                        <div key={`fastan-${index}`} className="fastan">
         
     | 
| 
      
 103 
     | 
    
         
            +
                          <div className="section-content">
         
     | 
| 
      
 104 
     | 
    
         
            +
                            <div className="modal-error">{errorMessage}</div>
         
     | 
| 
      
 105 
     | 
    
         
            +
                          </div>
         
     | 
| 
      
 106 
     | 
    
         
            +
                        </div>
         
     | 
| 
      
 107 
     | 
    
         
            +
                      ))
         
     | 
| 
       108 
108 
     | 
    
         
             
                    }
         
     | 
| 
       109 
109 
     | 
    
         
             
                    {this.renderForm()}
         
     | 
| 
       110 
110 
     | 
    
         
             
                  </>
         
     | 
    
        data/public/js/form.js
    CHANGED
    
    | 
         @@ -34,6 +34,8 @@ export class Form extends Component { 
     | 
|
| 
       34 
34 
     | 
    
         
             
                    this.handleAlgoChanged = this.handleAlgoChanged.bind(this);
         
     | 
| 
       35 
35 
     | 
    
         
             
                    this.handleFormSubmission = this.handleFormSubmission.bind(this);
         
     | 
| 
       36 
36 
     | 
    
         
             
                    this.formRef = createRef();
         
     | 
| 
      
 37 
     | 
    
         
            +
                    this.query = createRef();
         
     | 
| 
      
 38 
     | 
    
         
            +
                    this.button = createRef();
         
     | 
| 
       37 
39 
     | 
    
         
             
                    this.setButtonState = this.setButtonState.bind(this);
         
     | 
| 
       38 
40 
     | 
    
         
             
                }
         
     | 
| 
       39 
41 
     | 
    
         | 
| 
         @@ -65,7 +67,7 @@ export class Form extends Component { 
     | 
|
| 
       65 
67 
     | 
    
         
             
                         * (if any).
         
     | 
| 
       66 
68 
     | 
    
         
             
                         */
         
     | 
| 
       67 
69 
     | 
    
         
             
                        if (data['query']) {
         
     | 
| 
       68 
     | 
    
         
            -
                            this. 
     | 
| 
      
 70 
     | 
    
         
            +
                            this.query.current.value(data['query']);
         
     | 
| 
       69 
71 
     | 
    
         
             
                        }
         
     | 
| 
       70 
72 
     | 
    
         | 
| 
       71 
73 
     | 
    
         
             
                        setTimeout(function () {
         
     | 
| 
         @@ -96,7 +98,7 @@ export class Form extends Component { 
     | 
|
| 
       96 
98 
     | 
    
         
             
                    evt.preventDefault();
         
     | 
| 
       97 
99 
     | 
    
         
             
                    const form = this.formRef.current;
         
     | 
| 
       98 
100 
     | 
    
         
             
                    const formData = new FormData(form);
         
     | 
| 
       99 
     | 
    
         
            -
                    formData.append('method', this. 
     | 
| 
      
 101 
     | 
    
         
            +
                    formData.append('method', this.button.current.state.methods[0]);
         
     | 
| 
       100 
102 
     | 
    
         
             
                    fetch(window.location.href, {
         
     | 
| 
       101 
103 
     | 
    
         
             
                        method: 'POST',
         
     | 
| 
       102 
104 
     | 
    
         
             
                        body: formData
         
     | 
| 
         @@ -118,7 +120,7 @@ export class Form extends Component { 
     | 
|
| 
       118 
120 
     | 
    
         
             
                    var database_type = this.databaseType;
         
     | 
| 
       119 
121 
     | 
    
         
             
                    var sequence_type = this.sequenceType;
         
     | 
| 
       120 
122 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
                    if (this. 
     | 
| 
      
 123 
     | 
    
         
            +
                    if (this.query.current.isEmpty()) {
         
     | 
| 
       122 
124 
     | 
    
         
             
                        return [];
         
     | 
| 
       123 
125 
     | 
    
         
             
                    }
         
     | 
| 
       124 
126 
     | 
    
         | 
| 
         @@ -165,8 +167,8 @@ export class Form extends Component { 
     | 
|
| 
       165 
167 
     | 
    
         
             
                }
         
     | 
| 
       166 
168 
     | 
    
         | 
| 
       167 
169 
     | 
    
         
             
                setButtonState() {
         
     | 
| 
       168 
     | 
    
         
            -
                    this. 
     | 
| 
       169 
     | 
    
         
            -
                        hasQuery: !this. 
     | 
| 
      
 170 
     | 
    
         
            +
                    this.button.current.setState({
         
     | 
| 
      
 171 
     | 
    
         
            +
                        hasQuery: !this.query.current.isEmpty(),
         
     | 
| 
       170 
172 
     | 
    
         
             
                        hasDatabases: !!this.databaseType,
         
     | 
| 
       171 
173 
     | 
    
         
             
                        methods: this.determineBlastMethods()
         
     | 
| 
       172 
174 
     | 
    
         
             
                    });
         
     | 
| 
         @@ -205,16 +207,16 @@ export class Form extends Component { 
     | 
|
| 
       205 
207 
     | 
    
         
             
                            <form id="blast" ref={this.formRef} onSubmit={this.handleFormSubmission}>
         
     | 
| 
       206 
208 
     | 
    
         
             
                                <input type="hidden" name="_csrf" value={document.querySelector('meta[name="_csrf"]').content} />
         
     | 
| 
       207 
209 
     | 
    
         
             
                                <div className="px-4">
         
     | 
| 
       208 
     | 
    
         
            -
                                    <SearchQueryWidget ref= 
     | 
| 
      
 210 
     | 
    
         
            +
                                    <SearchQueryWidget ref={this.query} onSequenceTypeChanged={this.handleSequenceTypeChanged} onSequenceChanged={this.handleSequenceChanged}/>
         
     | 
| 
       209 
211 
     | 
    
         | 
| 
       210 
212 
     | 
    
         
             
                                    {this.useTreeWidget() ?
         
     | 
| 
       211 
     | 
    
         
            -
                                        <DatabasesTree 
     | 
| 
      
 213 
     | 
    
         
            +
                                        <DatabasesTree
         
     | 
| 
       212 
214 
     | 
    
         
             
                                            databases={this.state.databases} tree={this.state.tree}
         
     | 
| 
       213 
215 
     | 
    
         
             
                                            preSelectedDbs={this.state.preSelectedDbs}
         
     | 
| 
       214 
216 
     | 
    
         
             
                                            onDatabaseTypeChanged={this.handleDatabaseTypeChanged}
         
     | 
| 
       215 
217 
     | 
    
         
             
                                            onDatabaseSelectionChanged={this.handleDatabaseSelectionChanged} />
         
     | 
| 
       216 
218 
     | 
    
         
             
                                        :
         
     | 
| 
       217 
     | 
    
         
            -
                                        <Databases  
     | 
| 
      
 219 
     | 
    
         
            +
                                        <Databases databases={this.state.databases}
         
     | 
| 
       218 
220 
     | 
    
         
             
                                            preSelectedDbs={this.state.preSelectedDbs}
         
     | 
| 
       219 
221 
     | 
    
         
             
                                            onDatabaseTypeChanged={this.handleDatabaseTypeChanged}
         
     | 
| 
       220 
222 
     | 
    
         
             
                                            onDatabaseSelectionChanged={this.handleDatabaseSelectionChanged} />
         
     | 
| 
         @@ -234,7 +236,7 @@ export class Form extends Component { 
     | 
|
| 
       234 
236 
     | 
    
         
             
                                    <label className="block my-4 md:my-2">
         
     | 
| 
       235 
237 
     | 
    
         
             
                                        <input type="checkbox" id="toggleNewTab" /> Open results in new tab
         
     | 
| 
       236 
238 
     | 
    
         
             
                                    </label>
         
     | 
| 
       237 
     | 
    
         
            -
                                    <SearchButton ref= 
     | 
| 
      
 239 
     | 
    
         
            +
                                    <SearchButton ref={this.button} onAlgoChanged={this.handleAlgoChanged} />
         
     | 
| 
       238 
240 
     | 
    
         
             
                                </div>
         
     | 
| 
       239 
241 
     | 
    
         | 
| 
       240 
242 
     | 
    
         
             
                            </form>
         
     | 
| 
         @@ -305,4 +307,4 @@ class MixedNotification extends Component { 
     | 
|
| 
       305 
307 
     | 
    
         
             
                        </div>
         
     | 
| 
       306 
308 
     | 
    
         
             
                    );
         
     | 
| 
       307 
309 
     | 
    
         
             
                }
         
     | 
| 
       308 
     | 
    
         
            -
            }
         
     | 
| 
      
 310 
     | 
    
         
            +
            }
         
     | 
    
        data/public/js/grapher.js
    CHANGED
    
    | 
         @@ -17,7 +17,7 @@ export default function Grapher(Graph) { 
     | 
|
| 
       17 
17 
     | 
    
         
             
                return class extends React.Component {
         
     | 
| 
       18 
18 
     | 
    
         
             
                    constructor(props) {
         
     | 
| 
       19 
19 
     | 
    
         
             
                        super(props);
         
     | 
| 
       20 
     | 
    
         
            -
                        this.name = Graph.name();
         
     | 
| 
      
 20 
     | 
    
         
            +
                        this.name = Graph.name(this.props);
         
     | 
| 
       21 
21 
     | 
    
         
             
                        this.collapsePreferences = new CollapsePreferences(this);
         
     | 
| 
       22 
22 
     | 
    
         
             
                        let isCollapsed = this.collapsePreferences.preferenceStoredAsCollapsed();
         
     | 
| 
       23 
23 
     | 
    
         
             
                        this.state = { collapsed: Graph.canCollapse() && (this.props.collapsed || isCollapsed) };
         
     | 
| 
         @@ -30,12 +30,12 @@ export default function Grapher(Graph) { 
     | 
|
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                    render() {
         
     | 
| 
       32 
32 
     | 
    
         
             
                        // Do not render when Graph.name() is null
         
     | 
| 
       33 
     | 
    
         
            -
                        if (Graph.name() === null) {
         
     | 
| 
      
 33 
     | 
    
         
            +
                        if (Graph.name(this.props) === null) {
         
     | 
| 
       34 
34 
     | 
    
         
             
                            return null;
         
     | 
| 
       35 
35 
     | 
    
         
             
                        } else {
         
     | 
| 
       36 
36 
     | 
    
         
             
                            var cssClasses = Graph.className() + ' grapher';
         
     | 
| 
       37 
37 
     | 
    
         
             
                            return (
         
     | 
| 
       38 
     | 
    
         
            -
                                <div  
     | 
| 
      
 38 
     | 
    
         
            +
                                <div className={cssClasses}>
         
     | 
| 
       39 
39 
     | 
    
         
             
                                    {this.header()}
         
     | 
| 
       40 
40 
     | 
    
         
             
                                    {this.svgContainerJSX()}
         
     | 
| 
       41 
41 
     | 
    
         
             
                                </div>
         
     | 
| 
         @@ -51,7 +51,7 @@ export default function Grapher(Graph) { 
     | 
|
| 
       51 
51 
     | 
    
         
             
                                    onClick={() => this.collapsePreferences.toggleCollapse()}
         
     | 
| 
       52 
52 
     | 
    
         
             
                                >
         
     | 
| 
       53 
53 
     | 
    
         
             
                                    {this.collapsePreferences.renderCollapseIcon()}
         
     | 
| 
       54 
     | 
    
         
            -
                           {Graph.name()}
         
     | 
| 
      
 54 
     | 
    
         
            +
                           {Graph.name(this.props)}
         
     | 
| 
       55 
55 
     | 
    
         
             
                                </h4>
         
     | 
| 
       56 
56 
     | 
    
         
             
                                {!this.state.collapsed && this.graphLinksJSX()}
         
     | 
| 
       57 
57 
     | 
    
         
             
                            </div>;
         
     | 
    
        data/public/js/hit.js
    CHANGED
    
    | 
         @@ -80,14 +80,14 @@ export default class extends Component { 
     | 
|
| 
       80 
80 
     | 
    
         
             
                    return `get_sequence/?sequence_ids=${sequenceIDs}&database_ids=${databaseIDs}`;
         
     | 
| 
       81 
81 
     | 
    
         
             
                }
         
     | 
| 
       82 
82 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
                downloadFASTA( 
     | 
| 
      
 83 
     | 
    
         
            +
                downloadFASTA(_event) {
         
     | 
| 
       84 
84 
     | 
    
         
             
                    var sequenceIDs = [this.sequenceID()];
         
     | 
| 
       85 
85 
     | 
    
         
             
                    downloadFASTA(sequenceIDs, this.databaseIDs());
         
     | 
| 
       86 
86 
     | 
    
         
             
                }
         
     | 
| 
       87 
87 
     | 
    
         | 
| 
       88 
88 
     | 
    
         
             
                // Event-handler for exporting alignments.
         
     | 
| 
       89 
89 
     | 
    
         
             
                // Calls relevant method on AlignmentExporter defined in alignment_exporter.js.
         
     | 
| 
       90 
     | 
    
         
            -
                downloadAlignment( 
     | 
| 
      
 90 
     | 
    
         
            +
                downloadAlignment(_event) {
         
     | 
| 
       91 
91 
     | 
    
         
             
                    var hsps = _.map(this.props.hit.hsps, _.bind(function (hsp) {
         
     | 
| 
       92 
92 
     | 
    
         
             
                        hsp.query_id = this.props.query.id;
         
     | 
| 
       93 
93 
     | 
    
         
             
                        hsp.hit_id = this.props.hit.id;
         
     | 
| 
         @@ -246,4 +246,4 @@ export default class extends Component { 
     | 
|
| 
       246 
246 
     | 
    
         
             
                        </div>
         
     | 
| 
       247 
247 
     | 
    
         
             
                    );
         
     | 
| 
       248 
248 
     | 
    
         
             
                }
         
     | 
| 
       249 
     | 
    
         
            -
            }
         
     | 
| 
      
 249 
     | 
    
         
            +
            }
         
     | 
    
        data/public/js/hits.js
    ADDED
    
    | 
         @@ -0,0 +1,276 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            /* eslint-disable no-unused-vars */
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { Component } from 'react';
         
     | 
| 
      
 3 
     | 
    
         
            +
            import _ from 'underscore';
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            import { ReportQuery } from './query';
         
     | 
| 
      
 6 
     | 
    
         
            +
            import Hit from './hit';
         
     | 
| 
      
 7 
     | 
    
         
            +
            import HSP from './hsp';
         
     | 
| 
      
 8 
     | 
    
         
            +
            import AlignmentExporter from './alignment_exporter';
         
     | 
| 
      
 9 
     | 
    
         
            +
            /* eslint-enable no-unused-vars */
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            class Hits extends Component {
         
     | 
| 
      
 12 
     | 
    
         
            +
                constructor(props) {
         
     | 
| 
      
 13 
     | 
    
         
            +
                    super(props);
         
     | 
| 
      
 14 
     | 
    
         
            +
                    this.numUpdates = 0;
         
     | 
| 
      
 15 
     | 
    
         
            +
                    this.nextQuery = 0;
         
     | 
| 
      
 16 
     | 
    
         
            +
                    this.nextHit = 0;
         
     | 
| 
      
 17 
     | 
    
         
            +
                    this.nextHSP = 0;
         
     | 
| 
      
 18 
     | 
    
         
            +
                    this.maxHSPs = 3; // max HSPs to render in a cycle
         
     | 
| 
      
 19 
     | 
    
         
            +
                    this.state = props.state;
         
     | 
| 
      
 20 
     | 
    
         
            +
                    this.prepareAlignmentOfSelectedHits = this.prepareAlignmentOfSelectedHits.bind(this);
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                componentDidMount() {
         
     | 
| 
      
 24 
     | 
    
         
            +
                    this.componentDidUpdate(this.props, this.state);
         
     | 
| 
      
 25 
     | 
    
         
            +
                }
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                /**
         
     | 
| 
      
 28 
     | 
    
         
            +
                * Called for the first time after as BLAST results have been retrieved from
         
     | 
| 
      
 29 
     | 
    
         
            +
                * the server and added to this.state by fetchResults. Only summary overview
         
     | 
| 
      
 30 
     | 
    
         
            +
                * and circos would have been rendered at this point. At this stage we kick
         
     | 
| 
      
 31 
     | 
    
         
            +
                * start iteratively adding 1 HSP to the page every 25 milli-seconds.
         
     | 
| 
      
 32 
     | 
    
         
            +
                */
         
     | 
| 
      
 33 
     | 
    
         
            +
                componentDidUpdate(prevProps, prevState) {
         
     | 
| 
      
 34 
     | 
    
         
            +
                    // Log to console how long the last update take?
         
     | 
| 
      
 35 
     | 
    
         
            +
                    // console.log((Date.now() - this.lastTimeStamp) / 1000);
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    // Lock sidebar in its position on the first update.
         
     | 
| 
      
 38 
     | 
    
         
            +
                    if (this.nextQuery == 0 && this.nextHit == 0 && this.nextHSP == 0) {
         
     | 
| 
      
 39 
     | 
    
         
            +
                        this.affixSidebar();
         
     | 
| 
      
 40 
     | 
    
         
            +
                    }
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    // Queue next update if we have not rendered all results yet.
         
     | 
| 
      
 43 
     | 
    
         
            +
                    if (this.nextQuery < this.state.queries.length) {
         
     | 
| 
      
 44 
     | 
    
         
            +
                        // setTimeout is used to clear call stack and space out
         
     | 
| 
      
 45 
     | 
    
         
            +
                        // the updates giving the browser a chance to respond
         
     | 
| 
      
 46 
     | 
    
         
            +
                        // to user interactions.
         
     | 
| 
      
 47 
     | 
    
         
            +
                        setTimeout(() => this.updateState(), 25);
         
     | 
| 
      
 48 
     | 
    
         
            +
                    } else {
         
     | 
| 
      
 49 
     | 
    
         
            +
                        this.props.componentFinishedUpdating();
         
     | 
| 
      
 50 
     | 
    
         
            +
                    }
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    this.props.plugins.componentDidUpdate(prevProps, prevState);
         
     | 
| 
      
 53 
     | 
    
         
            +
                }
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                /* eslint complexity: ["error", 6] */
         
     | 
| 
      
 56 
     | 
    
         
            +
                /* ---------------------
         
     | 
| 
      
 57 
     | 
    
         
            +
                * Push next slice of results to React for rendering.
         
     | 
| 
      
 58 
     | 
    
         
            +
                */
         
     | 
| 
      
 59 
     | 
    
         
            +
                updateState() {
         
     | 
| 
      
 60 
     | 
    
         
            +
                    var results = { items: [], numHSPsProcessed: 0 };
         
     | 
| 
      
 61 
     | 
    
         
            +
                    this.processQueries(results);
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    // Push the components to react for rendering.
         
     | 
| 
      
 64 
     | 
    
         
            +
                    this.numUpdates++;
         
     | 
| 
      
 65 
     | 
    
         
            +
                    this.lastTimeStamp = Date.now();
         
     | 
| 
      
 66 
     | 
    
         
            +
                    this.setState({
         
     | 
| 
      
 67 
     | 
    
         
            +
                        results: this.state.results.concat(results.items),
         
     | 
| 
      
 68 
     | 
    
         
            +
                        veryBig: this.numUpdates >= 250,
         
     | 
| 
      
 69 
     | 
    
         
            +
                    });
         
     | 
| 
      
 70 
     | 
    
         
            +
                }
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                processQueries(results) {
         
     | 
| 
      
 73 
     | 
    
         
            +
                    while (this.nextQuery < this.state.queries.length) {
         
     | 
| 
      
 74 
     | 
    
         
            +
                        var query = this.state.queries[this.nextQuery];
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                        // We may see a query multiple times during rendering because only
         
     | 
| 
      
 77 
     | 
    
         
            +
                        // 3 hsps are rendered in each cycle, but we want to create the
         
     | 
| 
      
 78 
     | 
    
         
            +
                        // corresponding Query component only the first time we see it.
         
     | 
| 
      
 79 
     | 
    
         
            +
                        if (this.nextHit == 0 && this.nextHSP == 0) {
         
     | 
| 
      
 80 
     | 
    
         
            +
                            results.items.push(this.renderReportQuery(query));
         
     | 
| 
      
 81 
     | 
    
         
            +
                            results.items.push(...this.props.plugins.queryResults(query));
         
     | 
| 
      
 82 
     | 
    
         
            +
                        }
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                        this.processHits(results, query);
         
     | 
| 
      
 85 
     | 
    
         
            +
                        this.itterateLoops(['nextQuery', 'nextHit'], query.hits.length);
         
     | 
| 
      
 86 
     | 
    
         
            +
                        if (results.numHSPsProcessed == this.maxHSPs) break;
         
     | 
| 
      
 87 
     | 
    
         
            +
                    }
         
     | 
| 
      
 88 
     | 
    
         
            +
                }
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                processHits(results, query) {
         
     | 
| 
      
 91 
     | 
    
         
            +
                    while (this.nextHit < query.hits.length) {
         
     | 
| 
      
 92 
     | 
    
         
            +
                        var hit = query.hits[this.nextHit];
         
     | 
| 
      
 93 
     | 
    
         
            +
                        // We may see a hit multiple times during rendering because only
         
     | 
| 
      
 94 
     | 
    
         
            +
                        // 10 hsps are rendered in each cycle, but we want to create the
         
     | 
| 
      
 95 
     | 
    
         
            +
                        // corresponding Hit component only the first time we see it.
         
     | 
| 
      
 96 
     | 
    
         
            +
                        if (this.nextHSP == 0) results.items.push(this.renderHit(query, hit));
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                        this.processHSPS(results, query, hit);
         
     | 
| 
      
 99 
     | 
    
         
            +
                        this.itterateLoops(['nextHit', 'nextHSP'], hit.hsps.length);
         
     | 
| 
      
 100 
     | 
    
         
            +
                        if (results.numHSPsProcessed == this.maxHSPs) break;
         
     | 
| 
      
 101 
     | 
    
         
            +
                    }
         
     | 
| 
      
 102 
     | 
    
         
            +
                }
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                processHSPS(results, query, hit) {
         
     | 
| 
      
 105 
     | 
    
         
            +
                    while (this.nextHSP < hit.hsps.length) {
         
     | 
| 
      
 106 
     | 
    
         
            +
                        // Get nextHSP and increment the counter.
         
     | 
| 
      
 107 
     | 
    
         
            +
                        var hsp = hit.hsps[this.nextHSP++];
         
     | 
| 
      
 108 
     | 
    
         
            +
                        results.items.push(
         
     | 
| 
      
 109 
     | 
    
         
            +
                            this.renderHsp(query, hit, hsp)
         
     | 
| 
      
 110 
     | 
    
         
            +
                        );
         
     | 
| 
      
 111 
     | 
    
         
            +
                        results.numHSPsProcessed++;
         
     | 
| 
      
 112 
     | 
    
         
            +
                        if (results.numHSPsProcessed == this.maxHSPs) break;
         
     | 
| 
      
 113 
     | 
    
         
            +
                    }
         
     | 
| 
      
 114 
     | 
    
         
            +
                }
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                /*
         
     | 
| 
      
 117 
     | 
    
         
            +
                *  this function check if 2nd argument is reach end of it
         
     | 
| 
      
 118 
     | 
    
         
            +
                */
         
     | 
| 
      
 119 
     | 
    
         
            +
                itterateLoops(args, length) {
         
     | 
| 
      
 120 
     | 
    
         
            +
                    if (this[args[1]] != length) return;
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                    this[args[0]]++;
         
     | 
| 
      
 123 
     | 
    
         
            +
                    this[args[1]] = 0;
         
     | 
| 
      
 124 
     | 
    
         
            +
                }
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                renderHsp(query, hit, hsp) {
         
     | 
| 
      
 127 
     | 
    
         
            +
                    return (
         
     | 
| 
      
 128 
     | 
    
         
            +
                        <HSP
         
     | 
| 
      
 129 
     | 
    
         
            +
                            key={
         
     | 
| 
      
 130 
     | 
    
         
            +
                                'Query_' +
         
     | 
| 
      
 131 
     | 
    
         
            +
                                    query.number +
         
     | 
| 
      
 132 
     | 
    
         
            +
                                    '_Hit_' +
         
     | 
| 
      
 133 
     | 
    
         
            +
                                    hit.number +
         
     | 
| 
      
 134 
     | 
    
         
            +
                                    '_HSP_' +
         
     | 
| 
      
 135 
     | 
    
         
            +
                                    hsp.number
         
     | 
| 
      
 136 
     | 
    
         
            +
                            }
         
     | 
| 
      
 137 
     | 
    
         
            +
                            query={query}
         
     | 
| 
      
 138 
     | 
    
         
            +
                            hit={hit}
         
     | 
| 
      
 139 
     | 
    
         
            +
                            hsp={hsp}
         
     | 
| 
      
 140 
     | 
    
         
            +
                            algorithm={this.state.program}
         
     | 
| 
      
 141 
     | 
    
         
            +
                            showHSPNumbers={hit.hsps.length > 1}
         
     | 
| 
      
 142 
     | 
    
         
            +
                            {...this.props}
         
     | 
| 
      
 143 
     | 
    
         
            +
                        />
         
     | 
| 
      
 144 
     | 
    
         
            +
                    );
         
     | 
| 
      
 145 
     | 
    
         
            +
                }
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                renderHit(query, hit) {
         
     | 
| 
      
 148 
     | 
    
         
            +
                    return (
         
     | 
| 
      
 149 
     | 
    
         
            +
                        <Hit
         
     | 
| 
      
 150 
     | 
    
         
            +
                            key={'Query_' + query.number + '_Hit_' + hit.number}
         
     | 
| 
      
 151 
     | 
    
         
            +
                            query={query}
         
     | 
| 
      
 152 
     | 
    
         
            +
                            hit={hit}
         
     | 
| 
      
 153 
     | 
    
         
            +
                            algorithm={this.state.program}
         
     | 
| 
      
 154 
     | 
    
         
            +
                            querydb={this.state.querydb}
         
     | 
| 
      
 155 
     | 
    
         
            +
                            selectHit={this.selectHit}
         
     | 
| 
      
 156 
     | 
    
         
            +
                            imported_xml={this.state.imported_xml}
         
     | 
| 
      
 157 
     | 
    
         
            +
                            non_parse_seqids={this.state.non_parse_seqids}
         
     | 
| 
      
 158 
     | 
    
         
            +
                            showQueryCrumbs={this.state.queries.length > 1}
         
     | 
| 
      
 159 
     | 
    
         
            +
                            showHitCrumbs={query.hits.length > 1}
         
     | 
| 
      
 160 
     | 
    
         
            +
                            veryBig={this.state.veryBig}
         
     | 
| 
      
 161 
     | 
    
         
            +
                            onChange={this.prepareAlignmentOfSelectedHits}
         
     | 
| 
      
 162 
     | 
    
         
            +
                            {...this.props}
         
     | 
| 
      
 163 
     | 
    
         
            +
                        />
         
     | 
| 
      
 164 
     | 
    
         
            +
                    );
         
     | 
| 
      
 165 
     | 
    
         
            +
                }
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                renderReportQuery(query) {
         
     | 
| 
      
 168 
     | 
    
         
            +
                    return (
         
     | 
| 
      
 169 
     | 
    
         
            +
                        <ReportQuery
         
     | 
| 
      
 170 
     | 
    
         
            +
                            key={'Query_' + query.id}
         
     | 
| 
      
 171 
     | 
    
         
            +
                            query={query}
         
     | 
| 
      
 172 
     | 
    
         
            +
                            program={this.state.program}
         
     | 
| 
      
 173 
     | 
    
         
            +
                            querydb={this.state.querydb}
         
     | 
| 
      
 174 
     | 
    
         
            +
                            showQueryCrumbs={this.state.queries.length > 1}
         
     | 
| 
      
 175 
     | 
    
         
            +
                            non_parse_seqids={this.state.non_parse_seqids}
         
     | 
| 
      
 176 
     | 
    
         
            +
                            imported_xml={this.state.imported_xml}
         
     | 
| 
      
 177 
     | 
    
         
            +
                            veryBig={this.state.veryBig}
         
     | 
| 
      
 178 
     | 
    
         
            +
                        />
         
     | 
| 
      
 179 
     | 
    
         
            +
                    );
         
     | 
| 
      
 180 
     | 
    
         
            +
                }
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                /**
         
     | 
| 
      
 183 
     | 
    
         
            +
                * Affixes the sidebar.
         
     | 
| 
      
 184 
     | 
    
         
            +
                */
         
     | 
| 
      
 185 
     | 
    
         
            +
                affixSidebar() {
         
     | 
| 
      
 186 
     | 
    
         
            +
                    var $sidebar = $('.sidebar');
         
     | 
| 
      
 187 
     | 
    
         
            +
                    var sidebarOffset = $sidebar.offset();
         
     | 
| 
      
 188 
     | 
    
         
            +
                    if (sidebarOffset) {
         
     | 
| 
      
 189 
     | 
    
         
            +
                        $sidebar.affix({
         
     | 
| 
      
 190 
     | 
    
         
            +
                            offset: {
         
     | 
| 
      
 191 
     | 
    
         
            +
                                top: sidebarOffset.top,
         
     | 
| 
      
 192 
     | 
    
         
            +
                            },
         
     | 
| 
      
 193 
     | 
    
         
            +
                        });
         
     | 
| 
      
 194 
     | 
    
         
            +
                    }
         
     | 
| 
      
 195 
     | 
    
         
            +
                }
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                /* eslint complexity: ["error", 6] */
         
     | 
| 
      
 198 
     | 
    
         
            +
                /* -----------------------------------
         
     | 
| 
      
 199 
     | 
    
         
            +
                * Event-handler when hit is selected
         
     | 
| 
      
 200 
     | 
    
         
            +
                * Adds glow to hit component.
         
     | 
| 
      
 201 
     | 
    
         
            +
                * Updates number of Fasta that can be downloaded
         
     | 
| 
      
 202 
     | 
    
         
            +
                */
         
     | 
| 
      
 203 
     | 
    
         
            +
                selectHit(id) {
         
     | 
| 
      
 204 
     | 
    
         
            +
                    var checkbox = $('#' + id);
         
     | 
| 
      
 205 
     | 
    
         
            +
                    var num_checked = $('.hit-links :checkbox:checked').length;
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
                    if (!checkbox || !checkbox.val()) return;
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                    var $hit = $(checkbox.data('target'));
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                    // Highlight selected hit and enable 'Download FASTA/Alignment of
         
     | 
| 
      
 212 
     | 
    
         
            +
                    // selected' links.
         
     | 
| 
      
 213 
     | 
    
         
            +
                    if (checkbox.is(':checked')) {
         
     | 
| 
      
 214 
     | 
    
         
            +
                        $hit.addClass('glow');
         
     | 
| 
      
 215 
     | 
    
         
            +
                        $hit.next('.hsp').addClass('glow');
         
     | 
| 
      
 216 
     | 
    
         
            +
                        $('.download-fasta-of-selected').enable();
         
     | 
| 
      
 217 
     | 
    
         
            +
                        $('.download-alignment-of-selected').enable();
         
     | 
| 
      
 218 
     | 
    
         
            +
                    } else {
         
     | 
| 
      
 219 
     | 
    
         
            +
                        $hit.removeClass('glow');
         
     | 
| 
      
 220 
     | 
    
         
            +
                        $hit.next('.hsp').removeClass('glow');
         
     | 
| 
      
 221 
     | 
    
         
            +
                        $('.download-fasta-of-selected').attr('href', '#').removeAttr('download');
         
     | 
| 
      
 222 
     | 
    
         
            +
                    }
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                    var $a = $('.download-fasta-of-selected');
         
     | 
| 
      
 225 
     | 
    
         
            +
                    var $b = $('.download-alignment-of-selected');
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                    if (num_checked >= 1) {
         
     | 
| 
      
 228 
     | 
    
         
            +
                        $a.find('.text-bold').html(num_checked);
         
     | 
| 
      
 229 
     | 
    
         
            +
                        $b.find('.text-bold').html(num_checked);
         
     | 
| 
      
 230 
     | 
    
         
            +
                    }
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
      
 232 
     | 
    
         
            +
                    if (num_checked == 0) {
         
     | 
| 
      
 233 
     | 
    
         
            +
                        $a.addClass('disabled').find('.text-bold').html('');
         
     | 
| 
      
 234 
     | 
    
         
            +
                        $b.addClass('disabled').find('.text-bold').html('');
         
     | 
| 
      
 235 
     | 
    
         
            +
                    }
         
     | 
| 
      
 236 
     | 
    
         
            +
                }
         
     | 
| 
      
 237 
     | 
    
         
            +
             
     | 
| 
      
 238 
     | 
    
         
            +
                prepareAlignmentOfSelectedHits() {
         
     | 
| 
      
 239 
     | 
    
         
            +
                    var sequence_ids = $('.hit-links :checkbox:checked').map(function () {
         
     | 
| 
      
 240 
     | 
    
         
            +
                        return this.value;
         
     | 
| 
      
 241 
     | 
    
         
            +
                    }).get();
         
     | 
| 
      
 242 
     | 
    
         
            +
             
     | 
| 
      
 243 
     | 
    
         
            +
                    if(!sequence_ids.length){
         
     | 
| 
      
 244 
     | 
    
         
            +
                        // remove attributes from link if sequence_ids array is empty
         
     | 
| 
      
 245 
     | 
    
         
            +
                        $('.download-alignment-of-selected').attr('href', '#').removeAttr('download');
         
     | 
| 
      
 246 
     | 
    
         
            +
                        return;
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
                    }
         
     | 
| 
      
 249 
     | 
    
         
            +
                    if(this.state.alignment_blob_url){
         
     | 
| 
      
 250 
     | 
    
         
            +
                        // always revoke existing url if any because this method will always create a new url
         
     | 
| 
      
 251 
     | 
    
         
            +
                        window.URL.revokeObjectURL(this.state.alignment_blob_url);
         
     | 
| 
      
 252 
     | 
    
         
            +
                    }
         
     | 
| 
      
 253 
     | 
    
         
            +
                    var hsps_arr = [];
         
     | 
| 
      
 254 
     | 
    
         
            +
                    var aln_exporter = new AlignmentExporter();
         
     | 
| 
      
 255 
     | 
    
         
            +
                    const self = this;
         
     | 
| 
      
 256 
     | 
    
         
            +
                    _.each(this.state.queries, _.bind(function (query) {
         
     | 
| 
      
 257 
     | 
    
         
            +
                        _.each(query.hits, function (hit) {
         
     | 
| 
      
 258 
     | 
    
         
            +
                            if (_.indexOf(sequence_ids, hit.id) != -1) {
         
     | 
| 
      
 259 
     | 
    
         
            +
                                hsps_arr = hsps_arr.concat(self.props.populate_hsp_array(hit, query.id));
         
     | 
| 
      
 260 
     | 
    
         
            +
                            }
         
     | 
| 
      
 261 
     | 
    
         
            +
                        });
         
     | 
| 
      
 262 
     | 
    
         
            +
                    }, this));
         
     | 
| 
      
 263 
     | 
    
         
            +
                    const filename = 'alignment-' + sequence_ids.length + '_hits.txt';
         
     | 
| 
      
 264 
     | 
    
         
            +
                    const blob_url = aln_exporter.prepare_alignments_for_export(hsps_arr, filename);
         
     | 
| 
      
 265 
     | 
    
         
            +
                    // set required download attributes for link
         
     | 
| 
      
 266 
     | 
    
         
            +
                    $('.download-alignment-of-selected').attr('href', blob_url).attr('download', filename);
         
     | 
| 
      
 267 
     | 
    
         
            +
                    // track new url for future removal
         
     | 
| 
      
 268 
     | 
    
         
            +
                    this.setState({alignment_blob_url: blob_url});
         
     | 
| 
      
 269 
     | 
    
         
            +
                }
         
     | 
| 
      
 270 
     | 
    
         
            +
             
     | 
| 
      
 271 
     | 
    
         
            +
                render() {
         
     | 
| 
      
 272 
     | 
    
         
            +
                    return this.state.results;
         
     | 
| 
      
 273 
     | 
    
         
            +
                }
         
     | 
| 
      
 274 
     | 
    
         
            +
            }
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
            export default Hits;
         
     | 
    
        data/public/js/jquery_world.js
    CHANGED
    
    
    
        data/public/js/mailto.js
    CHANGED
    
    | 
         @@ -5,9 +5,7 @@ export default function asMailtoHref(querydb, program, numQueries, url, isOpenAc 
     | 
|
| 
       5 
5 
     | 
    
         
             
            }
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            function formatDatabases(querydb) {
         
     | 
| 
       8 
     | 
    
         
            -
                return querydb
         
     | 
| 
       9 
     | 
    
         
            -
                    .slice(0, 15)
         
     | 
| 
       10 
     | 
    
         
            -
                    .map(db => ' ' + db.title);
         
     | 
| 
      
 8 
     | 
    
         
            +
                return querydb ? querydb.slice(0, 15).map(db => ' ' + db.title) : "";
         
     | 
| 
       11 
9 
     | 
    
         
             
            }
         
     | 
| 
       12 
10 
     | 
    
         | 
| 
       13 
11 
     | 
    
         
             
            function composeEmail(dbsArr, program, numQueries, url, isOpenAccess) {
         
     | 
    
        data/public/js/options.js
    CHANGED
    
    | 
         @@ -110,18 +110,14 @@ export class Options extends Component { 
     | 
|
| 
       110 
110 
     | 
    
         
             
                }
         
     | 
| 
       111 
111 
     | 
    
         | 
| 
       112 
112 
     | 
    
         
             
                advancedParamsJSX() {
         
     | 
| 
       113 
     | 
    
         
            -
                     
     | 
| 
       114 
     | 
    
         
            -
                        return null;
         
     | 
| 
       115 
     | 
    
         
            -
                    }
         
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
                    let classNames = 'flex-grow block px-4 py-1 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 text-base';
         
     | 
| 
      
 113 
     | 
    
         
            +
                    let classNames = 'flex-grow block px-4 py-1 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 text-base font-mono';
         
     | 
| 
       118 
114 
     | 
    
         | 
| 
       119 
115 
     | 
    
         
             
                    if (this.state.textValue) {
         
     | 
| 
       120 
116 
     | 
    
         
             
                        classNames += ' bg-yellow-100';
         
     | 
| 
       121 
117 
     | 
    
         
             
                    }
         
     | 
| 
       122 
118 
     | 
    
         | 
| 
       123 
119 
     | 
    
         
             
                    return(
         
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
      
 120 
     | 
    
         
            +
                         <div className={this.state.paramsMode !== 'advanced' ? 'w-full hidden' : 'w-full'}>
         
     | 
| 
       125 
121 
     | 
    
         
             
                            <div className="flex items-end">
         
     | 
| 
       126 
122 
     | 
    
         
             
                                <label className="flex items-center" htmlFor="advanced">
         
     | 
| 
       127 
123 
     | 
    
         
             
                                    Advanced parameters
         
     | 
    
        data/public/js/query.js
    CHANGED
    
    | 
         @@ -348,7 +348,7 @@ export class SearchQueryWidget extends Component { 
     | 
|
| 
       348 
348 
     | 
    
         
             
                                className="sequence">
         
     | 
| 
       349 
349 
     | 
    
         
             
                                <textarea
         
     | 
| 
       350 
350 
     | 
    
         
             
                                    id="sequence" ref={this.textareaRef}
         
     | 
| 
       351 
     | 
    
         
            -
                                    className="block w-full p-4 text-gray-900 border border-gray-300 rounded-l-lg rounded-tr-lg bg-gray-50 text-base  
     | 
| 
      
 351 
     | 
    
         
            +
                                    className="block w-full p-4 text-gray-900 border border-gray-300 rounded-l-lg rounded-tr-lg bg-gray-50 text-base font-mono"
         
     | 
| 
       352 
352 
     | 
    
         
             
                                    name="sequence" value={this.state.value}
         
     | 
| 
       353 
353 
     | 
    
         
             
                                    rows="6"
         
     | 
| 
       354 
354 
     | 
    
         
             
                                    required="required"
         
     |