sequenceserver 1.1.0.beta8 → 1.1.0.beta10
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.
Potentially problematic release.
This version of sequenceserver might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/sequenceserver/blast/hsp.rb +2 -174
- data/lib/sequenceserver/blast/report.rb +4 -3
- data/lib/sequenceserver/job.rb +2 -1
- data/lib/sequenceserver/version.rb +1 -1
- data/public/css/grapher.css +50 -35
- data/public/css/sequenceserver.css +25 -16
- data/public/css/sequenceserver.min.css +3 -3
- data/public/js/circos.js +1 -1
- data/public/js/grapher.js +8 -3
- data/public/js/hits_overview.js +4 -6
- data/public/js/hsp.js +283 -0
- data/public/js/kablammo.js +3 -3
- data/public/js/report.js +668 -850
- data/public/js/search.js +208 -180
- data/public/js/utils.js +77 -0
- data/public/sequenceserver-report.min.js +14 -14
- data/public/sequenceserver-search.min.js +2 -2
- metadata +4 -2
    
        data/public/js/circos.js
    CHANGED
    
    
    
        data/public/js/grapher.js
    CHANGED
    
    | @@ -18,8 +18,9 @@ export default function Grapher(Graph) { | |
| 18 18 | 
             
                    }
         | 
| 19 19 |  | 
| 20 20 | 
             
                    render () {
         | 
| 21 | 
            +
                        var cssClasses = Graph.className() + ' grapher';
         | 
| 21 22 | 
             
                        return (
         | 
| 22 | 
            -
                            <div  | 
| 23 | 
            +
                            <div ref="grapher" className={cssClasses}>
         | 
| 23 24 | 
             
                                <div className="grapher-header">
         | 
| 24 25 | 
             
                                    <h5 className="caption" data-toggle="collapse"
         | 
| 25 26 | 
             
                                        data-target={"#"+this.collapseId()}>
         | 
| @@ -113,9 +114,13 @@ $(window).resize(_.debounce(function () { | |
| 113 114 | 
             
            // Swap-icon and toggle .graph-links on collapse.
         | 
| 114 115 | 
             
            $('body').on('hidden.bs.collapse', ".collapse", function () {
         | 
| 115 116 | 
             
                var component = Graphers[$(this).attr('id')];
         | 
| 116 | 
            -
                component | 
| 117 | 
            +
                if (component) {
         | 
| 118 | 
            +
                    component.setState({ collapsed: true });
         | 
| 119 | 
            +
                }
         | 
| 117 120 | 
             
            });
         | 
| 118 121 | 
             
            $('body').on('shown.bs.collapse', ".collapse", function () {
         | 
| 119 122 | 
             
                var component = Graphers[$(this).attr('id')];
         | 
| 120 | 
            -
                component | 
| 123 | 
            +
                if (component) {
         | 
| 124 | 
            +
                    component.setState({ collapsed: false });
         | 
| 125 | 
            +
                }
         | 
| 121 126 | 
             
            });
         | 
    
        data/public/js/hits_overview.js
    CHANGED
    
    | @@ -6,7 +6,7 @@ import * as Helpers from './visualisation_helpers'; | |
| 6 6 | 
             
            class Graph {
         | 
| 7 7 |  | 
| 8 8 | 
             
              static name() {
         | 
| 9 | 
            -
                return ' | 
| 9 | 
            +
                return 'Graphical overview of hits';
         | 
| 10 10 | 
             
              }
         | 
| 11 11 |  | 
| 12 12 | 
             
              static className() {
         | 
| @@ -207,14 +207,12 @@ class Graph { | |
| 207 207 |  | 
| 208 208 | 
             
              graphIt($queryDiv, $graphDiv, index, howMany, opts, inhits) {
         | 
| 209 209 | 
             
                /* barHeight: Height of each hit track.
         | 
| 210 | 
            -
                 * barPadding: Padding around each hit track.
         | 
| 211 210 | 
             
                 * legend: Height reserved for the overview legend.
         | 
| 212 211 | 
             
                 * margin: Margin around the svg element.
         | 
| 213 212 | 
             
                 */
         | 
| 214 213 | 
             
                var defaults = {
         | 
| 215 | 
            -
                    barHeight:  | 
| 216 | 
            -
                     | 
| 217 | 
            -
                    legend: 5,
         | 
| 214 | 
            +
                    barHeight: 3,
         | 
| 215 | 
            +
                    legend: inhits.length > 1 ? 3 : 0,
         | 
| 218 216 | 
             
                    margin: 20
         | 
| 219 217 | 
             
                },
         | 
| 220 218 | 
             
                    options = $.extend(defaults, opts);
         | 
| @@ -233,7 +231,7 @@ class Graph { | |
| 233 231 | 
             
                var q_i = $queryDiv.attr('id');
         | 
| 234 232 |  | 
| 235 233 | 
             
                var width = $graphDiv.width();
         | 
| 236 | 
            -
                var height = hits.length * (options.barHeight | 
| 234 | 
            +
                var height = hits.length * (options.barHeight) +
         | 
| 237 235 | 
             
                    2 * options.legend + 5 * options.margin;
         | 
| 238 236 | 
             
                // var height = $graphDiv.height();
         | 
| 239 237 |  | 
    
        data/public/js/hsp.js
    ADDED
    
    | @@ -0,0 +1,283 @@ | |
| 1 | 
            +
            import React from 'react';
         | 
| 2 | 
            +
            import _ from 'underscore';
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            import Utils from './utils';
         | 
| 5 | 
            +
            import * as Helpers from './visualisation_helpers';
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            var HSPComponents = {};
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            /**
         | 
| 10 | 
            +
             * Alignment viewer.
         | 
| 11 | 
            +
             */
         | 
| 12 | 
            +
            export default class HSP extends React.Component {
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                constructor(props) {
         | 
| 15 | 
            +
                    super(props);
         | 
| 16 | 
            +
                    this.hsp = props.hsp;
         | 
| 17 | 
            +
                }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                domID() {
         | 
| 20 | 
            +
                    return "Query_" + this.props.query.number + "_hit_" +
         | 
| 21 | 
            +
                        this.props.hit.number + "_" + this.props.hsp.number;
         | 
| 22 | 
            +
                }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                // Renders pretty formatted alignment.
         | 
| 25 | 
            +
                render () {
         | 
| 26 | 
            +
                    return (
         | 
| 27 | 
            +
                        <div className="hsp" id={this.domID()} key={this.domID()} ref="hsp">
         | 
| 28 | 
            +
                            <pre className="pre-reset hsp-stats">
         | 
| 29 | 
            +
                                {Helpers.toLetters(this.hsp.number) + "."} {this.hspStats()}
         | 
| 30 | 
            +
                            </pre>
         | 
| 31 | 
            +
                            {this.hspLines()}
         | 
| 32 | 
            +
                        </div>
         | 
| 33 | 
            +
                    );
         | 
| 34 | 
            +
                }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                componentDidMount () {
         | 
| 37 | 
            +
                    HSPComponents[this.domID()] = this;
         | 
| 38 | 
            +
                    this.draw();
         | 
| 39 | 
            +
                }
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                draw () {
         | 
| 42 | 
            +
                    this.chars = $(React.findDOMNode(this.refs.hsp)).width() / 7.35;
         | 
| 43 | 
            +
                    this.forceUpdate();
         | 
| 44 | 
            +
                }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                /**
         | 
| 47 | 
            +
                 * Return prettified stats for the given hsp and based on the BLAST
         | 
| 48 | 
            +
                 * algorithm.
         | 
| 49 | 
            +
                 */
         | 
| 50 | 
            +
                hspStats () {
         | 
| 51 | 
            +
                    let line = [];
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    // Bit score and total score.
         | 
| 54 | 
            +
                    line.push(`Score: ${Utils.inTwoDecimal(this.hsp.bit_score)} (${this.hsp.score}), `);
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    // E value
         | 
| 57 | 
            +
                    line.push(`E value: `); line.push(Utils.inExponential(this.hsp.evalue)); line.push(', ');
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    // Identity
         | 
| 60 | 
            +
                    line.push([`Identities: ${Utils.inFraction(this.hsp.identity, this.hsp.length)} (${Utils.inPercentage(this.hsp.identity, this.hsp.length)}), `]);
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    // Positives (if this is a protein alignment).
         | 
| 63 | 
            +
                    if (this.props.algorithm === 'blastp' ||
         | 
| 64 | 
            +
                        this.props.algorithm === 'tblastx') {
         | 
| 65 | 
            +
                        line.push(`Positives: ${Utils.inFraction(this.hsp.positives, this.hsp.length)} (${Utils.inPercentage(this.hsp.positives, this.hsp.length)}), `)
         | 
| 66 | 
            +
                    }
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    // Gaps
         | 
| 69 | 
            +
                    line.push(`Gaps: ${Utils.inFraction(this.hsp.gaps, this.hsp.length)} (${Utils.inPercentage(this.hsp.gaps, this.hsp.length)}), `);
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    // Query coverage
         | 
| 72 | 
            +
                    //line.push(`Query coverage: ${this.hsp.qcovhsp}%, `)
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    switch (this.props.algorithm) {
         | 
| 75 | 
            +
                        case 'tblastx':
         | 
| 76 | 
            +
                            line.push(`Frame: ${Utils.inFraction(this.hsp.qframe, this.hsp.sframe)}`)
         | 
| 77 | 
            +
                            break;
         | 
| 78 | 
            +
                        case 'blastn':
         | 
| 79 | 
            +
                            line.push(`Strand: ${(this.hsp.qframe > 0 ? '+' : '-')} / ${(this.hsp.sframe > 0 ? '+' : '-')}`)
         | 
| 80 | 
            +
                            break;
         | 
| 81 | 
            +
                        case 'blastx':
         | 
| 82 | 
            +
                            line.push(`Query Frame: ${this.hsp.qframe}`)
         | 
| 83 | 
            +
                            break;
         | 
| 84 | 
            +
                        case 'tblastn':
         | 
| 85 | 
            +
                            line.push(`Hit Frame: ${this.hsp.sframe}`)
         | 
| 86 | 
            +
                            break;
         | 
| 87 | 
            +
                    }
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    return line;
         | 
| 90 | 
            +
                }
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                hspLines () {
         | 
| 93 | 
            +
                    var pp = [];
         | 
| 94 | 
            +
                    var lines = this.lines();
         | 
| 95 | 
            +
                    var nqseq = this.nqseq();
         | 
| 96 | 
            +
                    var nsseq = this.nsseq();
         | 
| 97 | 
            +
                    var width = this.width();
         | 
| 98 | 
            +
                    var chars = this.chars - 2 * width - 8;
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    for (let i = 1; i <= lines; i++) {
         | 
| 101 | 
            +
                        let line = [];
         | 
| 102 | 
            +
                        let seq_start_index = chars * (i - 1);
         | 
| 103 | 
            +
                        let seq_stop_index = seq_start_index + chars;
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                        let lqstart = nqseq;
         | 
| 106 | 
            +
                        let lqseq = this.hsp.qseq.slice(seq_start_index, seq_stop_index);
         | 
| 107 | 
            +
                        let lqend = nqseq + (lqseq.length - lqseq.split('-').length) *
         | 
| 108 | 
            +
                            this.qframe_unit() * this.qframe_sign();
         | 
| 109 | 
            +
                        nqseq = lqend + this.qframe_unit() * this.qframe_sign();
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                        let lmseq = this.hsp.midline.slice(seq_start_index, seq_stop_index);
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                        let lsstart = nsseq;
         | 
| 114 | 
            +
                        let lsseq = this.hsp.sseq.slice(seq_start_index, seq_stop_index);
         | 
| 115 | 
            +
                        let lsend = nsseq + (lsseq.length - lsseq.split('-').length) *
         | 
| 116 | 
            +
                            this.sframe_unit() * this.sframe_sign();
         | 
| 117 | 
            +
                        nsseq = lsend + this.sframe_unit() * this.sframe_sign();
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                        line.push(this.spanCoords('Query   ' + this.formatCoords(lqstart, width) + ' '));
         | 
| 120 | 
            +
                        line.push(lqseq);
         | 
| 121 | 
            +
                        line.push(this.spanCoords(' ' + lqend));
         | 
| 122 | 
            +
                        line.push(<br/>);
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                        line.push(this.formatCoords('', width + 8) + ' ');
         | 
| 125 | 
            +
                        line.push(lmseq);
         | 
| 126 | 
            +
                        line.push(<br/>);
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                        line.push(this.spanCoords('Subject ' + this.formatCoords(lsstart, width) + ' '));
         | 
| 129 | 
            +
                        line.push(lsseq);
         | 
| 130 | 
            +
                        line.push(this.spanCoords(' ' + lsend))
         | 
| 131 | 
            +
                        line.push(<br/>);
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                        pp.push((<pre className="pre-reset hsp-lines">{line}</pre>));
         | 
| 134 | 
            +
                    }
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    return pp;
         | 
| 137 | 
            +
                }
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                // Number of lines of pairwise-alignment (i.e., each line consists of 3
         | 
| 140 | 
            +
                // lines). We draw as many pre tags.
         | 
| 141 | 
            +
                lines() {
         | 
| 142 | 
            +
                    return Math.ceil(this.hsp.length / this.chars);
         | 
| 143 | 
            +
                }
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                // Width of each line of alignment.
         | 
| 146 | 
            +
                width() {
         | 
| 147 | 
            +
                    return _.max(_.map([this.hsp.qstart, this.hsp.qend,
         | 
| 148 | 
            +
                                            this.hsp.sstart, this.hsp.send],
         | 
| 149 | 
            +
                                            (n) => { return n.toString().length }));
         | 
| 150 | 
            +
                }
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                // Alignment start coordinate for query sequence.
         | 
| 153 | 
            +
                //
         | 
| 154 | 
            +
                // This will be qstart or qend depending on the direction in which the
         | 
| 155 | 
            +
                // (translated) query sequence aligned.
         | 
| 156 | 
            +
                nqseq () {
         | 
| 157 | 
            +
                    switch (this.props.algorithm) {
         | 
| 158 | 
            +
                        case 'blastp':
         | 
| 159 | 
            +
                        case 'blastx':
         | 
| 160 | 
            +
                        case 'tblastn':
         | 
| 161 | 
            +
                        case 'tblastx':
         | 
| 162 | 
            +
                            return this.hsp.qframe >= 0 ? this.hsp.qstart : this.hsp.qend;
         | 
| 163 | 
            +
                        case 'blastn':
         | 
| 164 | 
            +
                            // BLASTN is a bit weird in that, no matter which direction the query
         | 
| 165 | 
            +
                            // sequence aligned in, qstart is taken as alignment start coordinate
         | 
| 166 | 
            +
                            // for query.
         | 
| 167 | 
            +
                            return this.hsp.qstart;
         | 
| 168 | 
            +
                    }
         | 
| 169 | 
            +
                }
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                // Alignment start coordinate for subject sequence.
         | 
| 172 | 
            +
                //
         | 
| 173 | 
            +
                // This will be sstart or send depending on the direction in which the
         | 
| 174 | 
            +
                // (translated) subject sequence aligned.
         | 
| 175 | 
            +
                nsseq () {
         | 
| 176 | 
            +
                    switch (this.props.algorithm) {
         | 
| 177 | 
            +
                        case 'blastp':
         | 
| 178 | 
            +
                        case 'blastx':
         | 
| 179 | 
            +
                        case 'tblastn':
         | 
| 180 | 
            +
                        case 'tblastx':
         | 
| 181 | 
            +
                            return this.hsp.sframe >= 0 ? this.hsp.sstart : this.hsp.send;
         | 
| 182 | 
            +
                        case 'blastn':
         | 
| 183 | 
            +
                            // BLASTN is a bit weird in that, no matter which direction the
         | 
| 184 | 
            +
                            // subject sequence aligned in, sstart is taken as alignment
         | 
| 185 | 
            +
                            // start coordinate for subject.
         | 
| 186 | 
            +
                            return this.hsp.sstart
         | 
| 187 | 
            +
                    }
         | 
| 188 | 
            +
                }
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                // Jump in query coordinate.
         | 
| 191 | 
            +
                //
         | 
| 192 | 
            +
                // Roughly,
         | 
| 193 | 
            +
                //
         | 
| 194 | 
            +
                //   qend = qstart + n * qframe_unit
         | 
| 195 | 
            +
                //
         | 
| 196 | 
            +
                // This will be 1 or 3 depending on whether the query sequence was
         | 
| 197 | 
            +
                // translated or not.
         | 
| 198 | 
            +
                qframe_unit () {
         | 
| 199 | 
            +
                    switch (this.props.algorithm) {
         | 
| 200 | 
            +
                        case 'blastp':
         | 
| 201 | 
            +
                        case 'blastn':
         | 
| 202 | 
            +
                        case 'tblastn':
         | 
| 203 | 
            +
                            return 1;
         | 
| 204 | 
            +
                        case 'blastx':
         | 
| 205 | 
            +
                            // _Translated_ nucleotide query against protein database.
         | 
| 206 | 
            +
                        case 'tblastx':
         | 
| 207 | 
            +
                            // _Translated_ nucleotide query against translated
         | 
| 208 | 
            +
                            // nucleotide database.
         | 
| 209 | 
            +
                            return 3;
         | 
| 210 | 
            +
                    }
         | 
| 211 | 
            +
                }
         | 
| 212 | 
            +
             | 
| 213 | 
            +
                // Jump in subject coordinate.
         | 
| 214 | 
            +
                //
         | 
| 215 | 
            +
                // Roughly,
         | 
| 216 | 
            +
                //
         | 
| 217 | 
            +
                //   send = sstart + n * sframe_unit
         | 
| 218 | 
            +
                //
         | 
| 219 | 
            +
                // This will be 1 or 3 depending on whether the subject sequence was
         | 
| 220 | 
            +
                // translated or not.
         | 
| 221 | 
            +
                sframe_unit () {
         | 
| 222 | 
            +
                    switch (this.props.algorithm) {
         | 
| 223 | 
            +
                        case 'blastp':
         | 
| 224 | 
            +
                        case 'blastx':
         | 
| 225 | 
            +
                        case 'blastn':
         | 
| 226 | 
            +
                            return 1;
         | 
| 227 | 
            +
                        case 'tblastn':
         | 
| 228 | 
            +
                            // Protein query against _translated_ nucleotide database.
         | 
| 229 | 
            +
                            return 3;
         | 
| 230 | 
            +
                        case 'tblastx':
         | 
| 231 | 
            +
                            // Translated nucleotide query against _translated_
         | 
| 232 | 
            +
                            // nucleotide database.
         | 
| 233 | 
            +
                            return 3;
         | 
| 234 | 
            +
                    }
         | 
| 235 | 
            +
                }
         | 
| 236 | 
            +
             | 
| 237 | 
            +
                // If we should add or subtract qframe_unit from qstart to arrive at qend.
         | 
| 238 | 
            +
                //
         | 
| 239 | 
            +
                // Roughly,
         | 
| 240 | 
            +
                //
         | 
| 241 | 
            +
                //   qend = qstart + (qframe_sign) * n * qframe_unit
         | 
| 242 | 
            +
                //
         | 
| 243 | 
            +
                // This will be +1 or -1, depending on the direction in which the
         | 
| 244 | 
            +
                // (translated) query sequence aligned.
         | 
| 245 | 
            +
                qframe_sign () {
         | 
| 246 | 
            +
                    return this.hsp.qframe >= 0 ? 1 : -1;
         | 
| 247 | 
            +
                }
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                // If we should add or subtract sframe_unit from sstart to arrive at send.
         | 
| 250 | 
            +
                //
         | 
| 251 | 
            +
                // Roughly,
         | 
| 252 | 
            +
                //
         | 
| 253 | 
            +
                //   send = sstart + (sframe_sign) * n * sframe_unit
         | 
| 254 | 
            +
                //
         | 
| 255 | 
            +
                // This will be +1 or -1, depending on the direction in which the
         | 
| 256 | 
            +
                // (translated) subject sequence aligned.
         | 
| 257 | 
            +
                sframe_sign () {
         | 
| 258 | 
            +
                    return this.hsp.sframe >= 0 ? 1 : -1;
         | 
| 259 | 
            +
                }
         | 
| 260 | 
            +
             | 
| 261 | 
            +
             | 
| 262 | 
            +
                /**
         | 
| 263 | 
            +
                 * Pad given coord with ' ' till its length == width. Returns undefined if
         | 
| 264 | 
            +
                 * width is not supplied.
         | 
| 265 | 
            +
                 */
         | 
| 266 | 
            +
                formatCoords (coord, width) {
         | 
| 267 | 
            +
                    if (width) {
         | 
| 268 | 
            +
                        let padding = width - coord.toString().length;
         | 
| 269 | 
            +
                        return Array(padding + 1).join(' ').concat([coord]);
         | 
| 270 | 
            +
                    }
         | 
| 271 | 
            +
                }
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                spanCoords (text) {
         | 
| 274 | 
            +
                    return <span className="hsp-coords">{text}</span>
         | 
| 275 | 
            +
                }
         | 
| 276 | 
            +
            }
         | 
| 277 | 
            +
             | 
| 278 | 
            +
            // Redraw if window resized.
         | 
| 279 | 
            +
            $(window).resize(_.debounce(function () {
         | 
| 280 | 
            +
                _.each(HSPComponents, (comp) => {
         | 
| 281 | 
            +
                    comp.draw();
         | 
| 282 | 
            +
                });
         | 
| 283 | 
            +
            }, 100));
         | 
    
        data/public/js/kablammo.js
    CHANGED
    
    | @@ -19,7 +19,7 @@ import * as Helpers from './visualisation_helpers'; | |
| 19 19 |  | 
| 20 20 | 
             
            class Graph {
         | 
| 21 21 | 
             
              static name() {
         | 
| 22 | 
            -
                return ' | 
| 22 | 
            +
                return 'Graphical overview of aligning region(s)';
         | 
| 23 23 | 
             
              }
         | 
| 24 24 |  | 
| 25 25 | 
             
              static className() {
         | 
| @@ -36,7 +36,7 @@ class Graph { | |
| 36 36 |  | 
| 37 37 | 
             
              constructor($svgContainer, props) {
         | 
| 38 38 | 
             
                this._zoom_scale_by = 1.4;
         | 
| 39 | 
            -
                this._padding_x =  | 
| 39 | 
            +
                this._padding_x = 12;
         | 
| 40 40 | 
             
                this._padding_y = 50;
         | 
| 41 41 |  | 
| 42 42 | 
             
                this._canvas_height = $svgContainer.height();
         | 
| @@ -212,7 +212,7 @@ class Graph { | |
| 212 212 | 
             
                             var a = self._scales.query.height;
         | 
| 213 213 | 
             
                             var b = self._scales.subject.height;
         | 
| 214 214 | 
             
                             var middle = ( b - a ) / 2;
         | 
| 215 | 
            -
                             return a + middle +  | 
| 215 | 
            +
                             return a + middle + 2; // for font-height 10px
         | 
| 216 216 | 
             
                         })
         | 
| 217 217 | 
             
                         .text(function(hsp) {
         | 
| 218 218 | 
             
                           return Helpers.toLetters(hsp.number)
         | 
    
        data/public/js/report.js
    CHANGED
    
    | @@ -7,9 +7,11 @@ import HitsOverview from './hits_overview'; | |
| 7 7 | 
             
            import LengthDistribution from './length_distribution'; // length distribution of hits
         | 
| 8 8 | 
             
            import HSPOverview from './kablammo';
         | 
| 9 9 | 
             
            import AlignmentExporter from './alignment_exporter'; // to download textual alignment
         | 
| 10 | 
            +
            import HSP from './hsp';
         | 
| 10 11 | 
             
            import './sequence';
         | 
| 11 12 |  | 
| 12 13 | 
             
            import * as Helpers from './visualisation_helpers'; // for toLetters
         | 
| 14 | 
            +
            import Utils from './utils'; // to use as mixin in Hit and HitsTable
         | 
| 13 15 | 
             
            import showErrorModal from './error_modal';
         | 
| 14 16 |  | 
| 15 17 | 
             
            /**
         | 
| @@ -29,296 +31,557 @@ var downloadFASTA = function (sequence_ids, database_ids) { | |
| 29 31 | 
             
            }
         | 
| 30 32 |  | 
| 31 33 | 
             
            /**
         | 
| 32 | 
            -
             *  | 
| 34 | 
            +
             * Base component of report page. This component is later rendered into page's
         | 
| 35 | 
            +
             * '#view' element.
         | 
| 33 36 | 
             
             */
         | 
| 34 | 
            -
            var  | 
| 37 | 
            +
            var Page = React.createClass({
         | 
| 38 | 
            +
                render: function () {
         | 
| 39 | 
            +
                    return (
         | 
| 40 | 
            +
                        <div>
         | 
| 41 | 
            +
                            {/* Provide bootstrap .container element inside the #view for
         | 
| 42 | 
            +
                                the Report component to render itself in. */}
         | 
| 43 | 
            +
                            <div className="container"><Report ref="report"/></div>
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                            {/* Required by Grapher for SVG and PNG download */}
         | 
| 46 | 
            +
                            <canvas id="png-exporter" hidden></canvas>
         | 
| 47 | 
            +
                        </div>
         | 
| 48 | 
            +
                    );
         | 
| 49 | 
            +
                }
         | 
| 50 | 
            +
            });
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            /**
         | 
| 53 | 
            +
             * Renders entire report.
         | 
| 54 | 
            +
             *
         | 
| 55 | 
            +
             * Composed of Query and Sidebar components.
         | 
| 56 | 
            +
             */
         | 
| 57 | 
            +
            var Report = React.createClass({
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                // Model //
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                getInitialState: function () {
         | 
| 62 | 
            +
                    this.fetchResults();
         | 
| 63 | 
            +
                    this.updateCycle = 0;
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    return {
         | 
| 66 | 
            +
                        search_id:       '',
         | 
| 67 | 
            +
                        program:         '',
         | 
| 68 | 
            +
                        program_version: '',
         | 
| 69 | 
            +
                        submitted_at:    '',
         | 
| 70 | 
            +
                        queries:         [],
         | 
| 71 | 
            +
                        querydb:         [],
         | 
| 72 | 
            +
                        params:          [],
         | 
| 73 | 
            +
                        stats:           []
         | 
| 74 | 
            +
                    };
         | 
| 75 | 
            +
                },
         | 
| 35 76 |  | 
| 36 77 | 
             
                /**
         | 
| 37 | 
            -
                 *  | 
| 78 | 
            +
                 * Fetch results.
         | 
| 38 79 | 
             
                 */
         | 
| 39 | 
            -
                 | 
| 40 | 
            -
                     | 
| 41 | 
            -
                     | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 80 | 
            +
                fetchResults: function () {
         | 
| 81 | 
            +
                    var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];
         | 
| 82 | 
            +
                    var component = this;
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    function poll () {
         | 
| 85 | 
            +
                        $.getJSON(location.pathname + '.json')
         | 
| 86 | 
            +
                            .complete(function (jqXHR) {
         | 
| 87 | 
            +
                                switch (jqXHR.status) {
         | 
| 88 | 
            +
                                    case 202:
         | 
| 89 | 
            +
                                        var interval;
         | 
| 90 | 
            +
                                        if (intervals.length === 1) {
         | 
| 91 | 
            +
                                            interval = intervals[0];
         | 
| 92 | 
            +
                                        }
         | 
| 93 | 
            +
                                        else {
         | 
| 94 | 
            +
                                            interval = intervals.shift();
         | 
| 95 | 
            +
                                        }
         | 
| 96 | 
            +
                                        setTimeout(poll, interval);
         | 
| 97 | 
            +
                                        break;
         | 
| 98 | 
            +
                                    case 200:
         | 
| 99 | 
            +
                                        component.updateState(jqXHR.responseJSON);
         | 
| 100 | 
            +
                                        break;
         | 
| 101 | 
            +
                                    case 404:
         | 
| 102 | 
            +
                                    case 400:
         | 
| 103 | 
            +
                                    case 500:
         | 
| 104 | 
            +
                                        showErrorModal(jqXHR.responseJSON);
         | 
| 105 | 
            +
                                        break;
         | 
| 106 | 
            +
                                }
         | 
| 107 | 
            +
                            });
         | 
| 48 108 | 
             
                    }
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                    poll();
         | 
| 49 111 | 
             
                },
         | 
| 50 112 |  | 
| 113 | 
            +
                /**
         | 
| 114 | 
            +
                 * Incrementally update state so that the rendering process is
         | 
| 115 | 
            +
                 * not overwhelemed when there are too many queries.
         | 
| 116 | 
            +
                 */
         | 
| 117 | 
            +
                updateState: function(responseJSON) {
         | 
| 118 | 
            +
                    var queries = responseJSON.queries;
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                    // Render results for first 50 queries and set flag if total queries is
         | 
| 121 | 
            +
                    // more than 250.
         | 
| 122 | 
            +
                    var numHits = 0;
         | 
| 123 | 
            +
                    responseJSON.veryBig = queries.length > 250;
         | 
| 124 | 
            +
                    //responseJSON.veryBig = !_.every(queries, (query) => {
         | 
| 125 | 
            +
                        //numHits += query.hits.length;
         | 
| 126 | 
            +
                        //return (numHits <= 500);
         | 
| 127 | 
            +
                    //});
         | 
| 128 | 
            +
                    responseJSON.queries = queries.splice(0, 50);
         | 
| 129 | 
            +
                    this.setState(responseJSON);
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                    // Render results for remaining queries.
         | 
| 132 | 
            +
                    var update = function () {
         | 
| 133 | 
            +
                        if (queries.length > 0) {
         | 
| 134 | 
            +
                            this.setState({
         | 
| 135 | 
            +
                                queries: this.state.queries.concat(queries.splice(0, 50))
         | 
| 136 | 
            +
                            });
         | 
| 137 | 
            +
                            setTimeout(update.bind(this), 500);
         | 
| 138 | 
            +
                        }
         | 
| 139 | 
            +
                        else {
         | 
| 140 | 
            +
                            this.componentFinishedUpdating();
         | 
| 141 | 
            +
                        }
         | 
| 142 | 
            +
                    };
         | 
| 143 | 
            +
                    setTimeout(update.bind(this), 500);
         | 
| 144 | 
            +
                },
         | 
| 51 145 |  | 
| 52 | 
            -
                /***********************************
         | 
| 53 | 
            -
                 * Formatters for hits & hsp table *
         | 
| 54 | 
            -
                 ***********************************/
         | 
| 55 146 |  | 
| 56 | 
            -
                //  | 
| 57 | 
            -
                 | 
| 58 | 
            -
                    return ( | 
| 147 | 
            +
                // View //
         | 
| 148 | 
            +
                render: function () {
         | 
| 149 | 
            +
                    return this.isResultAvailable() ?
         | 
| 150 | 
            +
                        this.resultsJSX() : this.loadingJSX();
         | 
| 59 151 | 
             
                },
         | 
| 60 152 |  | 
| 61 153 | 
             
                /**
         | 
| 62 | 
            -
                 * Returns  | 
| 154 | 
            +
                 * Returns loading message
         | 
| 63 155 | 
             
                 */
         | 
| 64 | 
            -
                 | 
| 65 | 
            -
                    return ( | 
| 156 | 
            +
                loadingJSX: function () {
         | 
| 157 | 
            +
                    return (
         | 
| 158 | 
            +
                        <div
         | 
| 159 | 
            +
                            className="row">
         | 
| 160 | 
            +
                            <div
         | 
| 161 | 
            +
                                className="col-md-6 col-md-offset-3 text-center">
         | 
| 162 | 
            +
                                <h1>
         | 
| 163 | 
            +
                                    <i className="fa fa-cog fa-spin"></i>  BLAST-ing
         | 
| 164 | 
            +
                                </h1>
         | 
| 165 | 
            +
                                <p>
         | 
| 166 | 
            +
                                    <br/>
         | 
| 167 | 
            +
                                    This can take some time depending on the size of your query and
         | 
| 168 | 
            +
                                    database(s). The page will update automatically when BLAST is
         | 
| 169 | 
            +
                                    done.
         | 
| 170 | 
            +
                                    <br/>
         | 
| 171 | 
            +
                                    <br/>
         | 
| 172 | 
            +
                                    You can bookmark the page and come back to it later or share
         | 
| 173 | 
            +
                                    the link with someone.
         | 
| 174 | 
            +
                                </p>
         | 
| 175 | 
            +
                            </div>
         | 
| 176 | 
            +
                        </div>
         | 
| 177 | 
            +
                    );
         | 
| 66 178 | 
             
                },
         | 
| 67 179 |  | 
| 68 180 | 
             
                /**
         | 
| 69 | 
            -
                 *  | 
| 181 | 
            +
                 * Return results JSX.
         | 
| 70 182 | 
             
                 */
         | 
| 71 | 
            -
                 | 
| 72 | 
            -
                    return  | 
| 183 | 
            +
                resultsJSX: function () {
         | 
| 184 | 
            +
                    return (
         | 
| 185 | 
            +
                        <div className="row">
         | 
| 186 | 
            +
                            { this.shouldShowSidebar() &&
         | 
| 187 | 
            +
                                (
         | 
| 188 | 
            +
                                    <div
         | 
| 189 | 
            +
                                        className="col-md-3 hidden-sm hidden-xs">
         | 
| 190 | 
            +
                                        <SideBar data={this.state} shouldShowIndex={this.shouldShowIndex()}/>
         | 
| 191 | 
            +
                                    </div>
         | 
| 192 | 
            +
                                )
         | 
| 193 | 
            +
                            }
         | 
| 194 | 
            +
                            <div className={this.shouldShowSidebar() ?
         | 
| 195 | 
            +
                                'col-md-9' : 'col-md-12'}>
         | 
| 196 | 
            +
                                { this.overviewJSX() }
         | 
| 197 | 
            +
                                { this.isHitsAvailable() 
         | 
| 198 | 
            +
                                ? <Circos queries={this.state.queries}
         | 
| 199 | 
            +
                                    program={this.state.program} collapsed="true"/> 
         | 
| 200 | 
            +
                                : <span></span> }
         | 
| 201 | 
            +
                                {
         | 
| 202 | 
            +
                                    _.map(this.state.queries, _.bind(function (query) {
         | 
| 203 | 
            +
                                        return (
         | 
| 204 | 
            +
                                            <Query key={"Query_"+query.id} query={query} data={this.state}
         | 
| 205 | 
            +
                                                selectHit={this.selectHit}/>
         | 
| 206 | 
            +
                                            );
         | 
| 207 | 
            +
                                    }, this))
         | 
| 208 | 
            +
                                }
         | 
| 209 | 
            +
                            </div>
         | 
| 210 | 
            +
                        </div>
         | 
| 211 | 
            +
                    );
         | 
| 73 212 | 
             
                },
         | 
| 74 213 |  | 
| 75 214 | 
             
                /**
         | 
| 76 | 
            -
                 *  | 
| 215 | 
            +
                 * Renders report overview.
         | 
| 77 216 | 
             
                 */
         | 
| 78 | 
            -
                 | 
| 79 | 
            -
                    return  | 
| 217 | 
            +
                overviewJSX: function () {
         | 
| 218 | 
            +
                    return (
         | 
| 219 | 
            +
                        <div className="overview">
         | 
| 220 | 
            +
                            <pre className="pre-reset">
         | 
| 221 | 
            +
                                {this.state.program_version}{this.state.submitted_at
         | 
| 222 | 
            +
                                        && `; query submitted on ${this.state.submitted_at}`}
         | 
| 223 | 
            +
                                <br/>
         | 
| 224 | 
            +
                                Databases ({this.state.stats.nsequences} sequences, 
         | 
| 225 | 
            +
                                {this.state.stats.ncharacters} characters): {
         | 
| 226 | 
            +
                                    this.state.querydb.map((db) => { return db.title }).join(", ")
         | 
| 227 | 
            +
                                }
         | 
| 228 | 
            +
                                <br/>
         | 
| 229 | 
            +
                                Parameters: {
         | 
| 230 | 
            +
                                    _.map(this.state.params, function (val, key) {
         | 
| 231 | 
            +
                                        return key + " " + val;
         | 
| 232 | 
            +
                                    }).join(", ")
         | 
| 233 | 
            +
                                }
         | 
| 234 | 
            +
                            </pre>
         | 
| 235 | 
            +
                        </div>
         | 
| 236 | 
            +
                    );
         | 
| 80 237 | 
             
                },
         | 
| 81 238 |  | 
| 239 | 
            +
             | 
| 240 | 
            +
                // Controller //
         | 
| 241 | 
            +
             | 
| 82 242 | 
             
                /**
         | 
| 83 | 
            -
                 * Returns  | 
| 84 | 
            -
                 * | 
| 243 | 
            +
                 * Returns true if results have been fetched.
         | 
| 244 | 
            +
                 *
         | 
| 245 | 
            +
                 * A holding message is shown till results are fetched.
         | 
| 85 246 | 
             
                 */
         | 
| 86 | 
            -
                 | 
| 87 | 
            -
                     | 
| 88 | 
            -
             | 
| 89 | 
            -
                        return 0
         | 
| 90 | 
            -
                    }
         | 
| 247 | 
            +
                isResultAvailable: function () {
         | 
| 248 | 
            +
                    return this.state.queries.length >= 1;
         | 
| 249 | 
            +
                },
         | 
| 91 250 |  | 
| 92 | 
            -
             | 
| 93 | 
            -
                     | 
| 94 | 
            -
                    {
         | 
| 95 | 
            -
                         | 
| 96 | 
            -
                    }
         | 
| 251 | 
            +
                isHitsAvailable: function () {
         | 
| 252 | 
            +
                    var cnt = 0;
         | 
| 253 | 
            +
                    _.each(this.state.queries, function (query) {
         | 
| 254 | 
            +
                        if(query.hits.length == 0) cnt++;
         | 
| 255 | 
            +
                    });
         | 
| 256 | 
            +
                    return !(cnt == this.state.queries.length);
         | 
| 257 | 
            +
                },
         | 
| 97 258 |  | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 259 | 
            +
                /**
         | 
| 260 | 
            +
                 * Returns true if sidebar should be shown.
         | 
| 261 | 
            +
                 *
         | 
| 262 | 
            +
                 * Sidebar is not shown if there is only one query and there are no hits
         | 
| 263 | 
            +
                 * corresponding to the query.
         | 
| 264 | 
            +
                 */
         | 
| 265 | 
            +
                shouldShowSidebar: function () {
         | 
| 266 | 
            +
                    return !(this.state.queries.length == 1 &&
         | 
| 267 | 
            +
                             this.state.queries[0].hits.length == 0);
         | 
| 268 | 
            +
                },
         | 
| 107 269 |  | 
| 108 | 
            -
            /**
         | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 270 | 
            +
                /**
         | 
| 271 | 
            +
                 * Returns true if index should be shown in the sidebar.
         | 
| 272 | 
            +
                 *
         | 
| 273 | 
            +
                 * Index is not shown in the sidebar if there are more than eight queries
         | 
| 274 | 
            +
                 * in total.
         | 
| 275 | 
            +
                 */
         | 
| 276 | 
            +
                shouldShowIndex: function () {
         | 
| 277 | 
            +
                    return this.state.queries.length <= 8;
         | 
| 278 | 
            +
                },
         | 
| 112 279 |  | 
| 113 | 
            -
                 | 
| 280 | 
            +
                /**
         | 
| 281 | 
            +
                 * Called after first call to render. The results may not be available at
         | 
| 282 | 
            +
                 * this stage and thus results DOM cannot be scripted here, unless using
         | 
| 283 | 
            +
                 * delegated events bound to the window, document, or body.
         | 
| 284 | 
            +
                 */
         | 
| 285 | 
            +
                componentDidMount: function () {
         | 
| 286 | 
            +
                    // This sets up an event handler which enables users to select text
         | 
| 287 | 
            +
                    // from hit header without collapsing the hit.
         | 
| 288 | 
            +
                    this.preventCollapseOnSelection();
         | 
| 289 | 
            +
                },
         | 
| 114 290 |  | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 291 | 
            +
                /**
         | 
| 292 | 
            +
                 * Called after each state change. Only a part of results DOM may be
         | 
| 293 | 
            +
                 * available after a state change.
         | 
| 294 | 
            +
                 */
         | 
| 295 | 
            +
                componentDidUpdate: function () {
         | 
| 296 | 
            +
                    // We track the number of updates to the component.
         | 
| 297 | 
            +
                    this.updateCycle += 1;
         | 
| 120 298 |  | 
| 121 | 
            -
                    //  | 
| 299 | 
            +
                    // Lock sidebar in its position on first update of
         | 
| 300 | 
            +
                    // results DOM.
         | 
| 301 | 
            +
                    if (this.updateCycle === 1 ) this.affixSidebar();
         | 
| 302 | 
            +
                },
         | 
| 122 303 |  | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 304 | 
            +
                /**
         | 
| 305 | 
            +
                 * Prevents folding of hits during text-selection, etc.
         | 
| 306 | 
            +
                 */
         | 
| 126 307 |  | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
                                        {this.props.sequence.id}
         | 
| 134 | 
            -
                                        <small>
         | 
| 135 | 
            -
                                              {this.props.sequence.title}
         | 
| 136 | 
            -
                                        </small>
         | 
| 137 | 
            -
                                    </h4>
         | 
| 138 | 
            -
                                </div>
         | 
| 139 | 
            -
                                <div
         | 
| 140 | 
            -
                                    className="section-content">
         | 
| 141 | 
            -
                                    <div
         | 
| 142 | 
            -
                                        className={this.widgetClass} id={this.widgetID}>
         | 
| 143 | 
            -
                                    </div>
         | 
| 144 | 
            -
                                </div>
         | 
| 145 | 
            -
                            </div>
         | 
| 146 | 
            -
                        );
         | 
| 147 | 
            -
                    },
         | 
| 308 | 
            +
                /**
         | 
| 309 | 
            +
                 * Called after all results have been rendered.
         | 
| 310 | 
            +
                 */
         | 
| 311 | 
            +
                componentFinishedUpdating: function () {
         | 
| 312 | 
            +
                    this.shouldShowIndex() && this.setupScrollSpy();
         | 
| 313 | 
            +
                },
         | 
| 148 314 |  | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
                                 | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
                                 | 
| 315 | 
            +
                /**
         | 
| 316 | 
            +
                 * Prevents folding of hits during text-selection.
         | 
| 317 | 
            +
                 */
         | 
| 318 | 
            +
                preventCollapseOnSelection: function () {
         | 
| 319 | 
            +
                    $('body').on('mousedown', ".hit > .section-header > h4", function (event) {
         | 
| 320 | 
            +
                        var $this = $(this);
         | 
| 321 | 
            +
                        $this.on('mouseup mousemove', function handler(event) {
         | 
| 322 | 
            +
                            if (event.type === 'mouseup') {
         | 
| 323 | 
            +
                                // user wants to toggle
         | 
| 324 | 
            +
                                $this.attr('data-toggle', 'collapse');
         | 
| 325 | 
            +
                                $this.find('.fa-chevron-down').toggleClass('fa-rotate-270');
         | 
| 326 | 
            +
                            } else {
         | 
| 327 | 
            +
                                // user wants to select
         | 
| 328 | 
            +
                                $this.attr('data-toggle', '');
         | 
| 162 329 | 
             
                            }
         | 
| 330 | 
            +
                            $this.off('mouseup mousemove', handler);
         | 
| 163 331 | 
             
                        });
         | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 166 | 
            -
                });
         | 
| 167 | 
            -
             | 
| 168 | 
            -
                return React.createClass({
         | 
| 169 | 
            -
             | 
| 170 | 
            -
                    // Kind of public API. //
         | 
| 171 | 
            -
             | 
| 172 | 
            -
                    /**
         | 
| 173 | 
            -
                     * Shows sequence viewer.
         | 
| 174 | 
            -
                     */
         | 
| 175 | 
            -
                    show: function () {
         | 
| 176 | 
            -
                        this.modal().modal('show');
         | 
| 177 | 
            -
                    },
         | 
| 178 | 
            -
             | 
| 179 | 
            -
             | 
| 180 | 
            -
                    // Internal helpers. //
         | 
| 181 | 
            -
             | 
| 182 | 
            -
                    modal: function () {
         | 
| 183 | 
            -
                        return $(React.findDOMNode(this.refs.modal));
         | 
| 184 | 
            -
                    },
         | 
| 332 | 
            +
                    });
         | 
| 333 | 
            +
                },
         | 
| 185 334 |  | 
| 186 | 
            -
             | 
| 187 | 
            -
             | 
| 188 | 
            -
             | 
| 189 | 
            -
             | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
                                                        {error_msg[0]}
         | 
| 198 | 
            -
                                                    </h4>
         | 
| 199 | 
            -
                                                </div>
         | 
| 200 | 
            -
                                                <div
         | 
| 201 | 
            -
                                                    className="section-content">
         | 
| 202 | 
            -
                                                    <pre
         | 
| 203 | 
            -
                                                        className="pre-reset">
         | 
| 204 | 
            -
                                                        {error_msg[1]}
         | 
| 205 | 
            -
                                                    </pre>
         | 
| 206 | 
            -
                                                </div>
         | 
| 207 | 
            -
                                            </div>
         | 
| 208 | 
            -
                                        );
         | 
| 209 | 
            -
                                    }, this))
         | 
| 210 | 
            -
                                }
         | 
| 211 | 
            -
                                {
         | 
| 212 | 
            -
                                    _.map(this.state.sequences, _.bind(function (sequence) {
         | 
| 213 | 
            -
                                        return (<Viewer sequence={sequence}/>);
         | 
| 214 | 
            -
                                    }, this))
         | 
| 215 | 
            -
                                }
         | 
| 216 | 
            -
                            </div>
         | 
| 217 | 
            -
                        );
         | 
| 218 | 
            -
                    },
         | 
| 335 | 
            +
                /**
         | 
| 336 | 
            +
                 * Affixes the sidebar.
         | 
| 337 | 
            +
                 */
         | 
| 338 | 
            +
                affixSidebar: function () {
         | 
| 339 | 
            +
                    var $sidebar = $('.sidebar');
         | 
| 340 | 
            +
                    $sidebar.affix({
         | 
| 341 | 
            +
                        offset: {
         | 
| 342 | 
            +
                            top: $sidebar.offset().top
         | 
| 343 | 
            +
                        }
         | 
| 344 | 
            +
                    });
         | 
| 345 | 
            +
                },
         | 
| 219 346 |  | 
| 220 | 
            -
             | 
| 221 | 
            -
             | 
| 222 | 
            -
             | 
| 223 | 
            -
             | 
| 224 | 
            -
             | 
| 225 | 
            -
             | 
| 226 | 
            -
                    },
         | 
| 347 | 
            +
                /**
         | 
| 348 | 
            +
                 * For the query in viewport, highlights corresponding entry in the index.
         | 
| 349 | 
            +
                 */
         | 
| 350 | 
            +
                setupScrollSpy: function () {
         | 
| 351 | 
            +
                    $('body').scrollspy({target: '.sidebar'});
         | 
| 352 | 
            +
                },
         | 
| 227 353 |  | 
| 354 | 
            +
                /**
         | 
| 355 | 
            +
                 * Event-handler when hit is selected
         | 
| 356 | 
            +
                 * Adds glow to hit component.
         | 
| 357 | 
            +
                 * Updates number of Fasta that can be downloaded
         | 
| 358 | 
            +
                 */
         | 
| 359 | 
            +
                selectHit: function (id) {
         | 
| 228 360 |  | 
| 229 | 
            -
                     | 
| 361 | 
            +
                    var checkbox = $("#" + id);
         | 
| 362 | 
            +
                    var num_checked  = $('.hit-links :checkbox:checked').length;
         | 
| 230 363 |  | 
| 231 | 
            -
                     | 
| 232 | 
            -
                        return | 
| 233 | 
            -
             | 
| 234 | 
            -
                            sequences:  [],
         | 
| 235 | 
            -
                            requestCompleted: false
         | 
| 236 | 
            -
                        };
         | 
| 237 | 
            -
                    },
         | 
| 364 | 
            +
                    if (!checkbox || !checkbox.val()) {
         | 
| 365 | 
            +
                        return;
         | 
| 366 | 
            +
                    }
         | 
| 238 367 |  | 
| 239 | 
            -
                     | 
| 240 | 
            -
                        return (
         | 
| 241 | 
            -
                            <div
         | 
| 242 | 
            -
                                className="modal sequence-viewer"
         | 
| 243 | 
            -
                                ref="modal" tabIndex="-1">
         | 
| 244 | 
            -
                                <div
         | 
| 245 | 
            -
                                    className="modal-dialog">
         | 
| 246 | 
            -
                                    <div
         | 
| 247 | 
            -
                                        className="modal-content">
         | 
| 248 | 
            -
                                        <div
         | 
| 249 | 
            -
                                            className="modal-header">
         | 
| 250 | 
            -
                                            <h3>View sequence</h3>
         | 
| 251 | 
            -
                                        </div>
         | 
| 368 | 
            +
                    var $hit = $(checkbox.data('target'));
         | 
| 252 369 |  | 
| 253 | 
            -
             | 
| 254 | 
            -
             | 
| 255 | 
            -
             | 
| 256 | 
            -
             | 
| 257 | 
            -
             | 
| 258 | 
            -
                        );
         | 
| 259 | 
            -
             | 
| 370 | 
            +
                    // Highlight selected hit and sync checkboxes if sequence viewer is open.
         | 
| 371 | 
            +
                    if (checkbox.is(":checked")) {
         | 
| 372 | 
            +
                        $hit
         | 
| 373 | 
            +
                        .addClass('glow')
         | 
| 374 | 
            +
                        .find(":checkbox").not(checkbox).check();
         | 
| 375 | 
            +
                        var $a = $('.download-fasta-of-selected');
         | 
| 376 | 
            +
                        var $b = $('.download-alignment-of-selected');
         | 
| 377 | 
            +
                        $b.enable()
         | 
| 378 | 
            +
                        var $n = $a.find('span');
         | 
| 379 | 
            +
                        $a
         | 
| 380 | 
            +
                        .enable()
         | 
| 381 | 
            +
                    }
         | 
| 260 382 |  | 
| 261 | 
            -
                     | 
| 262 | 
            -
                         | 
| 263 | 
            -
                         | 
| 383 | 
            +
                    else {
         | 
| 384 | 
            +
                        $hit
         | 
| 385 | 
            +
                        .removeClass('glow')
         | 
| 386 | 
            +
                        .find(":checkbox").not(checkbox).uncheck();
         | 
| 387 | 
            +
                    }
         | 
| 264 388 |  | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 267 | 
            -
             | 
| 268 | 
            -
             | 
| 269 | 
            -
             | 
| 270 | 
            -
             | 
| 271 | 
            -
             | 
| 272 | 
            -
                                })
         | 
| 273 | 
            -
                            }, this))
         | 
| 274 | 
            -
                            .fail(function (jqXHR, status, error) {
         | 
| 275 | 
            -
                                showErrorModal(jqXHR, function () {
         | 
| 276 | 
            -
                                    this.hide();
         | 
| 277 | 
            -
                                });
         | 
| 278 | 
            -
                            });
         | 
| 389 | 
            +
                    if (num_checked >= 1)
         | 
| 390 | 
            +
                    {
         | 
| 391 | 
            +
                        var $a = $('.download-fasta-of-selected');
         | 
| 392 | 
            +
                        var $b = $('.download-alignment-of-selected');
         | 
| 393 | 
            +
                        $a.find('.text-bold').html(num_checked);
         | 
| 394 | 
            +
                        $b.find('.text-bold').html(num_checked);
         | 
| 395 | 
            +
                    }
         | 
| 279 396 |  | 
| 280 | 
            -
             | 
| 281 | 
            -
             | 
| 282 | 
            -
             | 
| 283 | 
            -
             | 
| 397 | 
            +
                    if (num_checked == 0) {
         | 
| 398 | 
            +
                        var $a = $('.download-fasta-of-selected');
         | 
| 399 | 
            +
                        var $b = $('.download-alignment-of-selected');
         | 
| 400 | 
            +
                        $a.addClass('disabled').find('.text-bold').html('');
         | 
| 401 | 
            +
                        $b.addClass('disabled').find('.text-bold').html('');
         | 
| 402 | 
            +
                    }
         | 
| 403 | 
            +
                },
         | 
| 404 | 
            +
            });
         | 
