UNPKG

@gmod/jbrowse

Version:

JBrowse - client-side genome browser

170 lines (151 loc) 5.54 kB
define([ 'dojo/_base/declare', 'dojo/_base/lang', 'dojo/Deferred', 'JBrowse/Store/SeqFeature', 'JBrowse/Store/DeferredStatsMixin', 'JBrowse/Store/DeferredFeaturesMixin', 'JBrowse/Store/TabixIndexedFile', 'JBrowse/Store/SeqFeature/IndexedStatsEstimationMixin', 'JBrowse/Model/XHRBlob', './VCF/Parser' ], function( declare, lang, Deferred, SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, TabixIndexedFile, IndexedStatsEstimationMixin, 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 VCFIndexedFile = declare( TabixIndexedFile, { 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; } }); return declare( [ SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, IndexedStatsEstimationMixin, VCFParser ], { constructor: function( args ) { var thisB = this; var csiBlob, tbiBlob; if(args.csi || this.config.csiUrlTemplate) { csiBlob = args.csi || new XHRBlob( this.resolveUrl( this.getConf('csiUrlTemplate',[]) ) ); } else { tbiBlob = args.tbi || new XHRBlob( this.resolveUrl( this.getConf('tbiUrlTemplate',[]) || this.getConf('urlTemplate',[])+'.tbi' ) ); } var fileBlob = args.file || new XHRBlob( this.resolveUrl( this.getConf('urlTemplate',[]) ), { expectRanges: true } ); this.indexedData = new VCFIndexedFile( { tbi: tbiBlob, csi: csiBlob, 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() { var thisB = this; return this._parsedHeader || ( this._parsedHeader = function() { var d = new Deferred(); var reject = lang.hitch( d, 'reject' ); thisB.indexedData.indexLoaded.then( function() { var maxFetch = thisB.indexedData.index.firstDataLine ? thisB.indexedData.index.firstDataLine.block + thisB.indexedData.data.blockSize - 1 : null; thisB.indexedData.data.read( 0, maxFetch, function( bytes ) { thisB.parseHeader( new Uint8Array( bytes ) ); d.resolve( thisB.header ); }, reject ); }, reject ); return d; }.call(this)); }, _getFeatures: function( query, featureCallback, finishedCallback, errorCallback ) { var thisB = this; thisB.getVCFHeader().then( function() { thisB.indexedData.getLines( query.ref || thisB.refSeq.name, query.start, query.end, function( line ) { var f = thisB.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, tbiUrlTemplate: ((this.config.tbi)||{}).url, csiUrlTemplate: ((this.config.csi)||{}).url }; } }); });