@gmod/jbrowse
Version:
JBrowse - client-side genome browser
168 lines (149 loc) • 5.54 kB
JavaScript
define([ 'dojo/_base/declare',
'dojo/_base/lang',
'dojo/_base/array',
'dojo/request/xhr',
'JBrowse/Store/SeqFeature',
'JBrowse/Store/DeferredStatsMixin',
'JBrowse/Store/SeqFeature/GlobalStatsEstimationMixin',
'JBrowse/Util',
'JBrowse/Model/SimpleFeature',
'dojo/io-query'
],
function(
declare,
lang,
array,
xhr,
SeqFeatureStore,
DeferredStatsMixin,
GlobalStatsEstimationMixin,
Util,
SimpleFeature,
ioQuery
) {
return declare( [ SeqFeatureStore, DeferredStatsMixin, GlobalStatsEstimationMixin ],
/**
* @lends JBrowse.Store.SeqFeature.SPARQL
*/
{
/**
* JBrowse feature backend to retrieve features from a SPARQL endpoint.
* @constructs
*/
constructor: function(args) {
this.url = this.urlTemplate;
this.refSeq = args.refSeq;
this.baseUrl = args.baseUrl;
this.density = 0;
this.url = Util.resolveUrl(
this.baseUrl,
Util.fillTemplate( args.urlTemplate,
{ 'refseq': this.refSeq.name }
)
);
this.queryTemplate = args.queryTemplate;
if( ! this.queryTemplate ) {
console.error("No queryTemplate set for SPARQL backend, no data will be displayed");
}
var thisB = this;
this._estimateGlobalStats()
.then(
function( stats ) {
thisB.globalStats = stats;
thisB._deferred.stats.resolve( stats );
},
lang.hitch( this, '_failAllDeferred' )
);
},
// load: function() {
// // ping the endpoint to see if it's there
// dojo.xhrGet({ url: this.url+'?'+ioQuery.objectToQuery({ query: 'SELECT ?s WHERE { ?s ?p ?o } LIMIT 1' }),
// handleAs: "text",
// failOk: false,
// load: Util.debugHandler( this, function(o) { this.loadSuccess(o); }),
// error: dojo.hitch( this, function(error) { this.loadFail(error, this.url); } )
// });
// },
_makeQuery: function( query ) {
if( this.config.variables )
query = dojo.mixin( dojo.mixin( {}, this.config.variables ),
query
);
return Util.fillTemplate( this.queryTemplate, query );
},
_getFeatures: function() {
this.getFeatures.apply( this, arguments );
},
getFeatures: function( query, featCallback, finishCallback, errorCallback ) {
if( this.queryTemplate ) {
var thisB = this;
var headers = { "Accept": "application/json" };
if(this.config.disablePreflight) {
// https://www.sitepen.com/blog/2014/01/15/faq-cors-with-dojo/
headers["X-Requested-With"] = null;
}
xhr.get( this.url+'?'+ioQuery.objectToQuery({
query: this._makeQuery( query )
}),
{
headers: headers,
handleAs: "json",
failOk: true
})
.then( function(o) {
thisB._resultsToFeatures( o, featCallback );
finishCallback();
},
lang.hitch( this, '_failAllDeferred' )
);
} else {
finishCallback();
}
},
_resultsToFeatures: function( results, featCallback ) {
var rows = ((results||{}).results||{}).bindings || [];
if( ! rows.length )
return;
var fields = results.head.vars;
var requiredFields = ['start','end','strand','uniqueID'];
for( var i = 0; i<requiredFields.length; i++ ) {
if( fields.indexOf( requiredFields[i] ) == -1 ) {
console.error("Required field "+requiredFields[i]+" missing from feature data");
return;
}
};
var seenFeatures = {};
array.forEach( rows, function( row ) {
var f = { data: { subfeatures: [] } };
var data = f.data;
array.forEach( fields, function(field) {
if( field in row )
data[field] = row[field].value;
});
data.start = parseInt( data.start );
data.end = parseInt( data.end );
data.strand = parseInt( data.strand );
var id = data.uniqueID;
delete data.uniqueID;
f.id = id;
seenFeatures[ id ] = f;
},this);
// resolve subfeatures, keeping only top-level features in seenFeatures
for( var id in seenFeatures ) {
var f = seenFeatures[id];
var pid = f.data.parentUniqueID;
delete f.data.parentUniqueID;
if( pid ) {
var p = seenFeatures[ pid ];
if( p ) {
p.data.subfeatures.push( f.data );
delete seenFeatures[id];
}
}
}
for( var id in seenFeatures ) {
featCallback( new SimpleFeature( seenFeatures[id] ) );
}
}
});
});