| 284 405 |  | 
| 285 406 | 
             
            /**
         | 
| 286 | 
            -
             *  | 
| 407 | 
            +
             * Renders report for each query sequence.
         | 
| 408 | 
            +
             *
         | 
| 409 | 
            +
             * Composed of graphical overview, tabular summary (HitsTable),
         | 
| 410 | 
            +
             * and a list of Hits.
         | 
| 287 411 | 
             
             */
         | 
| 288 | 
            -
            var  | 
| 289 | 
            -
                mixins: [Utils],
         | 
| 412 | 
            +
            var Query = React.createClass({
         | 
| 290 413 |  | 
| 291 | 
            -
                 | 
| 292 | 
            -
                 * Returns accession number of the hit sequence.
         | 
| 293 | 
            -
                 */
         | 
| 294 | 
            -
                accession: function () {
         | 
| 295 | 
            -
                    return this.props.hit.accession;
         | 
| 296 | 
            -
                },
         | 
| 414 | 
            +
                // Kind of public API //
         | 
| 297 415 |  | 
| 298 416 | 
             
                /**
         | 
| 299 | 
            -
                 * Returns  | 
| 417 | 
            +
                 * Returns the id of query.
         | 
| 300 418 | 
             
                 */
         | 
| 301 | 
            -
                 | 
| 302 | 
            -
                    return this.props. | 
| 419 | 
            +
                domID: function () {
         | 
| 420 | 
            +
                    return "Query_" + this.props.query.number;
         | 
| 303 421 | 
             
                },
         | 
| 304 422 |  | 
| 305 | 
            -
                // Internal helpers. //
         | 
| 306 | 
            -
             | 
| 307 423 | 
             
                /**
         | 
| 308 | 
            -
                 * Returns  | 
| 424 | 
            +
                 * Returns number of hits.
         | 
| 309 425 | 
             
                 */
         | 
| 310 | 
            -
                 | 
| 311 | 
            -
                    return  | 
| 312 | 
            -
                },
         | 
| 313 | 
            -
             | 
| 314 | 
            -
                databaseIDs: function () {
         | 
| 315 | 
            -
                    return _.map(this.props.querydb, _.iteratee('id'));
         | 
| 426 | 
            +
                numhits: function () {
         | 
| 427 | 
            +
                    return this.props.query.hits.length;
         | 
| 316 428 | 
             
                },
         | 
| 317 429 |  | 
| 318 | 
            -
                 | 
| 319 | 
            -
             | 
| 320 | 
            -
             | 
| 321 | 
            -
             | 
| 430 | 
            +
                // Life cycle methods //
         | 
| 431 | 
            +
             | 
| 432 | 
            +
                render: function () {
         | 
| 433 | 
            +
                    return (
         | 
| 434 | 
            +
                        <div
         | 
| 435 | 
            +
                            className="resultn" id={this.domID()}
         | 
| 436 | 
            +
                            data-query-len={this.props.query.length}
         | 
| 437 | 
            +
                            data-algorithm={this.props.data.program}>
         | 
| 438 | 
            +
                            <div
         | 
| 439 | 
            +
                                className="section-header">
         | 
| 440 | 
            +
                                <h3>
         | 
| 441 | 
            +
                                    Query= {this.props.query.id}
         | 
| 442 | 
            +
                                     
         | 
| 443 | 
            +
                                    <small>
         | 
| 444 | 
            +
                                        {this.props.query.title}
         | 
| 445 | 
            +
                                    </small>
         | 
| 446 | 
            +
                                </h3>
         | 
| 447 | 
            +
                                <span
         | 
| 448 | 
            +
                                    className="label label-reset pos-label"
         | 
| 449 | 
            +
                                    title={"Query" + this.props.query.number + "."}
         | 
| 450 | 
            +
                                    data-toggle="tooltip">
         | 
| 451 | 
            +
                                    {this.props.query.number + "/" + this.props.data.queries.length}
         | 
| 452 | 
            +
                                </span>
         | 
| 453 | 
            +
                            </div>
         | 
| 454 | 
            +
                            {this.numhits() &&
         | 
| 455 | 
            +
                                (
         | 
| 456 | 
            +
                                    <div className="section-content">
         | 
| 457 | 
            +
                                        <HitsOverview key={"GO_"+this.props.query.number} query={this.props.query} program={this.props.data.program} collapsed={this.props.data.veryBig}/>
         | 
| 458 | 
            +
                                        <LengthDistribution key={"LD_"+this.props.query.id} query={this.props.query} algorithm={this.props.data.program} collapsed="true"/>
         | 
| 459 | 
            +
                                        <HitsTable key={"HT_"+this.props.query.number} query={this.props.query}/>
         | 
| 460 | 
            +
                                        <div
         | 
| 461 | 
            +
                                            id="hits">
         | 
| 462 | 
            +
                                            {
         | 
| 463 | 
            +
                                                _.map(this.props.query.hits, _.bind(function (hit) {
         | 
| 464 | 
            +
                                                    return (
         | 
| 465 | 
            +
                                                        <Hit
         | 
| 466 | 
            +
                                                            hit={hit}
         | 
| 467 | 
            +
                                                            key={"HIT_"+hit.number}
         | 
| 468 | 
            +
                                                            algorithm={this.props.data.program}
         | 
| 469 | 
            +
                                                            querydb={this.props.data.querydb}
         | 
| 470 | 
            +
                                                            query={this.props.query}
         | 
| 471 | 
            +
                                                            selectHit={this.props.selectHit}/>
         | 
| 472 | 
            +
                                                    );
         | 
| 473 | 
            +
                                                }, this))
         | 
| 474 | 
            +
                                            }
         | 
| 475 | 
            +
                                        </div>
         | 
| 476 | 
            +
                                    </div>
         | 
| 477 | 
            +
                                ) || (
         | 
| 478 | 
            +
                                    <div
         | 
| 479 | 
            +
                                        className="section-content">
         | 
| 480 | 
            +
                                        <p>
         | 
| 481 | 
            +
                                            Query length: {this.props.query.length}
         | 
| 482 | 
            +
                                        </p>
         | 
| 483 | 
            +
                                        <br/>
         | 
| 484 | 
            +
                                        <br/>
         | 
| 485 | 
            +
                                        <p>
         | 
| 486 | 
            +
                                            <strong> ****** No hits found ****** </strong>
         | 
| 487 | 
            +
                                        </p>
         | 
| 488 | 
            +
                                    </div>
         | 
| 489 | 
            +
                                )
         | 
| 490 | 
            +
                            }
         | 
| 491 | 
            +
                        </div>
         | 
| 492 | 
            +
                    )
         | 
| 493 | 
            +
                },
         | 
| 494 | 
            +
            });
         | 
