1 // VIEW
  2 
  3 /**
  4  * Track to display the underlying reference sequence, when zoomed in
  5  * far enough.
  6  * @class
  7  * @constructor
  8  * @param {Object} config
  9  *   key:   display text track name
 10  *   label: internal track name (no spaces or odd characters)
 11  *   urlTemplate: url of directory in which to find the sequence chunks
 12  *   chunkSize: size of sequence chunks, in characters
 13  * @param {Object} refSeq
 14  *  start: refseq start
 15  *  end:   refseq end
 16  *  name:  refseq name
 17  * @param {Object} browserParams
 18  *  changeCallback: function to call once JSON is loaded
 19  *  trackPadding: distance in px between tracks
 20  *  charWidth: width, in pixels, of sequence base characters
 21  *  seqHeight: height, in pixels, of sequence elements
 22  */
 23 function SequenceTrack(config, refSeq, browserParams) {
 24 
 25     Track.call( this, config.label, config.key,
 26                 false, browserParams.changeCallback );
 27 
 28     this.config = config;
 29 
 30     this.charWidth = browserParams.charWidth;
 31     this.seqHeight = browserParams.seqHeight;
 32 
 33     this.refSeq = refSeq;
 34 
 35     // TODO: this should be passed into the constructor instead of
 36     // being instantiated here
 37     this.sequenceStore = new SequenceStore.StaticChunked({
 38                                baseUrl: config.baseUrl,
 39                                urlTemplate: config.urlTemplate,
 40                                compress: config.compress
 41                              });
 42 }
 43 
 44 SequenceTrack.prototype = new Track("");
 45 SequenceTrack.prototype.load = function() {
 46     window.setTimeout( dojo.hitch( this, 'setLoaded' ), 10 );
 47 };
 48 
 49 SequenceTrack.prototype.startZoom = function(destScale, destStart, destEnd) {
 50     this.hide();
 51     this.heightUpdate(0);
 52 };
 53 
 54 SequenceTrack.prototype.endZoom = function(destScale, destBlockBases) {
 55     if (destScale == this.charWidth) this.show();
 56     Track.prototype.clear.apply(this);
 57 };
 58 
 59 SequenceTrack.prototype.setViewInfo = function(genomeView, numBlocks,
 60                                                trackDiv, labelDiv,
 61                                                widthPct, widthPx, scale) {
 62     Track.prototype.setViewInfo.apply(this, [genomeView, numBlocks,
 63                                              trackDiv, labelDiv,
 64                                              widthPct, widthPx, scale]);
 65     if (scale == this.charWidth) {
 66         this.show();
 67     } else {
 68         this.hide();
 69         this.heightUpdate(0);
 70     }
 71     this.setLabel(this.key);
 72 };
 73 
 74 SequenceTrack.nbsp = String.fromCharCode(160);
 75 SequenceTrack.prototype.fillBlock = function(blockIndex, block,
 76                                              leftBlock, rightBlock,
 77                                              leftBase, rightBase,
 78                                              scale, stripeWidth,
 79                                              containerStart, containerEnd) {
 80     var that = this;
 81     if (scale == this.charWidth) {
 82         this.show();
 83     } else {
 84         this.hide();
 85         this.heightUpdate(0);
 86     }
 87 
 88     if (this.shown) {
 89         this.sequenceStore.getRange( this.refSeq, leftBase, rightBase,
 90                        function( start, end, seq ) {
 91 
 92                            // fill with leading blanks if the
 93                            // sequence does not extend all the way
 94                            // across our range
 95                            for( ; start < 0; start++ ) {
 96                                seq = SequenceTrack.nbsp + seq; //nbsp is an " " entity
 97                            }
 98 
 99                            // make a div to contain the sequences
100                            var seqNode = document.createElement("div");
101                            seqNode.className = "sequence";
102                            block.appendChild(seqNode);
103 
104                            // add a div for the forward strand
105                            seqNode.appendChild( that.renderSeqDiv( start, end, seq ));
106 
107                            // and one for the reverse strand
108                            var comp = that.renderSeqDiv( start, end, that.complement(seq) );
109                            comp.className = 'revcom';
110                            seqNode.appendChild( comp );
111                        }
112                      );
113         this.heightUpdate(this.seqHeight, blockIndex);
114     } else {
115         this.heightUpdate(0, blockIndex);
116     }
117 };
118 
119 SequenceTrack.prototype.complement = (function() {
120     var compl_rx   = /[ACGT]/gi;
121 
122     // from bioperl: tr/acgtrymkswhbvdnxACGTRYMKSWHBVDNX/tgcayrkmswdvbhnxTGCAYRKMSWDVBHNX/
123     // generated with:
124     // perl -MJSON -E '@l = split "","acgtrymkswhbvdnxACGTRYMKSWHBVDNX"; print to_json({ map { my $in = $_; tr/acgtrymkswhbvdnxACGTRYMKSWHBVDNX/tgcayrkmswdvbhnxTGCAYRKMSWDVBHNX/; $in => $_ } @l})'
125     var compl_tbl  = {"S":"S","w":"w","T":"A","r":"y","a":"t","N":"N","K":"M","x":"x","d":"h","Y":"R","V":"B","y":"r","M":"K","h":"d","k":"m","C":"G","g":"c","t":"a","A":"T","n":"n","W":"W","X":"X","m":"k","v":"b","B":"V","s":"s","H":"D","c":"g","D":"H","b":"v","R":"Y","G":"C"};
126 
127     var compl_func = function(m) { return compl_tbl[m] || SequenceTrack.nbsp; };
128     return function( seq ) {
129         return seq.replace( compl_rx, compl_func );
130     };
131 })();
132 
133 //given the start and end coordinates, and the sequence bases, makes a
134 //div containing the sequence
135 SequenceTrack.prototype.renderSeqDiv = function ( start, end, seq ) {
136     var container  = document.createElement("div");
137     container.appendChild( document.createTextNode( seq ) );
138     return container;
139 };
140 
141