@gmod/jbrowse
Version:
JBrowse - client-side genome browser
261 lines (227 loc) • 11.1 kB
JavaScript
define( [ 'dojo/_base/declare',
'dojo/_base/lang',
'dojo/_base/array',
'dojo/_base/json',
'dojo/request',
'JBrowse/Util',
'JBrowse/Digest/Crc32'
], function(
declare,
lang,
array,
json,
request,
Util,
digest
) {
var dojof = Util.dojof;
return declare('JBrowse.ConfigAdaptor.JB_json_v1',null,
/**
* @lends JBrowse.ConfigAdaptor.JB_json_v1.prototype
*/
{
/**
* Configuration adaptor for JBrowse JSON version 1 configuration
* files (formerly known as trackList.json files).
* @constructs
*/
constructor: function() {},
/**
* Load the configuration file from a URL.
*
* @param args.config.url {String} URL for fetching the config file.
*/
load: function( /**Object*/ args ) {
var that = this;
if( args.config.url ) {
var url = Util.resolveUrl( args.baseUrl || window.location.href, args.config.url );
return request( url+'?v='+Math.random(), { handleAs: 'text', headers: {'X-Requested-With': null} })
.then( function( o ) {
o = that.parse_conf( o, args ) || {};
o.sourceUrl = url;
o = that.regularize_conf( o, args );
return o;
});
}
else if( args.config.data ) {
return Util.resolved( this.regularize_conf( args.config.data, args ) );
}
},
/**
* In this adaptor, just evals the conf text to parse the JSON, but
* other conf adaptors might want to inherit and override this.
* @param {String} conf_text the configuration text
* @param {Object} load_args the arguments that were passed to <code>load()</code>
* @returns {Object} the parsed JSON
*/
parse_conf: function( conf_text, load_args ) {
try {
return json.fromJson( conf_text );
} catch(e) {
throw e+" when parsing "+( load_args.config.url || 'configuration' )+".";
}
},
/**
* Applies defaults and any other necessary tweaks to the loaded JSON
* configuration. Called by <code>load()</code> on the JSON
* configuration before it calls the <code>onSuccess</code> callback.
* @param {Object} o the object containing the configuration, which it
* modifies in-place
* @param {Object} load_args the arguments that were passed to <code>load()</code>
* @returns the same object it was passed
*/
regularize_conf: function( o, load_args ) {
// if tracks is not an array, convert it to one
if( o.tracks && ! lang.isArray( o.tracks ) ) {
// if it's a single track config, wrap it in an arrayref
if( o.tracks.label ) {
o.tracks = [ o.tracks ];
}
// otherwise, coerce it to an array
else {
var tracks = [];
for( var label in o.tracks ) {
if( ! ( 'label' in o.tracks[label] ) )
o.tracks[label].label = label;
tracks.push( o.tracks[label] );
}
o.tracks = tracks;
}
}
// regularize trackMetadata.sources
var meta = o.trackMetadata;
if( meta && meta.sources ) {
// if it's a single source config, wrap it in an arrayref
if( meta.sources.url || ( typeof meta.sources == 'string' ) ) {
meta.sources = [ meta.sources ];
}
if( ! lang.isArray( meta.sources ) ) {
var sources = [];
for( var name in meta.sources ) {
if( ! ( 'name' in meta.sources ) )
meta.sources[name].name = name;
sources.push( meta.sources[name] );
}
meta.sources = sources;
}
// coerce any string source defs to be URLs, and try to detect their types
array.forEach( meta.sources, function( sourceDef, i ) {
if( typeof sourceDef == 'string' ) {
meta.sources[i] = { url: sourceDef };
var typeMatch = sourceDef.match( /\.(\w+)$/ );
if( typeMatch )
meta.sources[i].type = typeMatch[1].toLowerCase();
}
});
}
o.sourceUrl = o.sourceUrl || load_args.config.url;
o.baseUrl = o.baseUrl || Util.resolveUrl( o.sourceUrl, '.' );
if( o.baseUrl.length && ! /\/$/.test( o.baseUrl ) )
o.baseUrl += "/";
if( o.sourceUrl ) {
// set a default baseUrl in each of the track and store
// confs, and the names conf, if needed
var addBase =
[]
.concat( o.tracks || [] )
.concat( dojof.values(o.stores||{}) ) ;
if( o.names )
addBase.push( o.names );
array.forEach( addBase, function(t) {
if( ! t.baseUrl )
t.baseUrl = o.baseUrl || '/';
},this);
//resolve the refSeqs and nameUrl if present
if( o.refSeqs && typeof o.refSeqs == 'string' )
o.refSeqs = Util.resolveUrl( o.sourceUrl, o.refSeqs );
if( o.nameUrl )
o.nameUrl = Util.resolveUrl( o.sourceUrl, o.nameUrl );
}
o = this.regularizeTrackConfigs( o );
return o;
},
regularizeTrackConfigs: function( conf ) {
conf.stores = conf.stores || {};
array.forEach( conf.tracks || [], function( trackConfig ) {
// if there is a `config` subpart,
// just copy its keys in to the
// top-level config
if( trackConfig.config ) {
var c = trackConfig.config;
delete trackConfig.config;
for( var prop in c ) {
if( !(prop in trackConfig) && c.hasOwnProperty(prop) ) {
trackConfig[prop] = c[prop];
}
}
}
// skip if it's a new-style track def
if( trackConfig.store )
return;
var trackClassName = this._regularizeClass(
'JBrowse/View/Track', {
'FeatureTrack': 'JBrowse/View/Track/HTMLFeatures', 'ImageTrack': 'JBrowse/View/Track/FixedImage',
'ImageTrack.Wiggle': 'JBrowse/View/Track/FixedImage/Wiggle',
'SequenceTrack': 'JBrowse/View/Track/Sequence'
}[ trackConfig.type ]
|| trackConfig.type
);
trackConfig.type = trackClassName;
this._synthesizeTrackStoreConfig( conf, trackConfig );
if( trackConfig.histograms ) {
if( ! trackConfig.histograms.baseUrl )
trackConfig.histograms.baseUrl = trackConfig.baseUrl;
this._synthesizeTrackStoreConfig( conf, trackConfig.histograms );
}
}, this);
return conf;
},
_synthesizeTrackStoreConfig: function( mainconf, trackConfig ) {
// figure out what data store class to use with the track,
// applying some defaults if it is not explicit in the
// configuration
var urlTemplate = trackConfig.urlTemplate;
var storeClass = this._regularizeClass(
'JBrowse/Store',
trackConfig.storeClass ? trackConfig.storeClass :
/\/FixedImage/.test(trackConfig.type) ? 'JBrowse/Store/TiledImage/Fixed' +( trackConfig.backendVersion == 0 ? '_v0' : '' ) :
/\.jsonz?$/i.test( urlTemplate ) ? 'JBrowse/Store/SeqFeature/NCList'+( trackConfig.backendVersion == 0 ? '_v0' : '' ) :
/\.bam$/i.test( urlTemplate ) ? 'JBrowse/Store/SeqFeature/BAM' :
/\.cram$/i.test( urlTemplate ) ? 'JBrowse/Store/SeqFeature/CRAM' :
/\.(bw|bigwig)$/i.test( urlTemplate ) ? 'JBrowse/Store/SeqFeature/BigWig' :
/\/Sequence$/.test(trackConfig.type) ? 'JBrowse/Store/Sequence/StaticChunked' :
null
);
if( ! storeClass ) {
console.warn( "Unable to determine an appropriate data store to use with track '"
+ trackConfig.label + "', please explicitly specify a "
+ "storeClass in the configuration." );
return;
}
// synthesize a separate store conf
var storeConf = lang.mixin( {}, trackConfig );
lang.mixin( storeConf, {
type: storeClass
});
// if this is the first sequence store we see, and we
// have no refseqs store defined explicitly, make this the refseqs store.
if( (storeClass == 'JBrowse/Store/Sequence/StaticChunked' || trackConfig.useAsRefSeqStore) && !mainconf.stores['refseqs'] )
storeConf.name = 'refseqs';
else
storeConf.name = 'store'+digest.objectFingerprint( storeConf );
// record it
mainconf.stores[storeConf.name] = storeConf;
// connect it to the track conf
trackConfig.store = storeConf.name;
},
_regularizeClass: function( root, class_ ) {
if( ! class_ )
return null;
// prefix the class names with JBrowse/* if they contain no slashes
if( ! /\//.test( class_ ) )
class_ = root+'/'+class_;
class_ = class_.replace(/^\//);
return class_;
}
});
});