trackster: caching works, faster loops, displays exon blocks, passes JSLint

This commit is contained in:
Kanwei Li
2009-10-12 19:00:39 -04:00
parent 56f673adc3
commit 74e6735d2c
4 changed files with 140 additions and 152 deletions
@@ -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_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,formatItem: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{}});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")};
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};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);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,formatItem: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")};
File diff suppressed because one or more lines are too long
+134 -136
View File
@@ -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 @@ $.extend( View.prototype, {
}).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 @@ $.extend( View.prototype, {
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 @@ $.extend( View.prototype, {
}
},
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 @@ var Track = function ( name, parent_element ) {
};
$.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 @@ $.extend( Track.prototype, {
});
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 @@ $.extend( TiledTrack.prototype, Track.prototype, {
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 @@ $.extend( TiledTrack.prototype, Track.prototype, {
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 @@ $.extend( LabelTrack.prototype, Track.prototype, {
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;
}
var element = $("<canvas class='tile'></canvas>");
element.css( {
var tile_low = tile_index * DENSITY * resolution,
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 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 @@ $.extend( LineTrack.prototype, TiledTrack.prototype, {
} 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 @@ $.extend( LineTrack.prototype, TiledTrack.prototype, {
}
}
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 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
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 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
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;
},
}
});
+4 -14
View File
@@ -85,13 +85,15 @@ body {
.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 @@ body {
border-left: solid gray 1px;
padding-left: 2px;
}
.feature-track .feature {
}
.feature-track .feature .forward {
}
.feature-track .feature .reverse {
}