| 495 | 
            +
             | 
| 496 | 
            +
            /**
         | 
| 497 | 
            +
             * Renders summary of all hits per query in a tabular form.
         | 
| 498 | 
            +
             */
         | 
| 499 | 
            +
            var HitsTable = React.createClass({
         | 
| 500 | 
            +
                mixins: [Utils],
         | 
| 501 | 
            +
                render: function () {
         | 
| 502 | 
            +
                    var count = 0,
         | 
| 503 | 
            +
                      hasName = _.every(this.props.query.hits, function(hit) {
         | 
| 504 | 
            +
                        return hit.sciname !== '';
         | 
| 505 | 
            +
                      });
         | 
| 506 | 
            +
             | 
| 507 | 
            +
                    return (
         | 
| 508 | 
            +
                        <table
         | 
| 509 | 
            +
                            className="table table-hover table-condensed tabular-view">
         | 
| 510 | 
            +
                            <thead>
         | 
| 511 | 
            +
                                <th className="text-left">#</th>
         | 
| 512 | 
            +
                                <th>Similar sequences</th>
         | 
| 513 | 
            +
                                {hasName && <th className="text-left">Species</th>}
         | 
| 514 | 
            +
                                <th className="text-right">Query coverage (%)</th>
         | 
| 515 | 
            +
                                <th className="text-right">Total score</th>
         | 
| 516 | 
            +
                                <th className="text-right">E value</th>
         | 
| 517 | 
            +
                                <th className="text-right" data-toggle="tooltip"
         | 
| 518 | 
            +
                                    data-placement="left" title="Total identity of all hsps / total length of all hsps">
         | 
| 519 | 
            +
                                    Identity (%)
         | 
| 520 | 
            +
                                </th>
         | 
| 521 | 
            +
                            </thead>
         | 
| 522 | 
            +
                            <tbody>
         | 
| 523 | 
            +
                                {
         | 
| 524 | 
            +
                                    _.map(this.props.query.hits, _.bind(function (hit) {
         | 
| 525 | 
            +
                                        return (
         | 
| 526 | 
            +
                                            <tr key={hit.number}>
         | 
| 527 | 
            +
                                                <td className="text-left">{hit.number + "."}</td>
         | 
| 528 | 
            +
                                                <td>
         | 
| 529 | 
            +
                                                    <a href={"#Query_" + this.props.query.number + "_hit_" + hit.number}>
         | 
| 530 | 
            +
                                                        {hit.id}
         | 
| 531 | 
            +
                                                    </a>
         | 
| 532 | 
            +
                                                </td>
         | 
| 533 | 
            +
                                                {hasName && <td className="text-left">{hit.sciname}</td>}
         | 
| 534 | 
            +
                                                <td className="text-right">{hit.qcovs}</td>
         | 
| 535 | 
            +
                                                <td className="text-right">{hit.score}</td>
         | 
| 536 | 
            +
                                                <td className="text-right">{this.inExponential(hit.hsps[0].evalue)}</td>
         | 
| 537 | 
            +
                                                <td className="text-right">{hit.identity}</td>
         | 
| 538 | 
            +
                                            </tr>
         | 
| 539 | 
            +
                                        )
         | 
| 540 | 
            +
                                    }, this))
         | 
| 541 | 
            +
                                }
         | 
| 542 | 
            +
                            </tbody>
         | 
| 543 | 
            +
                        </table>
         | 
| 544 | 
            +
                    );
         | 
| 545 | 
            +
                }
         | 
| 546 | 
            +
            });
         | 
