@gmod/jbrowse
Version:
JBrowse - client-side genome browser
257 lines (207 loc) • 7.96 kB
JavaScript
define([
'dojo/_base/declare',
'dojo/_base/array',
'dojox/color/Palette',
'JBrowse/Model/SimpleFeature',
'JBrowse/View/FeatureGlyph/Segments'
],
function(
declare,
array,
Palette,
SimpleFeature,
SegmentsGlyph
) {
return declare( SegmentsGlyph, {
_defaultConfig: function() {
return this._mergeConfigs(
this.inherited(arguments),
{
style: {
utrColor: function( feature, variable, glyph, track ) {
return glyph._utrColor( glyph.getStyle( feature.parent(), 'color' ) ).toString();
},
utrHeightPercent: 65
},
subParts: 'CDS, UTR, five_prime_UTR, three_prime_UTR',
impliedUTRs: false,
inferCdsParts: false,
subSubParts: () => true, // render sub-subparts by default
});
},
_getSubparts: function( f ) {
var c = f.children();
if( ! c ) return [];
if( c && this.config.inferCdsParts )
c = this._makeCDSs( f, c );
if( c && this.config.impliedUTRs )
c = this._makeUTRs( f, c );
var filtered = [];
for( var i = 0; i<c.length; i++ )
if( this._filterSubpart( c[i] ) )
filtered.push( c[i] );
return filtered;
},
_makeCDSs: function( parent, subparts ) {
// infer CDS parts from exon coordinates
var codeStart = Infinity,
codeEnd = -Infinity;
var i;
// gather exons, find coding start and end
var type, codeIndices = [], exons = [];
for( i = 0; i < subparts.length; i++ ) {
type = subparts[i].get('type');
if( /^cds/i.test( type ) ) {
// if any CDSs parts are present already,
// bail and return all subparts as-is
if( /:CDS:/i.test( subparts[i].get('name') ) )
return subparts;
codeIndices.push(i);
if( codeStart > subparts[i].get('start') )
codeStart = subparts[i].get('start');
if( codeEnd < subparts[i].get('end') )
codeEnd = subparts[i].get('end');
}
else {
if( /exon/i.test( type ) ) {
exons.push( subparts[i] );
}
}
}
// splice out unspliced cds parts
codeIndices.sort( function(a,b) { return b - a; } );
for ( i = codeIndices.length - 1; i >= 0; i-- )
subparts.splice(codeIndices[i], 1);
// bail if we don't have exons and cds
if( !( exons.length && codeStart < Infinity && codeEnd > -Infinity ) )
return subparts;
// make sure the exons are sorted by coord
exons.sort( function(a,b) { return a.get('start') - b.get('start'); } );
// iterate thru exons again, and calculate cds parts
var strand = parent.get('strand');
var codePartStart = Infinity,
codePartEnd = -Infinity;
for ( i = 0; i < exons.length; i++ ) {
var start = exons[i].get('start');
var end = exons[i].get('end');
// CDS containing exon
if( codeStart >= start && codeEnd <= end ) {
codePartStart = codeStart;
codePartEnd = codeEnd;
}
// 5' terminal CDS part
else if( codeStart >= start && codeStart < end ) {
codePartStart = codeStart;
codePartEnd = end;
}
// 3' terminal CDS part
else if( codeEnd > start && codeEnd <= end ) {
codePartStart = start;
codePartEnd = codeEnd;
}
// internal CDS part
else if( start < codeEnd && end > codeStart ) {
codePartStart = start;
codePartEnd = end;
}
// "splice in" the calculated cds part into subparts
// at beginning of _makeCDSs() method, bail if cds subparts are encountered
subparts.splice(i, 0, ( new SimpleFeature(
{ parent: parent,
data: {
start: codePartStart,
end: codePartEnd,
strand: strand,
type: 'CDS',
name: parent.get('uniqueID') + ":CDS:" + i
}})));
}
// make sure the subparts are sorted by coord
subparts.sort( function(a,b) { return a.get('start') - b.get('start'); } );
return subparts;
},
_makeUTRs: function( parent, subparts ) {
// based on Lincoln's UTR-making code in Bio::Graphics::Glyph::processed_transcript
var codeStart = Infinity,
codeEnd = -Infinity;
var i;
var haveLeftUTR, haveRightUTR;
// gather exons, find coding start and end, and look for UTRs
var type, exons = [];
for( i = 0; i<subparts.length; i++ ) {
type = subparts[i].get('type');
if( /^cds/i.test( type ) ) {
if( codeStart > subparts[i].get('start') )
codeStart = subparts[i].get('start');
if( codeEnd < subparts[i].get('end') )
codeEnd = subparts[i].get('end');
}
else if( /exon/i.test( type ) ) {
exons.push( subparts[i] );
}
else if( this._isUTR( subparts[i] ) ) {
haveLeftUTR = subparts[i].get('start') == parent.get('start');
haveRightUTR = subparts[i].get('end') == parent.get('end');
}
}
// bail if we don't have exons and CDS
if( !( exons.length && codeStart < Infinity && codeEnd > -Infinity ) )
return subparts;
// make sure the exons are sorted by coord
exons.sort( function(a,b) { return a.get('start') - b.get('start'); } );
var strand = parent.get('strand');
// make the left-hand UTRs
var start, end;
if( ! haveLeftUTR )
for (i=0; i<exons.length; i++) {
start = exons[i].get('start');
if ( start >= codeStart ) break;
end = codeStart > exons[i].get('end') ? exons[i].get('end') : codeStart;
subparts.unshift( new SimpleFeature(
{ parent: parent,
data: {
start: start,
end: end,
strand: strand,
type: strand >= 0 ? 'five_prime_UTR' : 'three_prime_UTR'
}}));
}
// make the right-hand UTRs
if( ! haveRightUTR )
for (i=exons.length-1; i>=0; i--) {
end = exons[i].get('end');
if( end <= codeEnd ) break;
start = codeEnd < exons[i].get('start') ? exons[i].get('start') : codeEnd;
subparts.push( new SimpleFeature(
{ parent: parent,
data: {
start: start,
end: end,
strand: strand,
type: strand >= 0 ? 'three_prime_UTR' : 'five_prime_UTR'
}}));
}
return subparts;
},
_utrColor: function( baseColor ) {
return (this._palette || (this._palette = Palette.generate( baseColor, "splitComplementary"))).colors[1];
},
_isUTR: function( feature ) {
return /(\bUTR|_UTR|untranslated[_\s]region)\b/.test( feature.get('type') || '' );
},
getStyle: function( feature, name ) {
if( name == 'color' ) {
if( this._isUTR( feature ) ) {
return this.getStyle( feature, 'utrColor' );
}
}
return this.inherited(arguments);
},
_getFeatureHeight: function( viewInfo, feature ) {
var height = this.inherited( arguments );
if( this._isUTR( feature ) )
return height*this.getStyle(feature,'utrHeightPercent')/100;
return height;
}
});
});