UNPKG

@gmod/jbrowse

Version:

JBrowse - client-side genome browser

115 lines (101 loc) 3.96 kB
const LRU = cjsRequire('quick-lru') const { IndexedFasta } = cjsRequire('@gmod/indexedfasta') const { Buffer } = cjsRequire('buffer') const fastaIndexedFilesCache = new LRU({ maxSize: 5 }) const BlobFilehandleWrapper = cjsRequire('../../Model/BlobFilehandleWrapper') define( [ 'dojo/_base/declare', 'JBrowse/Store/SeqFeature', 'JBrowse/Model/XHRBlob', 'JBrowse/Model/SimpleFeature', 'JBrowse/Store/DeferredFeaturesMixin' ], function( declare, SeqFeatureStore, XHRBlob, SimpleFeature, DeferredFeaturesMixin ) { return declare( [ SeqFeatureStore, DeferredFeaturesMixin ], { /** * Storage backend for sequences in indexed fasta files * served as static text files. * @constructs */ constructor: function(args) { let dataBlob if (args.fasta) dataBlob = new BlobFilehandleWrapper(args.fasta) else if (args.urlTemplate) dataBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.urlTemplate), { expectRanges: true })) else dataBlob = new BlobFilehandleWrapper(new XHRBlob('data.fa', { expectRanges: true })) let indexBlob if (args.fai) indexBlob = new BlobFilehandleWrapper(args.fai) else if (args.faiUrlTemplate) indexBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.faiUrlTemplate))) else if (args.urlTemplate) indexBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.urlTemplate+'.fai'))) else throw new Error('no index provided, must provide a FASTA index') this.source = dataBlob.toString() // LRU-cache the FASTA object so we don't have to re-download the // index when we switch chromosomes const cacheKey = `data: ${dataBlob}, index: ${indexBlob}` this.fasta = fastaIndexedFilesCache.get(cacheKey) if (!this.fasta) { this.fasta = new IndexedFasta({ fasta: dataBlob, fai: indexBlob, chunkSizeLimit: args.chunkSizeLimit || 1000000 }) fastaIndexedFilesCache.set(cacheKey, this.fasta) } this.fasta.getSequenceList().then(() => { this._deferred.features.resolve({success:true}) }, this._failAllDeferred.bind(this)) }, _getFeatures: function( query, featCallback, endCallback, errorCallback ) { if(query.start < 0) { query.start = 0; } var refname = query.ref; // if they both regularize to the same thing, use this.refSeq.name since that is guaranteed to be from refseq store if(!this.browser.compareReferenceNames( this.refSeq.name, refname ) ) refname = this.refSeq.name; this.fasta.getResiduesByName( refname, query.start, query.end ).then(seq => { featCallback(new SimpleFeature({data: {seq, start: query.start, end: query.end}})) endCallback() }, errorCallback ); }, hasRefSeq: function( seqName, callback, errorCallback ) { this.fasta.getSequenceSize(seqName) .then( size => { callback(size !== undefined) }, errorCallback, ) }, getRefSeqs: function( callback, errorCallback ) { this.fasta.getSequenceSizes() .then(sizes => Object.entries(sizes).map(([name,length]) => { return { name, length, end: length, start: 0, } })) .then(callback, errorCallback) }, saveStore: function() { return { urlTemplate: (this.config.file||this.config.blob).url, faiUrlTemplate: this.config.fai.url }; } }); });