<commit_before>define( [
            'dojo/_base/declare',
            'dojo/_base/lang',
            'dojo/_base/array',
            'JBrowse/Util',
            'JBrowse/Util/RejectableFastPromise',
            'dojo/promise/all',
            'JBrowse/Model/Range',
            'JBrowse/Model/SimpleFeature',
            'jszlib/inflate',
            'jszlib/arrayCopy'
        ],
        function(
            declare,
            dlang,
            array,
            Util,
            RejectableFastPromise,
            all,
            Range,
            SimpleFeature,
            inflate,
            arrayCopy
        ) {

var dlog = function(){ console.log.apply(console, arguments); };

var RequestWorker = declare( null,
 /**
  * @lends JBrowse.Store.BigWig.Window.RequestWorker.prototype
  */
 {

    BIG_WIG_TYPE_GRAPH: 1,
    BIG_WIG_TYPE_VSTEP: 2,
    BIG_WIG_TYPE_FSTEP: 3,

    /**
     * Worker object for reading data from a bigwig or bigbed file.
     * Manages the state necessary for traversing the index trees and
     * so forth.
     *
     * Adapted by Robert Buels from bigwig.js in the Dalliance Genome
     * Explorer by Thomas Down.
     * @constructs
     */
    constructor: function( window, chr, min, max, callback, errorCallback ) {
        this.window = window;
        this.source = window.bwg.name || undefined;

        this.blocksToFetch = [];
        this.outstanding = 0;

        this.chr = chr;
        this.min = min;
        this.max = max;
        this.callback = callback;
        this.errorCallback = errorCallback || function(e) { console.error( e, e.stack, arguments.caller ); };
    },

    cirFobRecur: function(offset, level) {
        this.outstanding += offset.length;

        var maxCirBlockSpan = 4 +  (this.window.cirBlockSize * 32);   // Upper bound on size, based on a completely full leaf node.
        var spans;
        for (var i = 0; i < offset.length; ++i) {
            var blockSpan = new Range(offset[i], Math.min(offset[i] + maxCirBlockSpan, this.window.cirTreeOffset + this.window.cirTreeLength));
            spans = spans ? spans.union( blockSpan ) : blockSpan;
        }

        var fetchRanges = spans.ranges();
        //dlog('fetchRanges: ' + fetchRanges);
        for (var r = 0; r < fetchRanges.length; ++r) {
            var fr = fetchRanges[r];
            this.cirFobStartFetch(offset, fr, level);
        }
    },

    cirFobStartFetch: function(offset, fr, level, attempts) {
        var length = fr.max() - fr.min();
        // dlog('fetching ' + fr.min() + '-' + fr.max() + ' (' + Util.humanReadableNumber(length) + ')');
        //console.log('cirfobstartfetch');
        this.window.bwg._read( fr.min(), length, dlang.hitch( this,function(resultBuffer) {
                for (var i = 0; i < offset.length; ++i) {
                        if (fr.contains(offset[i])) {
                            this.cirFobRecur2(resultBuffer, offset[i] - fr.min(), level);
                            --this.outstanding;
                            if (this.outstanding == 0) {
                                this.cirCompleted();
                            }
                        }
                    }
             }), this.errorCallback );
    },

    cirFobRecur2: function(cirBlockData, offset, level) {
        var data = this.window.bwg.newDataView( cirBlockData, offset );

        var isLeaf = data.getUint8();
        var cnt = data.getUint16( 2 );
        //dlog('cir level=' + level + '; cnt=' + cnt);

        if (isLeaf != 0) {
            for (var i = 0; i < cnt; ++i) {
                var startChrom = data.getUint32();
                var startBase = data.getUint32();
                var endChrom = data.getUint32();
                var endBase = data.getUint32();
                var blockOffset = data.getUint64();
                var blockSize   = data.getUint64();
                if ((startChrom < this.chr || (startChrom == this.chr && startBase <= this.max)) &&
                    (endChrom   > this.chr || (endChrom == this.chr && endBase >= this.min)))
                {
                    // dlog('Got an interesting block: startBase=' + startBase + '; endBase=' + endBase + '; offset=' + blockOffset + '; size=' + blockSize);
                    this.blocksToFetch.push({offset: blockOffset, size: blockSize});
                }
            }
        } else {
            var recurOffsets = [];
            for (var i = 0; i < cnt; ++i) {
                var startChrom = data.getUint32();
                var startBase = data.getUint32();
                var endChrom = data.getUint32();
                var endBase = data.getUint32();
                var blockOffset = data.getUint64();
                if ((startChrom < this.chr || (startChrom == this.chr && startBase <= this.max)) &&
                    (endChrom   > this.chr || (endChrom == this.chr && endBase >= this.min)))
                {
                    recurOffsets.push(blockOffset);
                }
            }
            if (recurOffsets.length > 0) {
                this.cirFobRecur(recurOffsets, level + 1);
            }
        }
    },

    cirCompleted: function() {
        // merge contiguous blocks
        this.blockGroupsToFetch = this.groupBlocks( this.blocksToFetch );

        if (this.blockGroupsToFetch.length == 0) {
            this.callback([]);
        } else {
            this.features = [];
            this.readFeatures();
        }
    },

    groupBlocks: function( blocks ) {

        // sort the blocks by file offset
        blocks.sort(function(b0, b1) {
                        return (b0.offset|0) - (b1.offset|0);
                    });

        // group blocks that are within 2KB of eachother
        var blockGroups = [];
        var lastBlock;
        var lastBlockEnd;
        for( var i = 0; i<blocks.length; i++ ) {
            if( lastBlock && (blocks[i].offset-lastBlockEnd) <= 2000 ) {
                lastBlock.size += blocks[i].size - lastBlockEnd + blocks[i].offset;
                lastBlock.blocks.push( blocks[i] );
            }
            else {
                blockGroups.push( lastBlock = { blocks: [blocks[i]], size: blocks[i].size, offset: blocks[i].offset } );
            }
            lastBlockEnd = lastBlock.offset + lastBlock.size;
        }

        return blockGroups;
    },

    createFeature: function(fmin, fmax, opts) {
        // dlog('createFeature(' + fmin +', ' + fmax + ', '+opts.score+')');

        var data = {
            start: fmin,
            end: fmax
        };

        for( var k in opts )
            data[k] = opts[k];

        var id = data.id;
        delete data.id;

        var f = new SimpleFeature({
            data: data,
            id: id ? id : data.start + '_' + data.end + '_' + data.score
        });

        this.features.push(f);
    },

    maybeCreateFeature: function(fmin, fmax, opts) {
        if (fmin <= this.max && fmax >= this.min) {
            this.createFeature( fmin, fmax, opts );
        }
    },

    parseSummaryBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var itemCount = bytes.byteLength/32;
        for (var i = 0; i < itemCount; ++i) {
            var chromId =   data.getInt32();
            var start =     data.getInt32();
            var end =       data.getInt32();
            var validCnt =  data.getInt32()||1;
            var minVal    = data.getFloat32();
            var maxVal    = data.getFloat32();
            var sumData   = data.getFloat32();
            var sumSqData = data.getFloat32();

            if (chromId == this.chr) {
                var summaryOpts = {score: sumData/validCnt,maxScore: maxVal,minScore:minVal};
                if (this.window.bwg.type == 'bigbed') {
                    summaryOpts.type = 'density';
                }
                this.maybeCreateFeature( start, end, summaryOpts);
            }
        }
    },

    parseBigWigBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var itemSpan = data.getUint32( 16 );
        var blockType = data.getUint8( 20 );
        var itemCount = data.getUint16( 22 );

        // dlog('processing bigwig block, type=' + blockType + '; count=' + itemCount);

        if (blockType == this.BIG_WIG_TYPE_FSTEP) {
            var blockStart = data.getInt32( 4 );
            var itemStep = data.getUint32( 12 );
            for (var i = 0; i < itemCount; ++i) {
                var score = data.getFloat32( 4*i+24 );
                this.maybeCreateFeature( blockStart + (i*itemStep), blockStart + (i*itemStep) + itemSpan, {score: score});
            }
        } else if (blockType == this.BIG_WIG_TYPE_VSTEP) {
            for (var i = 0; i < itemCount; ++i) {
                var start = data.getInt32( 8*i+24 );
                var score = data.getFloat32();
                this.maybeCreateFeature( start, start + itemSpan, {score: score});
            }
        } else if (blockType == this.BIG_WIG_TYPE_GRAPH) {
            for (var i = 0; i < itemCount; ++i) {
                var start = data.getInt32( 12*i + 24 );
                var end   = data.getInt32();
                var score = data.getFloat32();
                if (start > end) {
                    start = end;
                }
                this.maybeCreateFeature( start, end, {score: score});
            }
        } else {
            dlog('Currently not handling bwgType=' + blockType);
        }
    },

    parseBigBedBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var offset = 0;
        while (offset < bytes.byteLength) {
            var chromId = data.getUint32( offset );
            var start = data.getInt32( offset+4 );
            var end = data.getInt32( offset+8 );
            offset += 12;
            var rest = '';
            while( offset < bytes.byteLength ) {
                var ch = data.getUint8( offset++ );
                if (ch != 0) {
                    rest += String.fromCharCode(ch);
                } else {
                    break;
                }
            }

            var featureOpts = {};

            var bedColumns = rest.split('\t');
            if (bedColumns.length > 0) {
                featureOpts.name = bedColumns[0] == '.' ? null : bedColumns[0];
            }
            if (bedColumns.length > 1) {
                featureOpts.score = +bedColumns[1];
            }
            if (bedColumns.length > 2) {
                featureOpts.strand = {'-': -1, '+': 1}[bedColumns[2]];
            }
            if (bedColumns.length > 5) {
                var color = bedColumns[5];
                if (this.window.BED_COLOR_REGEXP.test(color)) {
                    featureOpts.override_color = 'rgb(' + color + ')';
                }
            }

            if (bedColumns.length < 9) {
                if (chromId == this.chr) {
                    this.maybeCreateFeature( start, end, featureOpts);
                }
            } else if (chromId == this.chr && start <= this.max && end >= this.min) {
                // Complex-BED?
                // FIXME this is currently a bit of a hack to do Clever Things with ensGene.bb
                var auto = this.window.autoSql;
                for(var i = 0; i < auto.fields.length-3; i++) {
                    if(bedColumns[i] != '.') {
                        featureOpts[auto.fields[i+3].name] = bedColumns[i];
                    }
                }
                delete featureOpts.blockCount;
                delete featureOpts.blockSizes;
                delete featureOpts.thickStart;
                delete featureOpts.thickEnd;
                delete featureOpts.reserved;
                delete featureOpts.chromStarts;
                delete featureOpts.override_color;

                var thickStart = bedColumns[3] | 0;
                var thickEnd  = bedColumns[4] | 0;
                var blockCount = bedColumns[6] | 0;
                var blockSizes = bedColumns[7].split(',');
                var blockStarts = bedColumns[8].split(',');

                var grp = dojo.mixin(featureOpts, {
                    id: bedColumns[0] + '_' + chromId + '_' + start + '_' + end,
                    type: 'mRNA',
                    strand: featureOpts.strand,
                    subfeatures: []
                });

                var ts = new Range(thickStart, thickEnd);

                for (var b = 0; b < blockCount; ++b) {
                    var bmin = (blockStarts[b]|0) + start;
                    var bmax = bmin + (blockSizes[b]|0);
                    grp.subfeatures.push({ start: bmin, end: bmax, type: 'exon' });

                    var span = new Range(bmin, bmax);
                    var tl = ts.intersection(span);
                    if(tl) {
                        grp.subfeatures.push({ start: tl.min(), end: tl.max(), type: 'CDS' });
                    }
                }

                this.createFeature(start, end, grp)
            }
        }
    },

    readFeatures: function() {
        var thisB = this;
        var blockFetches = array.map( thisB.blockGroupsToFetch, function( blockGroup ) {
            //console.log( 'fetching blockgroup with '+blockGroup.blocks.length+' blocks: '+blockGroup );
            var d = new RejectableFastPromise();
            thisB.window.bwg._read( blockGroup.offset, blockGroup.size, function( data ) {
                            blockGroup.data = data;
                            d.resolve( blockGroup );
                        }, dlang.hitch( d, 'reject' ) );
            return d;
        }, thisB );

        all( blockFetches ).then( function( blockGroups ) {
            array.forEach( blockGroups, function( blockGroup ) {
                array.forEach( blockGroup.blocks, function( block ) {
                                   var data;
                                   var offset = block.offset - blockGroup.offset;
                                   if( thisB.window.bwg.uncompressBufSize > 0 ) {
                                       // var beforeInf = new Date();
                                       data = inflate( blockGroup.data, offset+2, block.size - 2);
                                       offset = 0;
                                       //console.log( 'inflate', 2, block.size - 2);
                                       // var afterInf = new Date();
                                       // dlog('inflate: ' + (afterInf - beforeInf) + 'ms');
                                   } else {
                                       data = blockGroup.data;
                                   }

                                   if( thisB.window.isSummary ) {
                                       thisB.parseSummaryBlock( data, offset );
                                   } else if (thisB.window.bwg.type == 'bigwig') {
                                       thisB.parseBigWigBlock( data, offset );
                                   } else if (thisB.window.bwg.type == 'bigbed') {
                                       thisB.parseBigBedBlock( data, offset );
                                   } else {
                                       dlog("Don't know what to do with " + thisB.window.bwg.type);
                                   }
                });
            });

            thisB.callback( thisB.features );
       }, thisB.errorCallback );
    }
});

