diff --git a/static/scripts/lrucache.js b/static/scripts/lrucache.js
new file mode 100644
index 00000000000..00acb2b4717
--- /dev/null
+++ b/static/scripts/lrucache.js
@@ -0,0 +1,238 @@
+/*
+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
";
+ for (var key in this.items) {
+ var item = this.items[key];
+ returnStr = returnStr + "- " + item.key.toString() + " = " + item.value.toString() + "
";
+ }
+ returnStr = returnStr + "
";
+ return returnStr;
+}
diff --git a/static/scripts/packed/lrucache.js b/static/scripts/packed/lrucache.js
new file mode 100644
index 00000000000..0906947c6dd
--- /dev/null
+++ b/static/scripts/packed/lrucache.js
@@ -0,0 +1 @@
+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";for(var a in this.items){var c=this.items[a];b=b+"- "+c.key.toString()+" = "+c.value.toString()+"
"}b=b+"
";return b};
\ No newline at end of file
diff --git a/static/scripts/packed/trackster.js b/static/scripts/packed/trackster.js
index d1db185fd91..5422a6cd797 100644
--- a/static/scripts/packed/trackster.js
+++ b/static/scripts/packed/trackster.js
@@ -1 +1 @@
-var DENSITY=1000;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)*$("#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 a in this.tracks){this.tracks[a].draw()}$("#bottom-spacer").remove();$("#viewport").append('')},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.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=$("").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){if(b=="error"){a.content_div.addClass("error").text("There was an error in indexing this dataset.")}else{if(b=="no data"){a.content_div.addClass("nodata").text("No data for this chrom/contig.")}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,o,s,p){if(!this.vertical_range){return}var k=a*DENSITY*d,r=(a+1)*DENSITY*d,c=DENSITY*d;var n=this.cache.get(d,a);var h;if(n.state=="loading"){h=$("")}else{h=$("")}h.css({position:"absolute",top:0,left:(k-this.view.low)*s,});o.append(h);if(n.state=="loading"){e=false;return null}var b=h;b.get(0).width=Math.ceil(c*s);b.get(0).height=this.height_px;var q=b.get(0).getContext("2d");var e=false;q.beginPath();var g=n.values;if(!g){return}for(var f=0;f").get(0).getContext("2d");for(var b in this.values){feature=this.values[b];f_start=Math.floor(Math.max(this.view.max_low,(feature.start-this.view.max_low)*d));if(e){f_start-=c.measureText(feature.name).width}f_end=Math.ceil(Math.min(this.view.max_high,(feature.end-this.view.max_low)*d));j=0;while(true){if(a[j]==undefined||a[j]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=200,l=$("");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]*10+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]*10+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.";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)*$("#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 a in this.tracks){this.tracks[a].draw()}$("#bottom-spacer").remove();$("#viewport").append('')},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.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=$("").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=$("")}else{h=$("")}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").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]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=$("");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
diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js
index a0eda243423..8c4d5c53c26 100644
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -2,7 +2,9 @@
2009, James Taylor, Kanwei Li
*/
-var DENSITY = 1000;
+var DENSITY = 1000,
+ DATA_ERROR = "There was an error in indexing this dataset.",
+ DATA_NONE = "No data for this chrom/contig.";
var DataCache = function( type, track ) {
this.type = type;
@@ -239,18 +241,16 @@ $.extend( LineTrack.prototype, TiledTrack.prototype, {
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 ) {
- if (data) {
- if (data == "error") {
- track.content_div.addClass("error").text("There was an error in indexing this dataset.");
- } else if (data == "no data") {
- // console.log(track.content_div);
- track.content_div.addClass("nodata").text("No data for this chrom/contig.");
- } else {
- track.min_value = data['min'];
- track.max_value = data['max'];
- track.vertical_range = track.max_value - track.min_value;
- track.view.redraw();
- }
+ 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.vertical_range = track.max_value - track.min_value;
+ track.view.redraw();
}
});
},
@@ -321,27 +321,28 @@ var FeatureTrack = function ( name, dataset_id, height ) {
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( include_labels ) {
// console.log("num vals: " + this.values.length);
- var end_ary = new Array();
+ var end_ary = [];
var scale = this.container_div.width() / (this.view.high - this.view.low);
- // console.log(scale);
- if (include_labels) this.zi_slots = new Object();
+ // console.log(scale, this.view.high, this.view.low);
+ if (include_labels) this.zi_slots = {};
var dummy_canvas = $("").get(0).getContext("2d");
for (var i in this.values) {
- feature = this.values[i];
- f_start = Math.floor( Math.max(this.view.max_low, (feature.start - this.view.max_low) * scale) );
+ var feature = this.values[i];
+ var f_start = Math.floor( Math.max(this.view.max_low, (feature.start - this.view.max_low) * scale) );
if (include_labels) {
f_start -= dummy_canvas.measureText(feature.name).width;
+ f_start -= 10; // Spacing between text and line
}
- f_end = Math.ceil( Math.min(this.view.max_high, (feature.end - this.view.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); }
- j = 0;
+ var j = 0;
while (true) {
if (end_ary[j] == undefined || end_ary[j] < f_start) {
end_ary[j] = f_end;
@@ -355,17 +356,26 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
j++;
}
}
+ 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 ) {
- track.values = data;
- track.calc_slots();
- track.slots = track.zo_slots;
- // console.log(track.zo_slots);
- track.draw();
+ 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();
+ }
});
},
@@ -391,7 +401,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
range = view.high - view.low,
width = Math.ceil( tile_length * w_scale ),
slots = new Array(),
- height = 200,
+ height = this.height_px,
new_canvas = $("");
new_canvas.css({
@@ -412,7 +422,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
f_end = Math.ceil( Math.min(width, (feature.end - tile_low) * w_scale) );
// console.log(feature.start, feature.end, f_start, f_end, j);
ctx.fillStyle = "#000";
- ctx.fillRect(f_start, this.slots[feature.name] * 10 + 5, f_end - f_start, 1);
+ ctx.fillRect(f_start, this.slots[feature.name] * this.vertical_gap + 5, f_end - f_start, 1);
if (this.showing_labels && ctx.fillText) {
ctx.font = "10px monospace";
@@ -435,7 +445,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
if (exon_start && block_start >= exon_start && block_end <= exon_end) {
thickness = 5, y_start = 3;
}
- ctx.fillRect(exon_start, this.slots[feature.name] * 10 + y_start, block_end - block_start, thickness);
+ ctx.fillRect(exon_start, this.slots[feature.name] * this.vertical_gap + y_start, block_end - block_start, thickness);
// console.log(block_start, block_end);
}
diff --git a/static/trackster.css b/static/trackster.css
index c10d459501d..b3442d03f3c 100644
--- a/static/trackster.css
+++ b/static/trackster.css
@@ -86,7 +86,7 @@ body {
.track-content.error {
text-align: center;
padding-top: 30px;
- background-color: #600;
+ background-color: #ECB4AF;
}
.track-content.nodata {
text-align: center;