diff --git a/static/scripts/packed/trackster.js b/static/scripts/packed/trackster.js index 44a65111d80..84694d626ff 100644 --- a/static/scripts/packed/trackster.js +++ b/static/scripts/packed/trackster.js @@ -1 +1 @@ -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')},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.lowthis.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=$("
").text(this.name);this.content_div=$("
");this.container_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=$("
");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)
");while(a"+a+"
").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: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.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=$(""),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").get(0).getContext("2d");for(var f=0,h=this.values.length;fthis.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;this.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=$("");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=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=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 +var DENSITY=1000,DATA_ERROR="There was an error in indexing this dataset.",DATA_NONE="No data for this chrom/contig.",CACHED_TILES=50,CACHED_DATA=20;function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var View=function(b,a){this.chrom=b;this.tracks=[];this.max_low=0;this.max_high=a;this.center=(this.max_high-this.max_low)/2;this.span=this.max_high-this.max_low;this.zoom_factor=2;this.zoom_level=0};$.extend(View.prototype,{add_track:function(a){a.view=this;this.tracks.push(a);if(a.init){a.init()}},redraw:function(){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);$("#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").text(commatize(this.low));$("#high").text(commatize(this.high));for(var c=0,a=this.tracks.length;c')},zoom_in:function(a){if(this.max_high===0||this.high-this.low<5){return}if(a){this.center=a/$(document).width()*(this.high-this.low)+this.low}this.zoom_level+=1},zoom_out:function(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1}});var Track=function(a,b){this.name=a;this.parent_element=b;this.make_container()};$.extend(Track.prototype,{make_container:function(){this.header_div=$("
").text(this.name);this.content_div=$("
");this.container_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=$("
");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)
");while(a"+commatize(a)+"
").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: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.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,o){if(!this.vertical_range){return}var h=a*DENSITY*d,b=DENSITY*d,c=$(""),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)*o});c.get(0).width=Math.ceil(b*o);c.get(0).height=this.height_px;var n=c.get(0).getContext("2d");var e=false;n.beginPath();for(var f=0;f").get(0).getContext("2d");for(var f=0,h=this.values.length;fthis.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(n<=this.show_labels_scale&&this.showing_labels){this.showing_labels=false;this.slots=this.zo_slots}}var z=y*DENSITY*t,b=(y+1)*DENSITY*t,o=DENSITY*t;var r=Math.ceil(o*n),q=this.height_px,p=$("");p.css({position:"absolute",top:0,left:(z-this.view.low)*n});p.get(0).width=r;p.get(0).height=q;var s=p.get(0).getContext("2d");s.fillStyle="#000";s.font="10px monospace";s.textAlign="right";var v=0;for(var w=0,x=this.values.length;w=z){var e=Math.floor(Math.max(0,(f.start-z)*n)),h=Math.ceil(Math.min(r,(f.end-z)*n)),d=this.slots[f.name]*this.vertical_gap;s.fillRect(e,d+5,h-e,1);if(this.showing_labels&&s.fillText){s.fillText(f.name,e,d+8)}var c,D;if(f.exon_start&&f.exon_end){c=Math.floor(Math.max(0,(f.exon_start-z)*n));D=Math.ceil(Math.min(r,(f.exon_end-z)*n));s.fillRect(c,d+4,D-c,3)}if(f.blocks){for(var u=0,B=f.blocks.length;u=c&&A<=D){a=5;C=3}s.fillRect(l,d+C,A-l,a)}}v++}}g.append(p);return p}}); \ No newline at end of file diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index 71a78d32b82..b138109279a 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -5,17 +5,26 @@ 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_TILES = 50, CACHED_DATA = 20; -var View = function( chrom, max_length ) { +function commatize( number ) { + number += ''; // Convert to string + var rgx = /(\d+)(\d{3})/; + while (rgx.test(number)) { + number = number.replace(rgx, '$1' + ',' + '$2'); + } + return number; +} +var View = function( chrom, max_high ) { this.chrom = chrom; this.tracks = []; this.max_low = 0; - this.max_high = max_length; - this.low = this.max_low; - this.high = this.max_high; - this.length = this.max_high - this.max_low; + this.max_high = max_high; + this.center = (this.max_high - this.max_low) / 2; + this.span = this.max_high - this.max_low; + this.zoom_factor = 2; + this.zoom_level = 0; }; $.extend( View.prototype, { add_track: function ( track ) { @@ -24,82 +33,55 @@ $.extend( View.prototype, { if (track.init) { track.init(); } }, redraw: function () { + var span = this.span / Math.pow(this.zoom_factor, this.zoom_level), + low = this.center - (span / 2), + high = low + span; + + if (low < 0) { + low = 0; + high = low + span; + + } else if (high > this.max_high) { + high = this.max_high; + low = high - span; + } + this.low = Math.floor(low); + this.high = Math.ceil(high); + this.center = Math.round( this.low + (this.high - this.low) / 2 ); + // Overview $("#overview-box").css( { - left: ( this.low / this.length ) * $("#overview-viewport").width(), - width: Math.max( 4, ( ( this.high - this.low ) / this.length ) * $("#overview-viewport").width() ) + left: ( this.low / this.span ) * $("#overview-viewport").width(), + // Minimum width for usability + width: Math.max( 12, ( ( this.high - this.low ) / this.span ) * $("#overview-viewport").width() ) }).show(); - $("#low").text( this.low ); - $("#high").text( this.high ); + $("#low").text( commatize(this.low) ); + $("#high").text( commatize(this.high) ); for ( var i = 0, len = this.tracks.length; i < len; i++ ) { this.tracks[i].draw(); } $("#bottom-spacer").remove(); $("#viewport").append('
'); }, - move: function ( new_low, new_high ) { - this.low = Math.max( this.max_low, Math.floor( new_low ) ); - this.high = Math.min( this.length, Math.ceil( new_high ) ); - }, - zoom_in: function ( factor, point ) { - if (this.max_high === 0) { + zoom_in: function ( point ) { + if (this.max_high === 0 || this.high - this.low < 5) { return; } - var range = this.high - this.low; - var diff = range / factor / 2; - var center; - - if (point === undefined) { - center = ( this.low + this.high ) / 2; - } else { - center = this.low + range * point / $(document).width(); - } - this.low = Math.floor( center - diff ); - this.high = Math.ceil( center + diff ); - if (this.low < this.max_low) { - this.low = this.max_low; - this.high = range / factor; - } else if (this.high > this.max_high) { - this.high = this.max_high; - this.low = this.max_high - range / factor; - // console.log(this.high, this.low); - } - if (this.high - this.low < 1 ) { - this.high = this.low + 1; + if ( point ) { + this.center = point / $(document).width() * (this.high - this.low) + this.low; } + this.zoom_level += 1; }, - zoom_out: function ( factor ) { + zoom_out: function () { if (this.max_high === 0) { return; } - var center = ( this.low + this.high ) / 2; - var range = this.high - this.low; - var diff = range * factor / 2; - this.low = Math.floor( Math.max( 0, center - diff ) ); - this.high = Math.ceil( Math.min( this.length, center + diff ) ); - }, - left: function( factor ) { - var range = this.high - this.low; - var diff = Math.floor( range / factor ); - if ( this.low - diff < 0 ) { - this.low = 0; - this.high = this.low + range; - } else { - this.low -= diff; - this.high -= diff; - } - }, - right: function ( factor ) { - var range = this.high - this.low; - var diff = Math.floor( range / factor ); - if ( this.high + diff > this.length ) { - this.high = this.length; - this.low = this.high - range; - } else { - this.low += diff; - this.high += diff; + if (this.zoom_level <= 0) { + this.zoom_level = 0; + return; } + this.zoom_level -= 1; } }); @@ -143,7 +125,7 @@ $.extend( TiledTrack.prototype, Track.prototype, { var tile_index = Math.floor( low / resolution / DENSITY ); while ( ( tile_index * DENSITY * resolution ) < high ) { // Check in cache - var key = w_scale + "_" + tile_index; + var key = this.view.zoom_level + "_" + tile_index; if ( this.tile_cache[key] ) { // console.log("cached tile"); tile_element = this.tile_cache[key]; @@ -178,7 +160,7 @@ $.extend( LabelTrack.prototype, Track.prototype, { new_div = $("
"); while ( position < view.high ) { var screenPosition = ( position - view.low ) / range * width; - new_div.append( $("
" + position + "
").css( { + new_div.append( $("
" + commatize( position ) + "
").css( { position: "absolute", // Reduce by one to account for border left: screenPosition - 1 @@ -231,7 +213,7 @@ $.extend( LineTrack.prototype, TiledTrack.prototype, { $(document).trigger( "redraw" ); }); }, - draw_tile: function( resolution, tile_index, parent_element, w_scale, h_scale ) { + draw_tile: function( resolution, tile_index, parent_element, w_scale ) { if (!this.vertical_range) { // We don't have the necessary information yet return; } @@ -354,11 +336,11 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, { this.height_px = end_ary.length * this.vertical_gap + 15; this.content_div.css( "height", this.height_px + "px" ); }, - draw_tile: function( resolution, tile_index, parent_element, w_scale, h_scale ) { + draw_tile: function( resolution, tile_index, parent_element, w_scale ) { if (!this.values) { // Still loading return null; } - // Once we zoom in enough, show name labels + // 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) { @@ -373,9 +355,9 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, { var tile_low = tile_index * DENSITY * resolution, tile_high = ( tile_index + 1 ) * DENSITY * resolution, - tile_length = DENSITY * resolution; + tile_span = DENSITY * resolution; // console.log(tile_low, tile_high, tile_length, w_scale); - var width = Math.ceil( tile_length * w_scale ), + var width = Math.ceil( tile_span * w_scale ), height = this.height_px, new_canvas = $(""); diff --git a/static/trackster.css b/static/trackster.css index 0c910fad6f3..5ba5fd97149 100644 --- a/static/trackster.css +++ b/static/trackster.css @@ -2,16 +2,17 @@ body { margin: 0 0; padding: 0; font-family: verdana; - font-size: 75%; + font-size: 12px; overflow-y: hidden; } #content { width: 100%; + height: 100%; } -#center { - overflow: auto; +#center { + overflow: auto; } #nav { @@ -36,26 +37,35 @@ body { -webkit-border-radius: 1em; -moz-border-radius: 1em; } +#low { + margin-left: 0.1em; + float: left; +} +#high { + margin-right: 0.1em; + float: right; +} #overview { width: 100%; - padding: 10px 0 0 0; margin: 0px; background: #333; color: white; - font-weight: bold; } #overview-viewport { - height: 10px; - border-top: solid #666 1px; - border-bottom: solid #666 1px; + height: 20px; +/* border-top: solid #666 1px;*/ +/* border-bottom: solid #aaa 1px;*/ background: #888; } #overview-box { position: absolute; - height: 10px; - background: #ddd; + margin-top: 1px; + height: 16px; + background: #ddd url(/static/images/draggable_horizontal.png) center center no-repeat; + border-style: outset; + border-width: 1px; } #viewport { @@ -100,6 +110,12 @@ body { min-height: 100px; } +.label-track { + font-weight: bold; + font-size: 10px; + background-color: #333; + color: white; +} .label-track .label { border-left: solid gray 1px; padding-left: 2px; diff --git a/templates/tracks/browser.mako b/templates/tracks/browser.mako index 1953b4b7b4d..113c342c80d 100644 --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -16,7 +16,7 @@ ${h.js( "jquery", "jquery.event.drag", "jquery.mousewheel", "lrucache", "trackst $(function() { - view.add_track( new LabelTrack( $("#overview" ) ) ); + view.add_track( new LabelTrack( $("#viewport" ) ) ); view.add_track( new LabelTrack( $("#nav-labeltrack" ) ) ); %for track in tracks: @@ -27,41 +27,35 @@ ${h.js( "jquery", "jquery.event.drag", "jquery.mousewheel", "lrucache", "trackst view.redraw(); }); - $(document).bind("mousewheel", function(e, delta) { + $(document).bind("mousewheel", function( e, delta ) { if (delta > 0) { - view.zoom_in(2, e.pageX); + view.zoom_in(e.pageX); view.redraw(); } else { - view.zoom_out(2); + view.zoom_out(); view.redraw(); } }); - $(document).bind("dblclick", function(e) { - view.zoom_in(2, e.pageX); + $(document).bind("dblclick", function( e ) { + view.zoom_in(e.pageX); view.redraw(); }); - $("#overview-box").bind("dragstart", function(e) { + // To let the overview box be draggable + $("#overview-box").bind("dragstart", function( e ) { this.current_x = e.offsetX; - }).bind("drag", function(e) { + }).bind("drag", function( e ) { var delta = e.offsetX - this.current_x; this.current_x = e.offsetX; - var delta_chrom = Math.round(delta / $(document).width() * (view.max_high - view.max_low)); - var view_range = view.high - view.low; - - var new_low = view.low += delta_chrom; - var new_high = view.high += delta_chrom; - if (new_low < view.max_low) { - new_low = 0; - new_high = view_range; - } else if (new_high > view.max_high) { - new_high = view.max_high; - new_low = view.max_high - view_range; + var delta_chrom = Math.round(delta / $(document).width() * view.span); + view.center += delta_chrom; + if (view.center < 0) { + view.center = 0; + } else if (view.center > view.max_high) { + view.center = view.max_high; } - view.low = new_low; - view.high = new_high; view.redraw(); }); @@ -70,11 +64,12 @@ ${h.js( "jquery", "jquery.event.drag", "jquery.mousewheel", "lrucache", "trackst view.redraw(); }); - $("#viewport").bind( "dragstart", function ( e ) { + $("#viewport").bind( "dragstart", function( e ) { this.original_low = view.low; this.current_height = e.clientY; + this.current_x = e.offsetX; }).bind( "drag", function( e ) { - var move_amount = ( e.offsetX - this.offsetLeft ) / this.offsetWidth; + var delta = e.offsetX - this.current_x; var new_scroll = $(this).scrollTop() - (e.clientY - this.current_height); if ( new_scroll < $(this).get(0).scrollHeight - $(this).height() - 200) { @@ -82,19 +77,10 @@ ${h.js( "jquery", "jquery.event.drag", "jquery.mousewheel", "lrucache", "trackst } this.current_height = e.clientY; - var range = view.high - view.low; - var move_bases = Math.round( range * move_amount ); - var new_low = this.original_low - move_bases; - if ( new_low < 0 ) { - new_low = 0; - } - var new_high = new_low + range; - if ( new_high > view.length ) { - new_high = view.length; - new_low = new_high - range; - } - view.low = new_low; - view.high = new_high; + this.current_x = e.offsetX; + + var delta_chrom = Math.round(delta / $(document).width() * (view.max_high - view.max_low)); + view.center += delta_chrom; view.redraw(); }); (function () { @@ -122,28 +108,25 @@ ${h.js( "jquery", "jquery.event.drag", "jquery.mousewheel", "lrucache", "trackst
+
+
+ -