return RequestWorker;

});
<commit_msg>Support autosql annotations for files that are less than bed12<commit_after>define( [
            'dojo/_base/declare',
            'dojo/_base/lang',
            'dojo/_base/array',
            'JBrowse/Util',
            'JBrowse/Util/RejectableFastPromise',
            'dojo/promise/all',
            'JBrowse/Model/Range',
            'JBrowse/Model/SimpleFeature',
            'jszlib/inflate',
            'jszlib/arrayCopy'
        ],
        function(
            declare,
            dlang,
            array,
            Util,
            RejectableFastPromise,
            all,
            Range,
            SimpleFeature,
            inflate,
            arrayCopy
        ) {

var dlog = function(){ console.log.apply(console, arguments); };

var RequestWorker = declare( null,
 /**
  * @lends JBrowse.Store.BigWig.Window.RequestWorker.prototype
  */
 {

    BIG_WIG_TYPE_GRAPH: 1,
    BIG_WIG_TYPE_VSTEP: 2,
    BIG_WIG_TYPE_FSTEP: 3,

    /**
     * Worker object for reading data from a bigwig or bigbed file.
     * Manages the state necessary for traversing the index trees and
     * so forth.
     *
     * Adapted by Robert Buels from bigwig.js in the Dalliance Genome
     * Explorer by Thomas Down.
     * @constructs
     */
    constructor: function( window, chr, min, max, callback, errorCallback ) {
        this.window = window;
        this.source = window.bwg.name || undefined;

        this.blocksToFetch = [];
        this.outstanding = 0;

        this.chr = chr;
        this.min = min;
        this.max = max;
        this.callback = callback;
        this.errorCallback = errorCallback || function(e) { console.error( e, e.stack, arguments.caller ); };
    },

    cirFobRecur: function(offset, level) {
        this.outstanding += offset.length;

        var maxCirBlockSpan = 4 +  (this.window.cirBlockSize * 32);   // Upper bound on size, based on a completely full leaf node.
        var spans;
        for (var i = 0; i < offset.length; ++i) {
            var blockSpan = new Range(offset[i], Math.min(offset[i] + maxCirBlockSpan, this.window.cirTreeOffset + this.window.cirTreeLength));
            spans = spans ? spans.union( blockSpan ) : blockSpan;
        }

        var fetchRanges = spans.ranges();
        //dlog('fetchRanges: ' + fetchRanges);
        for (var r = 0; r < fetchRanges.length; ++r) {
            var fr = fetchRanges[r];
            this.cirFobStartFetch(offset, fr, level);
        }
    },

    cirFobStartFetch: function(offset, fr, level, attempts) {
        var length = fr.max() - fr.min();
        // dlog('fetching ' + fr.min() + '-' + fr.max() + ' (' + Util.humanReadableNumber(length) + ')');
        //console.log('cirfobstartfetch');
        this.window.bwg._read( fr.min(), length, dlang.hitch( this,function(resultBuffer) {
                for (var i = 0; i < offset.length; ++i) {
                        if (fr.contains(offset[i])) {
                            this.cirFobRecur2(resultBuffer, offset[i] - fr.min(), level);
                            --this.outstanding;
                            if (this.outstanding == 0) {
                                this.cirCompleted();
                            }
                        }
                    }
             }), this.errorCallback );
    },

    cirFobRecur2: function(cirBlockData, offset, level) {
        var data = this.window.bwg.newDataView( cirBlockData, offset );

        var isLeaf = data.getUint8();
        var cnt = data.getUint16( 2 );
        //dlog('cir level=' + level + '; cnt=' + cnt);

        if (isLeaf != 0) {
            for (var i = 0; i < cnt; ++i) {
                var startChrom = data.getUint32();
                var startBase = data.getUint32();
                var endChrom = data.getUint32();
                var endBase = data.getUint32();
                var blockOffset = data.getUint64();
                var blockSize   = data.getUint64();
                if ((startChrom < this.chr || (startChrom == this.chr && startBase <= this.max)) &&
                    (endChrom   > this.chr || (endChrom == this.chr && endBase >= this.min)))
                {
                    // dlog('Got an interesting block: startBase=' + startBase + '; endBase=' + endBase + '; offset=' + blockOffset + '; size=' + blockSize);
                    this.blocksToFetch.push({offset: blockOffset, size: blockSize});
                }
            }
        } else {
            var recurOffsets = [];
            for (var i = 0; i < cnt; ++i) {
                var startChrom = data.getUint32();
                var startBase = data.getUint32();
                var endChrom = data.getUint32();
                var endBase = data.getUint32();
                var blockOffset = data.getUint64();
                if ((startChrom < this.chr || (startChrom == this.chr && startBase <= this.max)) &&
                    (endChrom   > this.chr || (endChrom == this.chr && endBase >= this.min)))
                {
                    recurOffsets.push(blockOffset);
                }
            }
            if (recurOffsets.length > 0) {
                this.cirFobRecur(recurOffsets, level + 1);
            }
        }
    },

    cirCompleted: function() {
        // merge contiguous blocks
        this.blockGroupsToFetch = this.groupBlocks( this.blocksToFetch );

        if (this.blockGroupsToFetch.length == 0) {
            this.callback([]);
        } else {
            this.features = [];
            this.readFeatures();
        }
    },

    groupBlocks: function( blocks ) {

        // sort the blocks by file offset
        blocks.sort(function(b0, b1) {
                        return (b0.offset|0) - (b1.offset|0);
                    });

        // group blocks that are within 2KB of eachother
        var blockGroups = [];
        var lastBlock;
        var lastBlockEnd;
        for( var i = 0; i<blocks.length; i++ ) {
            if( lastBlock && (blocks[i].offset-lastBlockEnd) <= 2000 ) {
                lastBlock.size += blocks[i].size - lastBlockEnd + blocks[i].offset;
                lastBlock.blocks.push( blocks[i] );
            }
            else {
                blockGroups.push( lastBlock = { blocks: [blocks[i]], size: blocks[i].size, offset: blocks[i].offset } );
            }
            lastBlockEnd = lastBlock.offset + lastBlock.size;
        }

        return blockGroups;
    },

    createFeature: function(fmin, fmax, opts) {
        // dlog('createFeature(' + fmin +', ' + fmax + ', '+opts.score+')');

        var data = {
            start: fmin,
            end: fmax
        };

        for( var k in opts )
            data[k] = opts[k];

        var id = data.id;
        delete data.id;

        var f = new SimpleFeature({
            data: data,
            id: id ? id : data.start + '_' + data.end + '_' + data.score
        });

        this.features.push(f);
    },

    maybeCreateFeature: function(fmin, fmax, opts) {
        if (fmin <= this.max && fmax >= this.min) {
            this.createFeature( fmin, fmax, opts );
        }
    },

    parseSummaryBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var itemCount = bytes.byteLength/32;
        for (var i = 0; i < itemCount; ++i) {
            var chromId =   data.getInt32();
            var start =     data.getInt32();
            var end =       data.getInt32();
            var validCnt =  data.getInt32()||1;
            var minVal    = data.getFloat32();
            var maxVal    = data.getFloat32();
            var sumData   = data.getFloat32();
            var sumSqData = data.getFloat32();

            if (chromId == this.chr) {
                var summaryOpts = {score: sumData/validCnt,maxScore: maxVal,minScore:minVal};
                if (this.window.bwg.type == 'bigbed') {
                    summaryOpts.type = 'density';
                }
                this.maybeCreateFeature( start, end, summaryOpts);
            }
        }
    },

    parseBigWigBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var itemSpan = data.getUint32( 16 );
        var blockType = data.getUint8( 20 );
        var itemCount = data.getUint16( 22 );

        // dlog('processing bigwig block, type=' + blockType + '; count=' + itemCount);

        if (blockType == this.BIG_WIG_TYPE_FSTEP) {
            var blockStart = data.getInt32( 4 );
            var itemStep = data.getUint32( 12 );
            for (var i = 0; i < itemCount; ++i) {
                var score = data.getFloat32( 4*i+24 );
                this.maybeCreateFeature( blockStart + (i*itemStep), blockStart + (i*itemStep) + itemSpan, {score: score});
            }
        } else if (blockType == this.BIG_WIG_TYPE_VSTEP) {
            for (var i = 0; i < itemCount; ++i) {
                var start = data.getInt32( 8*i+24 );
                var score = data.getFloat32();
                this.maybeCreateFeature( start, start + itemSpan, {score: score});
            }
        } else if (blockType == this.BIG_WIG_TYPE_GRAPH) {
            for (var i = 0; i < itemCount; ++i) {
                var start = data.getInt32( 12*i + 24 );
                var end   = data.getInt32();
                var score = data.getFloat32();
                if (start > end) {
                    start = end;
                }
                this.maybeCreateFeature( start, end, {score: score});
            }
        } else {
            dlog('Currently not handling bwgType=' + blockType);
        }
    },

    parseBigBedBlock: function( bytes, startOffset ) {
        var data = this.window.bwg.newDataView( bytes, startOffset );

        var offset = 0;
        while (offset < bytes.byteLength) {
            var chromId = data.getUint32( offset );
            var start = data.getInt32( offset+4 );
            var end = data.getInt32( offset+8 );
            offset += 12;
            var rest = '';
            while( offset < bytes.byteLength ) {
                var ch = data.getUint8( offset++ );
                if (ch != 0) {
                    rest += String.fromCharCode(ch);
                } else {
                    break;
                }
            }

            var featureOpts = {};

            var bedColumns = rest.split('\t');
            if (bedColumns.length > 0) {
                featureOpts.name = bedColumns[0] == '.' ? null : bedColumns[0];
            }
            if (bedColumns.length > 1) {
                featureOpts.score = +bedColumns[1];
            }
            if (bedColumns.length > 2) {
                featureOpts.strand = {'-': -1, '+': 1}[bedColumns[2]];
            }
            if (bedColumns.length > 5) {
                var color = bedColumns[5];
                if (this.window.BED_COLOR_REGEXP.test(color)) {
                    featureOpts.override_color = 'rgb(' + color + ')';
                }
            }
            var auto = this.window.autoSql;
            for(var i = 0; i < auto.fields.length-3; i++) {
                if(bedColumns[i] != '.') {
                    featureOpts[auto.fields[i+3].name] = bedColumns[i];
                }
            }
            delete featureOpts.blockCount;
            delete featureOpts.blockSizes;
            delete featureOpts.thickStart;
            delete featureOpts.thickEnd;
            delete featureOpts.reserved;
            delete featureOpts.chromStarts;
            delete featureOpts.override_color;

            if (bedColumns.length < 9) {
                if (chromId == this.chr) {
                    this.maybeCreateFeature( start, end, featureOpts);
                }
            } else if (chromId == this.chr && start <= this.max && end >= this.min) {
                var thickStart = bedColumns[3] | 0;
                var thickEnd  = bedColumns[4] | 0;
                var blockCount = bedColumns[6] | 0;
                var blockSizes = bedColumns[7].split(',');
                var blockStarts = bedColumns[8].split(',');

                var grp = dojo.mixin(featureOpts, {
                    id: bedColumns[0] + '_' + chromId + '_' + start + '_' + end,
                    type: 'mRNA',
                    strand: featureOpts.strand,
                    subfeatures: []
                });

                var ts = new Range(thickStart, thickEnd);

                for (var b = 0; b < blockCount; ++b) {
                    var bmin = (blockStarts[b]|0) + start;
                    var bmax = bmin + (blockSizes[b]|0);
                    grp.subfeatures.push({ start: bmin, end: bmax, type: 'exon' });

                    var span = new Range(bmin, bmax);
                    var tl = ts.intersection(span);
                    if(tl) {
                        grp.subfeatures.push({ start: tl.min(), end: tl.max(), type: 'CDS' });
                    }
                }

                this.createFeature(start, end, grp)
            }
        }
    },

    readFeatures: function() {
        var thisB = this;
        var blockFetches = array.map( thisB.blockGroupsToFetch, function( blockGroup ) {
            //console.log( 'fetching blockgroup with '+blockGroup.blocks.length+' blocks: '+blockGroup );
            var d = new RejectableFastPromise();
            thisB.window.bwg._read( blockGroup.offset, blockGroup.size, function( data ) {
                            blockGroup.data = data;
                            d.resolve( blockGroup );
                        }, dlang.hitch( d, 'reject' ) );
            return d;
        }, thisB );

        all( blockFetches ).then( function( blockGroups ) {
            array.forEach( blockGroups, function( blockGroup ) {
                array.forEach( blockGroup.blocks, function( block ) {
                                   var data;
                                   var offset = block.offset - blockGroup.offset;
                                   if( thisB.window.bwg.uncompressBufSize > 0 ) {
                                       // var beforeInf = new Date();
                                       data = inflate( blockGroup.data, offset+2, block.size - 2);
                                       offset = 0;
                                       //console.log( 'inflate', 2, block.size - 2);
                                       // var afterInf = new Date();
                                       // dlog('inflate: ' + (afterInf - beforeInf) + 'ms');
                                   } else {
                                       data = blockGroup.data;
                                   }

                                   if( thisB.window.isSummary ) {
                                       thisB.parseSummaryBlock( data, offset );
                                   } else if (thisB.window.bwg.type == 'bigwig') {
                                       thisB.parseBigWigBlock( data, offset );
                                   } else if (thisB.window.bwg.type == 'bigbed') {
                                       thisB.parseBigBedBlock( data, offset );
                                   } else {
                                       dlog("Don't know what to do with " + thisB.window.bwg.type);
                                   }
                });
            });

            thisB.callback( thisB.features );
       }, thisB.errorCallback );
    }
});

