details: http://www.bx.psu.edu/hg/galaxy/rev/436c9d1c5e64 changeset: 3716:436c9d1c5e64 user: Kanwei Li <kanwei@gmail.com> date: Wed Apr 28 17:35:02 2010 -0400 description: trackster: BAM display supports paired-end reads, displays sequence when possible diffstat: lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py | 2 +- lib/galaxy/visualization/tracks/data/bam.py | 34 +- lib/galaxy/visualization/tracks/data/summary_tree.py | 4 +- lib/galaxy/visualization/tracks/summary.py | 6 +- lib/galaxy/web/controllers/tracks.py | 8 +- static/scripts/packed/trackster.js | 2 +- static/scripts/trackster.js | 284 +++++---- 7 files changed, 185 insertions(+), 155 deletions(-) diffs (524 lines): diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py --- a/lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py Wed Apr 28 16:06:58 2010 -0400 +++ b/lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py Wed Apr 28 17:35:02 2010 -0400 @@ -17,7 +17,7 @@ bamfile = csamtools.Samfile( filename=input_fname, mode='rb', index_filename=index_fname ) - st = SummaryTree(block_size=100, levels=4, draw_cutoff=100, detail_cutoff=20) + st = SummaryTree(block_size=25, levels=6, draw_cutoff=150, detail_cutoff=30) for read in bamfile.fetch(): st.insert_range(bamfile.getrname(read.rname), read.pos, read.pos + read.rlen) diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/visualization/tracks/data/bam.py --- a/lib/galaxy/visualization/tracks/data/bam.py Wed Apr 28 16:06:58 2010 -0400 +++ b/lib/galaxy/visualization/tracks/data/bam.py Wed Apr 28 17:35:02 2010 -0400 @@ -33,12 +33,36 @@ if chrom.startswith( 'chr' ): data = bamfile.fetch( start=start, end=end, reference=chrom[3:] ) else: - raise + return None # Encode reads as list of dictionaries results = [] + paired_pending = {} for read in data: - payload = [ str(read.pos) + str(read.seq), read.pos, read.pos + read.rlen, read.seq ] - results.append(payload) + qname = read.qname + if read.is_proper_pair: + if qname in paired_pending: # one in dict is always first + pair = paired_pending[qname] + results.append( [ qname, pair['start'], read.pos + read.rlen, read.seq, [pair['start'], pair['end'], pair['seq']], [read.pos, read.pos + read.rlen, read.seq] ] ) + # results.append( [read.qname, pair['start'], read.pos + read.rlen, qname, [pair['start'], pair['end']], [read.pos, read.pos + read.rlen] ] ) + del paired_pending[qname] + else: + paired_pending[qname] = { 'start': read.pos, 'end': read.pos + read.rlen, 'seq': read.seq, 'mate_start': read.mpos, 'rlen': read.rlen } + else: + results.append( [qname, read.pos, read.pos + read.rlen, read.seq] ) + # take care of reads whose mates are out of range + for qname, read in paired_pending.iteritems(): + if read['mate_start'] < read['start']: + start = read['mate_start'] + end = read['end'] + r1 = [read['mate_start'], read['mate_start'] + read['rlen']] + r2 = [read['start'], read['end'], read['seq']] + else: + start = read['start'] + end = read['mate_start'] + read['rlen'] + r1 = [read['start'], read['end'], read['seq']] + r2 = [read['mate_start'], read['mate_start'] + read['rlen']] + + results.append( [ qname, start, end, read['seq'], r1, r2 ] ) + bamfile.close() - return results - \ No newline at end of file + return results \ No newline at end of file diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/visualization/tracks/data/summary_tree.py --- a/lib/galaxy/visualization/tracks/data/summary_tree.py Wed Apr 28 16:06:58 2010 -0400 +++ b/lib/galaxy/visualization/tracks/data/summary_tree.py Wed Apr 28 17:35:02 2010 -0400 @@ -38,6 +38,6 @@ if results == "detail": return None elif results == "draw": - return "no_detail", None, None + return "no_detail" else: - return results, stats["max"], stats["avg"] + return results, stats[level]["max"], stats[level]["avg"], stats[level]["delta"] diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/visualization/tracks/summary.py --- a/lib/galaxy/visualization/tracks/summary.py Wed Apr 28 16:06:58 2010 -0400 +++ b/lib/galaxy/visualization/tracks/summary.py Wed Apr 28 17:35:02 2010 -0400 @@ -54,8 +54,10 @@ self.chrom_stats[chrom]["detail_level"] = level break else: - self.chrom_stats[chrom]["max"] = max_val - self.chrom_stats[chrom]["avg"] = float(max_val) / len(blocks[level]) + self.chrom_stats[chrom][level] = {} + self.chrom_stats[chrom][level]["delta"] = self.block_size ** level + self.chrom_stats[chrom][level]["max"] = max_val + self.chrom_stats[chrom][level]["avg"] = float(max_val) / len(blocks[level]) cur_best = level self.chrom_blocks[chrom] = dict([ (key, value) for key, value in blocks.iteritems() if key >= cur_best ]) diff -r a5ccb2ba4c74 -r 436c9d1c5e64 lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py Wed Apr 28 16:06:58 2010 -0400 +++ b/lib/galaxy/web/controllers/tracks.py Wed Apr 28 17:35:02 2010 -0400 @@ -236,12 +236,12 @@ indexer = dataset_type_to_data_provider[data_sources['index']]( dataset.get_converted_dataset(trans, data_sources['index']), dataset ) summary = indexer.get_summary( chrom, low, high, **kwargs ) if summary is not None: - frequencies, max_v, avg_v = summary - if frequencies != "no_detail": - return { "dataset_type": data_sources['index'], "data": frequencies, "max": max_v, "avg": avg_v } - else: + if summary == "no_detail": kwargs["no_detail"] = True # meh extra_info = "no_detail" + else: + frequencies, max_v, avg_v, delta = summary + return { "dataset_type": data_sources['index'], "data": frequencies, "max": max_v, "avg": avg_v, "delta": delta } dataset_type = data_sources['data'] data_provider = dataset_type_to_data_provider[ dataset_type ]( dataset.get_converted_dataset(trans, dataset_type), dataset ) diff -r a5ccb2ba4c74 -r 436c9d1c5e64 static/scripts/packed/trackster.js --- a/static/scripts/packed/trackster.js Wed Apr 28 16:06:58 2010 -0400 +++ b/static/scripts/packed/trackster.js Wed Apr 28 17:35:02 2010 -0400 @@ -1,1 +1,1 @@ -var DEBUG=false;var DENSITY=1000,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,! "repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_h! igh=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;th is.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.spa! n/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/b.width()*(this.h! igh-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:func tion(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_N! OCONVERTER)}else{if(d.data&&d.data.length===0||d.data===null){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(k);var l=this.content_div.width()/f;var h;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;var b=(g-! i)*l;if(this.left_offset){b-=this.left_offset}c.css({left:b});k.append (c);this.max_height=Math.max(this.max_height,c.height())}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove! ();this.content_div.append(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"Line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.mode!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_! maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.p refs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,q,c,e){if(this.vertical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,! b=$("<canvas class='tile'></canvas>"),u=o+"_"+q;if(this.data_cache.get(u)===undefined){this.get_data(o,q);return}var t=this.data_cache.get(u);if(t===null){return}b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="Intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,this.height_px)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=true}}}}n.stroke();c.append(b);return b},gen_options:function(n){var a=$("<div></! div>").addClass("form-row");var h="track_"+n+"_minval",k="track_"+n+"_ maxval",e="track_"+n+"_mode",l=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);th! is.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(b.block_color!==undefined){this.prefs.block_color=b.block_color}if(b.label_color!==undefined){this.prefs.! label_color=b.label_color}if(b.show_counts!==undefined){this.prefs.sho w_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,v=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,x=[];for(var s=0,t=g.length;s<t;s++){var e=g[s],l=e[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);x.push(this.inc_slots[a][l]! )}else{v.push(s)}}for(var s=0,t=v.length;s<t;s++){var e=g[v[s]];l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);f=Math.ceil((feature_end-n)*m);if(!c){var p=b.measureText(feature_name).width;if(d-p<0){f+=p}else{d-=p}}var r=0;while(true){var o=true;if(this.s_e_by_tile[a][r]!==undefined){for(var q=0,w=this.s_e_by_tile[a][r].length;q<w;q++){var u=this.s_e_by_tile[a][r][q];if(f>u[0]&&d<u[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][r]===undefined){this.s_e_by_tile[a][r]=[]}this.s_e_by_tile[a][r].push([d,f]);this.inc_slots[a][l]=r;h=Math.max(h,r);break}r++}}return h},draw_tile:function(R,h,m,ae){var z=h*DENSITY*R,X=(h+1)*DENSITY*R,w=DENSITY*R;var ac,ad,p;var Y=z+"_"+X;var ac=this.data_cache.get(Y);if(ac===undefined){this.data_queue[[z,X]]=true;this.get_data(z,X);return}if(ac.dataset_type=="array_tree"){p=30}else{var P=(ac.extra_info==="no_detail");var af=(P?this.vertical_nodetail_px:this.vertical_detail_px);p=this.incremental_slo! ts(this.view.zoom_res,ac.data,P)*af+15;m.parent().css("height",Math.ma x(this.height_px,p)+"px");ad=this.inc_slots[this.view.zoom_res]}var a=Math.ceil(w*ae),F=$("<canvas class='tile'></canvas>"),T=this.prefs.label_color,f=this.prefs.block_color,J=this.left_offset;F.css({position:"absolute",top:0,left:(z-this.view.low)*ae-J});F.get(0).width=a+J;F.get(0).height=p;var t=F.get(0).getContext("2d");t.fillStyle=this.prefs.block_color;t.font=this.default_font;t.textAlign="right";var C=55,W=255-C,g=W*2/3;if(ac.dataset_type=="summary_tree"){var L=ac.data;var v=ac.max;var l=ac.avg;if(ac.data.length>2){var b=Math.ceil((L[1][0]-L[0][0])*ae)}else{var b=50}for(var aa=0,s=L.length;aa<s;aa++){var N=Math.ceil((L[aa][0]-z)*ae);var M=L[aa][1];if(!M){continue}var E=Math.floor(W-(M/v)*W);t.fillStyle="rgb("+E+","+E+","+E+")";t.fillRect(N+J,0,b,20);if(this.prefs.show_counts){if(E>g){t.fillStyle="black"}else{t.fillStyle="#ddd"}t.textAlign="center";t.fillText(L[aa][1],N+J+(b/2),12)}}m.append(F);return F}var ac=ac.data;var Z=0;for(var aa=0,s=ac.length;aa<s;aa++){var G=ac! [aa],D=G[0],ab=G[1],O=G[2],A=G[3];if(ab<=X&&O>=z){var Q=Math.floor(Math.max(0,(ab-z)*ae)),u=Math.ceil(Math.min(a,(O-z)*ae)),K=ad[D]*af;if(P){t.fillRect(Q+J,K+5,u-Q,1)}else{var r=G[4],I=G[5],S=G[6],e=G[7];var q,U,B=null,ag=null;if(I&&S){B=Math.floor(Math.max(0,(I-z)*ae));ag=Math.ceil(Math.min(a,(S-z)*ae))}if(A!==undefined&&ab>z){t.fillStyle=T;if(h===0&&Q-t.measureText(A).width<0){t.textAlign="left";t.fillText(A,u+2+J,K+8)}else{t.textAlign="right";t.fillText(A,Q-2+J,K+8)}t.fillStyle=f}if(e){if(r){if(r=="+"){t.fillStyle=RIGHT_STRAND}else{if(r=="-"){t.fillStyle=LEFT_STRAND}}t.fillRect(Q+J,K,u-Q,10);t.fillStyle=f}for(var Y=0,d=e.length;Y<d;Y++){var n=e[Y],c=Math.floor(Math.max(0,(n[0]-z)*ae)),H=Math.ceil(Math.min(a,(n[1]-z)*ae));if(c>H){continue}q=5;U=3;t.fillRect(c+J,K+U,H-c,q);if(B!==undefined&&!(c>ag||H<B)){q=9;U=1;var V=Math.max(c,B),o=Math.min(H,ag);t.fillRect(V+J,K+U,o-V,q)}}}else{q=9;U=1;t.fillRect(Q+J,K+U,u-Q,q);if(G.strand){if(G.strand=="+"){t.fillStyle=RIGHT_STRAND_INV! }else{if(G.strand=="-"){t.fillStyle=LEFT_STRAND_INV}}t.fillRect(Q+J,K, u-Q,10);t.fillStyle=prefs.block_color}}}Z++}}m.append(F);return F},gen_options:function(i){var a=$("<div></div>").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label></label>").attr("for",e).text("Block color:"),l=$("<input></input>").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label></label>").attr("for",j).text("Text color:"),h=$("<input></input>").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label></label>").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div></div>").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.labe! l_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack"};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{}); \ No newline at end of file +var DEBUG=false;var DENSITY=200,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"! repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_hi! gh=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;thi s.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.span! /Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/b.width()*(this.hi! gh-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:funct ion(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NO! CONVERTER)}else{if(d.data&&d.data.length===0||d.data===null){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>"),l=this.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(k),this.max_height=0;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;! var b=(g-i)*l;if(this.left_offset){b-=this.left_offset}c.css({left:b}) ;k.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute! ",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"Line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.mode!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("! #track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval"). val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,q,c,e){if(this.vert! ical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,b=$("<canvas class='tile'></canvas>"),u=o+"_"+q;if(this.data_cache.get(u)===undefined){this.get_data(o,q);return}var t=this.data_cache.get(u);if(t===null){return}b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="Intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,this.height_px)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=true}}}}n.stroke();c! .append(b);return b},gen_options:function(n){var a=$("<div></div>").ad dClass("form-row");var h="track_"+n+"_minval",k="track_"+n+"_maxval",e="track_"+n+"_mode",l=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_val! ue||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(b.block_color!==undefined){this.prefs.block_! color=b.block_color}if(b.label_color!==undefined){this.prefs.label_col or=b.label_color}if(b.show_counts!==undefined){this.prefs.show_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,v=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,x=[];for(var s=0,t=g.length;s<t;s++){var e=g[s],l=e[0];if(this.inc_slots[a][l]!==undefined){h! =Math.max(h,this.inc_slots[a][l]);x.push(this.inc_slots[a][l])}else{v.push(s)}}for(var s=0,t=v.length;s<t;s++){var e=g[v[s]];l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);f=Math.ceil((feature_end-n)*m);if(!c){var p=b.measureText(feature_name).width;if(d-p<0){f+=p}else{d-=p}}var r=0;while(true){var o=true;if(this.s_e_by_tile[a][r]!==undefined){for(var q=0,w=this.s_e_by_tile[a][r].length;q<w;q++){var u=this.s_e_by_tile[a][r][q];if(f>u[0]&&d<u[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][r]===undefined){this.s_e_by_tile[a][r]=[]}this.s_e_by_tile[a][r].push([d,f]);this.inc_slots[a][l]=r;h=Math.max(h,r);break}r++}}return h},rect_or_text:function(n,o,g,f,m,b,d,k,e,i){n.textAlign="center";var j=Math.round(o/2);if(d!==undefined&&o>g){n.fillStyle="#555";n.fillRect(k,i+1,e,9);n.fillStyle="#eee";for(var h=0,l=d.length;h<l;h++){if(b+h>=f&&b+h<=m){var a=Math.floor(Math.max(0,(b+h-f)*o));n.fillText(d[h],a+this.left_offset+j,i+9)}}}else! {n.fillStyle="#555";n.fillRect(k,i+4,e,3)}},draw_tile:function(W,h,m,a k){var D=h*DENSITY*W,ac=(h+1)*DENSITY*W,C=DENSITY*W;var aj,r;var ad=D+"_"+ac;var w=this.data_cache.get(ad);if(w===undefined){this.data_queue[[D,ac]]=true;this.get_data(D,ac);return}if(w.dataset_type==="summary_tree"){r=30}else{var U=(w.extra_info==="no_detail");var al=(U?this.vertical_nodetail_px:this.vertical_detail_px);r=this.incremental_slots(this.view.zoom_res,w.data,U)*al+15;m.parent().css("height",Math.max(this.height_px,r)+"px");aj=this.inc_slots[this.view.zoom_res]}var a=Math.ceil(C*ak),K=$("<canvas class='tile'></canvas>"),Y=this.prefs.label_color,f=this.prefs.block_color,O=this.left_offset;K.css({position:"absolute",top:0,left:(D-this.view.low)*ak-O});K.get(0).width=a+O;K.get(0).height=r;var z=K.get(0).getContext("2d"),ah=z.measureText("A").width;z.fillStyle=this.prefs.block_color;z.font=this.default_font;z.textAlign="right";if(w.dataset_type=="summary_tree"){var J,G=55,ab=255-G,g=ab*2/3,Q=w.data,B=w.max,l=w.avg;if(Q.length>2){var b=Math.ceil((Q[1][0]-Q[0][0])*ak)}! else{var b=50}for(var af=0,v=Q.length;af<v;af++){var S=Math.ceil((Q[af][0]-D)*ak);var R=Q[af][1];if(!R){continue}J=Math.floor(ab-(R/B)*ab);z.fillStyle="rgb("+J+","+J+","+J+")";z.fillRect(S+O,0,b,20);if(this.prefs.show_counts){if(J>g){z.fillStyle="black"}else{z.fillStyle="#ddd"}z.textAlign="center";z.fillText(Q[af][1],S+O+(b/2),12)}}m.append(K);return K}var ai=w.data;var ae=0;for(var af=0,v=ai.length;af<v;af++){var L=ai[af],I=L[0],ag=L[1],T=L[2],E=L[3];if(ag<=ac&&T>=D){var V=Math.floor(Math.max(0,(ag-D)*ak)),A=Math.ceil(Math.min(a,Math.max(0,(T-D)*ak))),P=aj[I]*al;if(w.dataset_type==="bai"){z.fillStyle="#555";if(L[4] instanceof Array){var s=Math.floor(Math.max(0,(L[4][0]-D)*ak)),H=Math.ceil(Math.min(a,Math.max(0,(L[4][1]-D)*ak))),q=Math.floor(Math.max(0,(L[5][0]-D)*ak)),o=Math.ceil(Math.min(a,Math.max(0,(L[5][1]-D)*ak)));if(L[4][1]>=D&&L[4][0]<=ac){this.rect_or_text(z,ak,ah,D,ac,L[4][0],L[4][2],s+O,H-s,P)}if(L[5][1]>=D&&L[5][0]<=ac){this.rect_or_text(z,ak,ah,D,ac,L[5][0],L[5! ][2],q+O,o-q,P)}if(q>H){z.fillStyle="#999";z.fillRect(H+O,P+5,q-H,1)}} else{z.fillStyle="#555";this.rect_or_text(z,ak,ah,D,ac,ag,E,V+O,A-V,P)}if(!U&&ag>D){z.fillStyle=this.prefs.label_color;if(h===0&&V-z.measureText(E).width<0){z.textAlign="left";z.fillText(I,A+2+O,P+8)}else{z.textAlign="right";z.fillText(I,V-2+O,P+8)}z.fillStyle="#555"}}else{if(w.dataset_type==="interval_index"){if(U){z.fillRect(V+O,P+5,A-V,1)}else{var u=L[4],N=L[5],X=L[6],e=L[7];var t,Z,F=null,am=null;if(N&&X){F=Math.floor(Math.max(0,(N-D)*ak));am=Math.ceil(Math.min(a,Math.max(0,(X-D)*ak)))}if(E!==undefined&&ag>D){z.fillStyle=Y;if(h===0&&V-z.measureText(E).width<0){z.textAlign="left";z.fillText(E,A+2+O,P+8)}else{z.textAlign="right";z.fillText(E,V-2+O,P+8)}z.fillStyle=f}if(e){if(u){if(u=="+"){z.fillStyle=RIGHT_STRAND}else{if(u=="-"){z.fillStyle=LEFT_STRAND}}z.fillRect(V+O,P,A-V,10);z.fillStyle=f}for(var ad=0,d=e.length;ad<d;ad++){var n=e[ad],c=Math.floor(Math.max(0,(n[0]-D)*ak)),M=Math.ceil(Math.min(a,Math.max((n[1]-D)*ak)));if(c>M){continue}t=5;Z=3;z.fillRect(c+O,P+Z,M-c,t);i! f(F!==undefined&&!(c>am||M<F)){t=9;Z=1;var aa=Math.max(c,F),p=Math.min(M,am);z.fillRect(aa+O,P+Z,p-aa,t)}}}else{t=9;Z=1;z.fillRect(V+O,P+Z,A-V,t);if(L.strand){if(L.strand=="+"){z.fillStyle=RIGHT_STRAND_INV}else{if(L.strand=="-"){z.fillStyle=LEFT_STRAND_INV}}z.fillRect(V+O,P,A-V,10);z.fillStyle=prefs.block_color}}}}}ae++}}m.append(K);return K},gen_options:function(i){var a=$("<div></div>").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label></label>").attr("for",e).text("Block color:"),l=$("<input></input>").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label></label>").attr("for",j).text("Text color:"),h=$("<input></input>").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label></label>").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div></div>").appen! d(b).append(c);return a.append(k).append(l).append(g).append(h).append (d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{}); \ No newline at end of file diff -r a5ccb2ba4c74 -r 436c9d1c5e64 static/scripts/trackster.js --- a/static/scripts/trackster.js Wed Apr 28 16:06:58 2010 -0400 +++ b/static/scripts/trackster.js Wed Apr 28 17:35:02 2010 -0400 @@ -3,7 +3,7 @@ */ var DEBUG = false; -var DENSITY = 1000, +var DENSITY = 200, FEATURE_LEVELS = 10, DATA_ERROR = "There was an error in indexing this dataset.", DATA_NOCONVERTER = "A converter for this dataset is not installed. Please check your datatypes_conf.xml file.", @@ -271,16 +271,15 @@ range = high - low, resolution = this.view.resolution; - if (DEBUG) { $("#debug").text(resolution + " " + this.view.zoom_res); } - var parent_element = $("<div style='position: relative;'></div>"); - this.content_div.children( ":first" ).remove(); - this.content_div.append( parent_element ); + var parent_element = $("<div style='position: relative;'></div>"), + w_scale = this.content_div.width() / range, + tile_element; - var w_scale = this.content_div.width() / range; - - var tile_element; + this.content_div.children( ":first" ).remove(); + this.content_div.append( parent_element ), + this.max_height = 0; // Index of first tile that overlaps visible region var tile_index = Math.floor( low / resolution / DENSITY ); while ( ( tile_index * DENSITY * resolution ) < high ) { @@ -298,6 +297,7 @@ // Our responsibility to move the element to the new parent parent_element.append( cached ); this.max_height = Math.max( this.max_height, cached.height() ); + this.content_div.css("height", this.max_height + "px"); } else { this.delayed_draw(this, key, low, high, tile_index, resolution, parent_element, w_scale); } @@ -311,7 +311,7 @@ if ( tile_element ) { track.tile_cache.set(key, tile_element); track.max_height = Math.max( track.max_height, tile_element.height() ); - track.content_div.css( "height", track.max_height + "px"); + track.content_div.css("height", track.max_height + "px"); } } }, 50); @@ -679,12 +679,30 @@ return highest_slot; }, + rect_or_text: function( ctx, w_scale, px_per_char, tile_low, tile_high, feature_start, name, x, x_len, y_center ) { + ctx.textAlign = "center"; + var gap = Math.round(w_scale / 2); + if (name !== undefined && w_scale > px_per_char) { + ctx.fillStyle = "#555"; + ctx.fillRect(x, y_center + 1, x_len, 9); + ctx.fillStyle = "#eee"; + for (var c = 0, str_len = name.length; c < str_len; c++) { + if (feature_start + c >= tile_low && feature_start + c <= tile_high) { + var c_start = Math.floor( Math.max(0, (feature_start + c - tile_low) * w_scale) ); + ctx.fillText(name[c], c_start + this.left_offset + gap, y_center + 9); + } + } + } else { + ctx.fillStyle = "#555"; + ctx.fillRect(x, y_center + 4, x_len, 3); + } + }, draw_tile: function( resolution, tile_index, parent_element, w_scale ) { var tile_low = tile_index * DENSITY * resolution, tile_high = ( tile_index + 1 ) * DENSITY * resolution, tile_span = DENSITY * resolution; // console.log("drawing " + tile_index); - var data, slots, required_height; + var slots, required_height; /*for (var k in this.data_cache.obj_cache) { var k_split = k.split("_"), k_low = k_split[0], k_high = k_split[1]; @@ -696,23 +714,22 @@ // var k = this.view.low + '_' + this.view.high; var k = tile_low + '_' + tile_high; - var data = this.data_cache.get(k); + var result = this.data_cache.get(k); - if (data === undefined) { + if (result === undefined) { this.data_queue[ [tile_low, tile_high] ] = true; this.get_data(tile_low, tile_high); return; } - if (data.dataset_type == "array_tree") { + if (result.dataset_type === "summary_tree") { required_height = 30; - // Blah } else { // Calculate new slots incrementally for this new chunk of data and update height if necessary - var no_detail = (data.extra_info === "no_detail"); + var no_detail = (result.extra_info === "no_detail"); var y_scale = ( no_detail ? this.vertical_nodetail_px : this.vertical_detail_px ); - required_height = this.incremental_slots( this.view.zoom_res, data.data, no_detail ) * y_scale + 15; + required_height = this.incremental_slots( this.view.zoom_res, result.data, no_detail ) * y_scale + 15; parent_element.parent().css("height", Math.max(this.height_px, required_height) + "px"); slots = this.inc_slots[this.view.zoom_res]; } @@ -732,20 +749,21 @@ new_canvas.get(0).width = width + left_offset; new_canvas.get(0).height = required_height; // console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale); - var ctx = new_canvas.get(0).getContext("2d"); + var ctx = new_canvas.get(0).getContext("2d"), + px_per_char = ctx.measureText("A").width; ctx.fillStyle = this.prefs.block_color; ctx.font = this.default_font; ctx.textAlign = "right"; - var min_color = 55, - color_span = 255 - min_color, - color_cutoff = color_span*2/3; // Where text switches from black to white - - if (data.dataset_type == "summary_tree") { - var points = data.data; - var max = data.max; - var avg = data.avg; - if (data.data.length > 2) { + if (result.dataset_type == "summary_tree") { + var color, + min_color = 55, + color_span = 255 - min_color, + color_cutoff = color_span*2/3, // Where text switches from black to white + points = result.data, + max = result.max, + avg = result.avg; + if (points.length > 2) { var delta_x_px = Math.ceil((points[1][0] - points[0][0]) * w_scale); } else { var delta_x_px = 50; // Arbitrary, fix @@ -756,7 +774,7 @@ var y = points[i][1]; if (!y) { continue; } - var color = Math.floor( color_span - (y / max) * color_span ); + color = Math.floor( color_span - (y / max) * color_span ); ctx.fillStyle = "rgb(" +color+ "," +color+ "," +color+ ")"; ctx.fillRect(x + left_offset, 0, delta_x_px, 20); @@ -770,11 +788,12 @@ ctx.fillText(points[i][1], x + left_offset + (delta_x_px/2), 12); } } + parent_element.append( new_canvas ); return new_canvas; } - var data = data.data; + var data = result.data; var j = 0; for (var i = 0, len = data.length; i < len; i++) { var feature = data[i], @@ -785,80 +804,118 @@ if (feature_start <= tile_high && feature_end >= tile_low) { var f_start = Math.floor( Math.max(0, (feature_start - tile_low) * w_scale) ), - f_end = Math.ceil( Math.min(width, (feature_end - tile_low) * w_scale) ), + f_end = Math.ceil( Math.min(width, Math.max(0, (feature_end - tile_low) * w_scale)) ), y_center = slots[feature_uid] * y_scale; - - // console.log(feature_uid, feature_start, feature_end, f_start, f_end, y_center); - if (no_detail) { - ctx.fillRect(f_start + left_offset, y_center + 5, f_end - f_start, 1); - } else { - // Showing labels, blocks, details - var feature_strand = feature[4], - feature_ts = feature[5], - feature_te = feature[6], - feature_blocks = feature[7]; - var thickness, y_start, thick_start = null, thick_end = null; - if (feature_ts && feature_te) { - thick_start = Math.floor( Math.max(0, (feature_ts - tile_low) * w_scale) ); - thick_end = Math.ceil( Math.min(width, (feature_te - tile_low) * w_scale) ); + if (result.dataset_type === "bai") { + ctx.fillStyle = "#555"; + if (feature[4] instanceof Array) { + var b1_start = Math.floor( Math.max(0, (feature[4][0] - tile_low) * w_scale) ), + b1_end = Math.ceil( Math.min(width, Math.max(0, (feature[4][1] - tile_low) * w_scale)) ), + b2_start = Math.floor( Math.max(0, (feature[5][0] - tile_low) * w_scale) ), + b2_end = Math.ceil( Math.min(width, Math.max(0, (feature[5][1] - tile_low) * w_scale)) ); + + if (feature[4][1] >= tile_low && feature[4][0] <= tile_high) { + this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature[4][0], feature[4][2], b1_start + left_offset, b1_end - b1_start, y_center); + } + if (feature[5][1] >= tile_low && feature[5][0] <= tile_high) { + this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature[5][0], feature[5][2], b2_start + left_offset, b2_end - b2_start, y_center); + } + if (b2_start > b1_end) { + ctx.fillStyle = "#999"; + ctx.fillRect(b1_end + left_offset, y_center + 5, b2_start - b1_end, 1); + } + } else { + ctx.fillStyle = "#555"; + this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature_start, feature_name, f_start + left_offset, f_end - f_start, y_center); } - if (feature_name !== undefined && feature_start > tile_low) { - ctx.fillStyle = label_color; + if (!no_detail && feature_start > tile_low) { + // Draw label + ctx.fillStyle = this.prefs.label_color; if (tile_index === 0 && f_start - ctx.measureText(feature_name).width < 0) { ctx.textAlign = "left"; - ctx.fillText(feature_name, f_end + 2 + left_offset, y_center + 8); + ctx.fillText(feature_uid, f_end + 2 + left_offset, y_center + 8); } else { ctx.textAlign = "right"; - ctx.fillText(feature_name, f_start - 2 + left_offset, y_center + 8); + ctx.fillText(feature_uid, f_start - 2 + left_offset, y_center + 8); } - ctx.fillStyle = block_color; + ctx.fillStyle = "#555"; } - if (feature_blocks) { - // Draw introns - if (feature_strand) { - if (feature_strand == "+") { - ctx.fillStyle = RIGHT_STRAND; - } else if (feature_strand == "-") { - ctx.fillStyle = LEFT_STRAND; + + } else if (result.dataset_type === "interval_index") { + + // console.log(feature_uid, feature_start, feature_end, f_start, f_end, y_center); + if (no_detail) { + ctx.fillRect(f_start + left_offset, y_center + 5, f_end - f_start, 1); + } else { + // Showing labels, blocks, details + var feature_strand = feature[4], + feature_ts = feature[5], + feature_te = feature[6], + feature_blocks = feature[7]; + + var thickness, y_start, thick_start = null, thick_end = null; + if (feature_ts && feature_te) { + thick_start = Math.floor( Math.max(0, (feature_ts - tile_low) * w_scale) ); + thick_end = Math.ceil( Math.min(width, Math.max(0, (feature_te - tile_low) * w_scale)) ); + } + if (feature_name !== undefined && feature_start > tile_low) { + ctx.fillStyle = label_color; + if (tile_index === 0 && f_start - ctx.measureText(feature_name).width < 0) { + ctx.textAlign = "left"; + ctx.fillText(feature_name, f_end + 2 + left_offset, y_center + 8); + } else { + ctx.textAlign = "right"; + ctx.fillText(feature_name, f_start - 2 + left_offset, y_center + 8); } - ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10); ctx.fillStyle = block_color; } + if (feature_blocks) { + // Draw introns + if (feature_strand) { + if (feature_strand == "+") { + ctx.fillStyle = RIGHT_STRAND; + } else if (feature_strand == "-") { + ctx.fillStyle = LEFT_STRAND; + } + ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10); + ctx.fillStyle = block_color; + } - for (var k = 0, k_len = feature_blocks.length; k < k_len; k++) { - var block = feature_blocks[k], - block_start = Math.floor( Math.max(0, (block[0] - tile_low) * w_scale) ), - block_end = Math.ceil( Math.min(width, (block[1] - tile_low) * w_scale) ); - if (block_start > block_end) { continue; } - // Draw the block - thickness = 5; - y_start = 3; - ctx.fillRect(block_start + left_offset, y_center + y_start, block_end - block_start, thickness); + for (var k = 0, k_len = feature_blocks.length; k < k_len; k++) { + var block = feature_blocks[k], + block_start = Math.floor( Math.max(0, (block[0] - tile_low) * w_scale) ), + block_end = Math.ceil( Math.min(width, Math.max((block[1] - tile_low) * w_scale)) ); + if (block_start > block_end) { continue; } + // Draw the block + thickness = 5; + y_start = 3; + ctx.fillRect(block_start + left_offset, y_center + y_start, block_end - block_start, thickness); - // Draw thick regions: check if block intersects with thick region - if (thick_start !== undefined && !(block_start > thick_end || block_end < thick_start) ) { - thickness = 9; - y_start = 1; - var block_thick_start = Math.max(block_start, thick_start), - block_thick_end = Math.min(block_end, thick_end); - ctx.fillRect(block_thick_start + left_offset, y_center + y_start, block_thick_end - block_thick_start, thickness); + // Draw thick regions: check if block intersects with thick region + if (thick_start !== undefined && !(block_start > thick_end || block_end < thick_start) ) { + thickness = 9; + y_start = 1; + var block_thick_start = Math.max(block_start, thick_start), + block_thick_end = Math.min(block_end, thick_end); + ctx.fillRect(block_thick_start + left_offset, y_center + y_start, block_thick_end - block_thick_start, thickness); + } } - } - } else { - // If there are no blocks, we treat the feature as one big exon - thickness = 9; - y_start = 1; - ctx.fillRect(f_start + left_offset, y_center + y_start, f_end - f_start, thickness); - if ( feature.strand ) { - if (feature.strand == "+") { - ctx.fillStyle = RIGHT_STRAND_INV; - } else if (feature.strand == "-") { - ctx.fillStyle = LEFT_STRAND_INV; + } else { + // If there are no blocks, we treat the feature as one big exon + thickness = 9; + y_start = 1; + ctx.fillRect(f_start + left_offset, y_center + y_start, f_end - f_start, thickness); + if ( feature.strand ) { + if (feature.strand == "+") { + ctx.fillStyle = RIGHT_STRAND_INV; + } else if (feature.strand == "-") { + ctx.fillStyle = LEFT_STRAND_INV; + } + ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10); + ctx.fillStyle = prefs.block_color; } - ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10); - ctx.fillStyle = prefs.block_color; } } } @@ -899,62 +956,9 @@ var ReadTrack = function ( name, dataset_id, prefs ) { FeatureTrack.call( this, name, dataset_id, prefs ); this.track_type = "ReadTrack"; + this.vertical_detail_px = 10; + this.vertical_nodetail_px = 5; }; $.extend( ReadTrack.prototype, TiledTrack.prototype, FeatureTrack.prototype, { - /*draw_tile: function( resolution, tile_index, parent_element, w_scale ) { - if (!this.values) { - return; - } - var tile_low = tile_index * DENSITY * resolution, - tile_high = ( tile_index + 1 ) * DENSITY * resolution, - tile_span = DENSITY * resolution; - // console.log("drawing " + tile_index); - // Once we zoom in enough, show name labels - var data, slots, required_height; - required_height = this.height_px; - slots = this.zo_slots; - data = this.values; - - // console.log(tile_low, tile_high, tile_length, w_scale); - var width = Math.ceil( tile_span * w_scale ), - new_canvas = $("<canvas class='tile'></canvas>"); - - new_canvas.css({ - position: "absolute", - top: 0, - left: ( tile_low - this.view.low ) * w_scale - this.left_offset - }); - new_canvas.get(0).width = width + this.left_offset; - new_canvas.get(0).height = required_height; - // console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale); - var ctx = new_canvas.get(0).getContext("2d"); - ctx.fillStyle = this.prefs.block_color; - ctx.font = this.default_font; - ctx.textAlign = "right"; - var px_per_char = ctx.measureText("A").width; - - var j = 0; - for (var i = 0, len = data.length; i < len; i++) { - var feature = data[i]; - if (feature.start <= tile_high && feature.end >= tile_low) { - var f_start = Math.floor( Math.max(0, (feature.start - tile_low) * w_scale) ), - f_end = Math.ceil( Math.min(width, (feature.end - tile_low) * w_scale) ), - y_center = slots[feature.uid] * this.vertical_detail_px; - - var thickness, y_start, thick_start = null, thick_end = null; - if (w_scale > px_per_char) { - for (var c = 0, str_len = feature.name.length; c < str_len; c++) { - var c_start = Math.floor( Math.max(0, (feature.start + c - tile_low) * w_scale) ); - ctx.fillText(feature.name[c], c_start + this.left_offset, y_center + 8); - } - } else { - ctx.fillRect(f_start + this.left_offset, y_center + 4, f_end - f_start, 3); - } - } - } - - parent_element.append( new_canvas ); - return new_canvas; - }*/ });