diff --git a/plugins/WebApollo/js/View/Track/DraggableHTMLFeatures.js b/plugins/WebApollo/js/View/Track/DraggableHTMLFeatures.js index 330c98c1fb..bfd457267b 100644 --- a/plugins/WebApollo/js/View/Track/DraggableHTMLFeatures.js +++ b/plugins/WebApollo/js/View/Track/DraggableHTMLFeatures.js @@ -9,9 +9,10 @@ define( [ 'dijit/Dialog', 'jquery', 'jqueryui/draggable', - 'JBrowse/Util' + 'JBrowse/Util', + 'JBrowse/Model/SimpleFeature' ], - function( declare, array, HTMLFeatureTrack, FeatureSelectionManager, dijitMenu, dijitMenuItem, dijitCheckedMenuItem, dijitDialog, $, draggable, Util ) { + function( declare, array, HTMLFeatureTrack, FeatureSelectionManager, dijitMenu, dijitMenuItem, dijitCheckedMenuItem, dijitDialog, $, draggable, Util, SimpleFeature ) { /* Subclass of FeatureTrack that allows features to be selected, and dragged and dropped into the annotation track to create annotations. @@ -340,6 +341,148 @@ var draggableTrack = declare( HTMLFeatureTrack, return subfeatdiv; }, + _subfeatSorter: function( a, b ) { + var as = a.get('start'); + var bs = b.get('start'); + if ( as == bs ) { return 0; } + else if ( as > bs ) { return 1; } + else if ( as < bs ) { return -1; } + else { return 0; /* shouldn't fall through to here */ } + }, + + + /** + * if feature has translated region (CDS, wholeCDS, start_codon, ???), + * reworks feature's subfeatures for more annotation-editing-friendly selection + * + * Assumes: + * if translated, will either have + * CDS-ish term for each coding segment + * wholeCDS from start of translation to end of translation (so already pre-processed) + * mutually exclusive (either have CDS, or wholeCDS, but not both) + * if wholeCDS present, then pre-processed (no UTRs) + * if any exon-ish types present, then _all_ exons are present with exon-ish types + */ + _processTranslation: function( feature ) { + var track = this; + + var feat_type = feature.get('type'); + + // most very dense genomic feature tracks do not have CDS. Trying to minimize overhead for that case -- + // keep list of types that NEVER have CDS children (match, alignment, repeat, etc.) + // (WARNING in this case not sorting, but sorting (currently) only needed for features with CDS (for reading frame calcs)) + if (this.webapollo.neverHasCDS[feat_type]) { + feature.normalized = true; + return; + } + var subfeats = feature.get('subfeatures'); + + // var cds = subfeats.filter( function(feat) { return feat.get('type') === 'CDS'; } ); + var cds = subfeats.filter( function(feat) { + return track.webapollo.cdsTerms[feat.get('type')]; + } ); + var wholeCDS = subfeats.filter( function(feat) { return feat.get('type') === 'wholeCDS'; } ); + + // most very dense genomic feature tracks do not have CDS. Trying to minimize overhead for that case -- + // if no CDS, no wholeCDS, consider normalized + // (WARNING in this case not sorting, but sorting (currently) only needed for features with CDS (for reading frame calcs)) + // + if (cds.length === 0 && wholeCDS.length === 0) { + feature.normalized = true; + return; + } + + var newsubs; + // wholeCDS is specific to WebApollo, if seen can assume no CDS, and UTR/exon already normalized + if (wholeCDS.length > 0) { + // extract wholecds from subfeats, then sort subfeats + feature.wholeCDS = wholeCDS[0]; + newsubs = subfeats.filter( function(feat) { return feat.get('type') !== 'wholeCDS'; } ); + } + + // if has a CDS, remove CDS from subfeats and sort exons + else if (cds.length > 0) { + cds.sort(this._subfeatSorter); + var cdsmin = cds[0].get('start'); + var cdsmax = cds[cds.length-1].get('end'); + feature.wholeCDS = new SimpleFeature({ parent: feature, + data: { start: cdsmin, end: cdsmax, type: 'wholeCDS', + strand: feature.get('strand') } + } ); + var hasExons = false; + for (var i=0; i 0, guaranteed to have at least one CDS + var exonCount = 0; + var prevStart, prevEnd; + // scan through sorted subfeats, joining abutting UTR/CDS regions + for (var i=0; i bs ? 1 : - as < bs ? -1 : 0; - }); - if( cds.length ) { - return { start: cds[0].get('start'), - end: cds[cds.length-1].get('end'), - get: function(n) { return this[n]; } - }; - } - else { - return null; - } - }).call(this); - } -*/ if (wholeCDS) { var cdsStart = wholeCDS.get('start');