[hg] galaxy 2876: trackster: caching works, faster loops, displa...
details: http://www.bx.psu.edu/hg/galaxy/rev/e11e89fd2c82 changeset: 2876:e11e89fd2c82 user: Kanwei Li <kanwei@gmail.com> date: Mon Oct 12 19:00:39 2009 -0400 description: trackster: caching works, faster loops, displays exon blocks, passes JSLint 4 file(s) affected in this change: static/scripts/packed/autocomplete_tagging.js static/scripts/packed/trackster.js static/scripts/trackster.js static/trackster.css diffs (513 lines): diff -r ebc1c19fc2d5 -r e11e89fd2c82 static/scripts/packed/autocomplete_tagging.js --- a/static/scripts/packed/autocomplete_tagging.js Mon Oct 12 16:14:54 2009 -0400 +++ b/static/scripts/packed/autocomplete_tagging.js Mon Oct 12 19:00:39 2009 -0400 @@ -1,1 +1,1 @@ -var ac_tag_area_id_gen=1;jQuery.fn.autocomplete_tagging=function(c){var e={get_toggle_link_text_fn:function(u){var w="";var v=o(u);if(v!=0){w=v+(v!=0?" Tags":" Tag")}else{w="Add tags"}return w},tag_click_fn:function(u,v){},input_size:20,in_form:false,tags:{},use_toggle_link:true,item_id:"",add_tag_img:"",add_tag_img_rollover:"",delete_tag_img:"",ajax_autocomplete_tag_url:"",ajax_retag_url:"",ajax_delete_tag_url:"",ajax_add_tag_url:""};var p=jQuery.extend(e,c);var k="tag-area-"+(ac_tag_area_id_gen)++;var m=$("<div>").attr("id",k).addClass("tag-area");this.append(m);var o=function(u){if(u.length){return u.length}var v=0;for(element in u){v++}return v};var b=function(){var u=p.get_toggle_link_text_fn(p.tags);var v=$("<a href='/history/tags'>").text(u).addClass("toggle-link");v.click(function(){var w=(m.css("display")=="none");var x;if(w){x=function(){var y=o(p.tags);if(y==0){m.click()}}}else{x=function(){m.blur()}}m.slideToggle("fast",x);return false});return v};var s=b();if(p. use_toggle_link){this.prepend(s)}var t=function(u){var v=new Array();for(key in u){v[v.length]=key+"-->"+u[key]}return"{"+v.join(",")+"}"};var a=function(v,u){return v+((u!=""&&u)?":"+u:"")};var h=function(u){return u.split(":")};var i=function(u){var v=$("<img src='"+p.add_tag_img+"' rollover='"+p.add_tag_img_rollover+"'/>").addClass("add-tag-button");v.click(function(){$(this).hide();m.click();return false});return v};var j=function(u){var v=$("<img src='"+p.delete_tag_img+"'/>").addClass("delete-tag-img");v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)});v.click(function(){var D=$(this).parent();var C=D.find(".tag-name").eq(0);var B=C.text();var z=h(B);var F=z[0];var y=z[1];var E=D.prev();D.remove();delete p.tags[F];var A=p.get_toggle_link_text_fn(p.tags);s.text(A);$.ajax({url:p.ajax_delete_tag_url,data:{tag_name:F},error:function(){p.tags[F]=y;if(E.hasClass("tag-button")){E.after(D)}else {m.prepend(D)}var G=p.get_toggle_link_text_fn(p.tags);alert("Remove tag failed");s.text(G);v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)})},success:function(){}});return true});var w=$("<span>").text(u).addClass("tag-name");w.click(function(){tag_name_and_value=u.split(":");p.tag_click_fn(tag_name_and_value[0],tag_name_and_value[1]);return true});var x=$("<span></span>").addClass("tag-button");x.append(w);x.append(v);return x};var d=function(v){var u;if(p.in_form){u=$("<textarea id='history-tag-input' rows='1' cols='"+p.input_size+"' value='"+escape(v)+"'></textarea>")}else{u=$("<input id='history-tag-input' type='text' size='"+p.input_size+"' value='"+escape(v)+"'></input>")}u.keyup(function(D){if(D.keyCode==27){$(this).trigger("blur")}else{if((D.keyCode==13)||(D.keyCode==188)||(D.keyCode==32)){new_value=this.value;if(return_key_pressed_for_autocomplete==true){return_key_pressed_for_autoc omplete=false;return false}if(new_value.indexOf(": ",new_value.length-2)!=-1){this.value=new_value.substring(0,new_value.length-1);return false}if((D.keyCode==188)||(D.keyCode==32)){new_value=new_value.substring(0,new_value.length-1)}new_value=new_value.replace(/^\s+|\s+$/g,"");if(new_value.length<3){return false}this.value="";var A=j(new_value);var z=m.children(".tag-button");if(z.length!=0){var E=z.slice(z.length-1);E.after(A)}else{m.prepend(A)}var y=new_value.split(":");p.tags[y[0]]=y[1];var B=p.get_toggle_link_text_fn(p.tags);s.text(B);var C=$(this);$.ajax({url:p.ajax_add_tag_url,data:{new_tag:new_value},error:function(){A.remove();delete p.tags[y[0]];var F=p.get_toggle_link_text_fn(p.tags);s.text(F);alert("Add tag failed")},success:function(){C.flushCache()}});return false}}});var w=function(A,z,y,C,B){tag_name_and_value=C.split(":");return(tag_name_and_value.length==1?tag_name_and_value[0]:tag_name_and_value[1])};var x={selectFirst:false,formatItem:w,autoFill:false,hig hlight:false};u.autocomplete(p.ajax_autocomplete_tag_url,x);u.addClass("tag-input");return u};for(tag_name in p.tags){var q=p.tags[tag_name];var l=a(tag_name,q);var g=j(l,s,p.tags);m.append(g)}var n=d("");var f=i(n);m.blur(function(u){r=o(p.tags);if(r!=0){f.show();n.hide();m.removeClass("active-tag-area")}else{}});m.append(f);m.append(n);n.hide();m.click(function(w){var v=$(this).hasClass("active-tag-area");if($(w.target).hasClass("delete-tag-img")&&!v){return false}if($(w.target).hasClass("tag-name")&&!v){return false}$(this).addClass("active-tag-area");f.hide();n.show();n.focus();var u=function(y){var x=m.attr("id");if(($(y.target).attr("id")!=x)&&($(y.target).parents().filter(x).length==0)){m.blur();$(document).unbind("click",u)}};$(window).click(u);return false});if(p.use_toggle_link){m.hide()}else{var r=o(p.tags);if(r==0){f.hide();n.show()}}return this.addClass("tag-element")}; \ No newline at end of file +var ac_tag_area_id_gen=1;jQuery.fn.autocomplete_tagging=function(c){var e={get_toggle_link_text_fn:function(u){var w="";var v=o(u);if(v!=0){w=v+(v!=0?" Tags":" Tag")}else{w="Add tags"}return w},tag_click_fn:function(u,v){},editable:true,input_size:20,in_form:false,tags:{},use_toggle_link:true,item_id:"",add_tag_img:"",add_tag_img_rollover:"",delete_tag_img:"",ajax_autocomplete_tag_url:"",ajax_retag_url:"",ajax_delete_tag_url:"",ajax_add_tag_url:""};var p=jQuery.extend(e,c);var k="tag-area-"+(ac_tag_area_id_gen)++;var m=$("<div>").attr("id",k).addClass("tag-area");this.append(m);var o=function(u){if(u.length){return u.length}var v=0;for(element in u){v++}return v};var b=function(){var u=p.get_toggle_link_text_fn(p.tags);var v=$("<a href='/history/tags'>").text(u).addClass("toggle-link");v.click(function(){var w=(m.css("display")=="none");var x;if(w){x=function(){var y=o(p.tags);if(y==0){m.click()}}}else{x=function(){m.blur()}}m.slideToggle("fast",x);return false});return v};v ar s=b();if(p.use_toggle_link){this.prepend(s)}var t=function(u){var v=new Array();for(key in u){v[v.length]=key+"-->"+u[key]}return"{"+v.join(",")+"}"};var a=function(v,u){return v+((u!=""&&u)?":"+u:"")};var h=function(u){return u.split(":")};var i=function(u){var v=$("<img src='"+p.add_tag_img+"' rollover='"+p.add_tag_img_rollover+"'/>").addClass("add-tag-button");v.click(function(){$(this).hide();m.click();return false});return v};var j=function(u){var v=$("<img src='"+p.delete_tag_img+"'/>").addClass("delete-tag-img");v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)});v.click(function(){var D=$(this).parent();var C=D.find(".tag-name").eq(0);var B=C.text();var z=h(B);var F=z[0];var y=z[1];var E=D.prev();D.remove();delete p.tags[F];var A=p.get_toggle_link_text_fn(p.tags);s.text(A);$.ajax({url:p.ajax_delete_tag_url,data:{tag_name:F},error:function(){p.tags[F]=y;if(E.hasClass("tag-button")){E .after(D)}else{m.prepend(D)}var G=p.get_toggle_link_text_fn(p.tags);alert("Remove tag failed");s.text(G);v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)})},success:function(){}});return true});var w=$("<span>").text(u).addClass("tag-name");w.click(function(){tag_name_and_value=u.split(":");p.tag_click_fn(tag_name_and_value[0],tag_name_and_value[1]);return true});var x=$("<span></span>").addClass("tag-button");x.append(w);if(p.editable){x.append(v)}return x};var d=function(v){var u;if(p.in_form){u=$("<textarea id='history-tag-input' rows='1' cols='"+p.input_size+"' value='"+escape(v)+"'></textarea>")}else{u=$("<input id='history-tag-input' type='text' size='"+p.input_size+"' value='"+escape(v)+"'></input>")}u.keyup(function(D){if(D.keyCode==27){$(this).trigger("blur")}else{if((D.keyCode==13)||(D.keyCode==188)||(D.keyCode==32)){new_value=this.value;if(return_key_pressed_for_autocomplete==true) {return_key_pressed_for_autocomplete=false;return false}if(new_value.indexOf(": ",new_value.length-2)!=-1){this.value=new_value.substring(0,new_value.length-1);return false}if((D.keyCode==188)||(D.keyCode==32)){new_value=new_value.substring(0,new_value.length-1)}new_value=new_value.replace(/^\s+|\s+$/g,"");if(new_value.length<3){return false}this.value="";var A=j(new_value);var z=m.children(".tag-button");if(z.length!=0){var E=z.slice(z.length-1);E.after(A)}else{m.prepend(A)}var y=new_value.split(":");p.tags[y[0]]=y[1];var B=p.get_toggle_link_text_fn(p.tags);s.text(B);var C=$(this);$.ajax({url:p.ajax_add_tag_url,data:{new_tag:new_value},error:function(){A.remove();delete p.tags[y[0]];var F=p.get_toggle_link_text_fn(p.tags);s.text(F);alert("Add tag failed")},success:function(){C.flushCache()}});return false}}});var w=function(A,z,y,C,B){tag_name_and_value=C.split(":");return(tag_name_and_value.length==1?tag_name_and_value[0]:tag_name_and_value[1])};var x={selectFirst:false,fo rmatItem:w,autoFill:false,highlight:false};u.autocomplete(p.ajax_autocomplete_tag_url,x);u.addClass("tag-input");return u};for(tag_name in p.tags){var q=p.tags[tag_name];var l=a(tag_name,q);var g=j(l,s,p.tags);m.append(g)}var n=d("");var f=i(n);m.blur(function(u){r=o(p.tags);if(r!=0){f.show();n.hide();m.removeClass("active-tag-area")}else{}});if(p.editable){m.append(f);m.append(n);n.hide();m.click(function(w){var v=$(this).hasClass("active-tag-area");if($(w.target).hasClass("delete-tag-img")&&!v){return false}if($(w.target).hasClass("tag-name")&&!v){return false}$(this).addClass("active-tag-area");f.hide();n.show();n.focus();var u=function(y){var x=m.attr("id");if(($(y.target).attr("id")!=x)&&($(y.target).parents().filter(x).length==0)){m.blur();$(document).unbind("click",u)}};$(window).click(u);return false})}if(p.use_toggle_link){m.hide()}else{var r=o(p.tags);if(r==0){f.hide();n.show()}}return this.addClass("tag-element")}; \ No newline at end of file diff -r ebc1c19fc2d5 -r e11e89fd2c82 static/scripts/packed/trackster.js --- a/static/scripts/packed/trackster.js Mon Oct 12 16:14:54 2009 -0400 +++ b/static/scripts/packed/trackster.js Mon Oct 12 19:00:39 2009 -0400 @@ -1,1 +1,1 @@ -var DENSITY=1000,DATA_ERROR="There was an error in indexing this dataset.",DATA_NONE="No data for this chrom/contig.";var DataCache=function(b,a){this.type=b;this.track=a;this.cache=Object()};$.extend(DataCache.prototype,{get:function(d,b){var c=this.cache;if(!(c[d]&&c[d][b])){if(!c[d]){c[d]=Object()}var a=b*DENSITY*d;var e=(b+1)*DENSITY*d;c[d][b]={state:"loading"};$.getJSON(data_url,{track_type:this.track.track_type,chrom:this.track.view.chrom,low:a,high:e,dataset_id:this.track.dataset_id},function(f){if(f=="pending"){setTimeout(fetcher,5000)}else{c[d][b]={state:"loaded",values:f}}$(document).trigger("redraw")})}return c[d][b]}});var View=function(a,b){this.chrom=a;this.tracks=[];this.max_low=0;this.max_high=b;this.low=this.max_low;this.high=this.max_high;this.length=this.max_high-this.max_low};$.extend(View.prototype,{add_track:function(a){a.view=this;this.tracks.push(a);if(a.init){a.init()}},redraw:function(){$("#overview-box").css({left:(this.low/this.length)*$("#overvie w-viewport").width(),width:Math.max(4,((this.high-this.low)/this.length)*$("#overview-viewport").width())}).show();$("#low").text(this.low);$("#high").text(this.high);for(var a in this.tracks){this.tracks[a].draw()}$("#bottom-spacer").remove();$("#viewport").append('<div id="bottom-spacer" style="height: 200px;"></div>')},move:function(b,a){this.low=Math.max(this.max_low,Math.floor(b));this.high=Math.min(this.length,Math.ceil(a))},zoom_in:function(d,b){if(this.max_high==0){return}var c=this.high-this.low;var e=c/d/2;if(b==undefined){var a=(this.low+this.high)/2}else{var a=this.low+c*b/$(document).width()}this.low=Math.floor(a-e);this.high=Math.ceil(a+e);if(this.low<this.max_low){this.low=this.max_low;this.high=c/d}else{if(this.high>this.max_high){this.high=this.max_high;this.low=this.max_high-c/d}}if(this.high-this.low<1){this.high=this.low+1}},zoom_out:function(c){if(this.max_high==0){return}var a=(this.low+this.high)/2;var b=this.high-this.low;var d=b*c/2;this.low=Math.flo or(Math.max(0,a-d));this.high=Math.ceil(Math.min(this.length,a+d))},left:function(b){var a=this.high-this.low;var c=Math.floor(a/b);if(this.low-c<0){this.low=0;this.high=this.low+a}else{this.low-=c;this.high-=c}},right:function(b){var a=this.high-this.low;var c=Math.floor(a/b);if(this.high+c>this.length){this.high=this.length;this.low=this.high-a}else{this.low+=c;this.high+=c}}});var Track=function(a,b){this.name=a;this.parent_element=b;this.make_container()};$.extend(Track.prototype,{make_container:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div class='track'></div>").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)}});var TiledTrack=function(){this.last_resolution=null;this.last_w_scale=null;this.tile_cache={}};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,c=this.view.high,e=c-i;var b=Mat h.pow(10,Math.ceil(Math.log(e/DENSITY)/Math.log(10)));b=Math.max(b,1);b=Math.min(b,100000);var m=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(m);var k=this.content_div.width(),d=this.content_div.height(),n=k/e,j={},l={};if(this.last_resolution==b&&this.last_w_scale==n){j=this.tile_cache}var g;var a=Math.floor(i/b/DENSITY);while((a*1000*b)<c){if(a in j){g=j[a];var f=a*DENSITY*b;g.css({left:(f-this.view.low)*n});m.append(g)}else{g=this.draw_tile(b,a,m,n,d)}if(g){l[a]=g}a+=1}this.last_resolution=b;this.last_w_scale=n;this.tile_cache=l}});var LabelTrack=function(a){Track.call(this,null,a);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.hig h){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+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,b,a){Track.call(this,c,$("#viewport"));this.track_type="line";this.height_px=(a?a:100);this.container_div.addClass("line-track");this.content_div.css("height",this.height_px+"px");this.dataset_id=b;this.cache=new DataCache("",this)};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;$.getJSON(data_url,{stats:true,track_type:a.track_type,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(b){if(!b||b=="error"){a.content_div.addClass("error").text(DATA_ERROR)}else{if(b=="no data"){a.content_div.addClass("nodata").text(DATA_NONE)}else{a.min_value=b.min;a.max_value=b.max;a.vertical_range=a.max_value-a.min_value;a.view.redraw()}}})},draw_tile:function(d,a,n,r,o){if(!this.vertical_range){return}var j=a*DENSITY*d,q=(a+1)*DENSITY* d,c=DENSITY*d;var m=this.cache.get(d,a);var h;if(m.state=="loading"){h=$("<div class='loading tile'></div>")}else{h=$("<canvas class='tile'></canvas>")}h.css({position:"absolute",top:0,left:(j-this.view.low)*r,});n.append(h);if(m.state=="loading"){e=false;return null}var b=h;b.get(0).width=Math.ceil(c*r);b.get(0).height=this.height_px;var p=b.get(0).getContext("2d");var e=false;p.beginPath();var g=m.values;if(!g){return}for(var f=0;f<g.length-1;f++){var l=g[f][0]-j;var k=g[f][1];if(isNaN(k)){e=false}else{l=l*r;y_above_min=k-this.min_value;k=y_above_min/this.vertical_range*this.height_px;if(e){p.lineTo(l,k)}else{p.moveTo(l,k);e=true}}}p.stroke();return h}});var FeatureTrack=function(c,b,a){Track.call(this,c,$("#viewport"));this.track_type="feature";this.height_px=(a?a:100);this.container_div.addClass("feature-track");this.content_div.css("height",this.height_px+"px");this.dataset_id=b;this.zo_slots={};this.show_labels_scale=0.01;this.showing_labels=false;this.vertical_gap=10} ;$.extend(FeatureTrack.prototype,TiledTrack.prototype,{calc_slots:function(h){var b=[];var a=this.container_div.width()/(this.view.high-this.view.low);if(h){this.zi_slots={}}var g=$("<canvas></canvas>").get(0).getContext("2d");for(var d in this.values){var k=this.values[d];var e=Math.floor(Math.max(this.view.max_low,(k.start-this.view.max_low)*a));if(h){e-=g.measureText(k.name).width;e-=10}var f=Math.ceil(Math.min(this.view.max_high,(k.end-this.view.max_low)*a));var c=0;while(true){if(b[c]==undefined||b[c]<e){b[c]=f;if(h){this.zi_slots[k.name]=c}else{this.zo_slots[k.name]=c}break}c++}}this.height_px=b.length*this.vertical_gap+15;this.content_div.css("height",this.height_px+"px")},init:function(){var a=this;$.getJSON(data_url,{track_type:a.track_type,low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom},function(b){if(b=="error"){a.content_div.addClass("error").text(DATA_ERROR)}else{if(b.length==0||b=="no data"){a.content_div.addClass("nodata").t ext(DATA_NONE)}else{a.values=b;a.calc_slots();a.slots=a.zo_slots;a.draw()}}})},draw_tile:function(q,t,e,g,f){if(!this.values){return null}if(g>this.show_labels_scale&&!this.showing_labels){this.showing_labels=true;if(!this.zi_slots){this.calc_slots(true)}this.slots=this.zi_slots}else{if(g<=this.show_labels_scale&&this.showing_labels){this.showing_labels=false;this.slots=this.zo_slots}}var u=t*DENSITY*q,c=(t+1)*DENSITY*q,b=DENSITY*q;var k=this.view,m=k.high-k.low,o=Math.ceil(b*g),h=new Array(),n=this.height_px,l=$("<canvas class='tile'></canvas>");l.css({position:"absolute",top:0,left:(u-this.view.low)*g,});l.get(0).width=o;l.get(0).height=n;var p=l.get(0).getContext("2d");var r=0;for(var s in this.values){feature=this.values[s];if(feature.start<=c&&feature.end>=u){f_start=Math.floor(Math.max(0,(feature.start-u)*g));f_end=Math.ceil(Math.min(o,(feature.end-u)*g));p.fillStyle="#000";p.fillRect(f_start,this.slots[feature.name]*this.vertical_gap+5,f_end-f_start,1);if(this.showing _labels&&p.fillText){p.font="10px monospace";p.textAlign="right";p.fillText(feature.name,f_start,this.slots[feature.name]*10+8)}if(feature.exon_start&&feature.exon_end){var d=Math.floor(Math.max(0,(feature.exon_start-u)*g));var w=Math.ceil(Math.min(o,(feature.exon_end-u)*g))}for(var s in feature.blocks){block=feature.blocks[s];block_start=Math.floor(Math.max(0,(block[0]-u)*g));block_end=Math.ceil(Math.min(o,(block[1]-u)*g));var a=3,v=4;if(d&&block_start>=d&&block_end<=w){a=5,v=3}p.fillRect(d,this.slots[feature.name]*this.vertical_gap+v,block_end-block_start,a)}r++}}e.append(l);return l},}); \ No newline at end of file +var DENSITY=1000,DATA_ERROR="There was an error in indexing this dataset.",DATA_NONE="No data for this chrom/contig.",CACHED_TILES=200,CACHED_DATA=20;var View=function(a,b){this.chrom=a;this.tracks=[];this.max_low=0;this.max_high=b;this.low=this.max_low;this.high=this.max_high;this.length=this.max_high-this.max_low};$.extend(View.prototype,{add_track:function(a){a.view=this;this.tracks.push(a);if(a.init){a.init()}},redraw:function(){$("#overview-box").css({left:(this.low/this.length)*$("#overview-viewport").width(),width:Math.max(4,((this.high-this.low)/this.length)*$("#overview-viewport").width())}).show();$("#low").text(this.low);$("#high").text(this.high);for(var b=0,a=this.tracks.length;b<a;b++){this.tracks[b].draw()}$("#bottom-spacer").remove();$("#viewport").append('<div id="bottom-spacer" style="height: 200px;"></div>')},move:function(b,a){this.low=Math.max(this.max_low,Math.floor(b));this.high=Math.min(this.length,Math.ceil(a))},zoom_in:function(d,b){if(this.max_high ===0){return}var c=this.high-this.low;var e=c/d/2;var a;if(b===undefined){a=(this.low+this.high)/2}else{a=this.low+c*b/$(document).width()}this.low=Math.floor(a-e);this.high=Math.ceil(a+e);if(this.low<this.max_low){this.low=this.max_low;this.high=c/d}else{if(this.high>this.max_high){this.high=this.max_high;this.low=this.max_high-c/d}}if(this.high-this.low<1){this.high=this.low+1}},zoom_out:function(c){if(this.max_high===0){return}var a=(this.low+this.high)/2;var b=this.high-this.low;var d=b*c/2;this.low=Math.floor(Math.max(0,a-d));this.high=Math.ceil(Math.min(this.length,a+d))},left:function(b){var a=this.high-this.low;var c=Math.floor(a/b);if(this.low-c<0){this.low=0;this.high=this.low+a}else{this.low-=c;this.high-=c}},right:function(b){var a=this.high-this.low;var c=Math.floor(a/b);if(this.high+c>this.length){this.high=this.length;this.low=this.high-a}else{this.low+=c;this.high+=c}}});var Track=function(a,b){this.name=a;this.parent_element=b;this.make_container()};$.extend (Track.prototype,{make_container:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div class='track'></div>").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)}});var TiledTrack=function(){this.tile_cache=new Cache(CACHED_TILES)};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,c=this.view.high,e=c-i;var b=Math.pow(10,Math.ceil(Math.log(e/DENSITY)/Math.log(10)));b=Math.max(b,1);b=Math.min(b,100000);var l=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(l);var j=this.content_div.width(),d=this.content_div.height(),m=j/e;var g;var a=Math.floor(i/b/DENSITY);while((a*DENSITY*b)<c){var k=m+"_"+a;if(this.tile_cache[k]){g=this.tile_cache[k];var f=a*DENSITY*b;g.css({left:(f-this.view.low)*m});l.append(g)}else{g=this.draw_tile(b,a,l,m,d)}if(g){t his.tile_cache[k]=g}a+=1}}});var LabelTrack=function(a){Track.call(this,null,a);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'>"+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,b,a){Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.track_type="line";this.height_px=(a?a:100);this.container_div.addClass("line-track");this.dataset_id=b;this.cache=new Cache(CACHED_DATA)};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;$.getJSON(data_url,{stats:true,track_type:a.track_type,chrom:a.view.chrom,low:null,high:n ull,dataset_id:a.dataset_id},function(b){if(!b||b=="error"){a.content_div.addClass("error").text(DATA_ERROR)}else{if(b=="no data"){a.content_div.addClass("nodata").text(DATA_NONE)}else{a.content_div.css("height",a.height_px+"px");a.min_value=b.min;a.max_value=b.max;a.vertical_range=a.max_value-a.min_value;a.view.redraw()}}})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;$.getJSON(data_url,{track_type:this.track_type,chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id},function(g){c.cache[e]=g;$(document).trigger("redraw")})},draw_tile:function(d,a,m,p,n){if(!this.vertical_range){return}var h=a*DENSITY*d,b=DENSITY*d,c=$("<canvas class='tile'></canvas>"),l=d+"_"+a;if(!this.cache[l]){this.get_data(d,a);return}var g=this.cache[l];c.css({position:"absolute",top:0,left:(h-this.view.low)*p});c.get(0).width=Math.ceil(b*p);c.get(0).height=this.height_px;var o=c.get(0).getContext("2d");var e=false;o.beginPath();for(var f=0;f<g.length-1;f++){var k=g[f][0]-h;var j=g[f][1];if(isNaN(j)){e=false}else{k=k*p;j=(j-this.min_value)/this.vertical_range*this.height_px;if(e){o.lineTo(k,j)}else{o.moveTo(k,j);e=true}}}o.stroke();m.append(c);return c}});var FeatureTrack=function(c,b,a){Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.track_type="feature";this.height_px=(a?a:100);this.container_div.addClass("feature-track");this.dataset_id=b;this.zo_slots={};this.show_labels_scale=0.001;this.showing_labels=false;this.vertical_gap=10};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;$.getJSON(data_url,{track_type:a.track_type,low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom},function(b){if(b=="error"){a.content_div.addClass("error").text(DATA_ERROR)}else{if(b.length===0||b=="no data"){a.content_div.addClass("nodata").text(DATA_NONE)}else{a.content_div.css("height",a.height_px+"px");a.values=b;a.calc_slots();a.slots=a.zo_slots;a.draw()}}})},calc_slots:func tion(o){var c=[],b=this.container_div.width()/(this.view.high-this.view.low),g=this.show_labels_scale,a=this.view.max_high,e=this.view.max_low;if(o){this.zi_slots={}}var m=$("<canvas></canvas>").get(0).getContext("2d");for(var f=0,h=this.values.length;f<h;f++){var k,l,n=this.values[f];if(o){k=Math.floor(Math.max(e,(n.start-e)*g));k-=m.measureText(n.name).width;l=Math.ceil(Math.min(a,(n.end-e)*g))}else{k=Math.floor(Math.max(e,(n.start-e)*b));l=Math.ceil(Math.min(a,(n.end-e)*b))}var d=0;while(true){if(c[d]===undefined||c[d]<k){c[d]=l;if(o){this.zi_slots[n.name]=d}else{this.zo_slots[n.name]=d}break}d++}}this.height_px=c.length*this.vertical_gap+15;this.content_div.css("height",this.height_px+"px")},draw_tile:function(u,z,l,p,n){if(!this.values){return null}if(p>this.show_labels_scale&&!this.showing_labels){this.showing_labels=true;if(!this.zi_slots){this.calc_slots(true)}this.slots=this.zi_slots}else{if(p<=this.show_labels_scale&&this.showing_labels){this.showing_labels=false;t his.slots=this.zo_slots}}var A=z*DENSITY*u,c=(z+1)*DENSITY*u,b=DENSITY*u;var s=Math.ceil(b*p),r=this.height_px,q=$("<canvas class='tile'></canvas>");q.css({position:"absolute",top:0,left:(A-this.view.low)*p});q.get(0).width=s;q.get(0).height=r;var t=q.get(0).getContext("2d");t.fillStyle="#000";t.font="10px monospace";t.textAlign="right";var w=0;for(var x=0,y=this.values.length;x<y;x++){var h=this.values[x];if(h.start<=c&&h.end>=A){var f=Math.floor(Math.max(0,(h.start-A)*p)),g=Math.ceil(Math.min(s,(h.end-A)*p)),e=this.slots[h.name]*this.vertical_gap;t.fillRect(f,e+5,g-f,1);if(this.showing_labels&&t.fillText){t.fillText(h.name,f,e+8)}var d,E;if(h.exon_start&&h.exon_end){d=Math.floor(Math.max(0,(h.exon_start-A)*p));E=Math.ceil(Math.min(s,(h.exon_end-A)*p));t.fillRect(d,e+4,E-d,3)}if(h.blocks){for(var v=0,C=h.blocks.length;v<C;v++){var o=h.blocks[v],m=Math.floor(Math.max(0,(o[0]-A)*p)),B=Math.ceil(Math.min(s,(o[1]-A)*p));var a=3,D=4;if(d&&m>=d&&B<=E){a=5;D=3}t.fillRect(m,e+D,B-m ,a)}}w++}}l.append(q);return q}}); \ No newline at end of file diff -r ebc1c19fc2d5 -r e11e89fd2c82 static/scripts/trackster.js --- a/static/scripts/trackster.js Mon Oct 12 16:14:54 2009 -0400 +++ b/static/scripts/trackster.js Mon Oct 12 19:00:39 2009 -0400 @@ -4,7 +4,9 @@ var DENSITY = 1000, DATA_ERROR = "There was an error in indexing this dataset.", - DATA_NONE = "No data for this chrom/contig."; + DATA_NONE = "No data for this chrom/contig.", + CACHED_TILES = 200, + CACHED_DATA = 20; var View = function( chrom, max_length ) { this.chrom = chrom; @@ -29,7 +31,7 @@ }).show(); $("#low").text( this.low ); $("#high").text( this.high ); - for ( var i in this.tracks ) { + for ( var i = 0, len = this.tracks.length; i < len; i++ ) { this.tracks[i].draw(); } $("#bottom-spacer").remove(); @@ -40,18 +42,19 @@ this.high = Math.min( this.length, Math.ceil( new_high ) ); }, zoom_in: function ( factor, point ) { - if (this.max_high == 0) return; + if (this.max_high === 0) { + return; + } var range = this.high - this.low; var diff = range / factor / 2; + var center; - if (point == undefined) { - var center = ( this.low + this.high ) / 2; + if (point === undefined) { + center = ( this.low + this.high ) / 2; } else { - // console.log(100*point/$(document).width()); - var center = this.low + range * point / $(document).width(); + center = this.low + range * point / $(document).width(); } - // console.log(center); this.low = Math.floor( center - diff ); this.high = Math.ceil( center + diff ); if (this.low < this.max_low) { @@ -67,7 +70,9 @@ } }, zoom_out: function ( factor ) { - if (this.max_high == 0) return; + if (this.max_high === 0) { + return; + } var center = ( this.low + this.high ) / 2; var range = this.high - this.low; var diff = range * factor / 2; @@ -105,7 +110,7 @@ }; $.extend( Track.prototype, { make_container: function () { - this.header_div = $("<div class='track-header'>").text( this.name );; + this.header_div = $("<div class='track-header'>").text( this.name ); this.content_div = $("<div class='track-content'>"); this.container_div = $("<div class='track'></div>").append( this.header_div ).append( this.content_div ); this.parent_element.append( this.container_div ); @@ -113,9 +118,7 @@ }); var TiledTrack = function() { - this.last_resolution = null; - this.last_w_scale = null; - this.tile_cache = {}; + this.tile_cache = new Cache(CACHED_TILES); }; $.extend( TiledTrack.prototype, Track.prototype, { draw: function() { @@ -133,23 +136,17 @@ var w = this.content_div.width(), h = this.content_div.height(), - w_scale = w / range, - old_tiles = {}, - new_tiles = {}; - - // If resolution and scale are unchanged, try to reuse tiles - if ( this.last_resolution == resolution && this.last_w_scale == w_scale ) { - old_tiles = this.tile_cache; - } + w_scale = w / range; var tile_element; // Index of first tile that overlaps visible region var tile_index = Math.floor( low / resolution / DENSITY ); - while ( ( tile_index * 1000 * resolution ) < high ) { + while ( ( tile_index * DENSITY * resolution ) < high ) { // Check in cache - if ( tile_index in old_tiles ) { - // console.log( "tile from cache" ); - tile_element = old_tiles[tile_index]; + var key = w_scale + "_" + tile_index; + if ( this.tile_cache[key] ) { + // console.log("cached tile"); + tile_element = this.tile_cache[key]; var tile_low = tile_index * DENSITY * resolution; tile_element.css( { left: ( tile_low - this.view.low ) * w_scale @@ -160,14 +157,10 @@ tile_element = this.draw_tile( resolution, tile_index, parent_element, w_scale, h ); } if ( tile_element ) { - // console.log( typeof(tile_element) ); - new_tiles[tile_index] = tile_element; + this.tile_cache[key] = tile_element; } tile_index += 1; } - this.last_resolution = resolution; - this.last_w_scale = w_scale; - this.tile_cache = new_tiles; } }); @@ -199,70 +192,67 @@ var LineTrack = function ( name, dataset_id, height ) { Track.call( this, name, $("#viewport") ); + TiledTrack.call( this ); this.track_type = "line"; this.height_px = (height ? height : 100); this.container_div.addClass( "line-track" ); - this.content_div.css( "height", this.height_px + "px" ); this.dataset_id = dataset_id; - this.cache = new Cache(50); + this.cache = new Cache(CACHED_DATA); // We need to cache some data because of + // asynchronous calls }; $.extend( LineTrack.prototype, TiledTrack.prototype, { init: function() { var track = this; - $.getJSON( data_url, { stats: true, track_type: track.track_type, chrom: track.view.chrom, - low: null, high: null, dataset_id: track.dataset_id }, function ( data ) { + $.getJSON( data_url, { stats: true, track_type: track.track_type, + chrom: track.view.chrom, low: null, high: null, + dataset_id: track.dataset_id }, function ( data ) { if (!data || data == "error") { track.content_div.addClass("error").text(DATA_ERROR); } else if (data == "no data") { - // console.log(track.content_div); track.content_div.addClass("nodata").text(DATA_NONE); } else { - track.min_value = data['min']; - track.max_value = data['max']; + track.content_div.css( "height", track.height_px + "px" ); + track.min_value = data.min; + track.max_value = data.max; track.vertical_range = track.max_value - track.min_value; track.view.redraw(); } }); }, get_data: function( resolution, position ) { - var key = resolution + '-' + position, - cache = this.cache; - - if ( !cache[key] ) { - var low = position * DENSITY * resolution, - high = ( position + 1 ) * DENSITY * resolution; - - $.getJSON( data_url, { track_type: this.track_type, chrom: this.view.chrom, low: low, high: high, dataset_id: this.dataset_id }, function ( data ) { - cache[key] = data; - $(document).trigger( "redraw" ); - }); - } - return cache[key]; + var track = this, + low = position * DENSITY * resolution, + high = ( position + 1 ) * DENSITY * resolution, + key = resolution + "_" + position; + + $.getJSON( data_url, { track_type: this.track_type, chrom: this.view.chrom, low: low, high: high, dataset_id: this.dataset_id }, function ( data ) { + track.cache[key] = data; + $(document).trigger( "redraw" ); + }); }, draw_tile: function( resolution, tile_index, parent_element, w_scale, h_scale ) { - if (!this.vertical_range) // We don't have the necessary information yet + if (!this.vertical_range) { // We don't have the necessary information yet return; - + } + var tile_low = tile_index * DENSITY * resolution, - tile_high = ( tile_index + 1 ) * DENSITY * resolution, - tile_length = DENSITY * resolution; - var data = this.get_data( resolution, tile_index ); - if ( !data ) { - in_path = false; - return null; + tile_length = DENSITY * resolution, + canvas = $("<canvas class='tile'></canvas>"), + key = resolution + "_" + tile_index; + + if (!this.cache[key]) { + this.get_data( resolution, tile_index ); + return; } - var element = $("<canvas class='tile'></canvas>"); - - element.css( { + + var data = this.cache[key]; + canvas.css( { position: "absolute", top: 0, - left: ( tile_low - this.view.low ) * w_scale, + left: ( tile_low - this.view.low ) * w_scale }); - parent_element.append( element ); - // Chunk is still loading, do nothing - - var canvas = element; + canvas.get(0).width = Math.ceil( tile_length * w_scale ); canvas.get(0).height = this.height_px; var ctx = canvas.get(0).getContext("2d"); @@ -277,8 +267,7 @@ } else { // Translate x = x * w_scale; - y_above_min = y - this.min_value; - y = y_above_min / this.vertical_range * this.height_px; + y = (y - this.min_value) / this.vertical_range * this.height_px; if ( in_path ) { ctx.lineTo( x, y ); } else { @@ -288,44 +277,69 @@ } } ctx.stroke(); - return element; + parent_element.append( canvas ); + return canvas; } }); var FeatureTrack = function ( name, dataset_id, height ) { Track.call( this, name, $("#viewport") ); + TiledTrack.call( this ); + this.track_type = "feature"; this.height_px = (height ? height : 100); this.container_div.addClass( "feature-track" ); - this.content_div.css( "height", this.height_px + "px" ); this.dataset_id = dataset_id; this.zo_slots = {}; - this.show_labels_scale = 0.01; + this.show_labels_scale = 0.001; this.showing_labels = false; this.vertical_gap = 10; }; $.extend( FeatureTrack.prototype, TiledTrack.prototype, { + init: function() { + var track = this; + $.getJSON( data_url, { track_type: track.track_type, low: track.view.max_low, + high: track.view.max_high, dataset_id: track.dataset_id, + chrom: track.view.chrom }, function ( data ) { + if (data == "error") { + track.content_div.addClass("error").text(DATA_ERROR); + } else if (data.length === 0 || data == "no data") { + track.content_div.addClass("nodata").text(DATA_NONE); + } else { + track.content_div.css( "height", track.height_px + "px" ); + track.values = data; + track.calc_slots(); + track.slots = track.zo_slots; + track.draw(); + } + }); + }, calc_slots: function( include_labels ) { // console.log("num vals: " + this.values.length); - var end_ary = []; - var scale = this.container_div.width() / (this.view.high - this.view.low); + var end_ary = [], + scale = this.container_div.width() / (this.view.high - this.view.low), + labels_scale = this.show_labels_scale, + max_high = this.view.max_high, + max_low = this.view.max_low; // console.log(scale, this.view.high, this.view.low); - if (include_labels) this.zi_slots = {}; + if (include_labels) { + this.zi_slots = {}; + } var dummy_canvas = $("<canvas></canvas>").get(0).getContext("2d"); - for (var i in this.values) { - - var feature = this.values[i]; - var f_start = Math.floor( Math.max(this.view.max_low, (feature.start - this.view.max_low) * scale) ); + for (var i = 0, len = this.values.length; i < len; i++) { + var f_start, f_end, feature = this.values[i]; if (include_labels) { + f_start = Math.floor( Math.max(max_low, (feature.start - max_low) * labels_scale) ); f_start -= dummy_canvas.measureText(feature.name).width; - f_start -= 10; // Spacing between text and line + f_end = Math.ceil( Math.min(max_high, (feature.end - max_low) * labels_scale) ); + } else { + f_start = Math.floor( Math.max(max_low, (feature.start - max_low) * scale) ); + f_end = Math.ceil( Math.min(max_high, (feature.end - max_low) * scale) ); } - - var f_end = Math.ceil( Math.min(this.view.max_high, (feature.end - this.view.max_low) * scale) ); // if (include_labels) { console.log(f_start, f_end); } var j = 0; while (true) { - if (end_ary[j] == undefined || end_ary[j] < f_start) { + if (end_ary[j] === undefined || end_ary[j] < f_start) { end_ary[j] = f_end; if (include_labels) { this.zi_slots[feature.name] = j; @@ -340,33 +354,16 @@ this.height_px = end_ary.length * this.vertical_gap + 15; this.content_div.css( "height", this.height_px + "px" ); }, - - init: function() { - var track = this; - $.getJSON( data_url, { track_type: track.track_type, low: track.view.max_low, high: track.view.max_high, - dataset_id: track.dataset_id, chrom: track.view.chrom }, function ( data ) { - if (data == "error") { - track.content_div.addClass("error").text(DATA_ERROR); - } else if (data.length == 0 || data == "no data") { - // console.log(track.content_div); - track.content_div.addClass("nodata").text(DATA_NONE); - } else { - track.values = data; - track.calc_slots(); - track.slots = track.zo_slots; - // console.log(track.zo_slots); - track.draw(); - } - }); - }, - draw_tile: function( resolution, tile_index, parent_element, w_scale, h_scale ) { - if (!this.values) // Still loading + if (!this.values) { // Still loading return null; - + } + // Once we zoom in enough, show name labels if (w_scale > this.show_labels_scale && !this.showing_labels) { this.showing_labels = true; - if (!this.zi_slots) this.calc_slots(true); // Once we zoom in enough, show name labels + if (!this.zi_slots) { + this.calc_slots(true); + } this.slots = this.zi_slots; } else if (w_scale <= this.show_labels_scale && this.showing_labels) { this.showing_labels = false; @@ -378,63 +375,64 @@ tile_high = ( tile_index + 1 ) * DENSITY * resolution, tile_length = DENSITY * resolution; // console.log(tile_low, tile_high, tile_length, w_scale); - var view = this.view, - range = view.high - view.low, - width = Math.ceil( tile_length * w_scale ), - slots = new Array(), + var width = Math.ceil( tile_length * w_scale ), height = this.height_px, new_canvas = $("<canvas class='tile'></canvas>"); new_canvas.css({ position: "absolute", top: 0, - left: ( tile_low - this.view.low ) * w_scale, + left: ( tile_low - this.view.low ) * w_scale }); new_canvas.get(0).width = width; new_canvas.get(0).height = height; // console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale); var ctx = new_canvas.get(0).getContext("2d"); + ctx.fillStyle = "#000"; + ctx.font = "10px monospace"; + ctx.textAlign = "right"; var j = 0; - for (var i in this.values) { - feature = this.values[i]; + for (var i = 0, len = this.values.length; i < len; i++) { + var feature = this.values[i]; if (feature.start <= tile_high && feature.end >= tile_low) { - 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) ); + 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 = this.slots[feature.name] * this.vertical_gap; + // console.log(feature.start, feature.end, f_start, f_end, j); - ctx.fillStyle = "#000"; - ctx.fillRect(f_start, this.slots[feature.name] * this.vertical_gap + 5, f_end - f_start, 1); + ctx.fillRect(f_start, y_center + 5, f_end - f_start, 1); if (this.showing_labels && ctx.fillText) { - ctx.font = "10px monospace"; - ctx.textAlign = "right"; - ctx.fillText(feature.name, f_start, this.slots[feature.name] * 10 + 8); + ctx.fillText(feature.name, f_start, y_center + 8); } + var exon_start, exon_end; if (feature.exon_start && feature.exon_end) { - var exon_start = Math.floor( Math.max(0, (feature.exon_start - tile_low) * w_scale) ); - var exon_end = Math.ceil( Math.min(width, (feature.exon_end - tile_low) * w_scale) ); - // ctx.fillRect(exon_start, j * 10 + 3, exon_end - exon_start, 5); - // ctx.fillRect(exon_start, this.slots[feature.name] * 10 + 3, exon_end - exon_start, 3); + exon_start = Math.floor( Math.max(0, (feature.exon_start - tile_low) * w_scale) ); + exon_end = Math.ceil( Math.min(width, (feature.exon_end - tile_low) * w_scale) ); + ctx.fillRect(exon_start, y_center + 4, exon_end - exon_start, 3); } - for (var i in feature.blocks) { - block = feature.blocks[i]; - 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) ); - var thickness = 3, y_start = 4; - if (exon_start && block_start >= exon_start && block_end <= exon_end) { - thickness = 5, y_start = 3; - } - ctx.fillRect(exon_start, this.slots[feature.name] * this.vertical_gap + y_start, block_end - block_start, thickness); - // console.log(block_start, block_end); + if (feature.blocks) { + 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) ); + var thickness = 3, y_start = 4; + if (exon_start && block_start >= exon_start && block_end <= exon_end) { + thickness = 5; + y_start = 3; + } + ctx.fillRect(block_start, y_center + y_start, block_end - block_start, thickness); + // console.log(block_start, block_end); + } } - j++; } } parent_element.append( new_canvas ); return new_canvas; - }, + } }); diff -r ebc1c19fc2d5 -r e11e89fd2c82 static/trackster.css --- a/static/trackster.css Mon Oct 12 16:14:54 2009 -0400 +++ b/static/trackster.css Mon Oct 12 19:00:39 2009 -0400 @@ -85,13 +85,15 @@ .track-content.error { text-align: center; - padding-top: 30px; + padding-top: 10px; background-color: #ECB4AF; + height: 30px; } .track-content.nodata { text-align: center; - padding-top: 30px; + padding-top: 10px; background-color: #ddd; + height: 30px; } .loading { @@ -102,15 +104,3 @@ border-left: solid gray 1px; padding-left: 2px; } - -.feature-track .feature { - -} - -.feature-track .feature .forward { - -} - -.feature-track .feature .reverse { - -} \ No newline at end of file
participants (1)
-
Greg Von Kuster