diff --git a/tests/data/transcripts/input_10.js b/tests/data/transcripts/input_10.js new file mode 100644 index 00000000..7d93d77f --- /dev/null +++ b/tests/data/transcripts/input_10.js @@ -0,0 +1,49 @@ +define([ + 'JBrowse/Model/SimpleFeature' + ], +function (SimpleFeature) { + // This transcript is corresponding to RefSeq_2 + // which is located in file data/RefSeq_2.js + var feature = { + "data": { + "seq_id": "testRefSeq2", + "end": 28, + "start": 1, + "strand": 1, + "subfeatures": [ + { + "data": { + "seq_id": "testRefSeq2", + "end": 13, + "start": 1, + "strand": 1, + "type": "exon" + } + }, + { + "data": { + "seq_id": "testRefSeq2", + "end": 28, + "start": 18, + "strand": 1, + "type": "exon" + } + }, + { + "data": { + "seq_id": "testRefSeq2", + "end": 28, + "start": 18, + "strand": 1, + "type": "CDS" + } + } + ], + "type": "transcript" + }, + "normalized": true, +}; + +var transcript = SimpleFeature.fromJSON(feature); +return transcript; +}); diff --git a/tests/data/transcripts/resize_5.js b/tests/data/transcripts/resize_5.js new file mode 100644 index 00000000..bb43d122 --- /dev/null +++ b/tests/data/transcripts/resize_5.js @@ -0,0 +1,50 @@ + +define([ + 'JBrowse/Model/SimpleFeature' + ], +function (SimpleFeature) { + // This transcript is corresponding to RefSeq_2 + // which is located in file data/RefSeq_2.js + var feature = { + "data": { + "seq_id": "testRefSeq2", + "end": 28, + "start": 1, + "strand": 1, + "subfeatures": [ + { + "data": { + "seq_id": "testRefSeq2", + "end": 13, + "start": 1, + "strand": 1, + "type": "exon" + } + }, + { + "data": { + "seq_id": "testRefSeq2", + "end": 28, + "start": 21, + "strand": 1, + "type": "CDS" + } + }, + { + "data": { + "seq_id": "testRefSeq2", + "end": 28, + "start": 21, + "strand": 1, + "type": "exon" + } + } + ], + "type": "transcript" + }, + "normalized": true, +}; + +var transcript = SimpleFeature.fromJSON(feature); +return transcript; +}); diff --git a/tests/data/transcripts/transcript_data.js b/tests/data/transcripts/transcript_data.js index 4e6cc274..4310c412 100644 --- a/tests/data/transcripts/transcript_data.js +++ b/tests/data/transcripts/transcript_data.js @@ -8,10 +8,12 @@ define([ './input_7', './input_8', './input_9', + './input_10', './resize_1', './resize_2', './resize_3', './resize_4', + './resize_5', './orf_1', './orf_2', './cds_1', @@ -27,10 +29,12 @@ define([ input_7, input_8, input_9, + input_10, resize_1, resize_2, resize_3, resize_4, + resize_5, orf_1, orf_2, cds_1, @@ -39,8 +43,8 @@ define([ ) { var transcript_data = { "input": [input_1, input_2, input_3, input_4, input_5, input_6, input_7, - input_8], - "resize": [resize_1, resize_2, resize_3, resize_4], + input_8, input_9, input_10], + "resize": [resize_1, resize_2, resize_3, resize_4, resize_5], "orf": [orf_1, orf_2], "cds": [cds_1], "merge": [merge_1], diff --git a/tests/js/EditTrack.spec.js b/tests/js/EditTrack.spec.js index aa144b6f..3670a38f 100644 --- a/tests/js/EditTrack.spec.js +++ b/tests/js/EditTrack.spec.js @@ -103,7 +103,7 @@ describe( "Edit Track", function() { }); it( 'tests comparison function', function() { - expect(compareFeatures(transcript_data["input"][0], transcript_data["input"][0])).toBe(true); + expect(compareFeatures(transcript_data.input[0], transcript_data.input[0])).toBe(true); }); it('tests getWholeCDSCoordinates', function() { @@ -142,20 +142,35 @@ describe( "Edit Track", function() { expect(compareFeatures( editTrack.setCDS(transcript_data.input[4], 0, 24), transcript_data.cds[0])).toBe(true); + + var start = editTrack.getTranslationStart(transcript_data.input[1]); + var stop = editTrack.getTranslationStop(transcript_data.input[1]); + var outTranscript = editTrack.setCDS(transcript_data.input[1], start, stop); + expect(compareFeatures(outTranscript, transcript_data.input[1])).toBe(true); + }); it( 'tests resizeExon', function() { - exon = editTrack.filterExons(transcript_data["input"][0])[0]; - var right = 17120; - var left = exon.get('start'); - outTranscript = editTrack.resizeExon(refSeq, transcript_data["input"][0], exon, left, right); - expect(compareFeatures(transcript_data["resize"][0], outTranscript)).toBe(true); - - exon = editTrack.filterExons(transcript_data["input"][1])[1]; - var right = exon.get('end') + 3; - var left = exon.get('start'); - outTranscript = editTrack.resizeExon(refSeq, transcript_data["input"][1], exon, left, right); - expect(compareFeatures(transcript_data["resize"][2], outTranscript)).toBe(true); + var right, left; + + exon = editTrack.filterExons(transcript_data.input[0])[0]; + right = 17120; + left = exon.get('start'); + outTranscript = editTrack.resizeExon(refSeq, transcript_data.input[0], exon, left, right); + expect(compareFeatures(transcript_data.resize[0], outTranscript)).toBe(true); + + exon = editTrack.filterExons(transcript_data.input[1])[1]; + right = exon.get('end') + 3; + left = exon.get('start'); + outTranscript = editTrack.resizeExon(refSeq, transcript_data.input[1], exon, left, right); + expect(compareFeatures(transcript_data.resize[2], outTranscript)).toBe(true); + + exon = editTrack.filterExons(transcript_data.input[9])[1]; + right = exon.get('end'); + left = exon.get('start') + 3; + outTranscript = editTrack.resizeExon(refSeq_2, transcript_data.input[9], exon, left, right); + expect(compareFeatures(transcript_data.resize[4], outTranscript)).toBe(true); + }); it( 'tests areOnSameStrand', function() { diff --git a/www/JBrowse/View/Track/EditTrack.js b/www/JBrowse/View/Track/EditTrack.js index b2582de1..f9c3f562 100644 --- a/www/JBrowse/View/Track/EditTrack.js +++ b/www/JBrowse/View/Track/EditTrack.js @@ -613,34 +613,28 @@ var EditTrack = declare(DraggableFeatureTrack, return; } - var _exons, exons = []; + var origLeft = exonToResize.get("start"); + var origRight = exonToResize.get("end"); + + var _exons; _exons = this.filterExons(transcript); _exons = _.reject(_exons, function (exon) { return exon === exonToResize; }); if (left !== right) { _exons.push(this.copyFeature(exonToResize, {start: left, end: right})); } - _exons = this.sortAnnotationsByLocation(_exons); - _.each(_exons, _.bind(function (f) { - var last = exons[exons.length - 1]; - if (last && (f.get('start') - last.get('end') <= 1)) { - last.set('end', Math.max(last.get('end'), f.get('end'))); - } - else { - exons.push(this.copyFeature(f)); - } - }, this)); - var newTranscript = this.createTranscript(exons, transcript.get('name')); var translationStart = this.getTranslationStart(transcript); - if (translationStart) { - if (translationStart < newTranscript.get('start')) { - translationStart = newTranscript.get('start'); - } - if (translationStart > newTranscript.get('end')) { - translationStart = newTranscript.get('end'); - } - newTranscript = this.setORF(refSeq, newTranscript, translationStart); - } + var translationStop = this.getTranslationStop(transcript); + var strand = transcript.get("strand"); + + if (strand === 1) { + if (left >= translationStart && translationStart >= origLeft) {translationStart = left;} + if (right <= translationStop && translationStop <= origRight) {translationStop = right;} } + else { + if (right <= translationStart && translationStart <= origRight) {translationStart = right;} + if (left >= translationStop && translationStop >= origLeft) {translationStop = left;} } + + var newTranscript = this.createTranscript(_exons, transcript.get('name'), refSeq, translationStart, translationStop); return newTranscript; }, @@ -732,17 +726,8 @@ var EditTrack = declare(DraggableFeatureTrack, var exons = _.reject(this.filterExons(transcript), function (exon) { return _.indexOf(exonsToDelete, exon) !== -1; }); - var newTranscript = this.createTranscript(exons, transcript.get('name')); var translationStart = this.getTranslationStart(transcript); - if (translationStart) { - if (translationStart < newTranscript.get('start')) { - translationStart = newTranscript.get('start'); - } - if (translationStart > newTranscript.get('end')) { - translationStart = newTranscript.get('end'); - } - newTranscript = this.setORF(refSeq, newTranscript, translationStart); - } + var newTranscript = this.createTranscript(exons, transcript.get('name'), translationStart); return newTranscript; }, @@ -782,24 +767,10 @@ var EditTrack = declare(DraggableFeatureTrack, exons = exons.concat(this.filterExons(transcript)); }, this)); exons = this.sortAnnotationsByLocation(exons); + var name = 'afra-' + exons[0].get('seq_id') + '-mRNA-' + counter++; - // Combine partially or fully overlapping, and immediately adjacent - // exons into one. - var newexons = []; - _.each(exons, _.bind(function (f) { - var last = newexons[newexons.length - 1]; - if (last && (f.get('start') - last.get('end') <= 1)) { // we are looking for introns - newexons[newexons.length - 1] = this.copyFeature(last, {end: Math.max(last.get('end'), f.get('end'))}); - } - else { - newexons.push(f); - } - }, this)); + var newTranscript = this.createTranscript(exons, name, refSeq, translationStart); - // Create new transcript from the processed exons, and insert CDS. - var newTranscript = this.createTranscript(newexons); - newTranscript = this.setORF(refSeq, newTranscript, translationStart); - newTranscript.set('name', 'afra-' + newTranscript.get('seq_id') + '-mRNA-' + counter++); return newTranscript; }, @@ -1069,17 +1040,48 @@ var EditTrack = declare(DraggableFeatureTrack, return transcript; }, - createTranscript: function (subfeatures, name) { + /** + * Create Transcript from the given subfeatures + * + * createTranscript also combines partially or fully overlapping exons. If + * translation Start is not specified then all the non exonic subfeatures + * remain intact. `setORF` or `setCDS` is called on the created + * simpleFeature depeding on if translationStart and translationStop are + * specified. + * If only translation start is given then setORF is called. And if both + * translationStart and translationStop are given then setCDS is called. + **/ + createTranscript: function (subfeatures, name, refSeq, translationStart, translationStop) { // maintain a count of subfeatures seen, indexed by type var count = {}; + var exons = _.filter(subfeatures, function (f) { return f.get("type") === "exon";}); + exons = this.sortAnnotationsByLocation(exons); + + // Combine partially or fully overlapping, and immediately adjacent + // exons into one. + var newexons = []; + _.each(exons, _.bind(function (f) { + var last = newexons[newexons.length - 1]; + if (last && (f.get('start') - last.get('end') <= 0)) { // we are looking for introns + newexons[newexons.length - 1] = this.copyFeature(last, {end: Math.max(last.get('end'), f.get('end'))}); + } + else { + newexons.push(f); + } + }, this)); + + var nonExons = _.filter(subfeatures, function (f) { return f.get("type") !== "exon";}); + var newSubfeatures = nonExons.concat(newexons); + newSubfeatures = this.sortAnnotationsByLocation(newSubfeatures); + var transcript = new SimpleFeature({ data: { type: 'transcript', name: name, - seq_id: subfeatures[0].get('seq_id'), - strand: subfeatures[0].get('strand'), - subfeatures: _.map(subfeatures, function (f) { + seq_id: newSubfeatures[0].get('seq_id'), + strand: newSubfeatures[0].get('strand'), + subfeatures: _.map(newSubfeatures, function (f) { var type = f.get('type'); count[type] = count[type] || 1; return { @@ -1099,6 +1101,24 @@ var EditTrack = declare(DraggableFeatureTrack, var fmax = _.max(_.map(subfeatures, function (f) { return f.get('end'); })); transcript.set('start', fmin); transcript.set('end', fmax); + + if (translationStart) { + if (translationStart < fmin) { + translationStart = fmin; + } + if (translationStart > fmax) { + translationStart = fmax; + } + } + + if (!_.isUndefined(translationStart) && !_.isUndefined(refSeq)) { + if (!_.isUndefined(translationStop)) { + transcript = this.setCDS(transcript, translationStart, translationStop); + } + else{ + transcript = this.setORF(refSeq, transcript, translationStart); + } + } return transcript; },