From 17a82e78d3db23b93ddb6807dbd70d63b12eea91 Mon Sep 17 00:00:00 2001 From: Kanwei Li Date: Thu, 29 Oct 2009 18:28:11 -0400 Subject: [PATCH] trackster: fixed browser crashing (were drawing huge canvas tiles because of resolution restriction), implemented simple LRU --- static/scripts/lrucache.js | 238 ----------------------------- static/scripts/packed/lrucache.js | 1 - static/scripts/packed/trackster.js | 2 +- static/scripts/trackster.js | 46 ++++-- templates/tracks/browser.mako | 2 +- 5 files changed, 39 insertions(+), 250 deletions(-) delete mode 100644 static/scripts/lrucache.js delete mode 100644 static/scripts/packed/lrucache.js diff --git a/static/scripts/lrucache.js b/static/scripts/lrucache.js deleted file mode 100644 index 00acb2b4717..00000000000 --- a/static/scripts/lrucache.js +++ /dev/null @@ -1,238 +0,0 @@ -/* -MIT LICENSE -Copyright (c) 2007 Monsur Hossain (http://www.monsur.com) - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. -*/ - -// **************************************************************************** -// CachePriority ENUM -// An easier way to refer to the priority of a cache item -var CachePriority = { - Low: 1, - Normal: 2, - High: 4 -} - -// **************************************************************************** -// Cache constructor -// Creates a new cache object -// INPUT: maxSize (optional) - indicates how many items the cache can hold. -// default is -1, which means no limit on the -// number of items. -function Cache(maxSize) { - this.items = {}; - this.count = 0; - if (maxSize == null) - maxSize = -1; - this.maxSize = maxSize; - this.fillFactor = .75; - this.purgeSize = Math.round(this.maxSize * this.fillFactor); - - this.stats = {} - this.stats.hits = 0; - this.stats.misses = 0; -} - -// **************************************************************************** -// Cache.getItem -// retrieves an item from the cache, returns null if the item doesn't exist -// or it is expired. -// INPUT: key - the key to load from the cache -Cache.prototype.getItem = function(key) { - - // retrieve the item from the cache - var item = this.items[key]; - - if (item != null) { - if (!this._isExpired(item)) { - // if the item is not expired - // update its last accessed date - item.lastAccessed = new Date().getTime(); - } else { - // if the item is expired, remove it from the cache - this._removeItem(key); - item = null; - } - } - - // return the item value (if it exists), or null - var returnVal = null; - if (item != null) { - returnVal = item.value; - this.stats.hits++; - } else { - this.stats.misses++; - } - return returnVal; -} - -// **************************************************************************** -// Cache.setItem -// sets an item in the cache -// parameters: key - the key to refer to the object -// value - the object to cache -// options - an optional parameter described below -// the last parameter accepts an object which controls various caching options: -// expirationAbsolute: the datetime when the item should expire -// expirationSliding: an integer representing the seconds since -// the last cache access after which the item -// should expire -// priority: How important it is to leave this item in the cache. -// You can use the values CachePriority.Low, .Normal, or -// .High, or you can just use an integer. Note that -// placing a priority on an item does not guarantee -// it will remain in cache. It can still be purged if -// an expiration is hit, or if the cache is full. -// callback: A function that gets called when the item is purged -// from cache. The key and value of the removed item -// are passed as parameters to the callback function. -Cache.prototype.setItem = function(key, value, options) { - - function CacheItem(k, v, o) { - if ((k == null) || (k == '')) - throw new Error("key cannot be null or empty"); - this.key = k; - this.value = v; - if (o == null) - o = {}; - if (o.expirationAbsolute != null) - o.expirationAbsolute = o.expirationAbsolute.getTime(); - if (o.priority == null) - o.priority = CachePriority.Normal; - this.options = o; - this.lastAccessed = new Date().getTime(); - } - - // add a new cache item to the cache - if (this.items[key] != null) - this._removeItem(key); - this._addItem(new CacheItem(key, value, options)); - - // if the cache is full, purge it - if ((this.maxSize > 0) && (this.count > this.maxSize)) { - this._purge(); - } -} - -// **************************************************************************** -// Cache.clear -// Remove all items from the cache -Cache.prototype.clear = function() { - - // loop through each item in the cache and remove it - for (var key in this.items) { - this._removeItem(key); - } -} - -// **************************************************************************** -// Cache._purge (PRIVATE FUNCTION) -// remove old elements from the cache -Cache.prototype._purge = function() { - - var tmparray = new Array(); - - // loop through the cache, expire items that should be expired - // otherwise, add the item to an array - for (var key in this.items) { - var item = this.items[key]; - if (this._isExpired(item)) { - this._removeItem(key); - } else { - tmparray.push(item); - } - } - - if (tmparray.length > this.purgeSize) { - - // sort this array based on cache priority and the last accessed date - tmparray = tmparray.sort(function(a, b) { - if (a.options.priority != b.options.priority) { - return b.options.priority - a.options.priority; - } else { - return b.lastAccessed - a.lastAccessed; - } - }); - - // remove items from the end of the array - while (tmparray.length > this.purgeSize) { - var ritem = tmparray.pop(); - this._removeItem(ritem.key); - } - } -} - -// **************************************************************************** -// Cache._addItem (PRIVATE FUNCTION) -// add an item to the cache -Cache.prototype._addItem = function(item) { - this.items[item.key] = item; - this.count++; -} - -// **************************************************************************** -// Cache._removeItem (PRIVATE FUNCTION) -// Remove an item from the cache, call the callback function (if necessary) -Cache.prototype._removeItem = function(key) { - var item = this.items[key]; - delete this.items[key]; - this.count--; - - // if there is a callback function, call it at the end of execution - if (item.options.callback != null) { - var callback = function() { - item.options.callback(item.key, item.value); - } - setTimeout(callback, 0); - } -} - -// **************************************************************************** -// Cache._isExpired (PRIVATE FUNCTION) -// Returns true if the item should be expired based on its expiration options -Cache.prototype._isExpired = function(item) { - var now = new Date().getTime(); - var expired = false; - if ((item.options.expirationAbsolute) && (item.options.expirationAbsolute < now)) { - // if the absolute expiration has passed, expire the item - expired = true; - } - if ((expired == false) && (item.options.expirationSliding)) { - // if the sliding expiration has passed, expire the item - var lastAccess = item.lastAccessed + (item.options.expirationSliding * 1000); - if (lastAccess < now) { - expired = true; - } - } - return expired; -} - -Cache.prototype.toHtmlString = function() { - var returnStr = this.count + " item(s) in cache
"; - return returnStr; -} diff --git a/static/scripts/packed/lrucache.js b/static/scripts/packed/lrucache.js deleted file mode 100644 index 0906947c6dd..00000000000 --- a/static/scripts/packed/lrucache.js +++ /dev/null @@ -1 +0,0 @@ -var CachePriority={Low:1,Normal:2,High:4};function Cache(a){this.items={};this.count=0;if(a==null){a=-1}this.maxSize=a;this.fillFactor=0.75;this.purgeSize=Math.round(this.maxSize*this.fillFactor);this.stats={};this.stats.hits=0;this.stats.misses=0}Cache.prototype.getItem=function(a){var c=this.items[a];if(c!=null){if(!this._isExpired(c)){c.lastAccessed=new Date().getTime()}else{this._removeItem(a);c=null}}var b=null;if(c!=null){b=c.value;this.stats.hits++}else{this.stats.misses++}return b};Cache.prototype.setItem=function(c,d,b){function a(f,e,g){if((f==null)||(f=="")){throw new Error("key cannot be null or empty")}this.key=f;this.value=e;if(g==null){g={}}if(g.expirationAbsolute!=null){g.expirationAbsolute=g.expirationAbsolute.getTime()}if(g.priority==null){g.priority=CachePriority.Normal}this.options=g;this.lastAccessed=new Date().getTime()}if(this.items[c]!=null){this._removeItem(c)}this._addItem(new a(c,d,b));if((this.maxSize>0)&&(this.count>this.maxSize)){this._purge()}};Cache.prototype.clear=function(){for(var a in this.items){this._removeItem(a)}};Cache.prototype._purge=function(){var d=new Array();for(var a in this.items){var b=this.items[a];if(this._isExpired(b)){this._removeItem(a)}else{d.push(b)}}if(d.length>this.purgeSize){d=d.sort(function(f,e){if(f.options.priority!=e.options.priority){return e.options.priority-f.options.priority}else{return e.lastAccessed-f.lastAccessed}});while(d.length>this.purgeSize){var c=d.pop();this._removeItem(c.key)}}};Cache.prototype._addItem=function(a){this.items[a.key]=a;this.count++};Cache.prototype._removeItem=function(a){var b=this.items[a];delete this.items[a];this.count--;if(b.options.callback!=null){var c=function(){b.options.callback(b.key,b.value)};setTimeout(c,0)}};Cache.prototype._isExpired=function(c){var a=new Date().getTime();var b=false;if((c.options.expirationAbsolute)&&(c.options.expirationAbsolute";return b}; \ No newline at end of file diff --git a/static/scripts/packed/trackster.js b/static/scripts/packed/trackster.js index 84694d626ff..c540b77964d 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=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 +var DENSITY=1000,DATA_ERROR="There was an error in indexing this dataset.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES=5,CACHED_DATA=20,CONTEXT=$("").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.obj_cache={};this.key_ary=[]};$.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}});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").val(commatize(this.low));$("#high").val(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<30){return}if(a){this.center=a/$(document).width()*(this.high-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:function(){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.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 h=this.view.low,d=this.view.high,e=d-h;var c=Math.pow(10,Math.ceil(Math.log(e/DENSITY)/Math.log(10)));c=Math.max(c,0.1);c=Math.min(c,1000000);var j=$("
");this.content_div.children(":first").remove();this.content_div.append(j);var k=this.content_div.width()/e;var g;var a=Math.floor(h/c/DENSITY);while((a*DENSITY*c)
");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;a.content_div.text(DATA_LOADING);$.getJSON(data_url,{stats:true,track_type:a.track_type,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){if(!c||c=="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(c=="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(c=="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.min_value=c.min;a.max_value=c.max;a.vertical_range=a.max_value-a.min_value;var d=$("
"+a.min_value+"
");var b=$("
"+a.max_value+"
");b.css({position:"relative",top:"35px"});b.prependTo(a.container_div);d.css({position:"relative",top:a.height_px+32+"px",});d.prependTo(a.container_div);a.draw()}}}})},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 C=B*DENSITY*w,c=(B+1)*DENSITY*w,q=DENSITY*w;var u=Math.ceil(q*n),t=this.height_px,s=$("");s.css({position:"absolute",top:0,left:(C-this.view.low)*n});s.get(0).width=u;s.get(0).height=t;var v=s.get(0).getContext("2d");v.fillStyle=this.base_color;v.font="10px monospace";v.textAlign="right";var y=0;for(var z=0,A=this.values.length;z=C){var e=Math.floor(Math.max(0,(f.start-C)*n)),h=Math.ceil(Math.min(u,(f.end-C)*n)),d=this.slots[f.name]*this.vertical_gap;var a,G,b=null,o=null;if(f.thick_start&&f.thick_end){b=Math.floor(Math.max(0,(f.thick_start-C)*n));o=Math.ceil(Math.min(u,(f.thick_end-C)*n))}if(!this.showing_labels){v.fillRect(e,d+5,h-e,1)}else{if(v.fillText){v.fillText(f.name,e-1,d+8)}var E=f.blocks;if(E){if(f.strand){if(f.strand=="+"){v.fillStyle=RIGHT_STRAND}else{if(f.strand=="-"){v.fillStyle=LEFT_STRAND}}v.fillRect(e,d,h-e,10);v.fillStyle=this.base_color}for(var x=0,F=E.length;xb)){a=9;G=1;var r=Math.max(l,b),p=Math.min(D,o);v.fillRect(r,d+G,p-r,a)}}}else{a=9;G=1;v.fillRect(e,d+G,h-e,a);if(f.strand){if(f.strand=="+"){v.fillStyle=RIGHT_STRAND_INV}else{if(f.strand=="-"){v.fillStyle=LEFT_STRAND_INV}}v.fillRect(e,d,h-e,10);v.fillStyle=this.base_color}}}y++}}g.append(s);return s}}); \ No newline at end of file diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index 0a6fc1cf46b..fbd2ce444dd 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -7,7 +7,7 @@ var DENSITY = 1000, DATA_NONE = "No data for this chrom/contig.", DATA_PENDING = "Currently indexing... please wait", DATA_LOADING = "Loading data...", - CACHED_TILES = 50, + CACHED_TILES = 5, CACHED_DATA = 20, CONTEXT = $("").get(0).getContext("2d"), RIGHT_STRAND, LEFT_STRAND; @@ -42,6 +42,36 @@ function commatize( number ) { } return number; } + +var Cache = function( num_elements ) { + this.num_elements = num_elements; + this.obj_cache = {}; + this.key_ary = []; +} +$.extend( Cache.prototype, { + get: function( key ) { + var index = this.key_ary.indexOf(key); + if (index != -1) { + // Move to the end + this.key_ary.splice(index, 1); + this.key_ary.push(key); + } + return this.obj_cache[key]; + }, + set: function( key, value ) { + if (!this.obj_cache[key]) { + if (this.key_ary.length >= this.num_elements) { + // Remove first element + var deleted_key = this.key_ary.shift(); + delete this.obj_cache[deleted_key]; + } + this.key_ary.push(key); + } + this.obj_cache[key] = value; + return value; + } +}); + var View = function( chrom, max_high ) { this.chrom = chrom; this.tracks = []; @@ -138,16 +168,14 @@ $.extend( TiledTrack.prototype, Track.prototype, { range = high - low; var resolution = Math.pow( 10, Math.ceil( Math.log( range / DENSITY ) / Math.log( 10 ) ) ); - resolution = Math.max( resolution, 1 ); - resolution = Math.min( resolution, 100000 ); + resolution = Math.max( resolution, 0.1 ); + resolution = Math.min( resolution, 1000000 ); var parent_element = $("
"); this.content_div.children( ":first" ).remove(); this.content_div.append( parent_element ); - var w = this.content_div.width(), - h = this.content_div.height(), - w_scale = w / range; + var w_scale = this.content_div.width() / range; var tile_element; // Index of first tile that overlaps visible region @@ -155,7 +183,7 @@ $.extend( TiledTrack.prototype, Track.prototype, { while ( ( tile_index * DENSITY * resolution ) < high ) { // Check in cache var key = this.view.zoom_level + "_" + tile_index; - var cached = this.tile_cache.getItem(key); + var cached = this.tile_cache.get(key); if ( cached ) { // console.log("cached tile"); var tile_low = tile_index * DENSITY * resolution; @@ -165,10 +193,10 @@ $.extend( TiledTrack.prototype, Track.prototype, { // Our responsibility to move the element to the new parent parent_element.append( cached ); } else { - tile_element = this.draw_tile( resolution, tile_index, parent_element, w_scale, h ); + tile_element = this.draw_tile( resolution, tile_index, parent_element, w_scale ); } if ( tile_element ) { - this.tile_cache.setItem(key, tile_element); + this.tile_cache.set(key, tile_element); } tile_index += 1; } diff --git a/templates/tracks/browser.mako b/templates/tracks/browser.mako index f78a98384c6..6718d72379f 100644 --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -7,7 +7,7 @@ ${parent.stylesheets()} <%def name="javascripts()"> ${parent.javascripts()} -${h.js( "jquery", "jquery.event.drag", "jquery.mousewheel", "lrucache", "trackster" )} +${h.js( "jquery", "jquery.event.drag", "jquery.mousewheel", "trackster" )}