return RequestWorker;

});
<|endoftext|>


<commit_before>var fs = require('fs'),
    words = require('./data/weasels.json');

fs.writeFileSync('Supported-weasel-words.md',
    'Supported Weasel-words\n' +
    '=================\n' +
    '\n' +

    words.map(function (word) {
        return '* “' + word + '”';
    }).join(';\n') +

    '.\n'
);
<commit_msg>Add strict mode to support generation<commit_after>'use strict';

var fs = require('fs'),
    words = require('./data/weasels.json');

fs.writeFileSync('Supported-weasel-words.md',
    'Supported Weasel-words\n' +
    '=================\n' +
    '\n' +

    words.map(function (word) {
        return '* “' + word + '”';
    }).join(';\n') +

    '.\n'
);
<|endoftext|>


<commit_before>// tests for all write methods
"use strict";
var fluid = require("infusion");
var namespace = "gpii.ctr.record.tests.write";
var write = fluid.registerNamespace(namespace);

write.PouchDB = require('pouchdb');
write.tr = new write.PouchDB({"name": "tr", "db": require('memdown'), "prefix": '/tmp/my-temp-pouch/'});

write.loader = require("../../../configs/lib/config-loader");
write.config = write.loader.loadConfig(require("../../../configs/express/test.json"));

write.port = write.config.port || process.env.PORT || 4895;

// manually point at pouch instead of CouchDB
write.pouchBaseUrl = "http://localhost:" + write.port + "/pouch";
write.pouchDbUrl = write.pouchBaseUrl + "/tr";

write.config["couch.url"] = write.pouchDbUrl;

// TODO:  When we add support for versioning, we should disable it for these tests and test the version code separately

// TODO:  When we add support for attribution, we should disable it for these tests and test the attribution code separately

write.testUtils = require("../../tests/lib/testUtils")(write.config);
write.request = require("request");

write.validRecord = {
    "uniqueId": "testRecord",
    "type": "general",
    "termLabel": "A test term",
    "definition": "This is a sample term created for test purposes."
};
write.invalidRecord = {
    "uniqueId": "testRecord",
    "type": "alias",
    "termLabel": "A test alias with missing data.",
    "definition": "This record is missing an aliasOf field."
};

write.express = require('express');
write.app = write.express();

// Spin up an express instance
write.startExpress = function() {
    var app = write.app;
    app.set('port', write.port);

    // Mount all variations on the module
    var record = require('../../record')(write.config);
    app.use('/record', record);

    // Add PouchDB with simulated CouchDb REST endpoints
    app.use('/pouch', require('express-pouchdb')(write.PouchDB));

    var http = require("http");
    http.createServer(app).listen(app.get('port'), function(){
        console.log('Express server listening on port ' + app.get('port'));

        console.log("Express started...");
        write.loadData();
    });
};

write.loadData = function() {
    var data = require("../../tests/data/data.json");

    // Hit our express instance, for some reason the bulk docs function doesn't seem to like us
    var options = {
        "url": write.pouchDbUrl + "/_bulk_docs",
        "body": JSON.stringify(require("../../tests/data/data.json")),
        "headers": { "Content-Type": "application/json"}
    };

    write.request.post(options,function(e,r,b) {
        if (e && e !== null) {
            return console.log(e);
        }

        console.log("Data loaded...");
        write.loadViews();
    });
};

write.loadViews = function() {
    var couchappUtils = require("../../tests/lib/couchappUtils")(write.config);

    var path = __dirname + "/../../../../couchapp/api/";
    var viewContent = couchappUtils.loadCouchappViews(path);

    var options = {
        "url": write.pouchDbUrl + "/_design/api",
        "body": JSON.stringify(viewContent),
        "headers": { "Content-Type": "application/json"}
    };

    write.request.put(options,function(e,r,b) {
        if (e && e !== null) {
            return console.log(e);
        }

        console.log("Views loaded...");
        write.runTests();
    });
};

