@gmod/jbrowse
Version:
JBrowse - client-side genome browser
195 lines (169 loc) • 6.69 kB
JavaScript
define( [
'dojo/_base/declare',
'dojo/_base/lang',
'dojo/_base/array',
'dojo/Deferred',
'JBrowse/Model/SimpleFeature',
'JBrowse/Store/SeqFeature',
'JBrowse/Store/DeferredFeaturesMixin',
'JBrowse/Store/DeferredStatsMixin',
'JBrowse/Store/SeqFeature/GlobalStatsEstimationMixin',
'JBrowse/Model/XHRBlob',
'./BED/Parser'
],
function(
declare,
lang,
array,
Deferred,
SimpleFeature,
SeqFeatureStore,
DeferredFeatures,
DeferredStats,
GlobalStatsEstimationMixin,
XHRBlob,
Parser
) {
return declare([ SeqFeatureStore, DeferredFeatures, DeferredStats, GlobalStatsEstimationMixin ],
/**
* @lends JBrowse.Store.SeqFeature.BED
*/
{
constructor: function( args ) {
this.data = args.blob ||
new XHRBlob( this.resolveUrl(
this._evalConf(args.urlTemplate)
)
);
this.features = [];
this._loadFeatures();
},
_loadFeatures: function() {
var thisB = this;
var features = this.bareFeatures = [];
var featuresSorted = true;
var seenRefs = this.refSeqs = {};
var parser = new Parser(
{
featureCallback: function(fs) {
array.forEach( fs, function( feature ) {
var prevFeature = features[ features.length-1 ];
var regRefName = thisB.browser.regularizeReferenceName( feature.seq_id );
if( regRefName in seenRefs && prevFeature && prevFeature.seq_id != feature.seq_id )
featuresSorted = false;
if( prevFeature && prevFeature.seq_id == feature.seq_id && feature.start < prevFeature.start )
featuresSorted = false;
if( !( regRefName in seenRefs ))
seenRefs[ regRefName ] = features.length;
if(thisB.config.featureCallback) {
features.push(thisB.config.featureCallback(feature,thisB));
}
else {
features.push(feature);
}
});
},
endCallback: function() {
if( ! featuresSorted ) {
features.sort( thisB._compareFeatureData );
// need to rebuild the refseq index if changing the sort order
thisB._rebuildRefSeqs( features );
}
thisB._estimateGlobalStats()
.then( function( stats ) {
thisB.globalStats = stats;
thisB._deferred.stats.resolve();
});
thisB._deferred.features.resolve( features );
},
commentCallback: (this.config.commentCallback || function(i) { }),
store: this
});
var fail = lang.hitch( this, '_failAllDeferred' );
// parse the whole file and store it
this.data.fetchLines(
function( line ) {
try {
parser.addLine(line);
} catch(e) {
fail('Error parsing BED.');
throw e;
}
},
lang.hitch( parser, 'finish' ),
fail
);
},
_rebuildRefSeqs: function( features ) {
var refs = {};
for( var i = 0; i<features.length; i++ ) {
var regRefName = this.browser.regularizeReferenceName( features[i].seq_id );
if( !( regRefName in refs ) )
refs[regRefName] = i;
}
this.refSeqs = refs;
},
_compareFeatureData: function( a, b ) {
if( a.seq_id < b.seq_id )
return -1;
else if( a.seq_id > b.seq_id )
return 1;
return a.start - b.start;
},
_getFeatures: function( query, featureCallback, finishedCallback, errorCallback ) {
var thisB = this;
thisB._deferred.features.then( function() {
thisB._search( query, featureCallback, finishedCallback, errorCallback );
});
},
_search: function( query, featureCallback, finishCallback, errorCallback ) {
// search in this.features, which are sorted
// by ref and start coordinate, to find the beginning of the
// relevant range
var bare = this.bareFeatures;
var converted = this.features;
var refName = this.browser.regularizeReferenceName( query.ref );
var i = this.refSeqs[ refName ];
if( !( i >= 0 )) {
finishCallback();
return;
}
var checkEnd = 'start' in query
? function(f) { return f.get('end') >= query.start; }
: function() { return true; };
for( ; i<bare.length; i++ ) {
// lazily convert the bare feature data to JBrowse features
var f = converted[i] ||
( converted[i] = function(b,i) {
bare[i] = false;
return this._formatFeature( b,i );
}.call( this, bare[i], i )
);
// features are sorted by ref seq and start coord, so we
// can stop if we are past the ref seq or the end of the
// query region
if( f._reg_seq_id != refName || f.get('start') > query.end )
break;
if( checkEnd( f ) ) {
this.applyFeatureTransforms([f])
.forEach(featureCallback)
}
}
finishCallback();
},
supportsFeatureTransforms: true,
_formatFeature: function( data, i ) {
var f = new SimpleFeature({
data: data,
id: data.seq_id + "_"+ data.start + "_" +data.end+ "_" + data.name + "_" + i
});
f._reg_seq_id = this.browser.regularizeReferenceName( data.seq_id );
return f;
},
saveStore: function() {
return {
urlTemplate: this.config.blob.url
};
}
});
});