| 547 | 
            +
             | 
| 548 | 
            +
            /**
         | 
| 549 | 
            +
             * Component for each hit.
         | 
| 550 | 
            +
             */
         | 
| 551 | 
            +
            var Hit = React.createClass({
         | 
| 552 | 
            +
                mixins: [Utils],
         | 
| 553 | 
            +
             | 
| 554 | 
            +
                /**
         | 
| 555 | 
            +
                 * Returns accession number of the hit sequence.
         | 
| 556 | 
            +
                 */
         | 
| 557 | 
            +
                accession: function () {
         | 
| 558 | 
            +
                    return this.props.hit.accession;
         | 
| 559 | 
            +
                },
         | 
| 560 | 
            +
             | 
| 561 | 
            +
                /**
         | 
| 562 | 
            +
                 * Returns length of the hit sequence.
         | 
| 563 | 
            +
                 */
         | 
| 564 | 
            +
                length: function () {
         | 
| 565 | 
            +
                    return this.props.hit.length;
         | 
| 566 | 
            +
                },
         | 
| 567 | 
            +
             | 
| 568 | 
            +
                // Internal helpers. //
         | 
| 569 | 
            +
             | 
| 570 | 
            +
                /**
         | 
| 571 | 
            +
                 * Returns id that will be used for the DOM node corresponding to the hit.
         | 
| 572 | 
            +
                 */
         | 
| 573 | 
            +
                domID: function () {
         | 
| 574 | 
            +
                    return "Query_" + this.props.query.number + "_hit_" + this.props.hit.number;
         | 
| 575 | 
            +
                },
         | 
| 576 | 
            +
             | 
| 577 | 
            +
                databaseIDs: function () {
         | 
| 578 | 
            +
                    return _.map(this.props.querydb, _.iteratee('id'));
         | 
| 579 | 
            +
                },
         | 
| 580 | 
            +
             | 
| 581 | 
            +
                showSequenceViewer: function (event) {
         | 
| 582 | 
            +
                    this.setState({ showSequenceViewer: true });
         | 
| 583 | 
            +
                    event && event.preventDefault();
         | 
| 584 | 
            +
                },
         | 
| 322 585 |  | 
| 323 586 | 
             
                hideSequenceViewer: function () {
         | 
| 324 587 | 
             
                    this.setState({ showSequenceViewer: false });
         | 
| @@ -346,67 +609,6 @@ var Hit = React.createClass({ | |
| 346 609 | 
             
                    aln_exporter.export_alignments(hsps, this.props.query.id+"_"+this.props.hit.id);
         | 
| 347 610 | 
             
                },
         | 
| 348 611 |  | 
| 349 | 
            -
                /**
         | 
| 350 | 
            -
                 * Return prettified stats for the given hsp and based on the BLAST
         | 
| 351 | 
            -
                 * algorithm.
         | 
| 352 | 
            -
                 */
         | 
| 353 | 
            -
                getHSPStats: function (hsp) {
         | 
| 354 | 
            -
                    var stats = {
         | 
| 355 | 
            -
                        'Score': this.format_2_tuple([
         | 
| 356 | 
            -
                            this.inTwoDecimal(hsp.bit_score),
         | 
| 357 | 
            -
                            hsp.score
         | 
| 358 | 
            -
                        ]),
         | 
| 359 | 
            -
             | 
| 360 | 
            -
                        'E value': this.inExponential(hsp.evalue),
         | 
| 361 | 
            -
             | 
| 362 | 
            -
                        'Identities': this.format_2_tuple([
         | 
| 363 | 
            -
                            this.inFraction(hsp.identity, hsp.length),
         | 
| 364 | 
            -
                            this.inPercentage(hsp.identity, hsp.length)
         | 
| 365 | 
            -
                        ]),
         | 
| 366 | 
            -
             | 
| 367 | 
            -
                        'Gaps': this.format_2_tuple([
         | 
| 368 | 
            -
                            this.inFraction(hsp.gaps, hsp.length),
         | 
| 369 | 
            -
                            this.inPercentage(hsp.gaps, hsp.length)
         | 
| 370 | 
            -
                        ]),
         | 
| 371 | 
            -
             | 
| 372 | 
            -
                        'Coverage': hsp.qcovhsp
         | 
| 373 | 
            -
                    };
         | 
| 374 | 
            -
             | 
| 375 | 
            -
                    switch (this.props.algorithm) {
         | 
| 376 | 
            -
                    case 'tblastx':
         | 
| 377 | 
            -
                        _.extend(stats, {
         | 
| 378 | 
            -
                            'Frame': this.inFraction(hsp.qframe, hsp.sframe)
         | 
| 379 | 
            -
                        });
         | 
| 380 | 
            -
                        // fall-through
         | 
| 381 | 
            -
                    case 'blastp':
         | 
| 382 | 
            -
                        _.extend(stats, {
         | 
| 383 | 
            -
                            'Positives': this.format_2_tuple([
         | 
| 384 | 
            -
                                this.inFraction(hsp.positives, hsp.length),
         | 
| 385 | 
            -
                                this.inPercentage(hsp.positives, hsp.length)
         | 
| 386 | 
            -
                            ])
         | 
| 387 | 
            -
                        });
         | 
| 388 | 
            -
                        break;
         | 
| 389 | 
            -
                    case 'blastn':
         | 
| 390 | 
            -
                        _.extend(stats, {
         | 
| 391 | 
            -
                            'Strand': (hsp.qframe > 0 ? '+' : '-') +
         | 
| 392 | 
            -
                                      "/"                          +
         | 
| 393 | 
            -
                                      (hsp.sframe > 0 ? '+' : '-')
         | 
| 394 | 
            -
                        });
         | 
| 395 | 
            -
                        break;
         | 
| 396 | 
            -
                    case 'blastx':
         | 
| 397 | 
            -
                        _.extend(stats, {
         | 
| 398 | 
            -
                            'Query Frame': hsp.qframe
         | 
| 399 | 
            -
                        });
         | 
| 400 | 
            -
                        break;
         | 
| 401 | 
            -
                    case 'tblastn':
         | 
| 402 | 
            -
                        _.extend(stats, {
         | 
| 403 | 
            -
                            'Hit Frame': hsp.sframe
         | 
| 404 | 
            -
                        });
         | 
| 405 | 
            -
                        break;
         | 
| 406 | 
            -
                    }
         | 
| 407 | 
            -
             | 
| 408 | 
            -
                    return stats;
         | 
| 409 | 
            -
                },
         | 
| 410 612 |  | 
| 411 613 | 
             
                // Life cycle methods //
         | 
| 412 614 |  | 
| @@ -515,206 +717,200 @@ var Hit = React.createClass({ | |
| 515 717 | 
             
                                <HSPOverview key={"kablammo"+this.props.query.id}
         | 
| 516 718 | 
             
                                    query={this.props.query} hit={this.props.hit}
         | 
| 517 719 | 
             
                                    algorithm={this.props.algorithm}/>
         | 
| 518 | 
            -
                                 | 
| 519 | 
            -
                                  className="table hsps">
         | 
| 520 | 
            -
                                    <tbody>
         | 
| 521 | 
            -
                                        {
         | 
| 522 | 
            -
                                            _.map (this.props.hit.hsps, _.bind( function (hsp) {
         | 
| 523 | 
            -
                                                stats_returned = this.getHSPStats(hsp);
         | 
| 524 | 
            -
                                                return (
         | 
| 525 | 
            -
                                                    <tr
         | 
| 526 | 
            -
                                                      id={"Alignment_Query_" + this.props.query.number + "_hit_"
         | 
| 527 | 
            -
                                                              + this.props.hit.number + "_" + hsp.number}
         | 
| 528 | 
            -
                                                      key={"Query_"+this.props.query.id+"_Hit_"+this.props.hit.id+"_"+hsp.number}>
         | 
| 529 | 
            -
                                                        <td>
         | 
| 530 | 
            -
                                                            {Helpers.toLetters(hsp.number) + "."}
         | 
| 531 | 
            -
                                                        </td>
         | 
| 532 | 
            -
                                                        <td
         | 
| 533 | 
            -
                                                            style={{width: "100%"}}>
         | 
| 534 | 
            -
                                                            <div
         | 
| 535 | 
            -
                                                                className="hsp"
         | 
| 536 | 
            -
                                                                id={"Query_" + this.props.query.number + "_hit_"
         | 
| 537 | 
            -
                                                                     + this.props.hit.number + "_" + hsp.number}
         | 
| 538 | 
            -
                                                                data-hsp-evalue={hsp.evalue}
         | 
| 539 | 
            -
                                                                data-hsp-start={hsp.qstart}
         | 
| 540 | 
            -
                                                                data-hsp-end={hsp.qend}
         | 
| 541 | 
            -
                                                                data-hsp-frame={hsp.sframe}>
         | 
| 542 | 
            -
                                                                <table
         | 
| 543 | 
            -
                                                                  className="table table-condensed hsp-stats">
         | 
| 544 | 
            -
                                                                    <thead>
         | 
| 545 | 
            -
                                                                    {
         | 
| 546 | 
            -
                                                                        _.map(stats_returned, function (value , key) {
         | 
| 547 | 
            -
                                                                            return(<th key={value+"_"+key}>{key}</th>);
         | 
| 548 | 
            -
                                                                        })
         | 
| 549 | 
            -
                                                                    }
         | 
| 550 | 
            -
                                                                    </thead>
         | 
| 551 | 
            -
                                                                    <tbody>
         | 
| 552 | 
            -
                                                                        <tr>
         | 
| 553 | 
            -
                                                                            {
         | 
| 554 | 
            -
                                                                                _.map(stats_returned, _.bind(function (value, key) {
         | 
| 555 | 
            -
                                                                                    return(<th key={value+"_"+key}>{value}</th>);
         | 
| 556 | 
            -
                                                                                }, this))
         | 
| 557 | 
            -
                                                                            }
         | 
| 558 | 
            -
                                                                        </tr>
         | 
| 559 | 
            -
                                                                    </tbody>
         | 
| 560 | 
            -
                                                                </table>
         | 
| 561 | 
            -
                                                                <div className="alignment">{hsp.pp}</div>
         | 
| 562 | 
            -
                                                            </div>
         | 
| 563 | 
            -
                                                        </td>
         | 
| 564 | 
            -
                                                    </tr>
         | 
| 565 | 
            -
                                                )
         | 
| 566 | 
            -
                                            }, this))
         | 
| 567 | 
            -
                                        }
         | 
| 568 | 
            -
                                    </tbody>
         | 
| 569 | 
            -
                                </table>
         | 
| 720 | 
            +
                                { this.hspListJSX() }
         | 
| 570 721 | 
             
                            </div>
         | 
| 571 722 | 
             
                        </div>
         | 
| 572 723 | 
             
                    );
         | 
| 573 | 
            -
                }
         | 
| 574 | 
            -
            });
         | 
| 575 | 
            -
             | 
| 576 | 
            -
            /**
         | 
| 577 | 
            -
             * Renders summary of all hits per query in a tabular form.
         | 
| 578 | 
            -
             */
         | 
| 579 | 
            -
            var HitsTable = React.createClass({
         | 
| 580 | 
            -
                mixins: [Utils],
         | 
| 581 | 
            -
                render: function () {
         | 
| 582 | 
            -
                    var count = 0,
         | 
| 583 | 
            -
                      hasName = _.every(this.props.query.hits, function(hit) {
         | 
| 584 | 
            -
                        return hit.sciname !== '';
         | 
| 585 | 
            -
                      });
         | 
| 724 | 
            +
                },
         | 
| 586 725 |  | 
| 587 | 
            -
             | 
| 588 | 
            -
             | 
| 589 | 
            -
             | 
| 590 | 
            -
                             | 
| 591 | 
            -
                                < | 
| 592 | 
            -
             | 
| 593 | 
            -
             | 
| 594 | 
            -
             | 
| 595 | 
            -
                                <th className="text-right">Total score</th>
         | 
| 596 | 
            -
                                <th className="text-right">E value</th>
         | 
| 597 | 
            -
                                <th className="text-right" data-toggle="tooltip"
         | 
| 598 | 
            -
                                    data-placement="left" title="Total identity of all hsps / total length of all hsps">
         | 
| 599 | 
            -
                                    Identity (%)
         | 
| 600 | 
            -
                                </th>
         | 
| 601 | 
            -
                            </thead>
         | 
| 602 | 
            -
                            <tbody>
         | 
| 603 | 
            -
                                {
         | 
| 604 | 
            -
                                    _.map(this.props.query.hits, _.bind(function (hit) {
         | 
| 605 | 
            -
                                        return (
         | 
| 606 | 
            -
                                            <tr key={hit.number}>
         | 
| 607 | 
            -
                                                <td className="text-left">{hit.number + "."}</td>
         | 
| 608 | 
            -
                                                <td>
         | 
| 609 | 
            -
                                                    <a href={"#Query_" + this.props.query.number + "_hit_" + hit.number}>
         | 
| 610 | 
            -
                                                        {hit.id}
         | 
| 611 | 
            -
                                                    </a>
         | 
| 612 | 
            -
                                                </td>
         | 
| 613 | 
            -
                                                {hasName && <td className="text-left">{hit.sciname}</td>}
         | 
| 614 | 
            -
                                                <td className="text-right">{hit.qcovs}</td>
         | 
| 615 | 
            -
                                                <td className="text-right">{hit.score}</td>
         | 
| 616 | 
            -
                                                <td className="text-right">{this.inExponential(hit.hsps[0].evalue)}</td>
         | 
| 617 | 
            -
                                                <td className="text-right">{hit.identity}</td>
         | 
| 618 | 
            -
                                            </tr>
         | 
| 619 | 
            -
                                        )
         | 
| 620 | 
            -
                                    }, this))
         | 
| 621 | 
            -
                                }
         | 
| 622 | 
            -
                            </tbody>
         | 
| 623 | 
            -
                        </table>
         | 
| 624 | 
            -
                    );
         | 
| 726 | 
            +
                hspListJSX: function () {
         | 
| 727 | 
            +
                    return <div className="hsps">
         | 
| 728 | 
            +
                        {
         | 
| 729 | 
            +
                            this.props.hit.hsps.map((hsp) => {
         | 
| 730 | 
            +
                                return <HSP algorithm={this.props.algorithm} hsp={hsp}
         | 
| 731 | 
            +
                                    query={this.props.query} hit={this.props.hit}/>}, this)
         | 
| 732 | 
            +
                        }
         | 
| 733 | 
            +
                    </div>
         | 
| 625 734 | 
             
                }
         | 
| 626 735 | 
             
            });
         | 
| 627 736 |  | 
| 737 | 
            +
             | 
| 628 738 | 
             
            /**
         | 
| 629 | 
            -
             *  | 
| 630 | 
            -
             *
         | 
| 631 | 
            -
             * Composed of graphical overview, tabular summary (HitsTable),
         | 
| 632 | 
            -
             * and a list of Hits.
         | 
| 739 | 
            +
             * Component for sequence-viewer links.
         | 
| 633 740 | 
             
             */
         | 
| 634 | 
            -
            var  | 
| 741 | 
            +
            var SequenceViewer = (function () {
         | 
| 635 742 |  | 
| 636 | 
            -
                 | 
| 743 | 
            +
                var Viewer = React.createClass({
         | 
| 637 744 |  | 
| 638 | 
            -
             | 
| 639 | 
            -
             | 
| 640 | 
            -
             | 
| 641 | 
            -
             | 
| 642 | 
            -
                     | 
| 643 | 
            -
                },
         | 
| 745 | 
            +
                    /**
         | 
| 746 | 
            +
                     * The CSS class name that will be assigned to the widget container. ID
         | 
| 747 | 
            +
                     * assigned to the widget container is derived from the same.
         | 
| 748 | 
            +
                     */
         | 
| 749 | 
            +
                    widgetClass: 'biojs-vis-sequence',
         | 
| 644 750 |  | 
| 645 | 
            -
             | 
| 646 | 
            -
                 * Returns number of hits.
         | 
| 647 | 
            -
                 */
         | 
| 648 | 
            -
                numhits: function () {
         | 
| 649 | 
            -
                    return this.props.query.hits.length;
         | 
| 650 | 
            -
                },
         | 
| 751 | 
            +
                    // Lifecycle methods. //
         | 
| 651 752 |  | 
| 652 | 
            -
             | 
| 753 | 
            +
                    render: function () {
         | 
| 754 | 
            +
                        this.widgetID =
         | 
| 755 | 
            +
                            this.widgetClass + '-' + (new Date().getUTCMilliseconds());
         | 
| 653 756 |  | 
| 654 | 
            -
             | 
| 655 | 
            -
                    return (
         | 
| 656 | 
            -
                        <div
         | 
| 657 | 
            -
                            className="resultn" id={this.domID()}
         | 
| 658 | 
            -
                            data-query-len={this.props.query.length}
         | 
| 659 | 
            -
                            data-algorithm={this.props.data.program}>
         | 
| 757 | 
            +
                        return (
         | 
| 660 758 | 
             
                            <div
         | 
| 661 | 
            -
                                className=" | 
| 662 | 
            -
                                < | 
| 663 | 
            -
                                     | 
| 664 | 
            -
                                     | 
| 665 | 
            -
             | 
| 666 | 
            -
                                         | 
| 667 | 
            -
             | 
| 668 | 
            -
             | 
| 669 | 
            -
             | 
| 670 | 
            -
             | 
| 671 | 
            -
             | 
| 672 | 
            -
                                     | 
| 673 | 
            -
                                     | 
| 674 | 
            -
             | 
| 759 | 
            +
                                className="fastan">
         | 
| 760 | 
            +
                                <div
         | 
| 761 | 
            +
                                    className="section-header">
         | 
| 762 | 
            +
                                    <h4>
         | 
| 763 | 
            +
                                        {this.props.sequence.id}
         | 
| 764 | 
            +
                                        <small>
         | 
| 765 | 
            +
                                              {this.props.sequence.title}
         | 
| 766 | 
            +
                                        </small>
         | 
| 767 | 
            +
                                    </h4>
         | 
| 768 | 
            +
                                </div>
         | 
| 769 | 
            +
                                <div
         | 
| 770 | 
            +
                                    className="section-content">
         | 
| 771 | 
            +
                                    <div
         | 
| 772 | 
            +
                                        className={this.widgetClass} id={this.widgetID}>
         | 
| 773 | 
            +
                                    </div>
         | 
| 774 | 
            +
                                </div>
         | 
| 675 775 | 
             
                            </div>
         | 
| 676 | 
            -
             | 
| 677 | 
            -
             | 
| 678 | 
            -
             | 
| 679 | 
            -
             | 
| 680 | 
            -
             | 
| 681 | 
            -
             | 
| 776 | 
            +
                        );
         | 
| 777 | 
            +
                    },
         | 
| 778 | 
            +
             | 
| 779 | 
            +
                    componentDidMount: function () {
         | 
| 780 | 
            +
                        // attach BioJS sequence viewer
         | 
| 781 | 
            +
                        var widget = new Sequence({
         | 
| 782 | 
            +
                            sequence: this.props.sequence.value,
         | 
| 783 | 
            +
                            target: this.widgetID,
         | 
| 784 | 
            +
                            format: 'PRIDE',
         | 
| 785 | 
            +
                            columns: {
         | 
| 786 | 
            +
                                size: 40,
         | 
| 787 | 
            +
                                spacedEach: 5
         | 
| 788 | 
            +
                            },
         | 
| 789 | 
            +
                            formatOptions: {
         | 
| 790 | 
            +
                                title: false,
         | 
| 791 | 
            +
                                footer: false
         | 
| 792 | 
            +
                            }
         | 
| 793 | 
            +
                        });
         | 
| 794 | 
            +
                        widget.hideFormatSelector();
         | 
| 795 | 
            +
                    }
         | 
| 796 | 
            +
                });
         | 
| 797 | 
            +
             | 
| 798 | 
            +
                return React.createClass({
         | 
| 799 | 
            +
             | 
| 800 | 
            +
                    // Kind of public API. //
         | 
| 801 | 
            +
             | 
| 802 | 
            +
                    /**
         | 
| 803 | 
            +
                     * Shows sequence viewer.
         | 
| 804 | 
            +
                     */
         | 
| 805 | 
            +
                    show: function () {
         | 
| 806 | 
            +
                        this.modal().modal('show');
         | 
| 807 | 
            +
                    },
         | 
| 808 | 
            +
             | 
| 809 | 
            +
             | 
| 810 | 
            +
                    // Internal helpers. //
         | 
| 811 | 
            +
             | 
| 812 | 
            +
                    modal: function () {
         | 
| 813 | 
            +
                        return $(React.findDOMNode(this.refs.modal));
         | 
| 814 | 
            +
                    },
         | 
| 815 | 
            +
             | 
| 816 | 
            +
                    resultsJSX: function () {
         | 
| 817 | 
            +
                        return (
         | 
| 818 | 
            +
                            <div className="modal-body">
         | 
| 819 | 
            +
                                {
         | 
| 820 | 
            +
                                    _.map(this.state.error_msgs, _.bind(function (error_msg) {
         | 
| 821 | 
            +
                                        return (
         | 
| 822 | 
            +
                                            <div
         | 
| 823 | 
            +
                                                className="fastan">
         | 
| 824 | 
            +
                                                <div
         | 
| 825 | 
            +
                                                    className="section-header">
         | 
| 826 | 
            +
                                                    <h4>
         | 
| 827 | 
            +
                                                        {error_msg[0]}
         | 
| 828 | 
            +
                                                    </h4>
         | 
| 829 | 
            +
                                                </div>
         | 
| 830 | 
            +
                                                <div
         | 
| 831 | 
            +
                                                    className="section-content">
         | 
| 832 | 
            +
                                                    <pre
         | 
| 833 | 
            +
                                                        className="pre-reset">
         | 
| 834 | 
            +
                                                        {error_msg[1]}
         | 
| 835 | 
            +
                                                    </pre>
         | 
| 836 | 
            +
                                                </div>
         | 
| 837 | 
            +
                                            </div>
         | 
| 838 | 
            +
                                        );
         | 
| 839 | 
            +
                                    }, this))
         | 
| 840 | 
            +
                                }
         | 
| 841 | 
            +
                                {
         | 
| 842 | 
            +
                                    _.map(this.state.sequences, _.bind(function (sequence) {
         | 
| 843 | 
            +
                                        return (<Viewer sequence={sequence}/>);
         | 
| 844 | 
            +
                                    }, this))
         | 
| 845 | 
            +
                                }
         | 
| 846 | 
            +
                            </div>
         | 
| 847 | 
            +
                        );
         | 
| 848 | 
            +
                    },
         | 
| 849 | 
            +
             | 
| 850 | 
            +
                    loadingJSX: function () {
         | 
| 851 | 
            +
                        return (
         | 
| 852 | 
            +
                            <div className="modal-body text-center">
         | 
| 853 | 
            +
                                <i className="fa fa-spinner fa-3x fa-spin"></i>
         | 
| 854 | 
            +
                            </div>
         | 
| 855 | 
            +
                        );
         | 
| 856 | 
            +
                    },
         | 
| 857 | 
            +
             | 
| 858 | 
            +
             | 
| 859 | 
            +
                    // Lifecycle methods. //
         | 
| 860 | 
            +
             | 
| 861 | 
            +
                    getInitialState: function () {
         | 
| 862 | 
            +
                        return {
         | 
| 863 | 
            +
                            error_msgs: [],
         | 
| 864 | 
            +
                            sequences:  [],
         | 
| 865 | 
            +
                            requestCompleted: false
         | 
| 866 | 
            +
                        };
         | 
| 867 | 
            +
                    },
         | 
| 868 | 
            +
             | 
| 869 | 
            +
                    render: function () {
         | 
| 870 | 
            +
                        return (
         | 
| 871 | 
            +
                            <div
         | 
| 872 | 
            +
                                className="modal sequence-viewer"
         | 
| 873 | 
            +
                                ref="modal" tabIndex="-1">
         | 
| 874 | 
            +
                                <div
         | 
| 875 | 
            +
                                    className="modal-dialog">
         | 
| 876 | 
            +
                                    <div
         | 
| 877 | 
            +
                                        className="modal-content">
         | 
| 682 878 | 
             
                                        <div
         | 
| 683 | 
            -
                                             | 
| 684 | 
            -
                                             | 
| 685 | 
            -
                                                _.map(this.props.query.hits, _.bind(function (hit) {
         | 
| 686 | 
            -
                                                    return (
         | 
| 687 | 
            -
                                                        <Hit
         | 
| 688 | 
            -
                                                            hit={hit}
         | 
| 689 | 
            -
                                                            key={"HIT_"+hit.number}
         | 
| 690 | 
            -
                                                            algorithm={this.props.data.program}
         | 
| 691 | 
            -
                                                            querydb={this.props.data.querydb}
         | 
| 692 | 
            -
                                                            query={this.props.query}
         | 
| 693 | 
            -
                                                            selectHit={this.props.selectHit}/>
         | 
| 694 | 
            -
                                                    );
         | 
| 695 | 
            -
                                                }, this))
         | 
| 696 | 
            -
                                            }
         | 
| 879 | 
            +
                                            className="modal-header">
         | 
| 880 | 
            +
                                            <h3>View sequence</h3>
         | 
| 697 881 | 
             
                                        </div>
         | 
| 882 | 
            +
             | 
| 883 | 
            +
                                        { this.state.requestCompleted &&
         | 
| 884 | 
            +
                                                this.resultsJSX() || this.loadingJSX() }
         | 
| 698 885 | 
             
                                    </div>
         | 
| 699 | 
            -
                                 | 
| 700 | 
            -
             | 
| 701 | 
            -
             | 
| 702 | 
            -
             | 
| 703 | 
            -
             | 
| 704 | 
            -
             | 
| 705 | 
            -
             | 
| 706 | 
            -
             | 
| 707 | 
            -
             | 
| 708 | 
            -
             | 
| 709 | 
            -
             | 
| 710 | 
            -
             | 
| 711 | 
            -
                                 | 
| 712 | 
            -
             | 
| 713 | 
            -
             | 
| 714 | 
            -
             | 
| 715 | 
            -
             | 
| 716 | 
            -
            }) | 
| 886 | 
            +
                                </div>
         | 
| 887 | 
            +
                            </div>
         | 
| 888 | 
            +
                        );
         | 
| 889 | 
            +
                    },
         | 
| 890 | 
            +
             | 
| 891 | 
            +
                    componentDidMount: function () {
         | 
| 892 | 
            +
                        // Display modal with a spinner.
         | 
| 893 | 
            +
                        this.show();
         | 
| 894 | 
            +
             | 
| 895 | 
            +
                        // Fetch sequence and update state.
         | 
| 896 | 
            +
                        $.getJSON(this.props.url)
         | 
| 897 | 
            +
                            .done(_.bind(function (response) {
         | 
| 898 | 
            +
                                this.setState({
         | 
| 899 | 
            +
                                    sequences: response.sequences,
         | 
| 900 | 
            +
                                    error_msgs: response.error_msgs,
         | 
| 901 | 
            +
                                    requestCompleted: true
         | 
| 902 | 
            +
                                })
         | 
| 903 | 
            +
                            }, this))
         | 
| 904 | 
            +
                            .fail(function (jqXHR, status, error) {
         | 
| 905 | 
            +
                                showErrorModal(jqXHR, function () {
         | 
| 906 | 
            +
                                    this.hide();
         | 
| 907 | 
            +
                                });
         | 
| 908 | 
            +
                            });
         | 
| 717 909 |  | 
| 910 | 
            +
                        this.modal().on('hidden.bs.modal', this.props.onHide);
         | 
| 911 | 
            +
                    },
         | 
| 912 | 
            +
                });
         | 