write.runTests = function() {
    console.log("Running tests...");

    var jqUnit = require("jqUnit");
    jqUnit.module("Record API (write)");

    jqUnit.asyncTest("Use PUT to create a new record", function() {
        var options = {
            "url": "http://localhost:" + write.port + "/record/",
            "json": write.validRecord
        };

        var request = require("request");
        request.put(options, function(e,r,b) {
            debugger;
            jqUnit.start();

            jqUnit.assertNull("There should be no errors returned",e);

            jqUnit.stop();

            // Make sure the record was actually created
            request.get("http://localhost:" + write.app.get('port') + "/record/" + write.validRecord.uniqueId,function(e,r,b) {
                jqUnit.start();
                jqUnit.assertNull("There should be no errors returned",e);

                var jsonData = JSON.parse(b);

                jqUnit.assertValue("There should be a record returned.", jsonData.record);

                if (jsonData.record && jsonData.record !== undefined) {
                    // The response should closely match the record we submitted
                    Object.keys(write.validRecord).forEach(function(field){
                        jqUnit.assertEquals("The field '" + field + "' should match what we submitted.", write.validRecord[field], jsonData.record[field]);
                    });
                }
            });
        });
    });

    // TODO:  Test using PUT to update an existing record

    // TODO:  Test using PUT with an invalid record

    jqUnit.asyncTest("Use POST to create a new record", function() {
        var options = {
            "url": "http://localhost:" + write.port + "/record/",
            "json": write.validRecord
        };

        var request = require("request");
        request.post(options, function(e,r,b) {
            debugger;
            jqUnit.start();

            jqUnit.assertNull("There should be no errors returned",e);

            jqUnit.stop();

            // Make sure the record was actually created
            request.get("http://localhost:" + write.app.get('port') + "/record/" + write.validRecord.uniqueId,function(e,r,b) {
                jqUnit.start();
                jqUnit.assertNull("There should be no errors returned",e);

                var jsonData = JSON.parse(b);

                jqUnit.assertValue("There should be a record returned.", jsonData.record);

                if (jsonData.record && jsonData.record !== undefined) {
                    // The response should closely match the record we submitted
                    Object.keys(write.validRecord).forEach(function(field){
                        jqUnit.assertEquals("The field '" + field + "' should match what we submitted.", write.validRecord[field], jsonData.record[field]);
                    });
                }
            });
        });
    });

    // TODO:  Test using POST with an invalid record

    // TODO:  Test using DELETE to delete an existing record

    // TODO:  Test versioning on all successful adds and updates

    //jqUnit.asyncTest("Retrieve record with the 'children' argument set to false...", function() {
    //    // TODO:  This test depends on the existence of a single record.  We should adjust to use test data instead.
    //    request.get("http://localhost:" + app.get('port') + "/record/xMPPChatID?children=false", function(error, response, body) {
    //        jqUnit.start();
    //
    //        testUtils.isSaneResponse(jqUnit, error, response, body);
    //        var jsonData = JSON.parse(body);
    //
    //        jqUnit.assertTrue("There should have been a record returned...", jsonData.record);
    //        if (jsonData.record) {
    //            jqUnit.assertUndefined("Record '" + jsonData.record.uniqueId + "' should not have contained any children", jsonData.record.aliases);
    //        }
    //    });
    //});

    jqUnit.onAllTestsDone.addListener(function() {
        // Shut down express (seems to happen implicitly, so commented out)
    //    http.server.close();
        write.tr.viewCleanup();
        write.tr.destroy();
    });
};

write.startExpress();
<commit_msg>Configured PouchDB to work in memory, which means our tests are now completely repeatable and don't dump a bunch of temporary directories in whatever directory they're started from.<commit_after>// tests for all write methods
"use strict";
var fluid = require("infusion");
var namespace = "gpii.ctr.record.tests.write";
var write = fluid.registerNamespace(namespace);

//write.PouchDB = require('pouchdb');
//write.tr = new write.PouchDB({"name": "tr", "db": require('memdown'), "prefix": '/tmp/my-temp-pouch/'});

write.PouchDB = require('pouchdb');
write.MemPouchDB = write.PouchDB.defaults({db: require('memdown')});
write.tr = new write.PouchDB({"name": "tr", "prefix": '/tmp'});

write.loader = require("../../../configs/lib/config-loader");
write.config = write.loader.loadConfig(require("../../../configs/express/test.json"));

write.port = write.config.port || process.env.PORT || 4895;

// manually point at pouch instead of CouchDB
write.pouchBaseUrl = "http://localhost:" + write.port + "/pouch";
write.pouchDbUrl = write.pouchBaseUrl + "/tr";

write.config["couch.url"] = write.pouchDbUrl;

// TODO:  When we add support for versioning, we should disable it for these tests and test the version code separately

// TODO:  When we add support for attribution, we should disable it for these tests and test the attribution code separately

write.testUtils = require("../../tests/lib/testUtils")(write.config);
write.request = require("request");

write.validRecord = {
    "uniqueId": "testRecord",
    "type": "general",
    "termLabel": "A test term",
    "definition": "This is a sample term created for test purposes."
};
write.invalidRecord = {
    "uniqueId": "testRecord",
    "type": "alias",
    "termLabel": "A test alias with missing data.",
    "definition": "This record is missing an aliasOf field."
};

write.express = require('express');
write.app = write.express();

// Spin up an express instance
write.startExpress = function() {
    var app = write.app;
    app.set('port', write.port);

    // Mount all variations on the module
    var record = require('../../record')(write.config);
    app.use('/record', record);

    // Add PouchDB with simulated CouchDb REST endpoints
    app.use('/pouch', require('express-pouchdb')(write.MemPouchDB));

    var http = require("http");
    http.createServer(app).listen(app.get('port'), function(){
        console.log('Express server listening on port ' + app.get('port'));

        console.log("Express started...");
        write.loadData();
    });
};

write.loadData = function() {
    var data = require("../../tests/data/data.json");

    // Hit our express instance, for some reason the bulk docs function doesn't seem to like us
    var options = {
        "url": write.pouchDbUrl + "/_bulk_docs",
        "body": JSON.stringify(require("../../tests/data/data.json")),
        "headers": { "Content-Type": "application/json"}
    };

    write.request.post(options,function(e,r,b) {
        if (e && e !== null) {
            return console.log(e);
        }

        console.log("Data loaded...");
        write.loadViews();
    });
};

write.loadViews = function() {
    var couchappUtils = require("../../tests/lib/couchappUtils")(write.config);

    var path = __dirname + "/../../../../couchapp/api/";
    var viewContent = couchappUtils.loadCouchappViews(path);

    var options = {
        "url": write.pouchDbUrl + "/_design/api",
        "body": JSON.stringify(viewContent),
        "headers": { "Content-Type": "application/json"}
    };

    write.request.put(options,function(e,r,b) {
        if (e && e !== null) {
            return console.log(e);
        }

        console.log("Views loaded...");
        write.runTests();
    });
};

write.runTests = function() {
    console.log("Running tests...");

    var jqUnit = require("jqUnit");
    jqUnit.module("Record API (write)");

    jqUnit.asyncTest("Use PUT to create a new record", function() {
        var options = {
            "url": "http://localhost:" + write.port + "/record/",
            "json": write.validRecord
        };

        var request = require("request");
        request.put(options, function(e,r,b) {
            debugger;
            jqUnit.start();

            jqUnit.assertNull("There should be no errors returned",e);

            jqUnit.stop();

            // Make sure the record was actually created
            request.get("http://localhost:" + write.app.get('port') + "/record/" + write.validRecord.uniqueId,function(e,r,b) {
                jqUnit.start();
                jqUnit.assertNull("There should be no errors returned",e);

                var jsonData = JSON.parse(b);

                jqUnit.assertValue("There should be a record returned.", jsonData.record);

                if (jsonData.record && jsonData.record !== undefined) {
                    // The response should closely match the record we submitted
                    Object.keys(write.validRecord).forEach(function(field){
                        jqUnit.assertEquals("The field '" + field + "' should match what we submitted.", write.validRecord[field], jsonData.record[field]);
                    });
                }
            });
        });
    });

    // TODO:  Test using PUT to update an existing record

    // TODO:  Test using PUT with an invalid record

    jqUnit.asyncTest("Use POST to create a new record", function() {
        var options = {
            "url": "http://localhost:" + write.port + "/record/",
            "json": write.validRecord
        };

        var request = require("request");
        request.post(options, function(e,r,b) {
            debugger;
            jqUnit.start();

            jqUnit.assertNull("There should be no errors returned",e);

            jqUnit.stop();

            // Make sure the record was actually created
            request.get("http://localhost:" + write.app.get('port') + "/record/" + write.validRecord.uniqueId,function(e,r,b) {
                jqUnit.start();
                jqUnit.assertNull("There should be no errors returned",e);

                var jsonData = JSON.parse(b);

                jqUnit.assertValue("There should be a record returned.", jsonData.record);

                if (jsonData.record && jsonData.record !== undefined) {
                    // The response should closely match the record we submitted
                    Object.keys(write.validRecord).forEach(function(field){
                        jqUnit.assertEquals("The field '" + field + "' should match what we submitted.", write.validRecord[field], jsonData.record[field]);
                    });
                }
            });
        });
    });

    // TODO:  Test using POST with an invalid record

    // TODO:  Test using DELETE to delete an existing record

    // TODO:  Test versioning on all successful adds and updates

    //jqUnit.asyncTest("Retrieve record with the 'children' argument set to false...", function() {
    //    // TODO:  This test depends on the existence of a single record.  We should adjust to use test data instead.
    //    request.get("http://localhost:" + app.get('port') + "/record/xMPPChatID?children=false", function(error, response, body) {
    //        jqUnit.start();
    //
    //        testUtils.isSaneResponse(jqUnit, error, response, body);
    //        var jsonData = JSON.parse(body);
    //
    //        jqUnit.assertTrue("There should have been a record returned...", jsonData.record);
    //        if (jsonData.record) {
    //            jqUnit.assertUndefined("Record '" + jsonData.record.uniqueId + "' should not have contained any children", jsonData.record.aliases);
    //        }
    //    });
    //});

    jqUnit.onAllTestsDone.addListener(function() {
        // Shut down express (seems to happen implicitly, so commented out)
    //    http.server.close();
        write.tr.viewCleanup();
        write.tr.destroy();
    });
};

