@gmod/jbrowse
Version:
JBrowse - client-side genome browser
152 lines (128 loc) • 5.28 kB
JavaScript
define([
'dojo/_base/declare',
'dojo/_base/array',
'dojo/_base/lang',
'JBrowse/Store/SeqFeature',
'JBrowse/Model/SimpleFeature',
'JBrowse/Errors',
'JBrowse/Util',
'JBrowse/CodonTable'
],
function(
declare,
array,
lang,
SeqFeatureStore,
SimpleFeature,
JBrowseErrors,
Util,
CodonTable
) {
return declare( SeqFeatureStore , {
constructor: function( args ) {
this.searchParams = args.searchParams;
},
_defaultConfig: function() {
return Util.deepUpdate(
dojo.clone( this.inherited(arguments) ),
{
regionSizeLimit: 200000 // 200kb
});
},
getFeatures: function( query, featCallback, doneCallback, errorCallback ) {
var searchParams = lang.mixin(
// store the original query bounds - this helps prevent features from randomly disappearing
{ orig: { start: query.start, end: query.end }},
this.searchParams,
query.searchParams
);
var regionSize = query.end - query.start;
if( regionSize > this.config.regionSizeLimit )
throw new JBrowseErrors.DataOverflow( 'Region too large to search' );
var thisB = this;
this.browser.getStore('refseqs', function( refSeqStore ) {
if( refSeqStore )
refSeqStore.getReferenceSequence(
query,
function( sequence ) {
thisB.doSearch( query, sequence, searchParams, featCallback );
doneCallback();
},
errorCallback
);
else
doneCallback();
});
},
doSearch: function( query, sequence, params, featCallback ) {
var expr = new RegExp(
params.regex ? params.expr : this.escapeString( params.expr ),
params.caseIgnore ? "gi" : "g"
);
var sequences = [];
if( params.fwdStrand )
sequences.push( [sequence,1] );
if( params.revStrand )
sequences.push( [Util.revcom( sequence ),-1] );
array.forEach( sequences, function( r ) {
if( params.translate ) {
for( var frameOffset = 0; frameOffset < 3; frameOffset++ ) {
this._searchSequence( query, r[0], expr, r[1], featCallback, true, frameOffset );
}
} else {
this._searchSequence( query, r[0], expr, r[1], featCallback );
}
}, this );
},
_searchSequence: function( query, sequence, expr, strand, featCallback, translated, frameOffset ) {
if( translated )
sequence = this.translateSequence( sequence, frameOffset );
frameOffset = frameOffset || 0;
var multiplier = translated ? 3 : 1;
var start = query.start, end = query.end;
var features = [];
var match;
while( (match = expr.exec( sequence )) !== null && match.length ) {
expr.lastIndex = match.index + 1;
var result = match[0];
var newStart = strand > 0 ? start + frameOffset + multiplier*match.index
: end - frameOffset - multiplier*(match.index + result.length);
var newEnd = strand > 0 ? start + frameOffset + multiplier*(match.index + result.length)
: end - frameOffset - multiplier*match.index;
var newFeat = new SimpleFeature(
{
data: {
type: "SEARCH",
start: newStart,
end: newEnd,
searchMatch: result,
strand: strand
},
id: [newStart,newEnd,result].join(',')
});
featCallback( newFeat );
}
},
translateSequence:function( sequence, frameOffset ) {
var slicedSeq = sequence.slice( frameOffset );
slicedSeq = slicedSeq.slice( 0, Math.floor( slicedSeq.length / 3 ) * 3);
var translated = "";
var codontable=new CodonTable();
var codons=codontable.generateCodonTable(codontable.defaultCodonTable);
for(var i = 0; i < slicedSeq.length; i += 3) {
var nextCodon = slicedSeq.slice(i, i + 3);
translated = translated + codons[nextCodon];
}
return translated;
},
escapeString: function( str ) {
return (str+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
},
saveStore: function() {
return {
searchParams: this.config.searchParams,
regionSizeLimit: this.config.regionSizeLimit
};
}
});
});