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     this.setLoaded();
 44 }
 45 
 46 SequenceTrack.prototype = new Track("");
 47 
 48 SequenceTrack.prototype.startZoom = function(destScale, destStart, destEnd) {
 49     this.hide();
 50     this.heightUpdate(0);
 51 };
 52 
 53 SequenceTrack.prototype.endZoom = function(destScale, destBlockBases) {
 54     if (destScale == this.charWidth) this.show();
 55     Track.prototype.clear.apply(this);
 56 };
 57 
 58 SequenceTrack.prototype.setViewInfo = function(genomeView, numBlocks,
 59                                                trackDiv, labelDiv,
 60                                                widthPct, widthPx, scale) {
 61     Track.prototype.setViewInfo.apply(this, [genomeView, numBlocks,
 62                                              trackDiv, labelDiv,
 63                                              widthPct, widthPx, scale]);
 64     if (scale == this.charWidth) {
 65         this.show();
 66     } else {
 67         this.hide();
 68         this.heightUpdate(0);
 69     }
 70     this.setLabel(this.key);
 71 };
 72 
 73 SequenceTrack.nbsp = String.fromCharCode(160);
 74 SequenceTrack.prototype.fillBlock = function(blockIndex, block,
 75                                              leftBlock, rightBlock,
 76                                              leftBase, rightBase,
 77                                              scale, stripeWidth,
 78                                              containerStart, containerEnd) {
 79     var that = this;
 80     if (scale == this.charWidth) {
 81         this.show();
 82     } else {
 83         this.hide();
 84         this.heightUpdate(0);
 85     }
 86 
 87     if (this.shown) {
 88         this.sequenceStore.getRange( this.refSeq, leftBase, rightBase,
 89                        function( start, end, seq ) {
 90 
 91                            // fill with leading blanks if the
 92                            // sequence does not extend all the way
 93                            // across our range
 94                            for( ; start < 0; start++ ) {
 95                                seq = SequenceTrack.nbsp + seq; //nbsp is an " " entity
 96                            }
 97 
 98                            // make a div to contain the sequences
 99                            var seqNode = document.createElement("div");
100                            seqNode.className = "sequence";
101                            block.appendChild(seqNode);
102 
103                            // add a div for the forward strand
104                            seqNode.appendChild( that.renderSeqDiv( start, end, seq ));
105 
106                            // and one for the reverse strand
107                            var comp = that.renderSeqDiv( start, end, that.complement(seq) );
108                            comp.className = 'revcom';
109                            seqNode.appendChild( comp );
110                        }
111                      );
112         this.heightUpdate(this.seqHeight, blockIndex);
113     } else {
114         this.heightUpdate(0, blockIndex);
115     }
116 };
117 
118 SequenceTrack.prototype.complement = (function() {
119     var compl_rx   = /[ACGT]/gi;
120 
121     // from bioperl: tr/acgtrymkswhbvdnxACGTRYMKSWHBVDNX/tgcayrkmswdvbhnxTGCAYRKMSWDVBHNX/
122     // generated with:
123     // perl -MJSON -E '@l = split "","acgtrymkswhbvdnxACGTRYMKSWHBVDNX"; print to_json({ map { my $in = $_; tr/acgtrymkswhbvdnxACGTRYMKSWHBVDNX/tgcayrkmswdvbhnxTGCAYRKMSWDVBHNX/; $in => $_ } @l})'
124     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"};
125 
126     var compl_func = function(m) { return compl_tbl[m] || SequenceTrack.nbsp; };
127     return function( seq ) {
128         return seq.replace( compl_rx, compl_func );
129     };
130 })();
131 
132 //given the start and end coordinates, and the sequence bases, makes a
133 //div containing the sequence
134 SequenceTrack.prototype.renderSeqDiv = function ( start, end, seq ) {
135     var container  = document.createElement("div");
136     container.appendChild( document.createTextNode( seq ) );
137     return container;
138 };
139 
140