| 913 | 
            +
            })();
         | 
| 718 914 |  | 
| 719 915 | 
             
            /**
         | 
| 720 916 | 
             
             * Renders links for downloading hit information in different formats.
         | 
| @@ -912,382 +1108,4 @@ var SideBar = React.createClass({ | |
| 912 1108 | 
             
                },
         | 
| 913 1109 | 
             
            });
         | 
| 914 1110 |  | 
| 915 | 
            -
            /**
         | 
| 916 | 
            -
             * Renders entire report.
         | 
| 917 | 
            -
             *
         | 
| 918 | 
            -
             * Composed of Query and Sidebar components.
         | 
| 919 | 
            -
             */
         | 
| 920 | 
            -
            var Report = React.createClass({
         | 
| 921 | 
            -
             | 
| 922 | 
            -
                // Model //
         | 
| 923 | 
            -
             | 
| 924 | 
            -
                getInitialState: function () {
         | 
| 925 | 
            -
                    this.fetchResults();
         | 
| 926 | 
            -
                    this.updateCycle = 0;
         | 
| 927 | 
            -
             | 
| 928 | 
            -
                    return {
         | 
| 929 | 
            -
                        search_id:       '',
         | 
| 930 | 
            -
                        program:         '',
         | 
| 931 | 
            -
                        program_version: '',
         | 
| 932 | 
            -
                        queries:         [],
         | 
| 933 | 
            -
                        querydb:         [],
         | 
| 934 | 
            -
                        params:          [],
         | 
| 935 | 
            -
                        stats:           []
         | 
| 936 | 
            -
                    };
         | 
| 937 | 
            -
                },
         | 
| 938 | 
            -
             | 
| 939 | 
            -
                /**
         | 
| 940 | 
            -
                 * Fetch results.
         | 
| 941 | 
            -
                 */
         | 
| 942 | 
            -
                fetchResults: function () {
         | 
| 943 | 
            -
                    var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];
         | 
| 944 | 
            -
                    var component = this;
         | 
| 945 | 
            -
             | 
| 946 | 
            -
                    function poll () {
         | 
| 947 | 
            -
                        $.getJSON(location.pathname + '.json')
         | 
| 948 | 
            -
                            .complete(function (jqXHR) {
         | 
| 949 | 
            -
                                switch (jqXHR.status) {
         | 
| 950 | 
            -
                                    case 202:
         | 
| 951 | 
            -
                                        var interval;
         | 
| 952 | 
            -
                                        if (intervals.length === 1) {
         | 
| 953 | 
            -
                                            interval = intervals[0];
         | 
| 954 | 
            -
                                        }
         | 
| 955 | 
            -
                                        else {
         | 
| 956 | 
            -
                                            interval = intervals.shift();
         | 
| 957 | 
            -
                                        }
         | 
| 958 | 
            -
                                        setTimeout(poll, interval);
         | 
| 959 | 
            -
                                        break;
         | 
| 960 | 
            -
                                    case 200:
         | 
| 961 | 
            -
                                        component.updateState(jqXHR.responseJSON);
         | 
| 962 | 
            -
                                        break;
         | 
| 963 | 
            -
                                    case 404:
         | 
| 964 | 
            -
                                    case 400:
         | 
| 965 | 
            -
                                    case 500:
         | 
| 966 | 
            -
                                        showErrorModal(jqXHR.responseJSON);
         | 
| 967 | 
            -
                                        break;
         | 
| 968 | 
            -
                                }
         | 
| 969 | 
            -
                            });
         | 
| 970 | 
            -
                    }
         | 
| 971 | 
            -
             | 
| 972 | 
            -
                    poll();
         | 
| 973 | 
            -
                },
         | 
| 974 | 
            -
             | 
| 975 | 
            -
                /**
         | 
| 976 | 
            -
                 * Incrementally update state so that the rendering process is
         | 
| 977 | 
            -
                 * not overwhelemed when there are too many queries.
         | 
| 978 | 
            -
                 */
         | 
| 979 | 
            -
                updateState: function(responseJSON) {
         | 
| 980 | 
            -
                    var queries = responseJSON.queries;
         | 
| 981 | 
            -
             | 
| 982 | 
            -
                    // Render results for first 50 queries and set flag if total queries is
         | 
| 983 | 
            -
                    // more than 250.
         | 
| 984 | 
            -
                    var numHits = 0;
         | 
| 985 | 
            -
                    responseJSON.veryBig = queries.length > 250;
         | 
| 986 | 
            -
                    //responseJSON.veryBig = !_.every(queries, (query) => {
         | 
| 987 | 
            -
                        //numHits += query.hits.length;
         | 
| 988 | 
            -
                        //return (numHits <= 500);
         | 
| 989 | 
            -
                    //});
         | 
| 990 | 
            -
                    responseJSON.queries = queries.splice(0, 50);
         | 
| 991 | 
            -
                    this.setState(responseJSON);
         | 
| 992 | 
            -
             | 
| 993 | 
            -
                    // Render results for remaining queries.
         | 
| 994 | 
            -
                    var update = function () {
         | 
| 995 | 
            -
                        if (queries.length > 0) {
         | 
| 996 | 
            -
                            this.setState({
         | 
| 997 | 
            -
                                queries: this.state.queries.concat(queries.splice(0, 50))
         | 
| 998 | 
            -
                            });
         | 
| 999 | 
            -
                            setTimeout(update.bind(this), 500);
         | 
| 1000 | 
            -
                        }
         | 
| 1001 | 
            -
                        else {
         | 
| 1002 | 
            -
                            this.componentFinishedUpdating();
         | 
| 1003 | 
            -
                        }
         | 
| 1004 | 
            -
                    };
         | 
| 1005 | 
            -
                    setTimeout(update.bind(this), 500);
         | 
| 1006 | 
            -
                },
         | 
| 1007 | 
            -
             | 
| 1008 | 
            -
             | 
| 1009 | 
            -
                // View //
         | 
| 1010 | 
            -
                render: function () {
         | 
| 1011 | 
            -
                    return this.isResultAvailable() ?
         | 
| 1012 | 
            -
                        this.resultsJSX() : this.loadingJSX();
         | 
| 1013 | 
            -
                },
         | 
| 1014 | 
            -
             | 
| 1015 | 
            -
                /**
         | 
| 1016 | 
            -
                 * Returns loading message
         | 
| 1017 | 
            -
                 */
         | 
| 1018 | 
            -
                loadingJSX: function () {
         | 
| 1019 | 
            -
                    return (
         | 
| 1020 | 
            -
                        <div
         | 
| 1021 | 
            -
                            className="row">
         | 
| 1022 | 
            -
                            <div
         | 
| 1023 | 
            -
                                className="col-md-6 col-md-offset-3 text-center">
         | 
| 1024 | 
            -
                                <h1>
         | 
| 1025 | 
            -
                                    <i
         | 
| 1026 | 
            -
                                        className="fa fa-cog fa-spin"></i> 
         | 
| 1027 | 
            -
                                    BLAST-ing
         | 
| 1028 | 
            -
                                </h1>
         | 
| 1029 | 
            -
                                <p>
         | 
| 1030 | 
            -
                                    <br/>
         | 
| 1031 | 
            -
                                    This can take some time depending on the size of your query and
         | 
| 1032 | 
            -
                                    database(s). The page will update automatically when BLAST is
         | 
| 1033 | 
            -
                                    done.
         | 
| 1034 | 
            -
                                    <br/>
         | 
| 1035 | 
            -
                                    <br/>
         | 
| 1036 | 
            -
                                    You can bookmark the page and come back to it later or share
         | 
| 1037 | 
            -
                                    the link with someone.
         | 
| 1038 | 
            -
                                </p>
         | 
| 1039 | 
            -
                            </div>
         | 
| 1040 | 
            -
                        </div>
         | 
| 1041 | 
            -
                    );
         | 
| 1042 | 
            -
                },
         | 
| 1043 | 
            -
             | 
| 1044 | 
            -
                /**
         | 
| 1045 | 
            -
                 * Return results JSX.
         | 
| 1046 | 
            -
                 */
         | 
| 1047 | 
            -
                resultsJSX: function () {
         | 
| 1048 | 
            -
                    return (
         | 
| 1049 | 
            -
                        <div className="row">
         | 
| 1050 | 
            -
                            { this.shouldShowSidebar() &&
         | 
| 1051 | 
            -
                                (
         | 
| 1052 | 
            -
                                    <div
         | 
| 1053 | 
            -
                                        className="col-md-3 hidden-sm hidden-xs">
         | 
| 1054 | 
            -
                                        <SideBar data={this.state} shouldShowIndex={this.shouldShowIndex()}/>
         | 
| 1055 | 
            -
                                    </div>
         | 
| 1056 | 
            -
                                )
         | 
| 1057 | 
            -
                            }
         | 
| 1058 | 
            -
                            <div className={this.shouldShowSidebar() ?
         | 
| 1059 | 
            -
                                'col-md-9' : 'col-md-12'}>
         | 
| 1060 | 
            -
                                { this.overviewJSX() }
         | 
| 1061 | 
            -
                                { this.isHitsAvailable() 
         | 
| 1062 | 
            -
                                ? <Circos queries={this.state.queries}
         | 
| 1063 | 
            -
                                    program={this.state.program} collapsed="true"/> 
         | 
| 1064 | 
            -
                                : <span></span> }
         | 
| 1065 | 
            -
                                {
         | 
| 1066 | 
            -
                                    _.map(this.state.queries, _.bind(function (query) {
         | 
| 1067 | 
            -
                                        return (
         | 
| 1068 | 
            -
                                            <Query key={"Query_"+query.id} query={query} data={this.state}
         | 
| 1069 | 
            -
                                                selectHit={this.selectHit}/>
         | 
| 1070 | 
            -
                                            );
         | 
| 1071 | 
            -
                                    }, this))
         | 
| 1072 | 
            -
                                }
         | 
| 1073 | 
            -
                            </div>
         | 
| 1074 | 
            -
                        </div>
         | 
| 1075 | 
            -
                    );
         | 
| 1076 | 
            -
                },
         | 
| 1077 | 
            -
             | 
| 1078 | 
            -
                /**
         | 
| 1079 | 
            -
                 * Renders report overview.
         | 
| 1080 | 
            -
                 */
         | 
| 1081 | 
            -
                overviewJSX: function () {
         | 
| 1082 | 
            -
                    return (
         | 
| 1083 | 
            -
                        <div
         | 
| 1084 | 
            -
                            className="overview">
         | 
| 1085 | 
            -
                            <pre
         | 
| 1086 | 
            -
                                className="pre-reset">
         | 
| 1087 | 
            -
                                {this.state.program_version}
         | 
| 1088 | 
            -
                                <br/>
         | 
| 1089 | 
            -
                                <br/>
         | 
| 1090 | 
            -
                                {
         | 
| 1091 | 
            -
                                    _.map(this.state.querydb, function (db) {
         | 
| 1092 | 
            -
                                        return db.title;
         | 
| 1093 | 
            -
                                    }).join(", ")
         | 
| 1094 | 
            -
                                }
         | 
| 1095 | 
            -
                                <br/>
         | 
| 1096 | 
            -
                                Total: {this.state.stats.nsequences} sequences,
         | 
| 1097 | 
            -
                                {this.state.stats.ncharacters} characters
         | 
| 1098 | 
            -
                                <br/>
         | 
| 1099 | 
            -
                                <br/>
         | 
| 1100 | 
            -
                                {
         | 
| 1101 | 
            -
                                    _.map(this.state.params, function (val, key) {
         | 
| 1102 | 
            -
                                        return key + " " + val;
         | 
| 1103 | 
            -
                                    }).join(", ")
         | 
| 1104 | 
            -
                                }
         | 
| 1105 | 
            -
                            </pre>
         | 
| 1106 | 
            -
                        </div>
         | 
| 1107 | 
            -
                    );
         | 
| 1108 | 
            -
                },
         | 
| 1109 | 
            -
             | 
| 1110 | 
            -
             | 
| 1111 | 
            -
                // Controller //
         | 
| 1112 | 
            -
             | 
| 1113 | 
            -
                /**
         | 
| 1114 | 
            -
                 * Returns true if results have been fetched.
         | 
| 1115 | 
            -
                 *
         | 
| 1116 | 
            -
                 * A holding message is shown till results are fetched.
         | 
| 1117 | 
            -
                 */
         | 
| 1118 | 
            -
                isResultAvailable: function () {
         | 
| 1119 | 
            -
                    return this.state.queries.length >= 1;
         | 
| 1120 | 
            -
                },
         | 
| 1121 | 
            -
             | 
| 1122 | 
            -
                isHitsAvailable: function () {
         | 
| 1123 | 
            -
                    var cnt = 0;
         | 
| 1124 | 
            -
                    _.each(this.state.queries, function (query) {
         | 
| 1125 | 
            -
                        if(query.hits.length == 0) cnt++;
         | 
| 1126 | 
            -
                    });
         | 
| 1127 | 
            -
                    return !(cnt == this.state.queries.length);
         | 
| 1128 | 
            -
                },
         | 
| 1129 | 
            -
             | 
| 1130 | 
            -
                /**
         | 
| 1131 | 
            -
                 * Returns true if sidebar should be shown.
         | 
| 1132 | 
            -
                 *
         | 
| 1133 | 
            -
                 * Sidebar is not shown if there is only one query and there are no hits
         | 
| 1134 | 
            -
                 * corresponding to the query.
         | 
| 1135 | 
            -
                 */
         | 
| 1136 | 
            -
                shouldShowSidebar: function () {
         | 
| 1137 | 
            -
                    return !(this.state.queries.length == 1 &&
         | 
| 1138 | 
            -
                             this.state.queries[0].hits.length == 0);
         | 
| 1139 | 
            -
                },
         | 
| 1140 | 
            -
             | 
| 1141 | 
            -
                /**
         | 
| 1142 | 
            -
                 * Returns true if index should be shown in the sidebar.
         | 
| 1143 | 
            -
                 *
         | 
| 1144 | 
            -
                 * Index is not shown in the sidebar if there are more than eight queries
         | 
| 1145 | 
            -
                 * in total.
         | 
| 1146 | 
            -
                 */
         | 
| 1147 | 
            -
                shouldShowIndex: function () {
         | 
| 1148 | 
            -
                    return this.state.queries.length <= 8;
         | 
| 1149 | 
            -
                },
         | 
| 1150 | 
            -
             | 
| 1151 | 
            -
                /**
         | 
| 1152 | 
            -
                 * Called after first call to render. The results may not be available at
         | 
| 1153 | 
            -
                 * this stage and thus results DOM cannot be scripted here, unless using
         | 
| 1154 | 
            -
                 * delegated events bound to the window, document, or body.
         | 
| 1155 | 
            -
                 */
         | 
| 1156 | 
            -
                componentDidMount: function () {
         | 
| 1157 | 
            -
                    // This sets up an event handler which enables users to select text
         | 
| 1158 | 
            -
                    // from hit header without collapsing the hit.
         | 
| 1159 | 
            -
                    this.preventCollapseOnSelection();
         | 
| 1160 | 
            -
                },
         | 
| 1161 | 
            -
             | 
| 1162 | 
            -
                /**
         | 
| 1163 | 
            -
                 * Called after each state change. Only a part of results DOM may be
         | 
| 1164 | 
            -
                 * available after a state change.
         | 
| 1165 | 
            -
                 */
         | 
| 1166 | 
            -
                componentDidUpdate: function () {
         | 
| 1167 | 
            -
                    // We track the number of updates to the component.
         | 
| 1168 | 
            -
                    this.updateCycle += 1;
         | 
| 1169 | 
            -
             | 
| 1170 | 
            -
                    // Lock sidebar in its position on first update of
         | 
| 1171 | 
            -
                    // results DOM.
         | 
| 1172 | 
            -
                    if (this.updateCycle === 1 ) this.affixSidebar();
         | 
| 1173 | 
            -
                },
         | 
| 1174 | 
            -
             | 
| 1175 | 
            -
                /**
         | 
| 1176 | 
            -
                 * Prevents folding of hits during text-selection, etc.
         | 
| 1177 | 
            -
                 */
         | 
| 1178 | 
            -
             | 
| 1179 | 
            -
                /**
         | 
| 1180 | 
            -
                 * Called after all results have been rendered.
         | 
| 1181 | 
            -
                 */
         | 
| 1182 | 
            -
                componentFinishedUpdating: function () {
         | 
| 1183 | 
            -
                    this.shouldShowIndex() && this.setupScrollSpy();
         | 
| 1184 | 
            -
                },
         | 
| 1185 | 
            -
             | 
| 1186 | 
            -
                /**
         | 
| 1187 | 
            -
                 * Prevents folding of hits during text-selection.
         | 
| 1188 | 
            -
                 */
         | 
| 1189 | 
            -
                preventCollapseOnSelection: function () {
         | 
| 1190 | 
            -
                    $('body').on('mousedown', ".hit > .section-header > h4", function (event) {
         | 
| 1191 | 
            -
                        var $this = $(this);
         | 
| 1192 | 
            -
                        $this.on('mouseup mousemove', function handler(event) {
         | 
| 1193 | 
            -
                            if (event.type === 'mouseup') {
         | 
| 1194 | 
            -
                                // user wants to toggle
         | 
| 1195 | 
            -
                                $this.attr('data-toggle', 'collapse');
         | 
| 1196 | 
            -
                                $this.find('.fa-chevron-down').toggleClass('fa-rotate-270');
         | 
| 1197 | 
            -
                            } else {
         | 
| 1198 | 
            -
                                // user wants to select
         | 
| 1199 | 
            -
                                $this.attr('data-toggle', '');
         | 
| 1200 | 
            -
                            }
         | 
| 1201 | 
            -
                            $this.off('mouseup mousemove', handler);
         | 
| 1202 | 
            -
                        });
         | 
| 1203 | 
            -
                    });
         | 
| 1204 | 
            -
                },
         | 
| 1205 | 
            -
             | 
| 1206 | 
            -
                /**
         | 
| 1207 | 
            -
                 * Affixes the sidebar.
         | 
| 1208 | 
            -
                 */
         | 
| 1209 | 
            -
                affixSidebar: function () {
         | 
| 1210 | 
            -
                    var $sidebar = $('.sidebar');
         | 
| 1211 | 
            -
                    $sidebar.affix({
         | 
| 1212 | 
            -
                        offset: {
         | 
| 1213 | 
            -
                            top: $sidebar.offset().top
         | 
| 1214 | 
            -
                        }
         | 
| 1215 | 
            -
                    });
         | 
| 1216 | 
            -
                },
         | 
| 1217 | 
            -
             | 
| 1218 | 
            -
                /**
         | 
| 1219 | 
            -
                 * For the query in viewport, highlights corresponding entry in the index.
         | 
| 1220 | 
            -
                 */
         | 
| 1221 | 
            -
                setupScrollSpy: function () {
         | 
| 1222 | 
            -
                    $('body').scrollspy({target: '.sidebar'});
         | 
| 1223 | 
            -
                },
         | 
| 1224 | 
            -
             | 
| 1225 | 
            -
                /**
         | 
| 1226 | 
            -
                 * Event-handler when hit is selected
         | 
| 1227 | 
            -
                 * Adds glow to hit component.
         | 
| 1228 | 
            -
                 * Updates number of Fasta that can be downloaded
         | 
| 1229 | 
            -
                 */
         | 
| 1230 | 
            -
                selectHit: function (id) {
         | 
| 1231 | 
            -
             | 
| 1232 | 
            -
                    var checkbox = $("#" + id);
         | 
| 1233 | 
            -
                    var num_checked  = $('.hit-links :checkbox:checked').length;
         | 
| 1234 | 
            -
             | 
| 1235 | 
            -
                    if (!checkbox || !checkbox.val()) {
         | 
| 1236 | 
            -
                        return;
         | 
| 1237 | 
            -
                    }
         | 
| 1238 | 
            -
             | 
| 1239 | 
            -
                    var $hit = $(checkbox.data('target'));
         | 
| 1240 | 
            -
             | 
| 1241 | 
            -
                    // Highlight selected hit and sync checkboxes if sequence viewer is open.
         | 
| 1242 | 
            -
                    if (checkbox.is(":checked")) {
         | 
| 1243 | 
            -
                        $hit
         | 
| 1244 | 
            -
                        .addClass('glow')
         | 
| 1245 | 
            -
                        .find(":checkbox").not(checkbox).check();
         | 
| 1246 | 
            -
                        var $a = $('.download-fasta-of-selected');
         | 
| 1247 | 
            -
                        var $b = $('.download-alignment-of-selected');
         | 
| 1248 | 
            -
                        $b.enable()
         | 
| 1249 | 
            -
                        var $n = $a.find('span');
         | 
| 1250 | 
            -
                        $a
         | 
| 1251 | 
            -
                        .enable()
         | 
| 1252 | 
            -
                    }
         | 
| 1253 | 
            -
             | 
| 1254 | 
            -
                    else {
         | 
| 1255 | 
            -
                        $hit
         | 
| 1256 | 
            -
                        .removeClass('glow')
         | 
| 1257 | 
            -
                        .find(":checkbox").not(checkbox).uncheck();
         | 
| 1258 | 
            -
                    }
         | 
| 1259 | 
            -
             | 
| 1260 | 
            -
                    if (num_checked >= 1)
         | 
| 1261 | 
            -
                    {
         | 
| 1262 | 
            -
                        var $a = $('.download-fasta-of-selected');
         | 
| 1263 | 
            -
                        var $b = $('.download-alignment-of-selected');
         | 
| 1264 | 
            -
                        $a.find('.text-bold').html(num_checked);
         | 
| 1265 | 
            -
                        $b.find('.text-bold').html(num_checked);
         | 
| 1266 | 
            -
                    }
         | 
| 1267 | 
            -
             | 
| 1268 | 
            -
                    if (num_checked == 0) {
         | 
| 1269 | 
            -
                        var $a = $('.download-fasta-of-selected');
         | 
| 1270 | 
            -
                        var $b = $('.download-alignment-of-selected');
         | 
| 1271 | 
            -
                        $a.addClass('disabled').find('.text-bold').html('');
         | 
| 1272 | 
            -
                        $b.addClass('disabled').find('.text-bold').html('');
         | 
| 1273 | 
            -
                    }
         | 
| 1274 | 
            -
                },
         | 
| 1275 | 
            -
            });
         | 
| 1276 | 
            -
             | 
| 1277 | 
            -
            var Page = React.createClass({
         | 
| 1278 | 
            -
                render: function () {
         | 
| 1279 | 
            -
                    return (
         | 
| 1280 | 
            -
                        <div>
         | 
| 1281 | 
            -
                            <div className="container">
         | 
| 1282 | 
            -
                                <Report ref="report"/>
         | 
| 1283 | 
            -
                            </div>
         | 
| 1284 | 
            -
             | 
| 1285 | 
            -
                            <div id='circos-demo' className='modal'></div>
         | 
| 1286 | 
            -
             | 
| 1287 | 
            -
                            <canvas id="png-exporter" hidden></canvas>
         | 
| 1288 | 
            -
                        </div>
         | 
| 1289 | 
            -
                    );
         | 
| 1290 | 
            -
                }
         | 
| 1291 | 
            -
            });
         | 
| 1292 | 
            -
             | 
| 1293 1111 | 
             
            React.render(<Page/>, document.getElementById('view'));
         |