write.startExpress();
<|endoftext|>


<commit_before>const Promise = require('bluebird');
const util = require('../util');
const cache = require('../cache');
const products = require('./products');

const categories = module.exports;

// Init routes
categories.init = (env, router, schema) => {
  router.get('/categories/:id', cache(env), categories.getById.bind(this, schema));
  router.get('/categories/:id/children', cache(env), categories.getChildren.bind(this, schema));
  router.get('/categories/:id/products', cache(env), categories.getAllProducts.bind(this, schema));
};

// Get category by ID
categories.getById = (schema, req) => {
  return schema.get('/categories/{id}', {
    id: req.params.id,
    where: categories.defaultQuery(req),
    sort: categories.defaultSort(req),
  });
};

// Get category children
categories.getChildren = (schema, req) => {
  return schema.get('/categories/{id}/children', {
    id: req.params.id,
    where: categories.defaultQuery(req),
    sort: categories.defaultSort(req),
    include: categories.filterIncludeQuery(req),
  });
};

// Get all nested category products
categories.getAllProducts = (schema, req) => {
  return schema.get('/categories/{id}', {
    id: req.params.id,
    fields: 'id'
  }).then(category => {
    if (!category) {
      return null;
    }
    return categories.getAllChildIdsRecursive(schema, req, [ category.id ]).then(allCategoryIds => {
      if (!allCategoryIds.length) {
        return null;
      }
      return schema.get('/products', {
        $or: allCategoryIds.map(categoryId => {
          return {
            'category_index.id': categoryId
          };
        }),
        where: products.defaultQuery(req),
      });
    });
  });
};

// Get child category IDs recursively
categories.getAllChildIdsRecursive = (schema, req, parentIds, ids) => {
  ids = ids || [];
  return schema.get('/categories', {
    id: { $in: parentIds },
    fields: 'id',
    where: categories.defaultQuery(req),
    sort: categories.defaultSort(req),
    include: {
      children: {
        url: '/categories',
        params: {
          parent_id: 'id'
        },
        data: {
          fields: 'id',
          limit: null
        }
      }
    }
  }).then(result => {
    if (!result || !result.results.length) {
      return ids;
    }
    return Promise.all(
      result.results.map(category => {
        ids.push(category.id);
        if (category.children.results.length) {
          const nextIds = category.children.results.map(child => child.id);
          return new Promise(resolve => {
            categories.getAllChildIdsRecursive(schema, req, nextIds, ids).then(resolve);
          });
        }
      })
    ).then(() => {
      return ids;
    });
  });
};

// Get a category

// Default query
categories.defaultQuery = (req) => {
  return {
    active: true,
    navigation: req.query.navigation || undefined,
  }
};

// Default sort
categories.defaultSort = (req) => {
  return {
    sort: 'sort ascending',
  }
};

// Filter include query for categories
categories.filterIncludeQuery = (req) => {
  let include = {};
  if (req.query.depth) {
    include = categories.filterIncludeQueryDepth(include, req);
  }
  return include;
};

// Filter depth query to include nested child categories
categories.filterIncludeQueryDepth = (include, req) => {
  // Depth query specifies how deep the children should fetch
  let depth = req.query.depth;
  if (depth > 1) {
    if (depth > 5) {
      throw util.error(400, 'Too much depth');
    }
    let deepInclude = include;
    while (--depth) {
      deepInclude.children = {};
      deepInclude.children.url = '/categories',
      deepInclude.children.params = { parent_id: 'id' };
      deepInclude.children.data = { include: {} };
      // Recursion here
      deepInclude = deepInclude.children.data.include;
    }
  }
  return include;
};
<commit_msg>Category sort and page<commit_after>const Promise = require('bluebird');
const util = require('../util');
const cache = require('../cache');
const products = require('./products');

const categories = module.exports;

// Init routes
categories.init = (env, router, schema) => {
  router.get('/categories/:id', cache(env), categories.getById.bind(this, schema));
  router.get('/categories/:id/children', cache(env), categories.getChildren.bind(this, schema));
  router.get('/categories/:id/products', cache(env), categories.getAllProducts.bind(this, schema));
};

// Get category by ID
categories.getById = (schema, req) => {
  return schema.get('/categories/{id}', {
    id: req.params.id,
    where: categories.defaultQuery(req),
    sort: categories.defaultSort(req),
  });
};

// Get category children
categories.getChildren = (schema, req) => {
  return schema.get('/categories/{id}/children', {
    id: req.params.id,
    where: categories.defaultQuery(req),
    sort: categories.defaultSort(req),
    include: categories.filterIncludeQuery(req),
  });
};

// Get all nested category products
categories.getAllProducts = (schema, req) => {
  return schema.get('/categories/{id}', {
    id: req.params.id,
    fields: 'id'
  }).then(category => {
    if (!category) {
      return null;
    }
    return categories.getAllChildIdsRecursive(schema, req, [ category.id ]).then(allCategoryIds => {
      if (!allCategoryIds.length) {
        return null;
      }
      return schema.get('/products', {
        limit: req.query.limit || 25,
        page: req.query.page || 1,
        $or: allCategoryIds.map(categoryId => {
          return {
            'category_index.id': categoryId,
          };
        }),
        where: products.defaultQuery(req),
        sort: categories.nestedProductSort(req, category.id),
      });
    });
  });
};

// Get child category IDs recursively
categories.getAllChildIdsRecursive = (schema, req, parentIds, ids) => {
  ids = ids || [];
  return schema.get('/categories', {
    id: { $in: parentIds },
    fields: 'id',
    where: categories.defaultQuery(req),
    include: {
      children: {
        url: '/categories',
        params: {
          parent_id: 'id'
        },
        data: {
          fields: 'id',
          limit: null
        }
      }
    }
  }).then(result => {
    if (!result || !result.results.length) {
      return ids;
    }
    return Promise.all(
      result.results.map(category => {
        ids.push(category.id);
        if (category.children.results.length) {
          const nextIds = category.children.results.map(child => child.id);
          return new Promise(resolve => {
            categories.getAllChildIdsRecursive(schema, req, nextIds, ids).then(resolve);
          });
        }
      })
    ).then(() => {
      return ids;
    });
  });
};

// Get a category

// Default query
categories.defaultQuery = (req) => {
  return {
    active: true,
    navigation: req.query.navigation || undefined,
  }
};

// Default sort
categories.defaultSort = (req) => {
  return {
    sort: 'sort ascending',
  }
};

// Sorting for nested category products
categories.nestedProductSort = (req, categoryId) => {
  const sort = req.query.sort;
  switch(sort) {
    case undefined:
    case 'featured':
      return `category_index.sort.${categoryId} asc`;
    default:
      return sort;
  }
};

// Filter include query for categories
categories.filterIncludeQuery = (req) => {
  let include = {};
  if (req.query.depth) {
    include = categories.filterIncludeQueryDepth(include, req);
  }
  return include;
};

// Filter depth query to include nested child categories
categories.filterIncludeQueryDepth = (include, req) => {
  // Depth query specifies how deep the children should fetch
  let depth = req.query.depth;
  if (depth > 1) {
    if (depth > 5) {
      throw util.error(400, 'Too much depth');
    }
    let deepInclude = include;
    while (--depth) {
      deepInclude.children = {};
      deepInclude.children.url = '/categories',
      deepInclude.children.params = { parent_id: 'id' };
      deepInclude.children.data = { include: {} };
      // Recursion here
      deepInclude = deepInclude.children.data.include;
    }
  }
  return include;
};
<|endoftext|>


<commit_before>/*
 * Ensures something has been found during the request.  Returns 404 if
 * res.template is unset, res.locals is empty and statusCode has not been set
 * to 204.
 *
 * Should be placed at the very end of the middleware pipeline,
 * after all project specific routes but before the error handler & responder.
 *
 * @module warepot/ensure-found
 */

'use strict'

const _ = require('lodash')

module.exports = function ensureFound(req, res, next) {
  if (res.template || _.some(res.locals) || res.statusCode === 204) {
    next()
  } else {
    // generates Not Found error if there is no page to render and no truthy values in data
    const err = new Error('Not found: ' + req.path)
    err.status = 404
    next(err)
  }
}
<commit_msg>Shorter Error creation.<commit_after>/*
 * Ensures something has been found during the request.  Returns 404 if
 * res.template is unset, res.locals is empty and statusCode has not been set
 * to 204.
 *
 * Should be placed at the very end of the middleware pipeline,
 * after all project specific routes but before the error handler & responder.
 *
 * @module warepot/ensure-found
 */

'use strict'

const _ = require('lodash')

module.exports = function ensureFound(req, res, next) {
  if (res.template || _.some(res.locals) || res.statusCode === 204) {
    next()
  } else {
    // generates Not Found error if there is no page to render and no truthy values in data
    next(Object.assign(new Error('Not found: ' + req.path), {
      status: 404
    }))
  }
}
<|endoftext|>


<commit_before>import React from "react";
import ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";
import { BrowserRouter } from "react-router-dom";
import { useStrict } from "mobx";

import { App } from "../src";

useStrict(true);

const render = (hydrate = false) => {
  const container = document.querySelector("#app");
  const element = (
    <AppContainer>
      <BrowserRouter>
        <App context={{ hostname: location.hostname }} />
      </BrowserRouter>
    </AppContainer>
  );
  if (hydrate) {
    ReactDOM.hydrate(element, container);
  } else {
    ReactDOM.render(element, container);
  }
};

render(true);

if (module.hot) {
  module.hot.accept("../src", function() {
    render();
  });
}
<commit_msg>update hot reload<commit_after>import React from "react";
import ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";
import { BrowserRouter } from "react-router-dom";
import { useStrict } from "mobx";

import { App } from "../src";

useStrict(true);

const render = (hydrate = false) => {
  const container = document.querySelector("#app");
  const element = (
    <AppContainer>
      <BrowserRouter>
        <App context={{ hostname: location.hostname }} />
      </BrowserRouter>
    </AppContainer>
  );
  if (hydrate) {
    ReactDOM.hydrate(element, container);
  } else {
    ReactDOM.render(element, container);
  }
};

