UNPKG

@gmod/jbrowse

Version:

JBrowse - client-side genome browser

162 lines (144 loc) 5.21 kB
define([ 'dojo/_base/declare', 'dojo/_base/lang', 'dojo/Deferred', 'JBrowse/Store/SeqFeature', 'JBrowse/Store/DeferredStatsMixin', 'JBrowse/Store/DeferredFeaturesMixin', 'JBrowse/Store/TribbleIndexedFile', 'JBrowse/Store/SeqFeature/GlobalStatsEstimationMixin', 'JBrowse/Model/XHRBlob', './VCF/Parser' ], function( declare, lang, Deferred, SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, TribbleIndexedFile, GlobalStatsEstimationMixin, XHRBlob, VCFParser ) { // subclass the TabixIndexedFile to modify the parsed items a little // bit so that the range filtering in TabixIndexedFile will work. VCF // files don't actually have an end coordinate, so we have to make it // here. also convert coordinates to interbase. var TribbleIndexedVCFFile = declare( TribbleIndexedFile, { parseLine: function() { var i = this.inherited( arguments ); if( i ) { var ret = i.fields[7].match(/^END=(\d+)|;END=(\d+)/); i.start--; i.end = ret ? (+ret[1] || +ret[2]) : i.start + i.fields[3].length; } return i; }, getColumnNumbers: function() { return { ref: 1, start: 2, end: -1 } } }); return declare( [ SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, GlobalStatsEstimationMixin, VCFParser ], { constructor: function( args ) { var thisB = this; var idxBlob = args.idx || new XHRBlob( this.resolveUrl( this.getConf('idxUrlTemplate',[]) || this.getConf('urlTemplate',[])+'.idx' ) ); var fileBlob = args.file || new XHRBlob( this.resolveUrl( this.getConf('urlTemplate',[]) ), { expectRanges: true } ); this.indexedData = new TribbleIndexedVCFFile( { idx: idxBlob, file: fileBlob, browser: this.browser, chunkSizeLimit: args.chunkSizeLimit || 1000000 }); this.getVCFHeader() .then( function( header ) { thisB._deferred.features.resolve({success:true}); thisB._estimateGlobalStats() .then( function( stats ) { thisB.globalStats = stats; thisB._deferred.stats.resolve( stats ); }, lang.hitch( thisB, '_failAllDeferred' ) ); }, lang.hitch( thisB, '_failAllDeferred' ) ); this.storeTimeout = args.storeTimeout || 3000; }, /** fetch and parse the VCF header lines */ getVCFHeader: function() { if (!this._parsedHeader) { this._parsedHeader = new Deferred() var reject = this._parsedHeader.reject.bind(this._parsedHeader) this.indexedData.indexLoaded.then( () => { const maxFetch = this.indexedData.index.data.firstDataOffset ? this.indexedData.index.data.firstDataOffset-1 : null; this.indexedData.data.read( 0, maxFetch, bytes => { this.parseHeader( new Uint8Array( bytes ) ); this._parsedHeader.resolve( this.header ); }, reject ); }, reject ); } return this._parsedHeader }, _getFeatures: function( query, featureCallback, finishedCallback, errorCallback ) { this.getVCFHeader().then( () => { this.indexedData.getLines( query.ref || this.refSeq.name, query.start, query.end, line => { var f = this.lineToFeature( line ) //console.log(f); featureCallback( f ) //return f; }, finishedCallback, errorCallback ); }, errorCallback ) }, /** * Interrogate whether a store has data for a given reference * sequence. Calls the given callback with either true or false. * * Implemented as a binary interrogation because some stores are * smart enough to regularize reference sequence names, while * others are not. */ hasRefSeq: function( seqName, callback, errorCallback ) { return this.indexedData.index.hasRefSeq( seqName, callback, errorCallback ); }, saveStore: function() { return { urlTemplate: this.config.file.url, idxUrlTemplate: this.config.idx.url }; } }); });