render(true);

if (module.hot) {
  module.hot.accept(() => {
    render();
  });
}
<|endoftext|>


<commit_before>/* eslint-env jest */

import React from 'react'
import {
  mount,
  shallow
} from 'enzyme'
import toJson from 'enzyme-to-json'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import SetUsernameMutation from 'mutations/SetUsernameMutation'

jest.mock('mutations/SetUsernameMutation')

const mockUserData = {
  id: 'abc-123'
}

afterEach(() => {
  jest.clearAllMocks()
})

describe('EnterUsernameForm tests', function () {
  it('renders without error', function () {
    const EnterUsernameForm = require('../EnterUsernameForm').default
    shallow(
      <EnterUsernameForm user={mockUserData} />
    )
  })

  it('calls SetUsernameMutation when entering a username', function () {
    const EnterUsernameForm = require('../EnterUsernameForm').default

    // TODO:
    // After updating to Material UI 1.x, we shouldn't have to wrap our tested
    // components in the MuiThemeProvider.
    // https://github.com/mui-org/material-ui/issues/5330#issuecomment-251843011
    const wrapper = mount(
      <MuiThemeProvider>
        <EnterUsernameForm user={mockUserData} />
      </MuiThemeProvider>
    )
    const usernameTextField = wrapper.find('[data-test-id="enter-username-form-username-field"] input')

    // Enter a username
    // https://github.com/airbnb/enzyme/issues/76#issuecomment-259734821
    usernameTextField.instance().value = 'bobert'
    wrapper.update()

    // TODO: put a data-test-id directly on the button component
    // after updating to Material UI 1.x. We need to do this in many tests.
    const button = wrapper.find('[data-test-id="enter-username-form-button-container"] button')
    button.simulate('click')
    expect(SetUsernameMutation).toHaveBeenCalled()
  })

  it('matches expected snapshot', function () {
    const EnterUsernameForm = require('../EnterUsernameForm').default
    const wrapper = shallow(
      <EnterUsernameForm user={mockUserData} />
    )
    expect(toJson(wrapper)).toMatchSnapshot()
  })
})
<commit_msg>Test with an invalid username<commit_after>/* eslint-env jest */

import React from 'react'
import {
  mount,
  shallow
} from 'enzyme'
import toJson from 'enzyme-to-json'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import SetUsernameMutation from 'mutations/SetUsernameMutation'

jest.mock('mutations/SetUsernameMutation')

const mockUserData = {
  id: 'abc-123'
}

afterEach(() => {
  jest.clearAllMocks()
})

describe('EnterUsernameForm tests', function () {
  it('renders without error', function () {
    const EnterUsernameForm = require('../EnterUsernameForm').default
    shallow(
      <EnterUsernameForm user={mockUserData} />
    )
  })

  it('calls SetUsernameMutation when entering a username', function () {
    const EnterUsernameForm = require('../EnterUsernameForm').default

    // TODO:
    // After updating to Material UI 1.x, we shouldn't have to wrap our tested
    // components in the MuiThemeProvider.
    // https://github.com/mui-org/material-ui/issues/5330#issuecomment-251843011
    const wrapper = mount(
      <MuiThemeProvider>
        <EnterUsernameForm user={mockUserData} />
      </MuiThemeProvider>
    )
    const usernameTextField = wrapper.find('[data-test-id="enter-username-form-username-field"] input')

    // Enter a username
    // https://github.com/airbnb/enzyme/issues/76#issuecomment-259734821
    usernameTextField.instance().value = 'bobert'
    wrapper.update()

    // TODO: put a data-test-id directly on the button component
    // after updating to Material UI 1.x. We need to do this in many tests.
    const button = wrapper.find('[data-test-id="enter-username-form-button-container"] button')
    button.simulate('click')
    expect(SetUsernameMutation).toHaveBeenCalled()
  })

  it('it does not call SetUsernameMutation when the username is invalid', function () {
    const EnterUsernameForm = require('../EnterUsernameForm').default
    const wrapper = mount(
      <MuiThemeProvider>
        <EnterUsernameForm user={mockUserData} />
      </MuiThemeProvider>
    )
    const usernameTextField = wrapper.find('[data-test-id="enter-username-form-username-field"] input')

    // Enter a too-short username
    usernameTextField.instance().value = 'x'
    wrapper.update()

    const button = wrapper.find('[data-test-id="enter-username-form-button-container"] button')
    button.simulate('click')

    // We shouldn't call to save the username
    expect(SetUsernameMutation).not.toHaveBeenCalled()
  })

  it('matches expected snapshot', function () {
    const EnterUsernameForm = require('../EnterUsernameForm').default
    const wrapper = shallow(
      <EnterUsernameForm user={mockUserData} />
    )
    expect(toJson(wrapper)).toMatchSnapshot()
  })
})
<|endoftext|>


<commit_before>/**
 * General testing of the router (BDD)
 */
var router = require('../index.js')
var http   = require('http')

/**
 * Simulate an http server
 */
function TestReqTo(method, path) {
    router({
        method: method,
        url: path
    }, {});
}

//Test test :)
exports.testTest = function(test) {
    test.ok(true)
    test.done()
}

//Get /document
exports.testStaticGetRoute = function(test) {
    router.get('/document', function(req, res) {
        test.done()
    })

    TestReqTo('GET', '/home')
}

//Post /document
exports.testStaticPostRoute = function(test) {
    router.post('/document', function(req, res) {
        test.done()
    })

    TestReqTo('POST', '/document')
}

//Head /document
exports.testStaticPostRoute = function(test) {
    router.head('/document', function(req, res) {
        test.done()
    })

    TestReqTo('HEAD', '/document')
}

//Put /document
exports.testStaticPostRoute = function(test) {
    router.put('/document', function(req, res) {
        test.done()
    })

    TestReqTo('PUT', '/document')
}

//Delete /document
exports.testStaticPostRoute = function(test) {
    router.delete('/document', function(req, res) {
        test.done()
    })

    TestReqTo('DELETE', '/document')
}
<commit_msg>dynamic routes tests<commit_after>/**
 * General testing of the router (BDD)
 */
var router = require('../index.js')
var http   = require('http')

/**
 * Simulate an http server
 */
function TestReqTo(method, path) {
    router({
        method: method,
        url: path
    }, {});
}

//Test test :)
exports.testTest = function(test) {
    test.ok(true)
    test.done()
}

//Get /document
exports.testStaticGetRoute = function(test) {
    router.get('/document', function(req, res) {
        test.done()
    })

    TestReqTo('GET', '/document')
}

//Post /document
exports.testStaticPostRoute = function(test) {
    router.post('/document', function(req, res) {
        test.done()
    })

    TestReqTo('POST', '/document')
}

//Head /document
exports.testStaticHeadRoute = function(test) {
    router.head('/document', function(req, res) {
        test.done()
    })

    TestReqTo('HEAD', '/document')
}

//Put /document
exports.testStaticPutRoute = function(test) {
    router.put('/document', function(req, res) {
        test.done()
    })

    TestReqTo('PUT', '/document')
}

//Delete /document
exports.testStaticDeleteRoute = function(test) {
    router.delete('/document', function(req, res) {
        test.done()
    })

    TestReqTo('DELETE', '/document')
}

//Dynamic routes
exports.testDynamicRoute = function(test) {
    router.get('/document/when/:date/set/:tomorrow', function(req, res) {
        test.expect(2)
        test.equal(req.parameters.date, '05102014', 'data param did not match')
        test.equal(req.parameters.tomorrow, '06102014', 'data param did not match')
        test.done()
    })

    TestReqTo('GET', '/document/when/05102014/set/06102014')
}

//Strange dynamic route
exports.testDynamicRoute2 = function(test) {
    router.get('/document/:name/:date/set/:tomorrow', function(req, res) {
        test.expect(3)
        test.equal(req.parameters.name, 'something', 'data param did not match')
        test.equal(req.parameters.date, '05102014', 'data param did not match')
        test.equal(req.parameters.tomorrow, '06102014', 'data param did not match')
        test.done()
    })

    TestReqTo('GET', '/document/something/05102014/set/06102014')
}

//Various dynamic route
exports.testDynamicRoute3 = function(test) {
    router.get('/:document/:name/:date/set/:tomorrow', function(req, res) {
        test.expect(4)
        test.equal(req.parameters.document, 'shala', 'data param did not match')
        test.equal(req.parameters.name, 'something', 'data param did not match')
        test.equal(req.parameters.date, '05102014', 'data param did not match')
        test.equal(req.parameters.tomorrow, '06102014', 'data param did not match')
        test.done()
    })

    TestReqTo('GET', '/shala/something/05102014/set/06102014')
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2015 GFT Appverse, S.L., Sociedad Unipersonal.
 This Source Code Form is subject to the terms of the Appverse Public License
 Version 2.0 (“APL v2.0”). If a copy of the APL was not distributed with this
 file, You can obtain one at http://www.appverse.mobi/licenses/apl_v2.0.pdf. [^]
 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the conditions of the AppVerse Public License v2.0
 are met.
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. EXCEPT IN CASE OF WILLFUL MISCONDUCT OR GROSS NEGLIGENCE, IN NO EVENT
 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

var path = require('path');
var assert = require('yeoman-generator').assert;
var helpers = require('yeoman-generator').test;
var os = require('os');
var fse = require('fs-extra');

describe('appverse-html5:webkit', function () {
    before(function (done) {
        helpers.run(path.join(__dirname, '../webkit'))
            .inDir(path.join(os.tmpdir(), 'testApp-webkit'), function (dir) {
                fse.copySync(path.join(__dirname, '../app/templates/package.json'), path.join(dir, 'package.json'));
            })
            .withPrompts({
                webkit: true
            })
            .withOptions({
                'skip-install': true
            })
            .on('end', done);
    });

    it('includes scripts', function () {
        assert.file([
            'config/nodewebkit.js',
            'tasks/webkit.js'
        ]);
    });

});
<commit_msg>FATE-154 Webkit package<commit_after>/*
 Copyright (c) 2015 GFT Appverse, S.L., Sociedad Unipersonal.
 This Source Code Form is subject to the terms of the Appverse Public License
 Version 2.0 (“APL v2.0”). If a copy of the APL was not distributed with this
 file, You can obtain one at http://www.appverse.mobi/licenses/apl_v2.0.pdf. [^]
 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the conditions of the AppVerse Public License v2.0
 are met.
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. EXCEPT IN CASE OF WILLFUL MISCONDUCT OR GROSS NEGLIGENCE, IN NO EVENT
 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

var path = require('path');
var assert = require('yeoman-generator').assert;
var helpers = require('yeoman-generator').test;
var os = require('os');
var fse = require('fs-extra');

describe('appverse-html5:webkit', function () {
    before(function (done) {
        helpers.run(path.join(__dirname, '../webkit'))
            .inDir(path.join(os.tmpdir(), 'testApp-webkit'), function (dir) {
                fse.copySync(path.join(__dirname, '../app/templates/package.json'), path.join(dir, 'package.json'));
            })
            .withPrompts({
                webkit: true
            })
            .withOptions({
                'skip-install': true
            })
            .on('end', done);
    });

    it('includes scripts', function () {
        assert.file([
            'config/nwjs.js',
            'tasks/webkit.js'
        ]);
    });

});
<|endoftext|>


<commit_before>var should = require('chai').should();
var Random = require('../lib/random');

describe('random', function() {

  describe('getRandomBuffer', function() {

    it('should return a buffer', function() {
      var bytes = Random.getRandomBuffer(8);
      bytes.length.should.equal(8);
      Buffer.isBuffer(bytes).should.equal(true);
    });

    it('should not equate two 256 bit random buffers', function() {
      var bytes1 = Random.getRandomBuffer(32);
      var bytes2 = Random.getRandomBuffer(32);
      bytes1.toString('hex').should.not.equal(bytes2.toString('hex'));
    });

    it('should generate 1000 8 byte buffers in a row that are not equal', function() {
      var bufs = [];
      for (var i = 0; i < 100; i++)
        bufs[i] = Random.getRandomBuffer(8);
      for (var i = 0; i < 100; i++)
        for (var j = i + 1; j < 100; j++)
          bufs[i].toString('hex').should.not.equal(bufs[j].toString('hex'));
    });

  });

  describe('getPseudoRandomBuffer', function() {

    it('should generate 7 random bytes', function() {
      var buf = Random.getPseudoRandomBuffer(7);
      buf.length.should.equal(7);
    });

    it('should generate 8 random bytes', function() {
      var buf = Random.getPseudoRandomBuffer(8);
      buf.length.should.equal(8);
    });

    it('should generate 9 random bytes', function() {
      var buf = Random.getPseudoRandomBuffer(9);
      buf.length.should.equal(9);
    });

    it('should generate 90 random bytes', function() {
      var buf = Random.getPseudoRandomBuffer(90);
      buf.length.should.equal(90);
    });

    it('should generate two 8 byte buffers that are not equal', function() {
      var buf1 = Random.getPseudoRandomBuffer(8);
      var buf2 = Random.getPseudoRandomBuffer(8);
      buf1.toString('hex').should.not.equal(buf2.toString('hex'));
    });

  });

});
<commit_msg>use hash marks in function names in tests<commit_after>var should = require('chai').should();
var Random = require('../lib/random');

describe('random', function() {

  describe('#getRandomBuffer', function() {

    it('should return a buffer', function() {
      var bytes = Random.getRandomBuffer(8);
      bytes.length.should.equal(8);
      Buffer.isBuffer(bytes).should.equal(true);
    });

    it('should not equate two 256 bit random buffers', function() {
      var bytes1 = Random.getRandomBuffer(32);
      var bytes2 = Random.getRandomBuffer(32);
      bytes1.toString('hex').should.not.equal(bytes2.toString('hex'));
    });

    it('should generate 1000 8 byte buffers in a row that are not equal', function() {
      var bufs = [];
      for (var i = 0; i < 100; i++)
        bufs[i] = Random.getRandomBuffer(8);
      for (var i = 0; i < 100; i++)
        for (var j = i + 1; j < 100; j++)
          bufs[i].toString('hex').should.not.equal(bufs[j].toString('hex'));
    });

  });

  describe('#getPseudoRandomBuffer', function() {

    it('should generate 7 random bytes', function() {
      var buf = Random.getPseudoRandomBuffer(7);
      buf.length.should.equal(7);
    });

    it('should generate 8 random bytes', function() {
      var buf = Random.getPseudoRandomBuffer(8);
      buf.length.should.equal(8);
    });

    it('should generate 9 random bytes', function() {
      var buf = Random.getPseudoRandomBuffer(9);
      buf.length.should.equal(9);
    });

    it('should generate 90 random bytes', function() {
      var buf = Random.getPseudoRandomBuffer(90);
      buf.length.should.equal(90);
    });

    it('should generate two 8 byte buffers that are not equal', function() {
      var buf1 = Random.getPseudoRandomBuffer(8);
      var buf2 = Random.getPseudoRandomBuffer(8);
      buf1.toString('hex').should.not.equal(buf2.toString('hex'));
    });

  });

});
<|endoftext|>


<commit_before>// attempt to keep app from going inactive

chrome.alarms.create("ping", {when: 5000, periodInMinutes: 1 });
chrome.alarms.onAlarm.addListener(function(alarm) { console.info("alarm name = " + alarm.name); });

const IP = "127.0.0.1";
const PORT = 7000;
const EXTENSION_ID = "epmfkpbifhkdhcedgfppmeeoonjenkee"; //FIXME: Hardcoded extension ID

const STATE_READING_LENGTH = 1;
const STATE_READING_OBJECT = 2;

var serverSocketId;
var state = STATE_READING_LENGTH;
var buf = new Uint8Array(4);
var bytesToRead = buf.length;

chrome.sockets.tcpServer.create({}, function(createInfo) {
  listenAndAccept(createInfo.socketId);
});

function listenAndAccept(socketId) {
  chrome.sockets.tcpServer.listen(socketId,
    IP, PORT, function(resultCode) {
      onListenCallback(socketId, resultCode)
  });
}

function onListenCallback(socketId, resultCode) {
  if (resultCode < 0) {
    console.log("Error listening:" +
      chrome.runtime.lastError.message);
    return;
  }
  serverSocketId = socketId;
  chrome.sockets.tcpServer.onAccept.addListener(onAccept);
}

function onAccept(info) {
  if (info.socketId != serverSocketId)
    return;
  console.log("Client connected.");
  chrome.sockets.tcp.onReceive.addListener(onReceive);
  chrome.sockets.tcp.setPaused(info.clientSocketId, false);
}

function readIntoBuf(data) {
  var n = Math.min(data.byteLength, bytesToRead);
  buf.subarray(buf.length - bytesToRead, n).set(new Uint8Array(data.slice(0, n)));
  bytesToRead -= n;
  return data.slice(n);
}

function onReceive(info) {
  console.log("Data received.");
  var data = info.data;
  switch (state) {
  case STATE_READING_LENGTH:
    data = readIntoBuf(data);
    if (bytesToRead > 0)
      return;

    var b = buf;
    bytesToRead = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
    console.log(bytesToRead);
    buf = new Uint8Array(bytesToRead);
    state = STATE_READING_OBJECT;

  case STATE_READING_OBJECT:
    data = readIntoBuf(data);
    if (bytesToRead > 0)
      return;

    var str = ab2str(buf);
    console.log(str);
    var request = JSON.parse(str);
    makeRequest(request, info.socketId);

    state = STATE_READING_LENGTH;
    buf = new Uint8Array(4);
    bytesToRead = buf.length;
  }
}

function makeRequest(request, client_socket) {
  chrome.runtime.sendMessage(EXTENSION_ID, request, function(response) {
    returnResponse(response, client_socket);
  });
}

function returnResponse(response, client_socket) {
  var str = JSON.stringify(response);
  var b = str2ab(str);

  var buf = new Uint8Array(4 + b.byteLength);
  var len = b.byteLength;
  buf[0] = (len >> 24) & 0xff;
  buf[1] = (len >> 16) & 0xff;
  buf[2] = (len >> 8) & 0xff;
  buf[3] = len & 0xff;
  buf.set(new Uint8Array(b), 4);

  chrome.sockets.tcp.send(client_socket, buf.buffer, function(info) {
    if (info.resultCode != 0)
      console.log("Send failed");
  });
}

function ab2str(buffer) {
  var encodedString = String.fromCharCode.apply(null, buffer),
      decodedString = decodeURIComponent(escape(encodedString));
  return decodedString;
}

function str2ab(string) {
    var string = unescape(encodeURIComponent(string)),
        charList = string.split(''),
        buf = [];
    for (var i = 0; i < charList.length; i++) {
      buf.push(charList[i].charCodeAt(0));
    }
    return (new Uint8Array(buf)).buffer;
}
<commit_msg>Debug info.<commit_after>// attempt to keep app from going inactive

chrome.alarms.create("ping", {when: 5000, periodInMinutes: 1 });
chrome.alarms.onAlarm.addListener(function(alarm) { console.info("alarm name = " + alarm.name); });

const IP = "127.0.0.1";
const PORT = 7000;
const EXTENSION_ID = "epmfkpbifhkdhcedgfppmeeoonjenkee"; //FIXME: Hardcoded extension ID

const STATE_READING_LENGTH = 1;
const STATE_READING_OBJECT = 2;

var serverSocketId;
var state = STATE_READING_LENGTH;
var buf = new Uint8Array(4);
var bytesToRead = buf.length;

chrome.sockets.tcpServer.create({}, function(createInfo) {
  listenAndAccept(createInfo.socketId);
});

function listenAndAccept(socketId) {
  console.log("listenAndAccept " + socketId);
  chrome.sockets.tcpServer.listen(socketId,
    IP, PORT, function(resultCode) {
      onListenCallback(socketId, resultCode)
  });
}

function onListenCallback(socketId, resultCode) {
  console.log("onListenCallback " + socketId);
  if (resultCode < 0) {
    console.log("Error listening:" +
      chrome.runtime.lastError.message);
    return;
  }
  serverSocketId = socketId;
  chrome.sockets.tcpServer.onAccept.addListener(onAccept);
}

function onAccept(info) {
  console.log("onAccept " + JSON.stringify(info));
  if (info.socketId != serverSocketId)
    return;

  chrome.sockets.tcp.onReceive.addListener(onReceive);
  chrome.sockets.tcp.setPaused(info.clientSocketId, false);
}

function readIntoBuf(data) {
  var n = Math.min(data.byteLength, bytesToRead);
  buf.subarray(buf.length - bytesToRead, n).set(new Uint8Array(data.slice(0, n)));
  bytesToRead -= n;
  return data.slice(n);
}

function onReceive(info) {
  console.log("onReceive " + JSON.stringify(info));
  var data = info.data;
  switch (state) {
  case STATE_READING_LENGTH:
    data = readIntoBuf(data);
    if (bytesToRead > 0)
      return;

    var b = buf;
    bytesToRead = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
    console.log(bytesToRead);
    buf = new Uint8Array(bytesToRead);
    state = STATE_READING_OBJECT;

  case STATE_READING_OBJECT:
    data = readIntoBuf(data);
    if (bytesToRead > 0)
      return;

    var str = ab2str(buf);
    console.log(str);
    var request = JSON.parse(str);
    makeRequest(request, info.socketId);

    state = STATE_READING_LENGTH;
    buf = new Uint8Array(4);
    bytesToRead = buf.length;
  }
}

function makeRequest(request, client_socket) {
  chrome.runtime.sendMessage(EXTENSION_ID, request, function(response) {
    returnResponse(response, client_socket);
  });
}

function returnResponse(response, client_socket) {
  var str = JSON.stringify(response);
  var b = str2ab(str);

  var buf = new Uint8Array(4 + b.byteLength);
  var len = b.byteLength;
  buf[0] = (len >> 24) & 0xff;
  buf[1] = (len >> 16) & 0xff;
  buf[2] = (len >> 8) & 0xff;
  buf[3] = len & 0xff;
  buf.set(new Uint8Array(b), 4);

  chrome.sockets.tcp.send(client_socket, buf.buffer, function(info) {
    if (info.resultCode != 0)
      console.log("Send failed " + info.resultCode);
  });
}

function ab2str(buffer) {
  var encodedString = String.fromCharCode.apply(null, buffer),
      decodedString = decodeURIComponent(escape(encodedString));
  return decodedString;
}

function str2ab(string) {
    var string = unescape(encodeURIComponent(string)),
        charList = string.split(''),
        buf = [];
    for (var i = 0; i < charList.length; i++) {
      buf.push(charList[i].charCodeAt(0));
    }
    return (new Uint8Array(buf)).buffer;
}
<|endoftext|>


<commit_before>/*global require: false, module: false, console: false, describe: false */
'use strict';

var assert = require('assert');
var jv = require('jsverify');
var _ = require('../js/underscore_ext');
var {getColSpec} = require('../js/models/datasetJoins');

describe('datasetJoins', function () {
	describe('getColSpec', function () {
		var {suchthat, constant, asciinestring, oneof, nearray, tuple} = jv;
		{
			let someGenes = fields => _.some(fields, ([type]) => type === 'genes');
			let fieldTypes = ['genes', 'geneProbes', 'null'].map(constant),
				typeDsID = tuple([oneof(fieldTypes), asciinestring]),
				typesPmap = tuple([suchthat(nearray(typeDsID), someGenes), asciinestring]);

			jv.property("defaults to genes with some 'genes' fields", typesPmap, function ([typesDsIDs, pmap]) {

				var fieldSpecs = typesDsIDs.map(([type, dsID]) => ({
						fields: ['foo'],
						fieldType: type,
						fetchType: type === 'null' ? 'null' : 'xena',
						...(type === 'null' ? null : {dsID})
					})),
					datasets = _.object(_.pluck(typesDsIDs, 1),
						_.times(typesDsIDs.length, _.constant({probemap: pmap})));
					var fs = getColSpec(fieldSpecs, datasets);

					assert.equal(fs.fieldType, 'genes');
					return true;
			});
		}
		{
			let someGeneProbes = fields => _.some(fields, ([type]) => type === 'geneProbes');
			let fieldTypes = ['geneProbes', 'null'].map(constant),
				typeDsID = tuple([oneof(fieldTypes), asciinestring]),
				typesPmap = tuple([suchthat(nearray(typeDsID), someGeneProbes), asciinestring]);

			jv.property('defaults to geneProbes with same probemap', typesPmap, function ([typesDsIDs, pmap]) {
				var fieldSpecs = typesDsIDs.map(([type, dsID]) => ({
						fields: ['foo'],
						fieldType: type,
						fetchType: type === 'null' ? 'null' : 'xena',
						...(type === 'null' ? null : {dsID})
					})),
					datasets = _.object(_.pluck(typesDsIDs, 1),
						_.times(typesDsIDs.length, _.constant({probemap: pmap})));
					var fs = getColSpec(fieldSpecs, datasets);

					assert.equal(fs.fieldType, 'geneProbes');
					return true;
			});
		}
		{
			let twoGeneProbes = fields => _.filter(fields, ([type]) => type === 'geneProbes').length >= 2;
			let diffProbemaps = fields => _.uniq(_.pluck(fields, 1)).length === fields.length;
			let fieldTypes = ['geneProbes', 'null'].map(constant),
				typeDsID = tuple([oneof(fieldTypes), asciinestring, asciinestring]),
				fields = suchthat(nearray(typeDsID), fds => twoGeneProbes(fds) && diffProbemaps(fds));

			// test: genes/geneProbes, probemap = or !=, fields > 1 or = 1
			// For any combination of genes, geneProbes, null, getColSpec should
			// set to geneProbes if probemaps match, and to genes if they don't.
			//
			// To test, we want a set of fieldSpec, at least one of type 'genes'.
			jv.property('defaults to genes with same different probemap', fields, function (typesDsIDsPmaps) {
				var fieldSpecs = typesDsIDsPmaps.map(([type, dsID]) => ({
						fields: ['foo'],
						fieldType: type,
						fetchType: type === 'null' ? 'null' : 'xena',
						...(type === 'null' ? null : {dsID})
					})),
					datasets = _.object(_.map(typesDsIDsPmaps, ([, dsID, pmap]) => [dsID, {probemap: pmap}]));
					var fs = getColSpec(fieldSpecs, datasets);

					assert.equal(fs.fieldType, 'genes');
					return true;
			});
		}

		return true;
	});
});
<commit_msg>Fix rare test failure.<commit_after>/*global it: false, require: false, module: false, console: false, describe: false */
'use strict';

var assert = require('assert');
var jv = require('jsverify');
var _ = require('../js/underscore_ext');
var {getColSpec} = require('../js/models/datasetJoins');

var opts = {
	tests: 1000
};

// like jv.property, but set global options
function property(name, ...args) {
    var prop = jv.forall(...args);
    it(name, function () {
      return jv.assert(prop, opts);
    });
}

describe('datasetJoins', function () {
	describe('getColSpec', function () {
		var {suchthat, constant, asciinestring, oneof, nearray, tuple} = jv;
		{
			let someGenes = fields => _.some(fields, ([type]) => type === 'genes');
			let fieldTypes = ['genes', 'geneProbes', 'null'].map(constant),
				typeDsID = tuple([oneof(fieldTypes), asciinestring]),
				typesPmap = tuple([suchthat(nearray(typeDsID), someGenes), asciinestring]);

			property("defaults to genes with some 'genes' fields", typesPmap, function ([typesDsIDs, pmap]) {

				var fieldSpecs = typesDsIDs.map(([type, dsID]) => ({
						fields: ['foo'],
						fieldType: type,
						fetchType: type === 'null' ? 'null' : 'xena',
						...(type === 'null' ? null : {dsID})
					})),
					datasets = _.object(_.pluck(typesDsIDs, 1),
						_.times(typesDsIDs.length, _.constant({probemap: pmap})));
					var fs = getColSpec(fieldSpecs, datasets);

					assert.equal(fs.fieldType, 'genes');
					return true;
			});
		}
		{
			let someGeneProbes = fields => _.some(fields, ([type]) => type === 'geneProbes');
			let fieldTypes = ['geneProbes', 'null'].map(constant),
				typeDsID = tuple([oneof(fieldTypes), asciinestring]),
				typesPmap = tuple([suchthat(nearray(typeDsID), someGeneProbes), asciinestring]);

			property('defaults to geneProbes with same probemap', typesPmap, function ([typesDsIDs, pmap]) {
				var fieldSpecs = typesDsIDs.map(([type, dsID]) => ({
						fields: ['foo'],
						fieldType: type,
						fetchType: type === 'null' ? 'null' : 'xena',
						...(type === 'null' ? null : {dsID})
					})),
					datasets = _.object(_.pluck(typesDsIDs, 1),
						_.times(typesDsIDs.length, _.constant({probemap: pmap})));
					var fs = getColSpec(fieldSpecs, datasets);

					assert.equal(fs.fieldType, 'geneProbes');
					return true;
			});
		}
		{
			let twoGeneProbes = fields => _.filter(fields, ([type]) => type === 'geneProbes').length >= 2;
			let diffDsIDs = fields => _.uniq(_.pluck(fields, 1)).length === fields.length;
			let diffProbemaps = fields => _.uniq(_.pluck(fields, 2)).length === fields.length;
			let fieldTypes = ['geneProbes', 'null'].map(constant),
				typeDsID = tuple([oneof(fieldTypes), asciinestring, asciinestring]),
				fields = suchthat(nearray(typeDsID), fds => twoGeneProbes(fds) && diffProbemaps(fds) && diffDsIDs(fds));

			// test: genes/geneProbes, probemap = or !=, fields > 1 or = 1
			// For any combination of genes, geneProbes, null, getColSpec should
			// set to geneProbes if probemaps match, and to genes if they don't.
			//
			// To test, we want a set of fieldSpec, at least one of type 'genes'.
			property('defaults to genes with same different probemap', fields, function (typesDsIDsPmaps) {
				var fieldSpecs = typesDsIDsPmaps.map(([type, dsID]) => ({
						fields: ['foo'],
						fieldType: type,
						fetchType: type === 'null' ? 'null' : 'xena',
						...(type === 'null' ? null : {dsID})
					})),
					datasets = _.object(_.map(typesDsIDsPmaps, ([, dsID, pmap]) => [dsID, {probemap: pmap}]));
					var fs = getColSpec(fieldSpecs, datasets);

					assert.equal(fs.fieldType, 'genes');
					return true;
			});
		}

		return true;
	});
});
<|endoftext|>