galaxy-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
commit/galaxy-central: davebgx: Fix for env.sh files being prematurely truncated in some environments.
by commits-noreply@bitbucket.org 04 Mar '14
by commits-noreply@bitbucket.org 04 Mar '14
04 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c8b0a7759ef6/
Changeset: c8b0a7759ef6
User: davebgx
Date: 2014-03-04 17:30:07
Summary: Fix for env.sh files being prematurely truncated in some environments.
Affected #: 1 file
diff -r e826721ef957d2d331331663153a509bb6f0ebed -r c8b0a7759ef65205df26a8b685cbb64d32ce3cc4 lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
@@ -211,7 +211,7 @@
if os.path.exists( file_path ):
try:
new_env_file_contents = []
- env_file_contents = file( file_path, 'a+' ).readlines()
+ env_file_contents = file( file_path, 'r' ).readlines()
# Clean out blank lines from the env.sh file.
for line in env_file_contents:
line = line.rstrip()
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Return mode fn results when calling modebutton through interface; pack scripts
by commits-noreply@bitbucket.org 04 Mar '14
by commits-noreply@bitbucket.org 04 Mar '14
04 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e826721ef957/
Changeset: e826721ef957
User: carlfeberhard
Date: 2014-03-04 17:26:16
Summary: Return mode fn results when calling modebutton through interface; pack scripts
Affected #: 7 files
diff -r a59ce5d9dd54eb2e2c7145c149aea7f92cb04d8b -r e826721ef957d2d331331663153a509bb6f0ebed static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -885,8 +885,7 @@
var modeFn = this.getMode( modeKey ).onclick;
if( modeFn && jQuery.type( modeFn === 'function' ) ){
// call with the element as context (std jquery pattern)
- modeFn.call( this.$element.get(0) );
- return this;
+ return modeFn.call( this.$element.get(0) );
}
return undefined;
};
diff -r a59ce5d9dd54eb2e2c7145c149aea7f92cb04d8b -r e826721ef957d2d331331663153a509bb6f0ebed static/scripts/packed/mvc/data.js
--- a/static/scripts/packed/mvc/data.js
+++ b/static/scripts/packed/mvc/data.js
@@ -1,1 +1,1 @@
-define([],function(){var d=Backbone.Model.extend({});var e=Backbone.Model.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda",metadata:null},initialize:function(){this._set_metadata();this.on("change",this._set_metadata,this)},_set_metadata:function(){var i=new d();_.each(_.keys(this.attributes),function(j){if(j.indexOf("metadata_")===0){var l=j.split("metadata_")[1];i.set(l,this.attributes[j]);delete this.attributes[j]}},this);this.set("metadata",i,{silent:true})},get_metadata:function(i){return this.attributes.metadata.get(i)},urlRoot:galaxy_config.root+"api/datasets"});var c=e.extend({defaults:_.extend({},e.prototype.defaults,{chunk_url:null,first_data_chunk:null,chunk_index:-1,at_eof:false}),initialize:function(i){e.prototype.initialize.call(this);this.attributes.chunk_index=(this.attributes.first_data_chunk?1:0)},get_next_chunk:function(){if(this.attributes.at_eof){return null}var i=this,j=$.Deferred();$.getJSON(this.attributes.chunk_url,{chunk:i.attributes.chunk_index++}).success(function(k){var l;if(k.ck_data!==""){l=k}else{i.attributes.at_eof=true;l=null}j.resolve(l)});return j}});var g=Backbone.Collection.extend({model:e});var f=Backbone.View.extend({initialize:function(i){new b(i)},render:function(){var m=$("<table/>").attr({id:"content_table",cellpadding:0});this.$el.append(m);var i=this.model.get_metadata("column_names");if(i){m.append("<tr><th>"+i.join("</th><th>")+"</th></tr>")}var k=this.model.get("first_data_chunk");if(k){this._renderChunk(k)}var j=this,n=_.find(this.$el.parents(),function(o){return $(o).css("overflow")==="auto"}),l=false;if(!n){n=window}n=$(n);n.scroll(function(){if(!l&&(j.$el.height()-n.scrollTop()-n.height()<=0)){l=true;$.when(j.model.get_next_chunk()).then(function(o){if(o){j._renderChunk(o);l=false}})}});$("#loading_indicator").ajaxStart(function(){$(this).show()}).ajaxStop(function(){$(this).hide()})},_renderCell:function(l,i,m){var j=$("<td>").text(l);var k=this.model.get_metadata("column_types");if(m!==undefined){j.attr("colspan",m).addClass("stringalign")}else{if(k){if(i<k.length){if(k[i]==="str"||k==="list"){j.addClass("stringalign")}}}}return j},_renderRow:function(i){var j=i.split("\t"),l=$("<tr>"),k=this.model.get_metadata("columns");if(j.length===k){_.each(j,function(n,m){l.append(this._renderCell(n,m))},this)}else{if(j.length>k){_.each(j.slice(0,k-1),function(n,m){l.append(this._renderCell(n,m))},this);l.append(this._renderCell(j.slice(k-1).join("\t"),k-1))}else{if(k>5&&j.length===k-1){_.each(j,function(n,m){l.append(this._renderCell(n,m))},this);l.append($("<td>"))}else{l.append(this._renderCell(i,0,k))}}}return l},_renderChunk:function(i){var j=this.$el.find("table");_.each(i.ck_data.split("\n"),function(k,l){j.append(this._renderRow(k))},this)}});var b=Backbone.View.extend({col:{chrom:null,start:null,end:null},url_viz:null,dataset_id:null,genome_build:null,data_type:null,initialize:function(k){var j=k.model.attributes;var m=k.model.attributes.metadata.attributes;if(typeof j.data_type!=="undefined"){this.data_type=j.data_type}else{console.log("TabularButtonTrackster : Data type missing.")}if(this.data_type=="bed"){if(typeof m.chromCol!=="undefined"||typeof m.startCol!=="undefined"||typeof m.endCol!=="undefined"){this.col.chrom=m.chromCol-1;this.col.start=m.startCol-1;this.col.end=m.endCol-1}else{console.log("TabularButtonTrackster : Bed-file metadata incomplete.")}}if(this.data_type=="vcf"){function l(o,p){for(var n=0;n<p.length;n++){if(p[n].match(o)){return n}}return -1}this.col.chrom=l("Chrom",m.column_names);this.col.start=l("Pos",m.column_names);this.col.end=null;if(this.col.chrom==-1||this.col.start==-1){console.log("TabularButtonTrackster : VCF-file metadata incomplete.")}}if(this.col.chrom===null){return}if(typeof k.model.attributes.id==="undefined"){console.log("TabularButtonTrackster : Dataset identification is missing.")}else{this.dataset_id=k.model.attributes.id}if(typeof k.model.attributes.url_viz==="undefined"){console.log("TabularButtonTrackster : Url for visualization controller is missing.")}else{this.url_viz=k.model.attributes.url_viz}if(typeof k.model.attributes.genome_build!=="undefined"){this.genome_build=k.model.attributes.genome_build}var i=new IconButtonView({model:new IconButton({title:"Visualize",icon_class:"chart_curve",id:"btn_viz"})});this.$el.append(i.render().$el);$("#btn_viz").hide()},events:{"mouseover tr":"btn_viz_show",mouseleave:"btn_viz_hide"},btn_viz_show:function(n){function m(s){return !isNaN(parseFloat(s))&&isFinite(s)}if(this.col.chrom===null){return}var r=$(n.target).parent();var o=r.children().eq(this.col.chrom).html();var i=r.children().eq(this.col.start).html();var k=this.col.end?r.children().eq(this.col.end).html():i;if(!o.match("^#")&&o!==""&&m(i)){var q={dataset_id:this.dataset_id,gene_region:o+":"+i+"-"+k};var l=r.offset();var j=l.left-10;var p=l.top-$(window).scrollTop();$("#btn_viz").css({position:"fixed",top:p+"px",left:j+"px"});$("#btn_viz").off("click");$("#btn_viz").click(this.create_trackster_action(this.url_viz,q,this.genome_build));$("#btn_viz").show()}else{$("#btn_viz").hide()}},btn_viz_hide:function(){$("#btn_viz").hide()},create_trackster_action:function(i,k,j){return function(){var l={};if(j){l["f-dbkey"]=j}$.ajax({url:i+"/list_tracks?"+$.param(l),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(m){var n=window.parent;n.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){n.Galaxy.modal.hide()},"View in saved visualization":function(){n.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:m,buttons:{Cancel:function(){n.Galaxy.modal.hide()},"Add to visualization":function(){$(n.document).find("input[name=id]:checked").each(function(){n.Galaxy.modal.hide();var o=$(this).val();k.id=o;n.Galaxy.frame.add({title:"Trackster",type:"url",content:i+"/trackster?"+$.param(k)})})}}})},"View in new visualization":function(){n.Galaxy.modal.hide();var o=i+"/trackster?"+$.param(k);n.Galaxy.frame.add({title:"Trackster",type:"url",content:o})}}})}});return false}}});var a=function(l,j,m,i){var k=new j({model:new l(m)});k.render();if(i){i.append(k.$el)}return k};var h=function(k,i){var j=$("<div/>").appendTo(i);return new f({el:j,model:new c(k)}).render()};return{Dataset:e,TabularDataset:c,DatasetCollection:g,TabularDatasetChunkedView:f,createTabularDatasetChunkedView:h}});
\ No newline at end of file
+define([],function(){var g=Backbone.Model.extend({});var b=Backbone.Model.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda",metadata:null},initialize:function(){this._set_metadata();this.on("change",this._set_metadata,this)},_set_metadata:function(){var k=new g();_.each(_.keys(this.attributes),function(l){if(l.indexOf("metadata_")===0){var m=l.split("metadata_")[1];k.set(m,this.attributes[l]);delete this.attributes[l]}},this);this.set("metadata",k,{silent:true})},get_metadata:function(k){return this.attributes.metadata.get(k)},urlRoot:galaxy_config.root+"api/datasets"});var h=b.extend({defaults:_.extend({},b.prototype.defaults,{chunk_url:null,first_data_chunk:null,chunk_index:-1,at_eof:false}),initialize:function(k){b.prototype.initialize.call(this);this.attributes.chunk_index=(this.attributes.first_data_chunk?1:0)},get_next_chunk:function(){if(this.attributes.at_eof){return null}var k=this,l=$.Deferred();$.getJSON(this.attributes.chunk_url,{chunk:k.attributes.chunk_index++}).success(function(m){var n;if(m.ck_data!==""){n=m}else{k.attributes.at_eof=true;n=null}l.resolve(n)});return l}});var e=Backbone.Collection.extend({model:b});var a=Backbone.View.extend({initialize:function(k){this.row_count=0;this.header_color="#AAA";this.dark_row_color="#DDD";new d(k)},render:function(){var o=$("<table/>").attr({id:"content_table",cellpadding:0});this.$el.append(o);var k=this.model.get_metadata("column_names"),p=$("<tr/>").css("background-color",this.header_color).appendTo(o);if(k){p.append("<th>"+k.join("</th><th>")+"</th>")}var m=this.model.get("first_data_chunk");if(m){this._renderChunk(m)}var l=this,n=false;this.scroll_elt.scroll(function(){if(!n&&l.scrolled_to_bottom()){n=true;$.when(l.model.get_next_chunk()).then(function(q){if(q){l._renderChunk(q);n=false}})}});$("#loading_indicator").ajaxStart(function(){$(this).show()}).ajaxStop(function(){$(this).hide()})},scrolled_to_bottom:function(){return false},_renderCell:function(n,k,o){var l=$("<td>").text(n);var m=this.model.get_metadata("column_types");if(o!==undefined){l.attr("colspan",o).addClass("stringalign")}else{if(m){if(k<m.length){if(m[k]==="str"||m==="list"){l.addClass("stringalign")}}}}return l},_renderRow:function(k){var l=k.split("\t"),n=$("<tr>"),m=this.model.get_metadata("columns");if(this.row_count%2!==0){n.css("background-color",this.dark_row_color)}if(l.length===m){_.each(l,function(p,o){n.append(this._renderCell(p,o))},this)}else{if(l.length>m){_.each(l.slice(0,m-1),function(p,o){n.append(this._renderCell(p,o))},this);n.append(this._renderCell(l.slice(m-1).join("\t"),m-1))}else{if(m>5&&l.length===m-1){_.each(l,function(p,o){n.append(this._renderCell(p,o))},this);n.append($("<td>"))}else{n.append(this._renderCell(k,0,m))}}}this.row_count++;return n},_renderChunk:function(k){var l=this.$el.find("table");_.each(k.ck_data.split("\n"),function(m,n){l.append(this._renderRow(m))},this)}});var f=a.extend({initialize:function(k){a.prototype.initialize.call(this,k);scroll_elt=_.find(this.$el.parents(),function(l){return $(l).css("overflow")==="auto"});if(!scroll_elt){scroll_elt=window}this.scroll_elt=$(scroll_elt)},scrolled_to_bottom:function(){return(this.$el.height()-this.scroll_elt.scrollTop()-this.scroll_elt.height()<=0)}});var j=a.extend({initialize:function(k){a.prototype.initialize.call(this,k);this.scroll_elt=this.$el.css({position:"relative",overflow:"scroll",height:this.options.height||"500px"})},scrolled_to_bottom:function(){return this.$el.scrollTop()+this.$el.innerHeight()>=this.el.scrollHeight}});var d=Backbone.View.extend({col:{chrom:null,start:null,end:null},url_viz:null,dataset_id:null,genome_build:null,data_type:null,initialize:function(m){var l=m.model.attributes;var o=m.model.attributes.metadata.attributes;if(typeof l.data_type!=="undefined"){this.data_type=l.data_type}else{console.log("TabularButtonTrackster : Data type missing.")}if(this.data_type=="bed"){if(typeof o.chromCol!=="undefined"||typeof o.startCol!=="undefined"||typeof o.endCol!=="undefined"){this.col.chrom=o.chromCol-1;this.col.start=o.startCol-1;this.col.end=o.endCol-1}else{console.log("TabularButtonTrackster : Bed-file metadata incomplete.")}}if(this.data_type=="vcf"){function n(q,r){for(var p=0;p<r.length;p++){if(r[p].match(q)){return p}}return -1}this.col.chrom=n("Chrom",o.column_names);this.col.start=n("Pos",o.column_names);this.col.end=null;if(this.col.chrom==-1||this.col.start==-1){console.log("TabularButtonTrackster : VCF-file metadata incomplete.")}}if(this.col.chrom===null){return}if(typeof m.model.attributes.id==="undefined"){console.log("TabularButtonTrackster : Dataset identification is missing.")}else{this.dataset_id=m.model.attributes.id}if(typeof m.model.attributes.url_viz==="undefined"){console.log("TabularButtonTrackster : Url for visualization controller is missing.")}else{this.url_viz=m.model.attributes.url_viz}if(typeof m.model.attributes.genome_build!=="undefined"){this.genome_build=m.model.attributes.genome_build}var k=new IconButtonView({model:new IconButton({title:"Visualize",icon_class:"chart_curve",id:"btn_viz"})});this.$el.append(k.render().$el);$("#btn_viz").hide()},events:{"mouseover tr":"btn_viz_show",mouseleave:"btn_viz_hide"},btn_viz_show:function(p){function o(u){return !isNaN(parseFloat(u))&&isFinite(u)}if(this.col.chrom===null){return}var t=$(p.target).parent();var q=t.children().eq(this.col.chrom).html();var k=t.children().eq(this.col.start).html();var m=this.col.end?t.children().eq(this.col.end).html():k;if(!q.match("^#")&&q!==""&&o(k)){var s={dataset_id:this.dataset_id,gene_region:q+":"+k+"-"+m};var n=t.offset();var l=n.left-10;var r=n.top-$(window).scrollTop();$("#btn_viz").css({position:"fixed",top:r+"px",left:l+"px"});$("#btn_viz").off("click");$("#btn_viz").click(this.create_trackster_action(this.url_viz,s,this.genome_build));$("#btn_viz").show()}else{$("#btn_viz").hide()}},btn_viz_hide:function(){$("#btn_viz").hide()},create_trackster_action:function(k,m,l){return function(){var n={};if(l){n["f-dbkey"]=l}$.ajax({url:k+"/list_tracks?"+$.param(n),dataType:"html",error:function(){alert(("Could not add this dataset to browser")+".")},success:function(o){var p=window.parent;p.Galaxy.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){p.Galaxy.modal.hide()},"View in saved visualization":function(){p.Galaxy.modal.show({title:"Add Data to Saved Visualization",body:o,buttons:{Cancel:function(){p.Galaxy.modal.hide()},"Add to visualization":function(){$(p.document).find("input[name=id]:checked").each(function(){p.Galaxy.modal.hide();var q=$(this).val();m.id=q;p.Galaxy.frame.add({title:"Trackster",type:"url",content:k+"/trackster?"+$.param(m)})})}}})},"View in new visualization":function(){p.Galaxy.modal.hide();var q=k+"/trackster?"+$.param(m);p.Galaxy.frame.add({title:"Trackster",type:"url",content:q})}}})}});return false}}});var i=function(n,l,o,k){var m=new l({model:new n(o)});m.render();if(k){k.append(m.$el)}return m};var c=function(m){m.model=new h(m.dataset_config);var l=m.parent_elt;var n=m.embedded;delete m.embedded;delete m.parent_elt;delete m.dataset_config;var k=(n?new j(m):new f(m));k.render();if(l){l.append(k.$el)}return k};return{Dataset:b,TabularDataset:h,DatasetCollection:e,TabularDatasetChunkedView:a,createTabularDatasetChunkedView:c}});
\ No newline at end of file
diff -r a59ce5d9dd54eb2e2c7145c149aea7f92cb04d8b -r e826721ef957d2d331331663153a509bb6f0ebed static/scripts/packed/mvc/ui.js
--- a/static/scripts/packed/mvc/ui.js
+++ b/static/scripts/packed/mvc/ui.js
@@ -1,1 +1,1 @@
-var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.create=function _create(b,a){return new PopupMenu(b,a)};PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){function a(j,p){var d=27,m=13,c=$(j),e=true,g={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(q){},minSearchLen:0,escWillClear:true,oninit:function(){}};function i(q){var r=$(this).parent().children("input");r.val("");r.trigger("clear:searchInput");p.onclear()}function o(r,q){$(this).trigger("search:searchInput",q);if(typeof p.onfirstsearch==="function"&&e){e=false;p.onfirstsearch(q)}else{p.onsearch(q)}}function f(){return['<input type="text" name="',p.name,'" placeholder="',p.placeholder,'" ','class="search-query ',p.classes,'" ',"/>"].join("")}function l(){return $(f()).focus(function(q){$(this).select()}).keyup(function(r){if(r.which===d&&p.escWillClear){i.call(this,r)}else{var q=$(this).val();if((r.which===m)||(p.minSearchLen&&q.length>=p.minSearchLen)){o.call(this,r,q)}else{if(!q.length){i.call(this,r)}}}}).val(p.initialVal)}function k(){return $(['<span class="search-clear fa fa-times-circle" ','title="',_l("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(q){i.call(this,q)})}function n(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',_l("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function h(){c.find(".search-loading").toggle();c.find(".search-clear").toggle()}if(jQuery.type(p)==="string"){if(p==="toggle-loading"){h()}return c}if(jQuery.type(p)==="object"){p=jQuery.extend(true,{},g,p)}return c.addClass("search-input").prepend([l(),k(),n()])}jQuery.fn.extend({searchInput:function b(c){return this.each(function(){return a(this,c)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o<this.options.modes.length;o+=1){if(this.options.modes[o].mode===n){return o}}throw new Error("mode not found: "+n)};c.prototype._setModeByIndex=function m(n){var o=this.options.modes[n];if(!o){throw new Error("mode index not found: "+n)}this.currModeIndex=n;if(o.html){this.$element.html(o.html)}return this};c.prototype.currentMode=function d(){return this.options.modes[this.currModeIndex]};c.prototype.current=function i(){return this.currentMode().mode};c.prototype.getMode=function g(n){if(!n){return this.currentMode()}return this.options.modes[(this._getModeIndex(n))]};c.prototype.hasMode=function b(n){try{return !!this.getMode(n)}catch(o){}return false};c.prototype.setMode=function a(n){return this._setModeByIndex(this._getModeIndex(n))};c.prototype.reset=function h(){this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this._setModeByIndex(this.currModeIndex)};c.prototype.callModeFn=function e(n){var o=this.getMode(n).onclick;if(o&&jQuery.type(o==="function")){o.call(this.$element.get(0));return this}return undefined};jQuery.fn.extend({modeButton:function k(n){if(!this.size()){return this}if(jQuery.type(n)==="object"){return this.map(function(){var r=$(this);r.data("mode-button",new c(r,n));return this})}var p=$(this[0]),o=p.data("mode-button");console.debug("first, button",p,o);if(!o){throw new Error("modeButton needs an options object or string name of a function")}if(o&&jQuery.type(n)==="string"){var q=n;if(o&&jQuery.type(o[q])==="function"){return o[q].apply(o,jQuery.makeArray(arguments).slice(1))}}return o}})}());function dropDownSelect(b,c){c=c||((!_.isEmpty(b))?(b[0]):(""));var a=$(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+c+"</span>","</button>","</div>"].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');a.append(['<ul class="dropdown-menu" role="menu">',_.map(b,function(e){return['<li><a href="javascript:void(0)">',e,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("<input/>").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());(function(){function i(o,n){this.numPages=null;this.currPage=0;return this.init(o,n)}i.prototype.DATA_KEY="pagination";i.prototype.defaults={startingPage:0,perPage:20,totalDataSize:null,currDataSize:null};i.prototype.init=function g(n,o){o=o||{};this.$element=n;this.options=jQuery.extend(true,{},this.defaults,o);this.currPage=this.options.startingPage;if(this.options.totalDataSize!==null){this.numPages=Math.ceil(this.options.totalDataSize/this.options.perPage);if(this.currPage>=this.numPages){this.currPage=this.numPages-1}}this.$element.data(i.prototype.DATA_KEY,this);this._render();return this};function m(n){return $(['<li><a href="javascript:void(0);">',n,"</a></li>"].join(""))}i.prototype._render=function e(){if(this.options.totalDataSize===0){return this}if(this.numPages===1){return this}if(this.numPages>0){this._renderPages();this._scrollToActivePage()}else{this._renderPrevNext()}return this};i.prototype._renderPrevNext=function b(){var o=this,p=m("Prev"),n=m("Next"),q=$("<ul/>").addClass("pagination pagination-prev-next");if(this.currPage===0){p.addClass("disabled")}else{p.click(function(){o.prevPage()})}if((this.numPages&&this.currPage===(this.numPages-1))||(this.options.currDataSize&&this.options.currDataSize<this.options.perPage)){n.addClass("disabled")}else{n.click(function(){o.nextPage()})}this.$element.html(q.append([p,n]));return this.$element};i.prototype._renderPages=function a(){var n=this,q=$("<div>").addClass("pagination-scroll-container"),s=$("<ul/>").addClass("pagination pagination-page-list"),r=function(t){n.goToPage($(this).data("page"))};for(var o=0;o<this.numPages;o+=1){var p=m(o+1).attr("data-page",o).click(r);if(o===this.currPage){p.addClass("active")}s.append(p)}return this.$element.html(q.html(s))};i.prototype._scrollToActivePage=function l(){var p=this.$element.find(".pagination-scroll-container");if(!p.size()){return this}var o=this.$element.find("li.active"),n=p.width()/2;p.scrollLeft(p.scrollLeft()+o.position().left-n);return this};i.prototype.goToPage=function j(n){if(n<=0){n=0}if(this.numPages&&n>=this.numPages){n=this.numPages-1}if(n===this.currPage){return this}this.currPage=n;this.$element.trigger("pagination.page-change",this.currPage);this._render();return this};i.prototype.prevPage=function c(){return this.goToPage(this.currPage-1)};i.prototype.nextPage=function h(){return this.goToPage(this.currPage+1)};i.prototype.page=function f(){return this.currPage};i.create=function k(n,o){return new i(n,o)};jQuery.fn.extend({pagination:function d(o){var n=jQuery.makeArray(arguments).slice(1);if(jQuery.type(o)==="object"){return this.map(function(){i.create($(this),o);return this})}var q=$(this[0]),r=q.data(i.prototype.DATA_KEY);if(r){if(jQuery.type(o)==="string"){var p=r[o];if(jQuery.type(p)==="function"){return p.apply(r,n)}}else{return r}}return undefined}})}());(function(){var g={renameColumns:false,columnNames:[],commentChar:"#",hideCommentRows:false,includePrompts:true,topLeftContent:"Columns:"},s="peek-control.change",t="peek-control.rename",l="peek-control",v="control",f="control-prompt",c="selected",n="disabled",u="button",q="renamable-header",p="column-index",a="column-name";function i(w){if(w.disabled&&jQuery.type(w.disabled)!=="array"){throw new Error('"disabled" must be defined as an array of indeces: '+JSON.stringify(w))}if(w.multiselect&&w.selected&&jQuery.type(w.selected)!=="array"){throw new Error('Mulitselect rows need an array for "selected": '+JSON.stringify(w))}if(!w.label||!w.id){throw new Error("Peek controls need a label and id for each control row: "+JSON.stringify(w))}if(w.disabled&&w.disabled.indexOf(w.selected)!==-1){throw new Error("Selected column is in the list of disabled columns: "+JSON.stringify(w))}return w}function o(x,w){return $("<div/>").addClass(u).text(x.label)}function k(x,w){var y=$("<td/>").html(o(x,w)).attr("data-"+p,w);if(x.disabled&&x.disabled.indexOf(w)!==-1){y.addClass(n)}return y}function e(y,z,w){var x=y.children("."+u);if(y.hasClass(c)){x.html((z.selectedText!==undefined)?(z.selectedText):(z.label))}else{x.html((z.unselectedText!==undefined)?(z.unselectedText):(z.label))}}function h(z,x){var y=k(z,x);if(z.selected===x){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);if(!E.hasClass(c)){var A=E.parent().children("."+c).removeClass(c);A.each(function(){e($(this),z,x)});E.addClass(c);e(E,z,x);var C={},B=E.parent().attr("id"),F=E.data(p);C[B]=F;E.parents(".peek").trigger(s,C)}})}return y}function m(z,x){var y=k(z,x);if(z.selected&&z.selected.indexOf(x)!==-1){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);E.toggleClass(c);e(E,z,x);var C=E.parent().find("."+c).map(function(G,H){return $(H).data(p)});var B={},A=E.parent().attr("id"),F=jQuery.makeArray(C);B[A]=F;E.parents(".peek").trigger(s,B)})}return y}function j(y,z){var w=[];for(var x=0;x<y;x+=1){w.push(z.multiselect?m(z,x):h(z,x))}return w}function r(y,z,x){var A=$("<tr/>").attr("id",z.id).addClass(v);if(x){var w=$("<td/>").addClass(f).text(z.label+":");A.append(w)}A.append(j(y,z));return A}function b(E){E=jQuery.extend(true,{},g,E);var D=$(this).addClass(l),A=D.find("table"),z=A.find("th").size(),C=A.find("tr").size(),w=A.find("td[colspan]").map(function(H,F){var G=$(this);if(G.text()&&G.text().match(new RegExp("^"+E.commentChar))){return $(this).css("color","grey").parent().get(0)}return null});if(E.hideCommentRows){w.hide();C-=w.size()}if(E.includePrompts){var y=$("<th/>").addClass("top-left").text(E.topLeftContent).attr("rowspan",C);A.find("tr").first().prepend(y)}var B=A.find("th:not(.top-left)").each(function(G,I){var H=$(this),J=H.text().replace(/^\d+\.*/,""),F=E.columnNames[G]||J;H.attr("data-"+a,F).text((G+1)+((F)?("."+F):("")))});if(E.renameColumns){B.addClass(q).click(function x(){var G=$(this),F=G.index()+(E.includePrompts?0:1),I=G.data(a),H=prompt("New column name:",I);if(H!==null&&H!==I){G.text(F+(H?("."+H):"")).data(a,H).attr("data-",a,H);var J=jQuery.makeArray(G.parent().children("th:not(.top-left)").map(function(){return $(this).data(a)}));G.parents(".peek").trigger(t,J)}})}E.controls.forEach(function(G,F){i(G);var H=r(z,G,E.includePrompts);A.find("tbody").append(H)});return this}jQuery.fn.extend({peekControl:function d(w){return this.map(function(){return b.call(this,w)})}})}());
\ No newline at end of file
+var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.create=function _create(b,a){return new PopupMenu(b,a)};PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){function a(j,p){var d=27,m=13,c=$(j),e=true,g={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(q){},minSearchLen:0,escWillClear:true,oninit:function(){}};function i(q){var r=$(this).parent().children("input");r.val("");r.trigger("clear:searchInput");p.onclear()}function o(r,q){$(this).trigger("search:searchInput",q);if(typeof p.onfirstsearch==="function"&&e){e=false;p.onfirstsearch(q)}else{p.onsearch(q)}}function f(){return['<input type="text" name="',p.name,'" placeholder="',p.placeholder,'" ','class="search-query ',p.classes,'" ',"/>"].join("")}function l(){return $(f()).focus(function(q){$(this).select()}).keyup(function(r){if(r.which===d&&p.escWillClear){i.call(this,r)}else{var q=$(this).val();if((r.which===m)||(p.minSearchLen&&q.length>=p.minSearchLen)){o.call(this,r,q)}else{if(!q.length){i.call(this,r)}}}}).val(p.initialVal)}function k(){return $(['<span class="search-clear fa fa-times-circle" ','title="',_l("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(q){i.call(this,q)})}function n(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',_l("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function h(){c.find(".search-loading").toggle();c.find(".search-clear").toggle()}if(jQuery.type(p)==="string"){if(p==="toggle-loading"){h()}return c}if(jQuery.type(p)==="object"){p=jQuery.extend(true,{},g,p)}return c.addClass("search-input").prepend([l(),k(),n()])}jQuery.fn.extend({searchInput:function b(c){return this.each(function(){return a(this,c)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o<this.options.modes.length;o+=1){if(this.options.modes[o].mode===n){return o}}throw new Error("mode not found: "+n)};c.prototype._setModeByIndex=function m(n){var o=this.options.modes[n];if(!o){throw new Error("mode index not found: "+n)}this.currModeIndex=n;if(o.html){this.$element.html(o.html)}return this};c.prototype.currentMode=function d(){return this.options.modes[this.currModeIndex]};c.prototype.current=function i(){return this.currentMode().mode};c.prototype.getMode=function g(n){if(!n){return this.currentMode()}return this.options.modes[(this._getModeIndex(n))]};c.prototype.hasMode=function b(n){try{return !!this.getMode(n)}catch(o){}return false};c.prototype.setMode=function a(n){return this._setModeByIndex(this._getModeIndex(n))};c.prototype.reset=function h(){this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this._setModeByIndex(this.currModeIndex)};c.prototype.callModeFn=function e(n){var o=this.getMode(n).onclick;if(o&&jQuery.type(o==="function")){return o.call(this.$element.get(0))}return undefined};jQuery.fn.extend({modeButton:function k(n){if(!this.size()){return this}if(jQuery.type(n)==="object"){return this.map(function(){var r=$(this);r.data("mode-button",new c(r,n));return this})}var p=$(this[0]),o=p.data("mode-button");console.debug("first, button",p,o);if(!o){throw new Error("modeButton needs an options object or string name of a function")}if(o&&jQuery.type(n)==="string"){var q=n;if(o&&jQuery.type(o[q])==="function"){return o[q].apply(o,jQuery.makeArray(arguments).slice(1))}}return o}})}());function dropDownSelect(b,c){c=c||((!_.isEmpty(b))?(b[0]):(""));var a=$(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+c+"</span>","</button>","</div>"].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');a.append(['<ul class="dropdown-menu" role="menu">',_.map(b,function(e){return['<li><a href="javascript:void(0)">',e,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("<input/>").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());(function(){function i(o,n){this.numPages=null;this.currPage=0;return this.init(o,n)}i.prototype.DATA_KEY="pagination";i.prototype.defaults={startingPage:0,perPage:20,totalDataSize:null,currDataSize:null};i.prototype.init=function g(n,o){o=o||{};this.$element=n;this.options=jQuery.extend(true,{},this.defaults,o);this.currPage=this.options.startingPage;if(this.options.totalDataSize!==null){this.numPages=Math.ceil(this.options.totalDataSize/this.options.perPage);if(this.currPage>=this.numPages){this.currPage=this.numPages-1}}this.$element.data(i.prototype.DATA_KEY,this);this._render();return this};function m(n){return $(['<li><a href="javascript:void(0);">',n,"</a></li>"].join(""))}i.prototype._render=function e(){if(this.options.totalDataSize===0){return this}if(this.numPages===1){return this}if(this.numPages>0){this._renderPages();this._scrollToActivePage()}else{this._renderPrevNext()}return this};i.prototype._renderPrevNext=function b(){var o=this,p=m("Prev"),n=m("Next"),q=$("<ul/>").addClass("pagination pagination-prev-next");if(this.currPage===0){p.addClass("disabled")}else{p.click(function(){o.prevPage()})}if((this.numPages&&this.currPage===(this.numPages-1))||(this.options.currDataSize&&this.options.currDataSize<this.options.perPage)){n.addClass("disabled")}else{n.click(function(){o.nextPage()})}this.$element.html(q.append([p,n]));return this.$element};i.prototype._renderPages=function a(){var n=this,q=$("<div>").addClass("pagination-scroll-container"),s=$("<ul/>").addClass("pagination pagination-page-list"),r=function(t){n.goToPage($(this).data("page"))};for(var o=0;o<this.numPages;o+=1){var p=m(o+1).attr("data-page",o).click(r);if(o===this.currPage){p.addClass("active")}s.append(p)}return this.$element.html(q.html(s))};i.prototype._scrollToActivePage=function l(){var p=this.$element.find(".pagination-scroll-container");if(!p.size()){return this}var o=this.$element.find("li.active"),n=p.width()/2;p.scrollLeft(p.scrollLeft()+o.position().left-n);return this};i.prototype.goToPage=function j(n){if(n<=0){n=0}if(this.numPages&&n>=this.numPages){n=this.numPages-1}if(n===this.currPage){return this}this.currPage=n;this.$element.trigger("pagination.page-change",this.currPage);this._render();return this};i.prototype.prevPage=function c(){return this.goToPage(this.currPage-1)};i.prototype.nextPage=function h(){return this.goToPage(this.currPage+1)};i.prototype.page=function f(){return this.currPage};i.create=function k(n,o){return new i(n,o)};jQuery.fn.extend({pagination:function d(o){var n=jQuery.makeArray(arguments).slice(1);if(jQuery.type(o)==="object"){return this.map(function(){i.create($(this),o);return this})}var q=$(this[0]),r=q.data(i.prototype.DATA_KEY);if(r){if(jQuery.type(o)==="string"){var p=r[o];if(jQuery.type(p)==="function"){return p.apply(r,n)}}else{return r}}return undefined}})}());(function(){var g={renameColumns:false,columnNames:[],commentChar:"#",hideCommentRows:false,includePrompts:true,topLeftContent:"Columns:"},s="peek-control.change",t="peek-control.rename",l="peek-control",v="control",f="control-prompt",c="selected",n="disabled",u="button",q="renamable-header",p="column-index",a="column-name";function i(w){if(w.disabled&&jQuery.type(w.disabled)!=="array"){throw new Error('"disabled" must be defined as an array of indeces: '+JSON.stringify(w))}if(w.multiselect&&w.selected&&jQuery.type(w.selected)!=="array"){throw new Error('Mulitselect rows need an array for "selected": '+JSON.stringify(w))}if(!w.label||!w.id){throw new Error("Peek controls need a label and id for each control row: "+JSON.stringify(w))}if(w.disabled&&w.disabled.indexOf(w.selected)!==-1){throw new Error("Selected column is in the list of disabled columns: "+JSON.stringify(w))}return w}function o(x,w){return $("<div/>").addClass(u).text(x.label)}function k(x,w){var y=$("<td/>").html(o(x,w)).attr("data-"+p,w);if(x.disabled&&x.disabled.indexOf(w)!==-1){y.addClass(n)}return y}function e(y,z,w){var x=y.children("."+u);if(y.hasClass(c)){x.html((z.selectedText!==undefined)?(z.selectedText):(z.label))}else{x.html((z.unselectedText!==undefined)?(z.unselectedText):(z.label))}}function h(z,x){var y=k(z,x);if(z.selected===x){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);if(!E.hasClass(c)){var A=E.parent().children("."+c).removeClass(c);A.each(function(){e($(this),z,x)});E.addClass(c);e(E,z,x);var C={},B=E.parent().attr("id"),F=E.data(p);C[B]=F;E.parents(".peek").trigger(s,C)}})}return y}function m(z,x){var y=k(z,x);if(z.selected&&z.selected.indexOf(x)!==-1){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);E.toggleClass(c);e(E,z,x);var C=E.parent().find("."+c).map(function(G,H){return $(H).data(p)});var B={},A=E.parent().attr("id"),F=jQuery.makeArray(C);B[A]=F;E.parents(".peek").trigger(s,B)})}return y}function j(y,z){var w=[];for(var x=0;x<y;x+=1){w.push(z.multiselect?m(z,x):h(z,x))}return w}function r(y,z,x){var A=$("<tr/>").attr("id",z.id).addClass(v);if(x){var w=$("<td/>").addClass(f).text(z.label+":");A.append(w)}A.append(j(y,z));return A}function b(E){E=jQuery.extend(true,{},g,E);var D=$(this).addClass(l),A=D.find("table"),z=A.find("th").size(),C=A.find("tr").size(),w=A.find("td[colspan]").map(function(H,F){var G=$(this);if(G.text()&&G.text().match(new RegExp("^"+E.commentChar))){return $(this).css("color","grey").parent().get(0)}return null});if(E.hideCommentRows){w.hide();C-=w.size()}if(E.includePrompts){var y=$("<th/>").addClass("top-left").text(E.topLeftContent).attr("rowspan",C);A.find("tr").first().prepend(y)}var B=A.find("th:not(.top-left)").each(function(G,I){var H=$(this),J=H.text().replace(/^\d+\.*/,""),F=E.columnNames[G]||J;H.attr("data-"+a,F).text((G+1)+((F)?("."+F):("")))});if(E.renameColumns){B.addClass(q).click(function x(){var G=$(this),F=G.index()+(E.includePrompts?0:1),I=G.data(a),H=prompt("New column name:",I);if(H!==null&&H!==I){G.text(F+(H?("."+H):"")).data(a,H).attr("data-",a,H);var J=jQuery.makeArray(G.parent().children("th:not(.top-left)").map(function(){return $(this).data(a)}));G.parents(".peek").trigger(t,J)}})}E.controls.forEach(function(G,F){i(G);var H=r(z,G,E.includePrompts);A.find("tbody").append(H)});return this}jQuery.fn.extend({peekControl:function d(w){return this.map(function(){return b.call(this,w)})}})}());
\ No newline at end of file
diff -r a59ce5d9dd54eb2e2c7145c149aea7f92cb04d8b -r e826721ef957d2d331331663153a509bb6f0ebed static/scripts/packed/utils/config.js
--- a/static/scripts/packed/utils/config.js
+++ b/static/scripts/packed/utils/config.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","viz/trackster/util","utils/config"],function(c,f,b){var d=Backbone.Model.extend({initialize:function(g){var h=this.get("key");this.set("id",h);var i=c.find(d.known_settings_defaults,function(j){return j.key===h});if(i){this.set(c.extend({},i,g))}if(this.get("type")==="color"&&!this.get("value")){this.set("value",f.get_random_color())}},set_value:function(h){var g=this.get("type");if(g==="float"){h=parseFloat(h)}else{if(g==="int"){h=parseInt(h,10)}}this.set("value",h)}},{known_settings_defaults:[{key:"name",label:"Name",type:"text",default_value:""},{key:"color",label:"Color",type:"color",default_value:null},{key:"min_value",label:"Min Value",type:"float",default_value:null},{key:"max_value",label:"Max Value",type:"float",default_value:null},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"block_color",label:"Block color",type:"color",default_value:null},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:null},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}]});var e=Backbone.Collection.extend({model:d,to_key_value_dict:function(){var g={};this.each(function(h){g[h.get("key")]=h.get("value")});return g},get_value:function(g){var h=this.get(g);if(h){return h.get("value")}return undefined}},{from_config_dict:function(h){var g=c.map(c.keys(h),function(i){return{key:i,value:h[i]}});return new e(g)}});var a=Backbone.View.extend({className:"config-settings-view",render:function(){var g=this.$el;this.collection.each(function(k,p){if(k.get("hidden")){return}var j="param_"+p,q=k.get("type"),u=k.get("value");var w=$("<div class='form-row' />").appendTo(g);w.append($("<label />").attr("for",j).text(k.get("label")+":"));if(q==="bool"){w.append($('<input type="checkbox" />').attr("id",j).attr("name",j).attr("checked",u))}else{if(q==="text"){w.append($('<input type="text"/>').attr("id",j).val(u).click(function(){$(this).select()}))}else{if(q==="select"){var s=$("<select />").attr("id",j);for(var n=0;n<k.options.length;n++){$("<option/>").text(k.options[n].label).attr("value",k.options[n].value).appendTo(s)}s.val(u);w.append(s)}else{if(q==="color"){var v=$("<div/>").appendTo(w),r=$("<input />").attr("id",j).attr("name",j).val(u).css("float","left").appendTo(v).click(function(x){$(".tooltip").removeClass("in");var i=$(this).siblings(".tooltip").addClass("in");i.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(i).height()/2)+($(this).height()/2)}).show();i.click(function(y){y.stopPropagation()});$(document).bind("click.color-picker",function(){i.hide();$(document).unbind("click.color-picker")});x.stopPropagation()}),o=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(v).attr("title","Set new random color").tooltip(),t=$("<div class='tooltip right' style='position: absolute;' />").appendTo(v).hide(),l=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(t),h=$("<div class='tooltip-arrow'></div>").appendTo(t),m=$.farbtastic(l,{width:100,height:100,callback:r,color:u});v.append($("<div/>").css("clear","both"));(function(i){o.click(function(){i.setColor(f.get_random_color())})})(m)}else{w.append($("<input />").attr("id",j).attr("name",j).val(u))}}}}if(k.help){w.append($("<div class='help'/>").text(k.help))}});return this},render_in_modal:function(k){var g=this,j=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},h=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc");g.update_from_form()},i=function(l){if((l.keyCode||l.which)===27){j()}else{if((l.keyCode||l.which)===13){h()}}};$(window).bind("keypress.check_enter_esc",i);if(this.$el.children().length===0){this.render()}Galaxy.modal.show({title:k||"Configure",body:this.$el,buttons:{Cancel:j,OK:h}})},update_from_form:function(){var g=this;this.collection.each(function(i,h){if(!i.get("hidden")){var k="param_"+h;var j=g.$el.find("#"+k).val();if(i.get("type")==="bool"){j=container.find("#"+k).is(":checked")}i.set_value(j)}})}});return{ConfigSettingCollection:e,ConfigSettingCollectionView:a}});
\ No newline at end of file
+define(["libs/underscore","viz/trackster/util","utils/config"],function(c,f,b){var d=Backbone.Model.extend({initialize:function(g){var h=this.get("key");this.set("id",h);var i=c.find(d.known_settings_defaults,function(j){return j.key===h});if(i){this.set(c.extend({},i,g))}if(!this.get("value")){if(this.get("type")==="color"){this.set("value",f.get_random_color())}else{this.set_value(this.get("default_value"))}}},set_value:function(h){var g=this.get("type");if(g==="float"){h=parseFloat(h)}else{if(g==="int"){h=parseInt(h,10)}}this.set("value",h)}},{known_settings_defaults:[{key:"name",label:"Name",type:"text",default_value:""},{key:"color",label:"Color",type:"color",default_value:null},{key:"min_value",label:"Min Value",type:"float",default_value:null},{key:"max_value",label:"Max Value",type:"float",default_value:null},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true},{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"block_color",label:"Block color",type:"color",default_value:null},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:null},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true}]});var e=Backbone.Collection.extend({model:d,to_key_value_dict:function(){var g={};this.each(function(h){g[h.get("key")]=h.get("value")});return g},get_value:function(g){var h=this.get(g);if(h){return h.get("value")}return undefined},set_value:function(g,i){var h=this.get(g);if(h){return h.set_value(i)}return undefined},set_default_value:function(h,g){var i=this.get(h);if(i){return i.set("default_value",g)}return undefined}},{from_models_and_saved_values:function(h,g){if(g){c.each(h,function(i){if(g[i.key]){i.value=g[i.key]}})}return new e(h)}});var a=Backbone.View.extend({className:"config-settings-view",render:function(){var g=this.$el;this.collection.each(function(k,p){if(k.get("hidden")){return}var j="param_"+p,q=k.get("type"),u=k.get("value");var w=$("<div class='form-row' />").appendTo(g);w.append($("<label />").attr("for",j).text(k.get("label")+":"));if(q==="bool"){w.append($('<input type="checkbox" />').attr("id",j).attr("name",j).attr("checked",u))}else{if(q==="text"){w.append($('<input type="text"/>').attr("id",j).val(u).click(function(){$(this).select()}))}else{if(q==="select"){var s=$("<select />").attr("id",j);for(var n=0;n<k.options.length;n++){$("<option/>").text(k.options[n].label).attr("value",k.options[n].value).appendTo(s)}s.val(u);w.append(s)}else{if(q==="color"){var v=$("<div/>").appendTo(w),r=$("<input />").attr("id",j).attr("name",j).val(u).css("float","left").appendTo(v).click(function(x){$(".tooltip").removeClass("in");var i=$(this).siblings(".tooltip").addClass("in");i.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(i).height()/2)+($(this).height()/2)}).show();i.click(function(y){y.stopPropagation()});$(document).bind("click.color-picker",function(){i.hide();$(document).unbind("click.color-picker")});x.stopPropagation()}),o=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(v).attr("title","Set new random color").tooltip(),t=$("<div class='tooltip right' style='position: absolute;' />").appendTo(v).hide(),l=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(t),h=$("<div class='tooltip-arrow'></div>").appendTo(t),m=$.farbtastic(l,{width:100,height:100,callback:r,color:u});v.append($("<div/>").css("clear","both"));(function(i){o.click(function(){i.setColor(f.get_random_color())})})(m)}else{w.append($("<input />").attr("id",j).attr("name",j).val(u))}}}}if(k.help){w.append($("<div class='help'/>").text(k.help))}});return this},render_in_modal:function(k){var g=this,j=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},h=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc");g.update_from_form()},i=function(l){if((l.keyCode||l.which)===27){j()}else{if((l.keyCode||l.which)===13){h()}}};$(window).bind("keypress.check_enter_esc",i);if(this.$el.children().length===0){this.render()}Galaxy.modal.show({title:k||"Configure",body:this.$el,buttons:{Cancel:j,OK:h}})},update_from_form:function(){var g=this;this.collection.each(function(i,h){if(!i.get("hidden")){var k="param_"+h;var j=g.$el.find("#"+k).val();if(i.get("type")==="bool"){j=g.$el.find("#"+k).is(":checked")}i.set_value(j)}})}});return{ConfigSettingCollection:e,ConfigSettingCollectionView:a}});
\ No newline at end of file
diff -r a59ce5d9dd54eb2e2c7145c149aea7f92cb04d8b -r e826721ef957d2d331331663153a509bb6f0ebed static/scripts/packed/viz/trackster.js
--- a/static/scripts/packed/viz/trackster.js
+++ b/static/scripts/packed/viz/trackster.js
@@ -1,1 +1,1 @@
-var ui=null;var view=null;var browser_router=null;require(["utils/utils","libs/jquery/jstorage","libs/jquery/jquery.event.drag","libs/jquery/jquery.event.hover","libs/jquery/jquery.mousewheel","libs/jquery/jquery-ui","libs/jquery/select2","libs/farbtastic","libs/jquery/jquery.form","libs/jquery/jquery.rating","mvc/ui"],function(a){a.cssLoadFile("static/style/jquery.rating.css");a.cssLoadFile("static/style/autocomplete_tagging.css");a.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");a.cssLoadFile("static/style/library.css");a.cssLoadFile("static/style/trackster.css")});define(["libs/backbone/backbone","viz/visualization","viz/trackster_ui"],function(c,a,b){var d=Backbone.View.extend({initialize:function(){ui=new b.TracksterUI(galaxy_config.root);ui.createButtonMenu();ui.buttonMenu.$el.attr("style","float: right");$("#center .unified-panel-header-inner").append(ui.buttonMenu.$el);$("#right .unified-panel-title").append("Bookmarks");$("#right .unified-panel-icons").append("<a id='add-bookmark-button' class='icon-button menu-button plus-button' href='javascript:void(0);' title='Add bookmark'></a>");$("#right-border").click(function(){view.resize_window()});force_right_panel("hide");if(galaxy_config.app.id){this.view_existing()}else{this.view_new()}},set_up_router:function(e){browser_router=new a.TrackBrowserRouter(e);Backbone.history.start()},view_existing:function(){var e=galaxy_config.app.viz_config;view=ui.create_visualization({container:$("#center .unified-panel-body"),name:e.title,vis_id:e.vis_id,dbkey:e.dbkey},e.viewport,e.tracks,e.bookmarks,true);this.init_editor()},view_new:function(){var e=this;$.ajax({url:galaxy_config.root+"api/genomes?chrom_info=True",data:{},error:function(){alert("Couldn't create new browser.")},success:function(f){Galaxy.modal.show({title:"New Visualization",body:e.template_view_new(f),buttons:{Cancel:function(){window.location=galaxy_config.root+"visualization/list"},Create:function(){e.create_browser($("#new-title").val(),$("#new-dbkey").val());Galaxy.modal.hide()}}});if(galaxy_config.app.default_dbkey){$("#new-dbkey").val(galaxy_config.app.default_dbkey)}$("#new-title").focus();$("select[name='dbkey']").select2();$("#overlay").css("overflow","auto")}})},template_view_new:function(e){var g='<form id="new-browser-form" action="javascript:void(0);" method="post" onsubmit="return false;"><div class="form-row"><label for="new-title">Browser name:</label><div class="form-row-input"><input type="text" name="title" id="new-title" value="Unnamed"></input></div><div style="clear: both;"></div></div><div class="form-row"><label for="new-dbkey">Reference genome build (dbkey): </label><div class="form-row-input"><select name="dbkey" id="new-dbkey">';for(var f=0;f<e.length;f++){g+='<option value="'+e[f][1]+'">'+e[f][0]+"</option>"}g+='</select></div><div style="clear: both;"></div></div><div class="form-row">Is the build not listed here? <a href="'+galaxy_config.root+'user/dbkeys?use_panels=True">Add a Custom Build</a></div></form>';return g},create_browser:function(f,e){$(document).trigger("convert_to_values");view=ui.create_visualization({container:$("#center .unified-panel-body"),name:f,dbkey:e},galaxy_config.app.gene_region);this.init_editor();view.editor=true},init_editor:function(){$("#center .unified-panel-title").text(view.prefs.name+" ("+view.dbkey+")");if(galaxy_config.app.add_dataset){$.ajax({url:galaxy_config.root+"api/datasets/"+galaxy_config.app.add_dataset,data:{hda_ldda:"hda",data_type:"track_config"},dataType:"json",success:function(e){view.add_drawable(b.object_from_template(e,view,view))}})}$("#add-bookmark-button").click(function(){var f=view.chrom+":"+view.low+"-"+view.high,e="Bookmark description";return ui.add_bookmark(f,e,true)});ui.init_keyboard_nav(view);this.set_up_router({view:view})}});return{GalaxyApp:d}});
\ No newline at end of file
+var ui=null;var view=null;var browser_router=null;require(["utils/utils","libs/jquery/jstorage","libs/jquery/jquery.event.drag","libs/jquery/jquery.event.hover","libs/jquery/jquery.mousewheel","libs/jquery/jquery-ui","libs/jquery/select2","libs/farbtastic","libs/jquery/jquery.form","libs/jquery/jquery.rating","mvc/ui"],function(a){a.cssLoadFile("static/style/jquery.rating.css");a.cssLoadFile("static/style/autocomplete_tagging.css");a.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");a.cssLoadFile("static/style/library.css");a.cssLoadFile("static/style/trackster.css")});define(["libs/backbone/backbone","viz/visualization","viz/trackster_ui"],function(c,a,b){var d=Backbone.View.extend({initialize:function(){ui=new b.TracksterUI(galaxy_config.root);ui.createButtonMenu();ui.buttonMenu.$el.attr("style","float: right");$("#center .unified-panel-header-inner").append(ui.buttonMenu.$el);$("#right .unified-panel-title").append("Bookmarks");$("#right .unified-panel-icons").append("<a id='add-bookmark-button' class='icon-button menu-button plus-button' href='javascript:void(0);' title='Add bookmark'></a>");$("#right-border").click(function(){view.resize_window()});force_right_panel("hide");if(galaxy_config.app.id){this.view_existing()}else{this.view_new()}},set_up_router:function(e){browser_router=new a.TrackBrowserRouter(e);Backbone.history.start()},view_existing:function(){var e=galaxy_config.app.viz_config;view=ui.create_visualization({container:$("#center .unified-panel-body"),name:e.title,vis_id:e.vis_id,dbkey:e.dbkey},e.viewport,e.tracks,e.bookmarks,true);this.init_editor()},view_new:function(){var e=this;$.ajax({url:galaxy_config.root+"api/genomes?chrom_info=True",data:{},error:function(){alert("Couldn't create new browser.")},success:function(f){Galaxy.modal.show({title:"New Visualization",body:e.template_view_new(f),buttons:{Cancel:function(){window.location=galaxy_config.root+"visualization/list"},Create:function(){e.create_browser($("#new-title").val(),$("#new-dbkey").val());Galaxy.modal.hide()}}});if(galaxy_config.app.default_dbkey){$("#new-dbkey").val(galaxy_config.app.default_dbkey)}$("#new-title").focus();$("select[name='dbkey']").select2();$("#overlay").css("overflow","auto")}})},template_view_new:function(e){var g='<form id="new-browser-form" action="javascript:void(0);" method="post" onsubmit="return false;"><div class="form-row"><label for="new-title">Browser name:</label><div class="form-row-input"><input type="text" name="title" id="new-title" value="Unnamed"></input></div><div style="clear: both;"></div></div><div class="form-row"><label for="new-dbkey">Reference genome build (dbkey): </label><div class="form-row-input"><select name="dbkey" id="new-dbkey">';for(var f=0;f<e.length;f++){g+='<option value="'+e[f][1]+'">'+e[f][0]+"</option>"}g+='</select></div><div style="clear: both;"></div></div><div class="form-row">Is the build not listed here? <a href="'+galaxy_config.root+'user/dbkeys?use_panels=True">Add a Custom Build</a></div></form>';return g},create_browser:function(f,e){$(document).trigger("convert_to_values");view=ui.create_visualization({container:$("#center .unified-panel-body"),name:f,dbkey:e},galaxy_config.app.gene_region);this.init_editor();view.editor=true},init_editor:function(){$("#center .unified-panel-title").text(view.config.get_value("name")+" ("+view.dbkey+")");if(galaxy_config.app.add_dataset){$.ajax({url:galaxy_config.root+"api/datasets/"+galaxy_config.app.add_dataset,data:{hda_ldda:"hda",data_type:"track_config"},dataType:"json",success:function(e){view.add_drawable(b.object_from_template(e,view,view))}})}$("#add-bookmark-button").click(function(){var f=view.chrom+":"+view.low+"-"+view.high,e="Bookmark description";return ui.add_bookmark(f,e,true)});ui.init_keyboard_nav(view);this.set_up_router({view:view})}});return{GalaxyApp:d}});
\ No newline at end of file
diff -r a59ce5d9dd54eb2e2c7145c149aea7f92cb04d8b -r e826721ef957d2d331331663153a509bb6f0ebed static/scripts/packed/viz/trackster/tracks.js
--- a/static/scripts/packed/viz/trackster/tracks.js
+++ b/static/scripts/packed/viz/trackster/tracks.js
@@ -1,1 +1,1 @@
-define(["libs/underscore","viz/visualization","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters","viz/trackster/filters","mvc/data","mvc/tools"],function(ab,y,l,u,L,i,X,R){var q=ab.extend;var n={};var k=function(ac,ad){n[ac.attr("id")]=ad};var m=function(ac,ad,af,ae){af=".group";n[ac.attr("id")]=ae;ac.bind("drag",{handle:"."+ad,relative:true},function(an,ao){var am=$(this),ar=$(this).parent(),aj=ar.children(".track,.group"),al=n[$(this).attr("id")],ai,ah,ap,ag,ak;ah=$(this).parents(af);if(ah.length!==0){ap=ah.position().top;ag=ap+ah.outerHeight();var aq=n[ah.attr("id")];if(ao.offsetY<ap){$(this).insertBefore(ah);aq.remove_drawable(al);aq.container.add_drawable_before(al,aq);return}else{if(ao.offsetY>ag){$(this).insertAfter(ah);aq.remove_drawable(al);aq.container.add_drawable(al);return}}}ah=null;for(ak=0;ak<aj.length;ak++){ai=$(aj.get(ak));ap=ai.position().top;ag=ap+ai.outerHeight();if(ai.is(af)&&this!==ai.get(0)&&ao.offsetY>=ap&&ao.offsetY<=ag){if(ao.offsetY-ap<ag-ao.offsetY){ai.find(".content-div").prepend(this)}else{ai.find(".content-div").append(this)}if(al.container){al.container.remove_drawable(al)}n[ai.attr("id")].add_drawable(al);return}}for(ak=0;ak<aj.length;ak++){ai=$(aj.get(ak));if(ao.offsetY<ai.position().top&&!(ai.hasClass("reference-track")||ai.hasClass("intro"))){break}}if(ak===aj.length){if(this!==aj.get(ak-1)){ar.append(this);n[ar.attr("id")].move_drawable(al,ak)}}else{if(this!==aj.get(ak)){$(this).insertBefore(aj.get(ak));n[ar.attr("id")].move_drawable(al,(ao.deltaY>0?ak-1:ak))}}}).bind("dragstart",function(){$(this).addClass("dragging")}).bind("dragend",function(){$(this).removeClass("dragging")})};var F=20,B=100,I=12000,S=400,K=5000,x=100,o="Cannot display dataset due to an error. ",J="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",G="No data for this chrom/contig.",w="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",z="Tool cannot be rerun: ",b="Loading data...",U="Ready for display",Q=10,H=20,C=["Histogram","Line","Filled","Intensity"];function V(ad,ac){if(!ac){ac=0}var ae=Math.pow(10,ac);return Math.round(ad*ae)/ae}var r=function(ad,ac,af){if(!r.id_counter){r.id_counter=0}this.id=r.id_counter++;this.view=ad;this.container=ac;this.drag_handle_class=af.drag_handle_class;this.is_overview=false;this.action_icons={};this.config=new j({params:this.config_params,onchange:this.config_onchange,saved_values:af.prefs});this.prefs=this.config.get("values");if(!this.prefs.name){this.prefs.name=af.name}if(this.config_onchange){this.config.on("change:values",this.config_onchange,this)}this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").addClass("track-icons").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(ag){ag.stopPropagation()});var ae=this;this.container_div.hover(function(){ae.icons_div.show()},function(){ae.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};r.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ac){if(ac.prefs.content_visible){ac.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ac.hide_contents();ac.prefs.content_visible=false}else{ac.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ac.prefs.content_visible=true;ac.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"gear",on_click_fn:function(ad){var ac=new a({model:ad.config});ac.render()}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ac){$(".tooltip").remove();ac.remove()}}];q(r.prototype,{config_params:[{key:"name",label:"Name",type:"text",default_value:""},{key:"content_visible",type:"bool",default_value:true,hidden:true}],config_onchange:function(){this.track.set_name(this.track.config.get("values").name)},init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.prefs.content_visible){return true}return false},request_draw:function(){},_draw:function(ac){},to_dict:function(){},set_name:function(ac){this.old_name=this.prefs.name;this.prefs.name=ac;this.name_div.text(this.prefs.name)},revert_name:function(){if(this.old_name){this.prefs.name=this.old_name;this.name_div.text(this.prefs.name)}},remove:function(){this.changed();this.container.remove_drawable(this);var ac=this.view;this.container_div.hide(0,function(){$(this).remove();ac.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ad,ai,ah,ag,ac,af){var ae=this;this.action_icons[ad]=$("<a/>").attr("title",ai).addClass("icon-button").addClass(ah).tooltip().click(function(){ag(ae)}).appendTo(this.icons_div);if(af){this.action_icons[ad].hide()}},build_action_icons:function(ac){var ae;for(var ad=0;ad<ac.length;ad++){ae=ac[ad];this.add_action_icon(ae.name,ae.title,ae.css_class,ae.on_click_fn,ae.prepend,ae.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var A=function(ad,ac,ae){r.call(this,ad,ac,ae);this.obj_type=ae.obj_type;this.drawables=[]};q(A.prototype,r.prototype,{unpack_drawables:function(ae){this.drawables=[];var ad;for(var ac=0;ac<ae.length;ac++){ad=p(ae[ac],this.view,this);this.add_drawable(ad)}},init:function(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac].init()}},_draw:function(ac){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad]._draw(ac)}},to_dict:function(){var ad=[];for(var ac=0;ac<this.drawables.length;ac++){ad.push(this.drawables[ac].to_dict())}return{prefs:this.prefs,obj_type:this.obj_type,drawables:ad}},add_drawable:function(ac){this.drawables.push(ac);ac.container=this;this.changed()},add_drawable_before:function(ae,ac){this.changed();var ad=this.drawables.indexOf(ac);if(ad!==-1){this.drawables.splice(ad,0,ae);return true}return false},replace_drawable:function(ae,ac,ad){var af=this.drawables.indexOf(ae);if(af!==-1){this.drawables[af]=ac;if(ad){ae.container_div.replaceWith(ac.container_div)}this.changed()}return af},remove_drawable:function(ad){var ac=this.drawables.indexOf(ad);if(ac!==-1){this.drawables.splice(ac,1);ad.container=null;this.changed();return true}return false},move_drawable:function(ad,ae){var ac=this.drawables.indexOf(ad);if(ac!==-1){this.drawables.splice(ac,1);this.drawables.splice(ae,0,ad);this.changed();return true}return false},get_drawables:function(){return this.drawables},get_tracks:function(af){var ac=this.drawables.slice(0),ad=[],ae;while(ac.length!==0){ae=ac.shift();if(ae instanceof af){ad.push(ae)}else{if(ae.drawables){ac=ac.concat(ae.drawables)}}}return ad}});var P=function(ad,ac,af){q(af,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});A.call(this,ad,ac,af);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);k(this.container_div,this);k(this.content_div,this);m(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new i.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in af){this.unpack_drawables(af.drawables)}if("filters" in af){var ae=this.filters_manager;this.filters_manager=new i.FiltersManager(this,af.filters);ae.parent_div.replaceWith(this.filters_manager.parent_div);if(af.filters.visible){this.setup_multitrack_filtering()}}};q(P.prototype,r.prototype,A.prototype,{action_icons_def:[r.prototype.action_icons_def[0],r.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ac){$(".tooltip").remove();ac.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ac){if(ac.filters_manager.visible()){ac.filters_manager.clear_filters();ac._restore_filter_managers()}else{ac.setup_multitrack_filtering();ac.request_draw({clear_tile_cache:true})}ac.filters_manager.toggle()}},r.prototype.action_icons_def[2]],build_container_div:function(){var ac=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(ac)}return ac},build_header_div:function(){var ac=$("<div/>").addClass("track-header");ac.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.prefs.name).appendTo(ac);return ac},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var ae=this.drawables.length;if(ae===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(ae===1){if(this.drawables[0] instanceof f){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var al,ak,ai,ao=true,ag=this.drawables[0].get_type(),ac=0;for(al=0;al<ae;al++){ai=this.drawables[al];if(ai.get_type()!==ag){can_composite=false;break}if(ai instanceof d){ac++}}if(ao||ac===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tooltip").remove()}if(ac>1&&ac===this.drawables.length){var ap={},ad;ai=this.drawables[0];for(ak=0;ak<ai.filters_manager.filters.length;ak++){ad=ai.filters_manager.filters[ak];ap[ad.name]=[ad]}for(al=1;al<this.drawables.length;al++){ai=this.drawables[al];for(ak=0;ak<ai.filters_manager.filters.length;ak++){ad=ai.filters_manager.filters[ak];if(ad.name in ap){ap[ad.name].push(ad)}}}this.filters_manager.remove_all();var af,ah,aj,am;for(var an in ap){af=ap[an];if(af.length===ac){ah=new i.NumberFilter({name:af[0].name,index:af[0].index});this.filters_manager.add_filter(ah)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac].filters_manager=this.saved_filters_managers[ac]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var ac=0;ac<this.drawables.length;ac++){drawable=this.drawables[ac];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var ad=new f(this.view,this.view,{name:this.prefs.name,drawables:this.drawables});var ac=this.container.replace_drawable(this,ad,true);ad.request_draw()},add_drawable:function(ac){A.prototype.add_drawable.call(this,ac);this.update_icons()},remove_drawable:function(ac){A.prototype.remove_drawable.call(this,ac);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ac=q(A.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return ac},request_draw:function(ac){ab.each(this.drawables,function(ad){ad.request_draw(ac)})}});var Y=Backbone.View.extend({initialize:function(ac){q(ac,{obj_type:"View"});A.call(this,"View",ac.container,ac);this.chrom=null;this.vis_id=ac.vis_id;this.dbkey=ac.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.render();this.canvas_manager=new y.CanvasManager(this.container.get(0).ownerDocument);this.reset();this.config=new j({track:this,params:[{key:"a_color",label:"A Color",type:"color",default_value:"#FF0000"},{key:"c_color",label:"C Color",type:"color",default_value:"#00FF00"},{key:"g_color",label:"G Color",type:"color",default_value:"#0000FF"},{key:"t_color",label:"T Color",type:"color",default_value:"#FF00FF"},{key:"n_color",label:"N Color",type:"color",default_value:"#AAAAAA"}],saved_values:ac.prefs,onchange:function(){track.request_redraw({clear_tile_cache:true})}})},render:function(){this.requested_redraw=false;var ae=this.container,ac=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ae);this.browser_content_div=$("<div/>").addClass("content").appendTo(ae);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ae);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;k(this.viewport_container,ac);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var af=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){y.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":ac.dbkey},function(ag){ab.each(ag,function(ah){ac.add_drawable(p(ah,ac,ac))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).addClass("chrom-nav").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ad=function(ag){if(ag.type==="focusout"||(ag.keyCode||ag.which)===13||(ag.keyCode||ag.which)===27){if((ag.keyCode||ag.which)!==27){ac.go_to($(this).val())}$(this).hide();$(this).val("");ac.location_span.show();ac.chrom_select.show()}ag.stopPropagation()};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ad).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("title","Click to change location").tooltip({placement:"bottom"}).appendTo(this.nav_controls);this.location_span.click(function(){ac.location_span.hide();ac.chrom_select.hide();ac.nav_input.val(ac.chrom+":"+ac.low+"-"+ac.high);ac.nav_input.css("display","inline-block");ac.nav_input.select();ac.nav_input.focus();ac.nav_input.autocomplete({source:function(ai,ag){var aj=[],ah=$.map(ac.get_tracks(d),function(ak){return ak.data_manager.search_features(ai.term).success(function(al){aj=aj.concat(al)})});$.when.apply($,ah).done(function(){ag($.map(aj,function(ak){return{label:ak[0],value:ak[1]}}))})}})});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tooltip({placement:"bottom"}).click(function(){ac.zoom_out()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){ac.zoom_in()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){ac.change_chrom(ac.chrom_select.val())});this.browser_content_div.click(function(ag){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(ag){ac.zoom_in(ag.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(ag,ah){this.current_x=ah.offsetX}).bind("drag",function(ag,ai){var aj=ai.offsetX-this.current_x;this.current_x=ai.offsetX;var ah=Math.round(aj/ac.viewport_container.width()*(ac.max_high-ac.max_low));ac.move_delta(-ah)});this.overview_close.click(function(){ac.reset_overview()});this.viewport_container.bind("draginit",function(ag,ah){if(ag.clientX>ac.viewport_container.width()-16){return false}}).bind("dragstart",function(ag,ah){ah.original_low=ac.low;ah.current_height=ag.clientY;ah.current_x=ah.offsetX}).bind("drag",function(ai,ak){var ag=$(this);var al=ak.offsetX-ak.current_x;var ah=ag.scrollTop()-(ai.clientY-ak.current_height);ag.scrollTop(ah);ak.current_height=ai.clientY;ak.current_x=ak.offsetX;var aj=Math.round(al/ac.viewport_container.width()*(ac.high-ac.low));ac.move_delta(aj)}).bind("mousewheel",function(ai,ak,ah,ag){if(ah){ah*=50;var aj=Math.round(-ah/ac.viewport_container.width()*(ac.high-ac.low));ac.move_delta(aj)}});this.top_labeltrack.bind("dragstart",function(ag,ah){return $("<div/>").addClass("zoom-area").css("height",ac.browser_content_div.height()+ac.top_labeltrack.height()+ac.nav_labeltrack.height()+1).appendTo($(this))}).bind("drag",function(ak,al){$(al.proxy).css({left:Math.min(ak.pageX,al.startX)-ac.container.offset().left,width:Math.abs(ak.pageX-al.startX)});var ah=Math.min(ak.pageX,al.startX)-ac.container.offset().left,ag=Math.max(ak.pageX,al.startX)-ac.container.offset().left,aj=(ac.high-ac.low),ai=ac.viewport_container.width();ac.update_location(Math.round(ah/ai*aj)+ac.low,Math.round(ag/ai*aj)+ac.low)}).bind("dragend",function(al,am){var ah=Math.min(al.pageX,am.startX),ag=Math.max(al.pageX,am.startX),aj=(ac.high-ac.low),ai=ac.viewport_container.width(),ak=ac.low;ac.low=Math.round(ah/ai*aj)+ak;ac.high=Math.round(ag/ai*aj)+ak;$(am.proxy).remove();ac.request_redraw()});this.add_label_track(new W(this,{content_div:this.top_labeltrack}));this.add_label_track(new W(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){ac.resize_window()},500)});$(document).bind("redraw",function(){ac.redraw()});this.reset();$(window).trigger("resize")},get_base_color:function(ac){return this.config.get("values")[ac.toLowerCase()+"_color"]||this.config.get("values").n_color}});q(Y.prototype,A.prototype,{changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(ad,af,ac,ag){if(this.timer){clearTimeout(this.timer)}if(ag){var ae=this;this.timer=setTimeout(function(){ae.trigger("navigate",ad+":"+af+"-"+ac)},500)}else{view.trigger("navigate",ad+":"+af+"-"+ac)}},update_location:function(ac,ae){this.location_span.text(commatize(ac)+" - "+commatize(ae));this.nav_input.val(this.chrom+":"+commatize(ac)+"-"+commatize(ae));var ad=view.chrom_select.val();if(ad!==""){this.trigger_navigate(ad,view.low,view.high,true)}},load_chroms:function(ae){ae.num=x;var ac=this,ad=$.Deferred();$.ajax({url:galaxy_config.root+"api/genomes/"+this.dbkey,data:ae,dataType:"json",success:function(ag){if(ag.chrom_info.length===0){return}if(ag.reference){var ah=new D(ac);ac.add_label_track(ah);ac.reference_track=ah}ac.chrom_data=ag.chrom_info;var ak='<option value="">Select Chrom/Contig</option>';for(var aj=0,af=ac.chrom_data.length;aj<af;aj++){var ai=ac.chrom_data[aj].chrom;ak+='<option value="'+ai+'">'+ai+"</option>"}if(ag.prev_chroms){ak+='<option value="previous">Previous '+x+"</option>"}if(ag.next_chroms){ak+='<option value="next">Next '+x+"</option>"}ac.chrom_select.html(ak);ac.chrom_start_index=ag.start_index;ad.resolve(ag.chrom_info)},error:function(){alert("Could not load chroms for this dbkey: "+ac.dbkey)}});return ad},change_chrom:function(ah,ad,aj){var ae=this;if(!ae.chrom_data){ae.load_chroms_deferred.then(function(){ae.change_chrom(ah,ad,aj)});return}if(!ah||ah==="None"){return}if(ah==="previous"){ae.load_chroms({low:this.chrom_start_index-x});return}if(ah==="next"){ae.load_chroms({low:this.chrom_start_index+x});return}var ai=$.grep(ae.chrom_data,function(ak,al){return ak.chrom===ah})[0];if(ai===undefined){ae.load_chroms({chrom:ah},function(){ae.change_chrom(ah,ad,aj)});return}else{if(ah!==ae.chrom){ae.chrom=ah;ae.chrom_select.val(ae.chrom);ae.max_high=ai.len-1;ae.reset();for(var ag=0,ac=ae.drawables.length;ag<ac;ag++){var af=ae.drawables[ag];if(af.init){af.init()}}if(ae.reference_track){ae.reference_track.init()}}if(ad===undefined&&aj===undefined){ae.low=0;ae.high=ae.max_high}else{ae.low=(ad!==undefined?Math.max(ad,0):0);if(aj===undefined){ae.low=Math.max(ae.low-15,0);ae.high=ae.low+30}else{ae.high=Math.min(aj,ae.max_high)}}ae.reset_overview();ae.request_redraw()}},go_to:function(ag){ag=ag.replace(/,/g,"");ag=ag.replace(/:|\-/g," ");var ad=ag.split(/\s+/),af=ad[0],ae=(ad[1]?parseInt(ad[1],10):undefined),ac=(ad[2]?parseInt(ad[2],10):undefined);this.change_chrom(af,ae,ac)},move_fraction:function(ae){var ac=this;var ad=ac.high-ac.low;this.move_delta(ae*ad)},move_delta:function(af){var ac=this;var ae=ac.high-ac.low;if(ac.low-af<ac.max_low){ac.low=ac.max_low;ac.high=ac.max_low+ae}else{if(ac.high-af>ac.max_high){ac.high=ac.max_high;ac.low=ac.max_high-ae}else{ac.high-=af;ac.low-=af}}ac.request_redraw({data_fetch:false});if(this.redraw_on_move_fn){clearTimeout(this.redraw_on_move_fn)}this.redraw_on_move_fn=setTimeout(function(){ac.request_redraw()},200);var ad=ac.chrom_select.val();this.trigger_navigate(ad,ac.low,ac.high,true)},add_drawable:function(ac){A.prototype.add_drawable.call(this,ac);ac.init();this.changed();this.update_intro_div()},add_label_track:function(ac){ac.view=this;ac.init();this.label_tracks.push(ac)},remove_drawable:function(ae,ad){A.prototype.remove_drawable.call(this,ae);if(ad){var ac=this;ae.container_div.hide(0,function(){$(this).remove();ac.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(ad,ae){var ac=this,af=(ae?[ae]:ac.drawables);ab.each(af,function(ag){var ah=ab.find(ac.tracks_to_be_redrawn,function(ai){return ai[0]===ag});if(ah){ah[1]=ad}else{ac.tracks_to_be_redrawn.push([ag,ad])}});if(!this.requested_redraw){requestAnimationFrame(function(){ac._redraw()});this.requested_redraw=true}},_redraw:function(){this.requested_redraw=false;var ac=this.low,ag=this.high;if(ac<this.max_low){ac=this.max_low}if(ag>this.max_high){ag=this.max_high}var ad=this.high-this.low;if(this.high!==0&&ad<this.min_separation){ag=ac+this.min_separation}this.low=Math.floor(ac);this.high=Math.ceil(ag);this.update_location(this.low,this.high);this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var af=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ah=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ae=13;this.overview_box.css({left:af,width:Math.max(ae,ah)}).show();if(ah<ae){this.overview_box.css("left",af-(ae-ah)/2)}if(this.overview_highlight){this.overview_highlight.css({left:af,width:ah})}ab.each(this.tracks_to_be_redrawn,function(ak){var ai=ak[0],aj=ak[1];if(ai){ai._draw(aj)}});this.tracks_to_be_redrawn=[];ab.each(this.label_tracks,function(ai){ai._draw()})},zoom_in:function(ad,ae){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var af=this.high-this.low,ag=af/2+this.low,ac=(af/this.zoom_factor)/2;if(ad){ag=ad/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(ag-ac);this.high=Math.round(ag+ac);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ad=this.high-this.low,ae=ad/2+this.low,ac=(ad*this.zoom_factor)/2;this.low=Math.round(ae-ac);this.high=Math.round(ae+ac);this.changed();this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.request_redraw()},set_overview:function(ae){if(this.overview_drawable){if(this.overview_drawable.dataset.id===ae.dataset.id){return}this.overview_viewport.find(".track").remove()}var ad=ae.copy({content_div:this.overview_viewport}),ac=this;ad.header_div.hide();ad.is_overview=true;ac.overview_drawable=ad;this.overview_drawable.postdraw_actions=function(){ac.overview_highlight.show().height(ac.overview_drawable.content_div.height());ac.overview_viewport.height(ac.overview_drawable.content_div.height()+ac.overview_box.outerHeight());ac.overview_close.show();ac.resize_window()};ac.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".tooltip").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var s=R.Tool.extend({defaults:{track:null},initialize:function(ac){R.Tool.prototype.initialize.call(this,ac);var ad=true;if(ac.tool_state!==undefined&&ac.tool_state.hidden!==undefined){ad=ac.tool_state.hidden}this.set("hidden",ad);this.remove_inputs(["data","hidden_data","conditional"])},state_dict:function(ac){return ab.extend(this.get_inputs_dict(),{hidden:!this.is_visible()})}});var v=Backbone.View.extend({events:{"change input":"update_value"},render:function(){var ae=this.$el.addClass("param-row"),af=this.model;var ac=$("<div>").addClass("param-label").text(af.get("label")).appendTo(ae);var ad=$("<div/>").addClass("param-input").html(af.get("html")).appendTo(ae);ad.find(":input").val(af.get("value"));$("<div style='clear: both;'/>").appendTo(ae)},update_value:function(ac){this.model.set_value($(ac.target).val())}});var aa=Backbone.View.extend({initialize:function(ac){this.model.on("change:hidden",this.set_visible,this)},render:function(){var ad=this;tool=this.model,parent_div=this.$el.addClass("dynamic-tool").hide();parent_div.bind("drag",function(ah){ah.stopPropagation()}).click(function(ah){ah.stopPropagation()}).bind("dblclick",function(ah){ah.stopPropagation()}).keydown(function(ah){ah.stopPropagation()});var ae=$("<div class='tool-name'>").appendTo(parent_div).text(tool.get("name"));tool.get("inputs").each(function(ai){var ah=new v({model:ai});ah.render();parent_div.append(ah.$el)});parent_div.find("input").click(function(){$(this).select()});var af=$("<div>").addClass("param-row").appendTo(parent_div);var ag=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(af);var ac=$("<input type='submit'>").attr("value","Run on visible region").appendTo(af);ac.click(function(){ad.run_on_region()});ag.click(function(){ad.run_on_dataset()});if(tool.is_visible()){this.$el.show()}},set_visible:function(){if(this.model.is_visible()){this.$el.show()}else{this.$el.hide()}},update_params:function(){for(var ac=0;ac<this.params.length;ac++){this.params[ac].update_value()}},run_on_dataset:function(){var ac=this.model;this.run({target_dataset_id:this.model.get("track").dataset.id,action:"rerun",tool_id:ac.id},null,function(ad){Galaxy.modal.show({title:ac.get("name")+" is Running",body:ac.get("name")+" is running on the complete dataset. Tool outputs are in dataset's history.",buttons:{Close:function(){Galaxy.modal.hide()}}})})},run_on_region:function(){var ad=this.model.get("track"),ag=this.model,ai=new y.GenomeRegion({chrom:ad.view.chrom,start:ad.view.low,end:ad.view.high}),aj={target_dataset_id:ad.dataset.id,action:"rerun",tool_id:ag.id,regions:[ai.toJSON()]},ah=ad,al=ag.get("name")+ah.tool_region_and_parameters_str(ai),ac;if(ah.container===view){var ak=new P(view,view,{name:ad.prefs.name});var af=ah.container.replace_drawable(ah,ak,false);ak.container_div.insertBefore(ah.view.content_div.children()[af]);ak.add_drawable(ah);ah.container_div.appendTo(ak.content_div);ac=ak}else{ac=ah.container}var ae=new ah.constructor(view,ac,{name:al,hda_ldda:"hda"});ae.init_for_tool_data();ae.change_mode(ah.mode);ae.set_filters_manager(ah.filters_manager.copy(ae));ae.update_icons();ac.add_drawable(ae);ae.tiles_div.text("Starting job.");this.run(aj,ae,function(am){ae.set_dataset(new X.Dataset(am));ae.tiles_div.text("Running job.");ae.init()})},run:function(ac,ae,af){ac.inputs=this.model.get_inputs_dict();var ad=new l.ServerStateDeferred({ajax_settings:{url:galaxy_config.root+"api/tools",data:JSON.stringify(ac),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(ag){return ag!=="pending"}});$.when(ad.go()).then(function(ag){if(ag==="no converter"){ae.container_div.addClass("error");ae.content_div.text(J)}else{if(ag.error){ae.container_div.addClass("error");ae.content_div.text(z+ag.message)}else{af(ag)}}})}});var E=function(ac,ad){L.Scaler.call(this,ad);this.filter=ac};E.prototype.gen_val=function(ac){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(ac[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var j=Backbone.Model.extend({initialize:function(ad){var ac={};ab.each(ad.params,function(ae){ac[ae.key]=(ae.type==="color"&&!ae.default_value?l.get_random_color():ae.default_value)});if(ad.saved_values){ab.each(this.get("params"),function(ae){if(ae.key in ad.saved_values){ac[ae.key]=ad.saved_values[ae.key]}})}this.set("values",ac)},set_param_default_value:function(ad,ac){var ae=ab.find(this.get("params"),function(af){return af.key===ad});if(ae){ae.default_value=ac}},set_param_value:function(ac,ad){var ae=ab.find(this.get("params"),function(af){return af.key===ac});if(ae){if(typeof ad==="string"||ad instanceof String){if(ad.trim()===""){ad=ae.default_value}else{if(ae.type==="float"){ad=parseFloat(ad)}else{if(ae.type==="int"){ad=parseInt(ad,10)}}}}}if(this.get("values")[ac]!==ad){this.get("values")[ac]=ad;this.trigger("change:values");return true}else{return false}}});var a=Backbone.View.extend({render:function(){var aj=this.model;var ac=$("<div/>").keydown(function(ak){ak.stopPropagation()});var ai;function ah(ao,ak){for(var at=0;at<ao.length;at++){ai=ao[at];if(ai.hidden){continue}var am="param_"+at;var ax=aj.get("values")[ai.key];var az=$("<div class='form-row' />").appendTo(ak);az.append($("<label />").attr("for",am).text(ai.label+":"));if(ai.type==="bool"){az.append($('<input type="checkbox" />').attr("id",am).attr("name",am).attr("checked",ax))}else{if(ai.type==="text"){az.append($('<input type="text"/>').attr("id",am).val(ax).click(function(){$(this).select()}))}else{if(ai.type==="select"){var av=$("<select />").attr("id",am);for(var aq=0;aq<ai.options.length;aq++){$("<option/>").text(ai.options[aq].label).attr("value",ai.options[aq].value).appendTo(av)}av.val(ax);az.append(av)}else{if(ai.type==="color"){var ay=$("<div/>").appendTo(az),au=$("<input/>").attr({id:am,name:am}).val(ax).addClass("color-input").appendTo(ay).click(function(aB){$(".tooltip").removeClass("in");var aA=$(this).siblings(".tooltip").addClass("in");aA.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top+Galaxy.modal.scrollTop()-($(aA).height()/2)+($(this).height()/2)}).show();aA.click(function(aC){aC.stopPropagation()});$(document).bind("click.color-picker",function(){aA.hide();$(document).unbind("click.color-picker")});aB.stopPropagation()}),ar=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(ay).attr("title","Set new random color").tooltip(),aw=$("<div class='tooltip right' style='position: absolute;' />").appendTo(ay).hide(),an=$("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(aw),al=$("<div class='tooltip-arrow'></div>").appendTo(aw),ap=$.farbtastic(an,{width:100,height:100,callback:au,color:ax});ay.append($("<div/>").css("clear","both"));(function(aA){ar.click(function(){aA.setColor(l.get_random_color())})})(ap)}else{az.append($("<input />").attr("id",am).attr("name",am).val(ax))}}}}if(ai.help){az.append($("<div class='help'/>").text(ai.help))}}}ah(aj.get("params"),ac);var ad=this,ag=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ae=function(){ad.update_from_form($(Galaxy.modal.el));Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},af=function(ak){if((ak.keyCode||ak.which)===27){ag()}else{if((ak.keyCode||ak.which)===13){ae()}}};$(window).bind("keypress.check_enter_esc",af);Galaxy.modal.show({title:"Configure",body:ac,buttons:{Cancel:ag,Ok:ae}})},update_from_form:function(ac){var ad=this.model;var ae=false;ab.each(ad.get("params"),function(ah,af){if(!ah.hidden){var ai="param_"+af;var ag=ac.find("#"+ai).val();if(ah.type==="bool"){ag=ac.find("#"+ai).is(":checked")}ae=ad.set_param_value(ah.key,ag)||ae}})}});var c=function(ac,af,ag,ad,ae){this.track=ac;this.region=af;this.low=af.get("start");this.high=af.get("end");this.w_scale=ag;this.canvas=ad;this.html_elt=$("<div class='track-tile'/>").append(ad);this.data=ae;this.stale=false};c.prototype.predisplay_actions=function(){};var M=function(ac,af,ag,ad,ae){c.call(this,ac,af,ag,ad,ae)};M.prototype.predisplay_actions=function(){};var O=function(af,am,ao,ae,ah,ai,ap,ad,al){c.call(this,af,am,ao,ae,ah);this.mode=ai;this.all_slotted=ad;this.feature_mapper=al;this.has_icons=false;if(ap){this.has_icons=true;var aj=this;ae=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:F,width:ae.width}).prependTo(this.html_elt);var ak=new y.GenomeRegion({chrom:af.view.chrom,start:this.low,end:this.high}),an=ah.length,ag=$("<a/>").addClass("icon more-down").attr("title","For speed, only the first "+an+" features in this region were obtained from server. Click to get more data including depth").tooltip().appendTo(message_div),ac=$("<a/>").addClass("icon more-across").attr("title","For speed, only the first "+an+" features in this region were obtained from server. Click to get more data excluding depth").tooltip().appendTo(message_div);ag.click(function(){aj.stale=true;af.data_manager.get_more_data(ak,af.mode,1/aj.w_scale,{},af.data_manager.DEEP_DATA_REQ);$(".tooltip").hide();af.request_draw()}).dblclick(function(aq){aq.stopPropagation()});ac.click(function(){aj.stale=true;af.data_manager.get_more_data(ak,af.mode,1/aj.w_scale,{},af.data_manager.BROAD_DATA_REQ);$(".tooltip").hide();af.request_draw()}).dblclick(function(aq){aq.stopPropagation()})}};q(O.prototype,c.prototype);O.prototype.predisplay_actions=function(){var ad=this,ac={};if(ad.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(ao){if(!this.hovered){return}var aj=$(this).offset(),an=ao.pageX-aj.left,am=ao.pageY-aj.top,at=ad.feature_mapper.get_feature_data(an,am),ak=(at?at[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!ak||$(this).attr("id")!==ak.toString()){$(this).remove()}});if(at){var af=ac[ak];if(!af){var ap={name:at[3],start:at[1],end:at[2],strand:at[4]},ai=ad.track.filters_manager.filters,ah;for(var al=0;al<ai.length;al++){ah=ai[al];ap[ah.name]=at[ah.index]}af=$("<div/>").attr("id",ak).addClass("feature-popup");var au=$("<table/>"),ar,aq,av;for(ar in ap){aq=ap[ar];av=$("<tr/>").appendTo(au);$("<th/>").appendTo(av).text(ar);$("<td/>").attr("align","left").appendTo(av).text(typeof(aq)==="number"?V(aq,2):aq)}af.append($("<div class='feature-popup-inner'>").append(au));ac[ak]=af}af.appendTo($(this).parents(".track-content").children(".overlay"));var ag=an+parseInt(ad.html_elt.css("left"),10)-af.width()/2,ae=am+parseInt(ad.html_elt.css("top"),10)+7;af.css("left",ag+"px").css("top",ae+"px")}else{if(!ao.isPropagationStopped()){ao.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ao)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var g=function(ad,ac,ae){q(ae,{drag_handle_class:"draghandle"});r.call(this,ad,ac,ae);this.dataset=null;if(ae.dataset){this.dataset=(ae.dataset instanceof Backbone.Model?ae.dataset:new X.Dataset(ae.dataset))}this.dataset_check_type="converted_datasets_state";this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in ae?ae.data_query_wait:K);this.data_manager=("data_manager" in ae?ae.data_manager:new y.GenomeDataManager({dataset:this.dataset,genome:new y.Genome({key:ad.dbkey,chroms_info:{chrom_info:ad.chrom_data}}),data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=this.prefs.height;this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in ae)||ae.resize){this.add_resize_handle()}}};q(g.prototype,r.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},r.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"application-dock-270",on_click_fn:function(ac){ac.view.set_overview(ac)}},r.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"ui-slider-050",on_click_fn:function(ac){if(ac.filters_manager.visible()){ac.filters_manager.clear_filters()}else{ac.filters_manager.init_filters()}ac.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(ac){ac.tool.toggle();if(ac.tool.is_visible()){ac.set_name(ac.prefs.name+ac.tool_region_and_parameters_str())}else{ac.revert_name()}$(".tooltip").remove()}},{name:"param_space_viz_icon",title:"Tool parameter space visualization",css_class:"arrow-split",on_click_fn:function(ac){var af='<strong>Tool</strong>: <%= track.tool.get("name") %><br/><strong>Dataset</strong>: <%= track.prefs.name %><br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>',ae=ab.template(af,{track:ac});var ah=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ad=function(){var aj=$('select[name="regions"] option:selected').val(),al,ai=new y.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),ak=ab.map($(".bookmark"),function(am){return new y.GenomeRegion({from_str:$(am).children(".position").text()})});if(aj==="cur"){al=[ai]}else{if(aj==="bookmarks"){al=ak}else{al=[ai].concat(ak)}}Galaxy.modal.hide();window.location.href=galaxy_config.root+"visualization/sweepster?"+$.param({dataset_id:ac.dataset.id,hda_ldda:ac.dataset.get("hda_ldda"),regions:JSON.stringify(new Backbone.Collection(al).toJSON())})},ag=function(ai){if((ai.keyCode||ai.which)===27){ah()}else{if((ai.keyCode||ai.which)===13){ad()}}};Galaxy.modal.show({title:"Visualize tool parameter space and output from different parameter settings?",body:ae,buttons:{No:ah,Yes:ad}})}},r.prototype.action_icons_def[2]],can_draw:function(){return this.dataset&&r.prototype.can_draw.call(this)},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id)},build_header_div:function(){var ac=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(ac)}this.name_div=$("<div/>").addClass("track-name").appendTo(ac).text(this.prefs.name).attr("id",this.prefs.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ac},set_dataset:function(ac){this.dataset=ac;this.data_manager.set("dataset",ac)},on_resize:function(){this.request_draw({clear_tile_cache:true})},add_resize_handle:function(){var ac=this;var af=false;var ae=false;var ad=$("<div class='track-resize'>");$(ac.container_div).hover(function(){if(ac.prefs.content_visible){af=true;ad.show()}},function(){af=false;if(!ae){ad.hide()}});ad.hide().bind("dragstart",function(ag,ah){ae=true;ah.original_height=$(ac.content_div).height()}).bind("drag",function(ah,ai){var ag=Math.min(Math.max(ai.original_height+ai.deltaY,ac.min_height_px),ac.max_height_px);$(ac.tiles_div).css("height",ag);ac.visible_height_px=(ac.max_height_px===ag?0:ag);ac.on_resize()}).bind("dragend",function(ag,ah){ac.tile_cache.clear();ae=false;if(!af){ad.hide()}ac.config.get("values").height=ac.visible_height_px;ac.changed()}).appendTo(ac.container_div)},set_display_modes:function(af,ai){this.display_modes=af;this.mode=(ai?ai:(this.config&&this.config.get("values").mode?this.config.get("values").mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ad=this,ag={};for(var ae=0,ac=ad.display_modes.length;ae<ac;ae++){var ah=ad.display_modes[ae];ag[ah]=function(aj){return function(){ad.change_mode(aj);ad.icons_div.show();ad.container_div.mouseleave(function(){ad.icons_div.hide()})}}(ah)}make_popupmenu(this.action_icons.mode_icon,ag)},build_action_icons:function(){r.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof W){return"LabelTrack"}else{if(this instanceof D){return"ReferenceTrack"}else{if(this instanceof h){return"LineTrack"}else{if(this instanceof T){return"ReadTrack"}else{if(this instanceof Z){return"VariantTrack"}else{if(this instanceof f){return"CompositeTrack"}else{if(this instanceof d){return"FeatureTrack"}}}}}}}return""},show_message:function(ac){this.tiles_div.remove();return $("<span/>").addClass("message").html(ac).appendTo(this.content_div)},init:function(ae){var ad=this;ad.enabled=false;ad.tile_cache.clear();ad.data_manager.clear();ad.content_div.children().remove();ad.container_div.removeClass("nodata error pending");ad.tiles_div=$("<div/>").addClass("tiles").appendTo(ad.content_div);if(!ad.dataset.id){return}var ac=$.Deferred(),af={hda_ldda:ad.dataset.get("hda_ldda"),data_type:this.dataset_check_type,chrom:ad.view.chrom,retry:ae};$.getJSON(this.dataset.url(),af,function(ag){if(!ag||ag==="error"||ag.kind==="error"){ad.container_div.addClass("error");var ah=ad.show_message(o);if(ag.message){ah.append($("<a href='javascript:void(0);'></a>").text("View error").click(function(){Galaxy.modal.show({title:"Trackster Error",body:"<pre>"+ag.message+"</pre>",buttons:{Close:function(){Galaxy.modal.hide()}}})}));ah.append($("<span/>").text(" "));ah.append($("<a href='javascript:void(0);'></a>").text("Try again").click(function(){ad.init(true)}))}}else{if(ag==="no converter"){ad.container_div.addClass("error");ad.show_message(J)}else{if(ag==="no data"||(ag.data!==undefined&&(ag.data===null||ag.data.length===0))){ad.container_div.addClass("nodata");ad.show_message(G)}else{if(ag==="pending"){ad.container_div.addClass("pending");ad.show_message(w);setTimeout(function(){ad.init()},ad.data_query_wait)}else{if(ag==="data"||ag.status==="data"){if(ag.valid_chroms){ad.valid_chroms=ag.valid_chroms;ad.update_icons()}ad.tiles_div.text(U);if(ad.view.chrom){ad.tiles_div.text("");ad.tiles_div.css("height",ad.visible_height_px+"px");ad.enabled=true;$.when.apply($,ad.predraw_init()).done(function(){ac.resolve();ad.container_div.removeClass("nodata error pending");ad.request_draw()})}else{ac.resolve()}}}}}}});this.update_icons();return ac},predraw_init:function(){var ac=this;return $.getJSON(ac.dataset.url(),{data_type:"data",stats:true,chrom:ac.view.chrom,low:0,high:ac.view.max_high,hda_ldda:ac.dataset.get("hda_ldda")},function(ad){var af=ad.data;if(af&&af.min&&af.max){var ae=af.min,ag=af.max;ae=Math.floor(Math.min(0,Math.max(ae,af.mean-2*af.sd)));ag=Math.ceil(Math.max(0,Math.min(ag,af.mean+2*af.sd)));ac.config.set_param_default_value("min_value",ae);ac.config.set_param_default_value("max_value",ag);ac.prefs.min_value=ae;ac.prefs.max_value=ag}})},get_drawables:function(){return this}});var N=function(ae,ad,ag){g.call(this,ae,ad,ag);var ac=this;m(ac.container_div,ac.drag_handle_class,".group",ac);this.filters_manager=new i.FiltersManager(this,("filters" in ag?ag.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=(ag.tool?new s(ab.extend(ag.tool,{track:this,tool_state:ag.tool_state})):null);this.tile_cache=new y.Cache(Q);this.left_offset=0;if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){var af=new aa({model:this.tool});af.render();this.dynamic_tool_div=af.$el;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);if(!this.prefs.content_visible){this.tiles_div.hide()}this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(ag.mode){this.change_mode(ag.mode)}};q(N.prototype,r.prototype,g.prototype,{action_icons_def:g.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ac){$(".tooltip").remove();ac.slotters[ac.view.resolution_px_b].max_rows*=2;ac.request_draw({clear_tile_cache:true})},hide:true}]),copy:function(ac){var ad=this.to_dict();q(ad,{data_manager:this.data_manager});var ae=new this.constructor(this.view,ac,ad);ae.change_mode(this.mode);ae.enabled=this.enabled;return ae},set_filters_manager:function(ac){this.filters_manager=ac;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),dataset:{id:this.dataset.id,hda_ldda:this.dataset.get("hda_ldda")},prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},set_min_max:function(){var ac=this;return $.getJSON(ac.dataset.url(),{data_type:"data",stats:true,chrom:ac.view.chrom,low:0,high:ac.view.max_high,hda_ldda:ac.dataset.get("hda_ldda")},function(ad){var af=ad.data;if(isNaN(parseFloat(ac.prefs.min_value))||isNaN(parseFloat(ac.prefs.max_value))){var ae=af.min,ag=af.max;ae=Math.floor(Math.min(0,Math.max(ae,af.mean-2*af.sd)));ag=Math.ceil(Math.max(0,Math.min(ag,af.mean+2*af.sd)));ac.prefs.min_value=ae;ac.prefs.max_value=ag}})},change_mode:function(ad){var ac=this;ac.mode=ad;ac.config.get("values").mode=ad;if(ad==="Auto"){this.data_manager.clear()}ac.request_draw({clear_tile_cache:true});this.action_icons.mode_icon.attr("title","Set display mode (now: "+ac.mode+")");return ac},update_icons:function(){var ac=this;if(ac.filters_available){ac.action_icons.filters_icon.show()}else{ac.action_icons.filters_icon.hide()}if(ac.tool){ac.action_icons.tools_icon.show();ac.action_icons.param_space_viz_icon.show()}else{ac.action_icons.tools_icon.hide();ac.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ad,ac){return ad+"_"+ac},request_draw:function(ac){if(ac&&ac.clear_tile_cache){this.tile_cache.clear()}this.view.request_redraw(ac,this)},before_draw:function(){this.max_height_px=0},_draw:function(aq){if(!this.can_draw()){return}var an=aq&&aq.clear_after,al=this.view.low,ag=this.view.high,aj=ag-al,ad=this.view.container.width(),ap=this.view.resolution_px_b,af=1/ap;if(this.is_overview){al=this.view.max_low;ag=this.view.max_high;ap=ad/(view.max_high-view.max_low);af=1/ap}this.before_draw();this.tiles_div.children().addClass("remove");var ac=Math.floor(al/(af*S)),ak,am,ah,ai=[],ao=[];while((ac*S*af)<ag){ak=Math.floor(ac*S*af);am=new y.GenomeRegion({chrom:this.view.chrom,start:ak,end:Math.min(ak+Math.ceil(S*af),this.view.max_high)});ah=this.draw_helper(am,ap,aq);ai.push(ah);$.when(ah).then(function(ar){ao.push(ar)});ac+=1}if(!an){this.tiles_div.children(".remove").removeClass("remove").remove()}var ae=this;$.when.apply($,ai).then(function(){ae.tiles_div.children(".remove").remove();ao=ab.filter(ao,function(ar){return ar!==null});if(ao.length!==0){ae.postdraw_actions(ao,ad,ap,an)}})},_add_yaxis_label:function(af,ah){var ad=this,ag=(af==="max"?"top":"bottom"),ai=(af==="max"?"max":"min"),ac=(af==="max"?"max_value":"min_value"),ae=this.container_div.find(".yaxislabel."+ag);ah=ah||function(){ad.request_draw({clear_tile_cache:true})};if(ae.length!==0){ae.text(ad.prefs[ac])}else{ae=$("<div/>").text(ad.prefs[ac]).make_text_editable({num_cols:12,on_finish:function(aj){$(".tooltip").remove();ad.config.set_param_value(ac,aj);ah()},help_text:"Set "+ai+" value"}).addClass("yaxislabel "+ag).css("color",this.prefs.label_color);this.container_div.prepend(ae)}},postdraw_actions:function(af,ag,ai,ac){var ae=ab.filter(af,function(aj){return(aj instanceof M)});if(ae.length>0){this.max_height_px=0;var ad=this;ab.each(af,function(aj){if(!(aj instanceof M)){aj.html_elt.remove();ad.draw_helper(aj.region,ai,{force:true,mode:"Coverage"})}});ad._add_yaxis_label("max")}else{this.container_div.find(".yaxislabel").remove();var ah=ab.find(af,function(aj){return aj.has_icons});if(ah){ab.each(af,function(aj){if(!aj.has_icons){aj.html_elt.css("padding-top",F)}})}}},get_mode:function(ac){return this.mode},update_auto_mode:function(ac){},_get_drawables:function(){return[this]},draw_helper:function(al,an,ao){if(!ao){ao={}}var ad=ao.force,ai=ao.mode||this.mode,af=1/an,ae=this,ag=this._get_drawables(),am=this._gen_tile_cache_key(an,al),ah=function(ap){return(ap&&"track" in ap)};var aj=(ad?undefined:ae.tile_cache.get_elt(am));if(aj){if(ah(aj)){ae.show_tile(aj,an)}return aj}if(ao.data_fetch===false){return null}var ak=function(){var ap=(ab.find(C,function(ar){return ar===ai})?"Coverage":ai);var aq=ab.map(ag,function(ar){return ar.data_manager.get_data(al,ap,af,ae.data_url_extra_params)});if(view.reference_track){aq.push(view.reference_track.data_manager.get_data(al,ai,af,view.reference_track.data_url_extra_params))}return aq};var ac=$.Deferred();ae.tile_cache.set_elt(am,ac);$.when.apply($,ak()).then(function(){var ap=ak(),av=ap,aB;if(view.reference_track){aB=view.reference_track.data_manager.subset_entry(ap.pop(),al)}var aw=[],at=[];ab.each(ag,function(aG,aD){var aF=aG.mode,aE=av[aD];if(aF==="Auto"){aF=aG.get_mode(aE);aG.update_auto_mode(aF)}aw.push(aF);at.push(aG.get_canvas_height(aE,aF,an,aq))});var au=ae.view.canvas_manager.new_canvas(),ax=al.get("start"),aC=al.get("end"),ar=0,aq=Math.ceil((aC-ax)*an)+ae.left_offset,az=ab.max(at),ay;au.width=aq;au.height=(ao.height||az);var aA=au.getContext("2d");aA.translate(ae.left_offset,0);if(ag.length>1){aA.globalAlpha=0.5;aA.globalCompositeOperation="source-over"}ab.each(ag,function(aE,aD){ay=aE.draw_tile(av[aD],aA,aw[aD],al,an,aB)});if(ay!==undefined){ae.tile_cache.set_elt(am,ay);ae.show_tile(ay,an)}ac.resolve(ay)});return ac},get_canvas_height:function(ac,ae,af,ad){return this.visible_height_px},_draw_line_track_tile:function(ac,ae,ah,ag,ai){var af=ae.canvas,ad=new L.LinePainter(ac.data,ag.get("start"),ag.get("end"),this.prefs,ah);ad.draw(ae,af.width,af.height,ai);return new M(this,ag,ai,af,ac.data)},draw_tile:function(ac,ad,ag,af,ah,ae){},show_tile:function(ae,ah){var ad=this,ac=ae.html_elt;ae.predisplay_actions();var ag=(ae.low-(this.is_overview?this.view.max_low:this.view.low))*ah;if(this.left_offset){ag-=this.left_offset}ac.css("left",ag);if(ac.hasClass("remove")){ac.removeClass("remove")}else{this.tiles_div.append(ac)}ac.css("height","auto");this.max_height_px=Math.max(this.max_height_px,ac.height()-2);ac.parent().children().css("height",this.max_height_px+"px");var af=this.max_height_px;if(this.visible_height_px!==0){af=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",af+"px")},tool_region_and_parameters_str:function(af){var ac=this,ae=(af!==undefined?af.toString():"all"),ad=ab.values(ac.tool.get_inputs_dict()).join(", ");return" - region=["+ae+"], parameters=["+ad+"]"},data_and_mode_compatible:function(ac,ad){if(ad==="Auto"){return true}else{if(ad==="Coverage"){return ac.dataset_type==="bigwig"}else{if(ac.dataset_type==="bigwig"||ac.extra_info==="no_detail"){return false}else{return true}}}},can_subset:function(ac){if(ac.message||ac.extra_info==="no_detail"){return false}else{if(ac.dataset_type==="bigwig"){return(ac.data[1][0]-ac.data[0][0]===1)}}return true},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state"}});var W=function(ad,ac){var ae={resize:false};g.call(this,ad,ac,ae);this.container_div.addClass("label-track")};q(W.prototype,g.prototype,{build_header_div:function(){},init:function(){this.enabled=true},predraw_init:function(){},_draw:function(ag){var ae=this.view,af=ae.high-ae.low,aj=Math.floor(Math.pow(10,Math.floor(Math.log(af)/Math.log(10)))),ac=Math.floor(ae.low/aj)*aj,ah=this.view.container.width(),ad=$("<div/>").addClass("label-container");while(ac<ae.high){var ai=(ac-ae.low)/af*ah;ad.append($("<div/>").addClass("label").text(commatize(ac)).css({left:ai}));ac+=aj}this.content_div.children(":first").remove();this.content_div.append(ad)}});var f=function(ad,ac,ag){N.call(this,ad,ac,ag);this.drawables=[];if("drawables" in ag){var af;for(var ae=0;ae<ag.drawables.length;ae++){af=ag.drawables[ae];this.drawables[ae]=p(af,ad,null);if(af.left_offset>this.left_offset){this.left_offset=af.left_offset}}this.enabled=true}ab.each(this.drawables,function(ah){if(ah instanceof d||ah instanceof T){ah.change_mode("Coverage")}});this.update_icons();this.obj_type="CompositeTrack"};q(f.prototype,N.prototype,{display_modes:C,action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ac){$(".tooltip").remove();ac.show_group()}}].concat(N.prototype.action_icons_def),to_dict:A.prototype.to_dict,add_drawable:A.prototype.add_drawable,unpack_drawables:A.prototype.unpack_drawables,change_mode:function(ac){N.prototype.change_mode.call(this,ac);for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].change_mode(ac)}},init:function(){var ae=[];for(var ad=0;ad<this.drawables.length;ad++){ae.push(this.drawables[ad].init())}var ac=this;$.when.apply($,ae).then(function(){ac.enabled=true;ac.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide();this.action_icons.param_space_viz_icon.hide()},can_draw:r.prototype.can_draw,_get_drawables:function(){return this.drawables},show_group:function(){var af=new P(this.view,this.container,{name:this.prefs.name}),ac;for(var ae=0;ae<this.drawables.length;ae++){ac=this.drawables[ae];ac.update_icons();af.add_drawable(ac);ac.container=af;af.content_div.append(ac.container_div)}var ad=this.container.replace_drawable(this,af,true);af.request_draw({clear_tile_cache:true})},before_draw:function(){N.prototype.before_draw.call(this);var ad=ab.min(ab.map(this.drawables,function(ae){return ae.prefs.min_value})),ac=ab.max(ab.map(this.drawables,function(ae){return ae.prefs.max_value}));this.prefs.min_value=ad;this.prefs.max_value=ac;ab.each(this.drawables,function(ae){ae.prefs.min_value=ad;ae.prefs.max_value=ac})},update_all_min_max:function(){var ac=this;ab.each(this.drawables,function(ad){ad.prefs.min_value=ac.prefs.min_value;ad.prefs.max_value=ac.prefs.max_value});this.request_draw({clear_tile_cache:true})},postdraw_actions:function(ai,ac,al,ah){N.prototype.postdraw_actions.call(this,ai,ac,al,ah);var ag=-1,ae;for(ae=0;ae<ai.length;ae++){var aj=ai[ae].html_elt.find("canvas").height();if(aj>ag){ag=aj}}for(ae=0;ae<ai.length;ae++){var af=ai[ae];if(af.html_elt.find("canvas").height()!==ag){this.draw_helper(af.region,al,{force:true,height:ag});af.html_elt.remove()}}var ad=this,ak=function(){ad.update_all_min_max()};this._add_yaxis_label("min",ak);this._add_yaxis_label("max",ak)}});var D=function(ac){N.call(this,ac,{content_div:ac.top_labeltrack},{resize:false});this.left_offset=ac.canvas_manager.char_width_px;this.container_div.addClass("reference-track");this.data_url=galaxy_config.root+"api/genomes/"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new y.GenomeReferenceDataManager({data_url:this.data_url,can_subset:this.can_subset});this.hide_contents()};q(D.prototype,r.prototype,N.prototype,{config_params:ab.union(r.prototype.config_params,[{key:"height",type:"int",default_value:13,hidden:true}]),build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},predraw_init:function(){},can_draw:r.prototype.can_draw,draw_helper:function(ad,ae,ac){if(ae>this.view.canvas_manager.char_width_px){this.tiles_div.show();return N.prototype.draw_helper.call(this,ad,ae,ac)}else{this.tiles_div.hide();return null}},can_subset:function(ac){return true},draw_tile:function(ae,ak,af,ah,al){var ad=this.data_manager.subset_entry(ae,ah),aj=ad.data;var ac=ak.canvas;ak.font=ak.canvas.manager.default_font;ak.textAlign="center";for(var ag=0,ai=aj.length;ag<ai;ag++){ak.fillStyle=this.view.get_base_color(aj[ag]);ak.fillText(aj[ag],Math.floor(ag*al),10)}return new c(this,ah,al,ac,ad)}});var h=function(ad,ac,ae){this.mode="Histogram";N.call(this,ad,ac,ae)};q(h.prototype,r.prototype,N.prototype,{display_modes:C,config_params:ab.union(r.prototype.config_params,[{key:"color",label:"Color",type:"color"},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:30,hidden:true}]),config_onchange:function(){this.set_name(this.prefs.name);this.request_draw({clear_tile_cache:true})},before_draw:function(){},draw_tile:function(ac,ad,af,ae,ag){return this._draw_line_track_tile(ac,ad,af,ae,ag)},can_subset:function(ac){return(ac.data[1][0]-ac.data[0][0]===1)},postdraw_actions:function(ad,ae,af,ac){this._add_yaxis_label("max");this._add_yaxis_label("min")}});var t=function(ad,ac,ae){this.mode="Heatmap";N.call(this,ad,ac,ae)};q(t.prototype,r.prototype,N.prototype,{display_modes:["Heatmap"],config_params:ab.union(r.prototype.config_params,[{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"min_value",label:"Min Value",type:"float",default_value:-1},{key:"max_value",label:"Max Value",type:"float",default_value:1},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:500,hidden:true}]),config_onchange:function(){this.set_name(this.prefs.name);this.request_draw({clear_tile_cache:true})},draw_tile:function(ac,ae,ah,ag,ai){var af=ae.canvas,ad=new L.DiagonalHeatmapPainter(ac.data,ag.get("start"),ag.get("end"),this.prefs,ah);ad.draw(ae,af.width,af.height,ai);return new c(this,ag,ai,af,ac.data)}});var d=function(ad,ac,ae){N.call(this,ad,ac,ae);this.container_div.addClass("feature-track");this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};q(d.prototype,r.prototype,N.prototype,{display_modes:["Auto","Coverage","Dense","Squish","Pack"],config_params:ab.union(r.prototype.config_params,[{key:"block_color",label:"Block color",type:"color"},{key:"reverse_strand_color",label:"Antisense strand color",type:"color"},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.prefs.name);this.set_painter_from_config();this.request_draw({clear_tile_cache:true})},set_painter_from_config:function(){if(this.config.get("values").connector_style==="arcs"){this.painter=L.ArcLinkedFeaturePainter}else{this.painter=L.LinkedFeaturePainter}},postdraw_actions:function(am,ae,an,al){N.prototype.postdraw_actions.call(this,am,ae,an,al);var ag=this,ah;if(ag.filters_manager){var af=ag.filters_manager.filters,aj;for(aj=0;aj<af.length;aj++){af[aj].update_ui_elt()}var ai=false,ak,ad;for(ah=0;ah<am.length;ah++){if(am[ah].data.length){ak=am[ah].data[0];for(aj=0;aj<af.length;aj++){ad=af[aj];if(ad.applies_to(ak)&&ad.min!==ad.max){ai=true;break}}}}if(ag.filters_available!==ai){ag.filters_available=ai;if(!ag.filters_available){ag.filters_manager.hide()}ag.update_icons()}}if(am[0] instanceof O){var ac=true;for(ah=0;ah<am.length;ah++){if(!am[ah].all_slotted){ac=false;break}}if(!ac){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(ac){if(this.mode==="Auto"){if(ac==="no_detail"){ac="feature spans"}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+ac+")")}},incremental_slots:function(ag,ac,af){var ad=this.view.canvas_manager.dummy_context,ae=this.slotters[ag];if(!ae||(ae.mode!==af)){ae=new (u.FeatureSlotter)(ag,af,B,function(ah){return ad.measureText(ah)});this.slotters[ag]=ae}return ae.slot_features(ac)},get_mode:function(ac){if(ac.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>I){mode="Squish"}else{mode="Pack"}}return mode},get_canvas_height:function(ac,ag,ah,ad){if(ag==="Coverage"||ac.dataset_type==="bigwig"){return this.summary_draw_height}else{var af=this.incremental_slots(ah,ac.data,ag);var ae=new (this.painter)(null,null,null,this.prefs,ag);return Math.max(this.min_height_px,ae.get_required_height(af,ad))}},draw_tile:function(am,aq,ao,af,aj,ae){var ap=this,ad=aq.canvas,ax=af.get("start"),ac=af.get("end"),ag=this.left_offset;if(am.dataset_type==="bigwig"){return this._draw_line_track_tile(am,aq,ao,af,aj)}var ai=[],an=this.slotters[aj].slots;all_slotted=true;if(am.data){var ak=this.filters_manager.filters;for(var ar=0,au=am.data.length;ar<au;ar++){var ah=am.data[ar];var at=false;var al;for(var aw=0,aB=ak.length;aw<aB;aw++){al=ak[aw];al.update_attrs(ah);if(!al.keep(ah)){at=true;break}}if(!at){ai.push(ah);if(!(ah[0] in an)){all_slotted=false}}}}var aA=(this.filters_manager.alpha_filter?new E(this.filters_manager.alpha_filter):null),ay=(this.filters_manager.height_filter?new E(this.filters_manager.height_filter):null),az=new (this.painter)(ai,ax,ac,this.prefs,ao,aA,ay,ae,function(aC){return ap.view.get_base_color(aC)});var av=null;aq.fillStyle=this.prefs.block_color;aq.font=aq.canvas.manager.default_font;aq.textAlign="right";if(am.data){av=az.draw(aq,ad.width,ad.height,aj,an);av.translation=-ag}return new O(ap,af,aj,ad,am.data,ao,am.message,all_slotted,av)}});var Z=function(ad,ac,ae){N.call(this,ad,ac,ae);this.painter=L.VariantPainter;this.summary_draw_height=30;this.left_offset=30};q(Z.prototype,r.prototype,N.prototype,{display_modes:["Auto","Coverage","Dense","Squish","Pack"],config_params:ab.union(r.prototype.config_params,[{key:"color",label:"Histogram color",type:"color"},{key:"show_sample_data",label:"Show sample data",type:"bool",default_value:true},{key:"show_labels",label:"Show summary and sample labels",type:"bool",default_value:true},{key:"summary_height",label:"Locus summary height",type:"float",default_value:20},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.prefs.name);this.request_draw({clear_tile_cache:true})},draw_tile:function(ac,af,ah,ag,ai){if(ac.dataset_type==="bigwig"){return this._draw_line_track_tile(ac,af,"Histogram",ag,ai)}else{var ae=this.view,ad=new (this.painter)(ac.data,ag.get("start"),ag.get("end"),this.prefs,ah,function(aj){return ae.get_base_color(aj)});ad.draw(af,af.canvas.width,af.canvas.height,ai);return new c(this,ag,ai,af.canvas,ac.data)}},get_canvas_height:function(ac,ag,ah,ae){if(ac.dataset_type==="bigwig"){return this.summary_draw_height}else{var ad=(this.dataset.get_metadata("sample_names")?this.dataset.get_metadata("sample_names").length:0);if(ad===0&&ac.data.length!==0){ad=ac.data[0][7].match(/,/g);if(ad===null){ad=1}else{ad=ad.length+1}}var af=new (this.painter)(null,null,null,this.prefs,ag);return af.get_required_height(ad)}},predraw_init:function(){var ac=[g.prototype.predraw_init.call(this)];if(!this.dataset.get_metadata("sample_names")){ac.push(this.dataset.fetch())}return ac},postdraw_actions:function(ag,ah,aj,ad){N.prototype.postdraw_actions.call(this,ag,ah,aj,ad);var af=ab.filter(ag,function(ak){return(ak instanceof M)});var ae=this.dataset.get_metadata("sample_names");if(af.length===0&&this.prefs.show_labels&&ae&&ae.length>1){var ac;if(this.container_div.find(".yaxislabel.variant").length===0){ac=this.prefs.summary_height/2;this.tiles_div.prepend($("<div/>").text("Summary").addClass("yaxislabel variant top").css({"font-size":ac+"px",top:(this.prefs.summary_height-ac)/2+"px"}));if(this.prefs.show_sample_data){var ai=ae.join("<br/>");this.tiles_div.prepend($("<div/>").html(ai).addClass("yaxislabel variant top sample").css({top:this.prefs.summary_height,}))}}ac=(this.mode==="Squish"?5:10)+"px";$(this.tiles_div).find(".sample").css({"font-size":ac,"line-height":ac});$(this.tiles_div).find(".yaxislabel").css("color",this.prefs.label_color)}else{this.container_div.find(".yaxislabel.variant").remove()}}});var T=function(ad,ac,ae){d.call(this,ad,ac,ae);this.painter=(ad.reference_track?L.RefBasedReadPainter:L.ReadPainter);this.update_icons()};q(T.prototype,r.prototype,N.prototype,d.prototype,{config_params:ab.union(r.prototype.config_params,[{key:"block_color",label:"Block and sense strand color",type:"color"},{key:"reverse_strand_color",label:"Antisense strand color",type:"color"},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.prefs.name);this.request_draw({clear_tile_cache:true})}});var e={CompositeTrack:f,DrawableGroup:P,DiagonalHeatmapTrack:t,FeatureTrack:d,LineTrack:h,ReadTrack:T,VariantTrack:Z,VcfTrack:Z};var p=function(ae,ad,ac){if("copy" in ae){return ae.copy(ac)}else{var af=ae.obj_type;if(!af){af=ae.track_type}return new e[af](ad,ac,ae)}};return{TracksterView:Y,DrawableGroup:P,LineTrack:h,FeatureTrack:d,DiagonalHeatmapTrack:t,ReadTrack:T,VariantTrack:Z,CompositeTrack:f,object_from_template:p}});
\ No newline at end of file
+define(["libs/underscore","viz/visualization","viz/trackster/util","viz/trackster/slotting","viz/trackster/painters","viz/trackster/filters","mvc/data","mvc/tools","utils/config"],function(aa,w,j,s,J,h,W,P,R){var o=aa.extend;var l={};var i=function(ab,ac){l[ab.attr("id")]=ac};var k=function(ab,ac,ae,ad){ae=".group";l[ab.attr("id")]=ad;ab.bind("drag",{handle:"."+ac,relative:true},function(am,an){var al=$(this),aq=$(this).parent(),ai=aq.children(".track,.group"),ak=l[$(this).attr("id")],ah,ag,ao,af,aj;ag=$(this).parents(ae);if(ag.length!==0){ao=ag.position().top;af=ao+ag.outerHeight();var ap=l[ag.attr("id")];if(an.offsetY<ao){$(this).insertBefore(ag);ap.remove_drawable(ak);ap.container.add_drawable_before(ak,ap);return}else{if(an.offsetY>af){$(this).insertAfter(ag);ap.remove_drawable(ak);ap.container.add_drawable(ak);return}}}ag=null;for(aj=0;aj<ai.length;aj++){ah=$(ai.get(aj));ao=ah.position().top;af=ao+ah.outerHeight();if(ah.is(ae)&&this!==ah.get(0)&&an.offsetY>=ao&&an.offsetY<=af){if(an.offsetY-ao<af-an.offsetY){ah.find(".content-div").prepend(this)}else{ah.find(".content-div").append(this)}if(ak.container){ak.container.remove_drawable(ak)}l[ah.attr("id")].add_drawable(ak);return}}for(aj=0;aj<ai.length;aj++){ah=$(ai.get(aj));if(an.offsetY<ah.position().top&&!(ah.hasClass("reference-track")||ah.hasClass("intro"))){break}}if(aj===ai.length){if(this!==ai.get(aj-1)){aq.append(this);l[aq.attr("id")].move_drawable(ak,aj)}}else{if(this!==ai.get(aj)){$(this).insertBefore(ai.get(aj));l[aq.attr("id")].move_drawable(ak,(an.deltaY>0?aj-1:aj))}}}).bind("dragstart",function(){$(this).addClass("dragging")}).bind("dragend",function(){$(this).removeClass("dragging")})};var D=20,z=100,G=12000,Q=400,I=5000,v=100,m="Cannot display dataset due to an error. ",H="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",E="No data for this chrom/contig.",u="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",x="Tool cannot be rerun: ",a="Loading data...",T="Ready for display",O=10,F=20,A=["Histogram","Line","Filled","Intensity"];function U(ac,ab){if(!ab){ab=0}var ad=Math.pow(10,ab);return Math.round(ac*ad)/ad}var p=function(ac,ab,ae){if(!p.id_counter){p.id_counter=0}this.id=p.id_counter++;this.view=ac;this.container=ab;this.drag_handle_class=ae.drag_handle_class;this.is_overview=false;this.action_icons={};this.config=R.ConfigSettingCollection.from_models_and_saved_values(this.config_params,ae.prefs);console.log(ae.prefs);this.config.each(function(af){console.log(af.id,af.get("value"))});if(!this.config.get_value("name")){this.config.set_value("name",ae.name)}if(this.config_onchange){this.config.on("change",this.config_onchange,this)}this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").addClass("track-icons").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(af){af.stopPropagation()});var ad=this;this.container_div.hover(function(){ad.icons_div.show()},function(){ad.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};p.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ab){if(ab.config.get_value("content_visible")){ab.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ab.hide_contents();ab.config.set_value("content_visible",false)}else{ab.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ab.config.set_value("content_visible",true);ab.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"gear",on_click_fn:function(ac){var ab=new R.ConfigSettingCollectionView({collection:ac.config});ab.render_in_modal("Configure Track")}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ab){$(".tooltip").remove();ab.remove()}}];o(p.prototype,{config_params:[{key:"name",label:"Name",type:"text",default_value:""},{key:"content_visible",type:"bool",default_value:true,hidden:true}],config_onchange:function(){this.track.set_name(this.track.config.get("values").name)},init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.config.get_value("content_visible")){return true}return false},request_draw:function(){},_draw:function(ab){},to_dict:function(){},set_name:function(ab){this.old_name=this.config.get_value("name");this.config.set_value("name",ab);this.name_div.text(ab)},revert_name:function(){if(this.old_name){this.config.set_value("name",this.old_name);this.name_div.text(this.old_name)}},remove:function(){this.changed();this.container.remove_drawable(this);var ab=this.view;this.container_div.hide(0,function(){$(this).remove();ab.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ac,ah,ag,af,ab,ae){var ad=this;this.action_icons[ac]=$("<a/>").attr("title",ah).addClass("icon-button").addClass(ag).tooltip().click(function(){af(ad)}).appendTo(this.icons_div);if(ae){this.action_icons[ac].hide()}},build_action_icons:function(ab){var ad;for(var ac=0;ac<ab.length;ac++){ad=ab[ac];this.add_action_icon(ad.name,ad.title,ad.css_class,ad.on_click_fn,ad.prepend,ad.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){},get_drawables:function(){}});var y=function(ac,ab,ad){p.call(this,ac,ab,ad);this.obj_type=ad.obj_type;this.drawables=[]};o(y.prototype,p.prototype,{unpack_drawables:function(ad){this.drawables=[];var ac;for(var ab=0;ab<ad.length;ab++){ac=n(ad[ab],this.view,this);this.add_drawable(ac)}},init:function(){for(var ab=0;ab<this.drawables.length;ab++){this.drawables[ab].init()}},_draw:function(ab){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac]._draw(ab)}},to_dict:function(){var ac=[];for(var ab=0;ab<this.drawables.length;ab++){ac.push(this.drawables[ab].to_dict())}return{prefs:this.config.to_key_value_dict(),obj_type:this.obj_type,drawables:ac}},add_drawable:function(ab){this.drawables.push(ab);ab.container=this;this.changed()},add_drawable_before:function(ad,ab){this.changed();var ac=this.drawables.indexOf(ab);if(ac!==-1){this.drawables.splice(ac,0,ad);return true}return false},replace_drawable:function(ad,ab,ac){var ae=this.drawables.indexOf(ad);if(ae!==-1){this.drawables[ae]=ab;if(ac){ad.container_div.replaceWith(ab.container_div)}this.changed()}return ae},remove_drawable:function(ac){var ab=this.drawables.indexOf(ac);if(ab!==-1){this.drawables.splice(ab,1);ac.container=null;this.changed();return true}return false},move_drawable:function(ac,ad){var ab=this.drawables.indexOf(ac);if(ab!==-1){this.drawables.splice(ab,1);this.drawables.splice(ad,0,ac);this.changed();return true}return false},get_drawables:function(){return this.drawables},get_tracks:function(ae){var ab=this.drawables.slice(0),ac=[],ad;while(ab.length!==0){ad=ab.shift();if(ad instanceof ae){ac.push(ad)}else{if(ad.drawables){ab=ab.concat(ad.drawables)}}}return ac}});var N=function(ac,ab,ae){o(ae,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});y.call(this,ac,ab,ae);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);i(this.container_div,this);i(this.content_div,this);k(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new h.FiltersManager(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in ae){this.unpack_drawables(ae.drawables)}if("filters" in ae){var ad=this.filters_manager;this.filters_manager=new h.FiltersManager(this,ae.filters);ad.parent_div.replaceWith(this.filters_manager.parent_div);if(ae.filters.visible){this.setup_multitrack_filtering()}}};o(N.prototype,p.prototype,y.prototype,{action_icons_def:[p.prototype.action_icons_def[0],p.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ab){$(".tooltip").remove();ab.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ab){if(ab.filters_manager.visible()){ab.filters_manager.clear_filters();ab._restore_filter_managers()}else{ab.setup_multitrack_filtering();ab.request_draw({clear_tile_cache:true})}ab.filters_manager.toggle()}},p.prototype.action_icons_def[2]],build_container_div:function(){var ab=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(ab)}return ab},build_header_div:function(){var ab=$("<div/>").addClass("track-header");ab.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.config.get_value("name")).appendTo(ab);return ab},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var ad=this.drawables.length;if(ad===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(ad===1){if(this.drawables[0] instanceof e){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var ak,aj,ah,an=true,af=this.drawables[0].get_type(),ab=0;for(ak=0;ak<ad;ak++){ah=this.drawables[ak];if(ah.get_type()!==af){can_composite=false;break}if(ah instanceof c){ab++}}if(an||ab===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tooltip").remove()}if(ab>1&&ab===this.drawables.length){var ao={},ac;ah=this.drawables[0];for(aj=0;aj<ah.filters_manager.filters.length;aj++){ac=ah.filters_manager.filters[aj];ao[ac.name]=[ac]}for(ak=1;ak<this.drawables.length;ak++){ah=this.drawables[ak];for(aj=0;aj<ah.filters_manager.filters.length;aj++){ac=ah.filters_manager.filters[aj];if(ac.name in ao){ao[ac.name].push(ac)}}}this.filters_manager.remove_all();var ae,ag,ai,al;for(var am in ao){ae=ao[am];if(ae.length===ab){ag=new h.NumberFilter({name:ae[0].name,index:ae[0].index});this.filters_manager.add_filter(ag)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var ab=0;ab<this.drawables.length;ab++){this.drawables[ab].filters_manager=this.saved_filters_managers[ab]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var ab=0;ab<this.drawables.length;ab++){drawable=this.drawables[ab];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var ac=new e(this.view,this.view,{name:this.config.get_value("name"),drawables:this.drawables});var ab=this.container.replace_drawable(this,ac,true);ac.request_draw()},add_drawable:function(ab){y.prototype.add_drawable.call(this,ab);this.update_icons()},remove_drawable:function(ab){y.prototype.remove_drawable.call(this,ab);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ab=o(y.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return ab},request_draw:function(ab){aa.each(this.drawables,function(ac){ac.request_draw(ab)})}});var X=Backbone.View.extend({initialize:function(ab){o(ab,{obj_type:"View"});y.call(this,"View",ab.container,ab);this.chrom=null;this.vis_id=ab.vis_id;this.dbkey=ab.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.render();this.canvas_manager=new w.CanvasManager(this.container.get(0).ownerDocument);this.reset();this.config=new R.ConfigSettingCollection({models:[{key:"a_color",label:"A Color",type:"color",default_value:"#FF0000"},{key:"c_color",label:"C Color",type:"color",default_value:"#00FF00"},{key:"g_color",label:"G Color",type:"color",default_value:"#0000FF"},{key:"t_color",label:"T Color",type:"color",default_value:"#FF00FF"},{key:"n_color",label:"N Color",type:"color",default_value:"#AAAAAA"}],saved_values:ab.prefs,})},render:function(){this.requested_redraw=false;var ad=this.container,ab=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ad);this.browser_content_div=$("<div/>").addClass("content").appendTo(ad);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ad);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;i(this.viewport_container,ab);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var af=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){w.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":ab.dbkey},function(ag){aa.each(ag,function(ah){ab.add_drawable(n(ah,ab,ab))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).addClass("chrom-nav").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ac=function(ag){if(ag.type==="focusout"||(ag.keyCode||ag.which)===13||(ag.keyCode||ag.which)===27){if((ag.keyCode||ag.which)!==27){ab.go_to($(this).val())}$(this).hide();$(this).val("");ab.location_span.show();ab.chrom_select.show()}ag.stopPropagation()};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ac).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("title","Click to change location").tooltip({placement:"bottom"}).appendTo(this.nav_controls);this.location_span.click(function(){ab.location_span.hide();ab.chrom_select.hide();ab.nav_input.val(ab.chrom+":"+ab.low+"-"+ab.high);ab.nav_input.css("display","inline-block");ab.nav_input.select();ab.nav_input.focus();ab.nav_input.autocomplete({source:function(ai,ag){var aj=[],ah=$.map(ab.get_tracks(c),function(ak){return ak.data_manager.search_features(ai.term).success(function(al){aj=aj.concat(al)})});$.when.apply($,ah).done(function(){ag($.map(aj,function(ak){return{label:ak[0],value:ak[1]}}))})}})});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tooltip({placement:"bottom"}).click(function(){ab.zoom_out()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tooltip({placement:"bottom"}).click(function(){ab.zoom_in()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){ab.change_chrom(ab.chrom_select.val())});this.browser_content_div.click(function(ag){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(ag){ab.zoom_in(ag.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(ag,ah){this.current_x=ah.offsetX}).bind("drag",function(ag,ai){var aj=ai.offsetX-this.current_x;this.current_x=ai.offsetX;var ah=Math.round(aj/ab.viewport_container.width()*(ab.max_high-ab.max_low));ab.move_delta(-ah)});this.overview_close.click(function(){ab.reset_overview()});this.viewport_container.bind("draginit",function(ag,ah){if(ag.clientX>ab.viewport_container.width()-16){return false}}).bind("dragstart",function(ag,ah){ah.original_low=ab.low;ah.current_height=ag.clientY;ah.current_x=ah.offsetX}).bind("drag",function(ai,ak){var ag=$(this);var al=ak.offsetX-ak.current_x;var ah=ag.scrollTop()-(ai.clientY-ak.current_height);ag.scrollTop(ah);ak.current_height=ai.clientY;ak.current_x=ak.offsetX;var aj=Math.round(al/ab.viewport_container.width()*(ab.high-ab.low));ab.move_delta(aj)}).bind("mousewheel",function(ai,ak,ah,ag){if(ah){ah*=50;var aj=Math.round(-ah/ab.viewport_container.width()*(ab.high-ab.low));ab.move_delta(aj)}});this.top_labeltrack.bind("dragstart",function(ag,ah){return $("<div/>").addClass("zoom-area").css("height",ab.browser_content_div.height()+ab.top_labeltrack.height()+ab.nav_labeltrack.height()+1).appendTo($(this))}).bind("drag",function(ak,al){$(al.proxy).css({left:Math.min(ak.pageX,al.startX)-ab.container.offset().left,width:Math.abs(ak.pageX-al.startX)});var ah=Math.min(ak.pageX,al.startX)-ab.container.offset().left,ag=Math.max(ak.pageX,al.startX)-ab.container.offset().left,aj=(ab.high-ab.low),ai=ab.viewport_container.width();ab.update_location(Math.round(ah/ai*aj)+ab.low,Math.round(ag/ai*aj)+ab.low)}).bind("dragend",function(al,am){var ah=Math.min(al.pageX,am.startX),ag=Math.max(al.pageX,am.startX),aj=(ab.high-ab.low),ai=ab.viewport_container.width(),ak=ab.low;ab.low=Math.round(ah/ai*aj)+ak;ab.high=Math.round(ag/ai*aj)+ak;$(am.proxy).remove();ab.request_redraw()});var ae=$("<div/>").addClass("mouse-pos").appendTo(ad);ad.hover(function(){ae.show();ad.mousemove(function(ag){ae.css({left:ag.pageX-1})})},function(){ad.off("mousemove");ae.hide()});this.add_label_track(new V(this,{content_div:this.top_labeltrack}));this.add_label_track(new V(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){ab.resize_window()},500)});$(document).bind("redraw",function(){ab.redraw()});this.reset();$(window).trigger("resize")},get_base_color:function(ab){return this.config.get("values")[ab.toLowerCase()+"_color"]||this.config.get("values").n_color}});o(X.prototype,y.prototype,{changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(ac,ae,ab,af){if(this.timer){clearTimeout(this.timer)}if(af){var ad=this;this.timer=setTimeout(function(){ad.trigger("navigate",ac+":"+ae+"-"+ab)},500)}else{view.trigger("navigate",ac+":"+ae+"-"+ab)}},update_location:function(ab,ad){this.location_span.text(commatize(ab)+" - "+commatize(ad));this.nav_input.val(this.chrom+":"+commatize(ab)+"-"+commatize(ad));var ac=view.chrom_select.val();if(ac!==""){this.trigger_navigate(ac,view.low,view.high,true)}},load_chroms:function(ad){ad.num=v;var ab=this,ac=$.Deferred();$.ajax({url:galaxy_config.root+"api/genomes/"+this.dbkey,data:ad,dataType:"json",success:function(af){if(af.chrom_info.length===0){return}if(af.reference){var ag=new B(ab);ab.add_label_track(ag);ab.reference_track=ag}ab.chrom_data=af.chrom_info;var aj='<option value="">Select Chrom/Contig</option>';for(var ai=0,ae=ab.chrom_data.length;ai<ae;ai++){var ah=ab.chrom_data[ai].chrom;aj+='<option value="'+ah+'">'+ah+"</option>"}if(af.prev_chroms){aj+='<option value="previous">Previous '+v+"</option>"}if(af.next_chroms){aj+='<option value="next">Next '+v+"</option>"}ab.chrom_select.html(aj);ab.chrom_start_index=af.start_index;ac.resolve(af.chrom_info)},error:function(){alert("Could not load chroms for this dbkey: "+ab.dbkey)}});return ac},change_chrom:function(ag,ac,ai){var ad=this;if(!ad.chrom_data){ad.load_chroms_deferred.then(function(){ad.change_chrom(ag,ac,ai)});return}if(!ag||ag==="None"){return}if(ag==="previous"){ad.load_chroms({low:this.chrom_start_index-v});return}if(ag==="next"){ad.load_chroms({low:this.chrom_start_index+v});return}var ah=$.grep(ad.chrom_data,function(aj,ak){return aj.chrom===ag})[0];if(ah===undefined){ad.load_chroms({chrom:ag},function(){ad.change_chrom(ag,ac,ai)});return}else{if(ag!==ad.chrom){ad.chrom=ag;ad.chrom_select.val(ad.chrom);ad.max_high=ah.len-1;ad.reset();for(var af=0,ab=ad.drawables.length;af<ab;af++){var ae=ad.drawables[af];if(ae.init){ae.init()}}if(ad.reference_track){ad.reference_track.init()}}if(ac===undefined&&ai===undefined){ad.low=0;ad.high=ad.max_high}else{ad.low=(ac!==undefined?Math.max(ac,0):0);if(ai===undefined){ad.low=Math.max(ad.low-15,0);ad.high=ad.low+30}else{ad.high=Math.min(ai,ad.max_high)}}ad.reset_overview();ad.request_redraw()}},go_to:function(af){af=af.replace(/,/g,"");af=af.replace(/:|\-/g," ");var ac=af.split(/\s+/),ae=ac[0],ad=(ac[1]?parseInt(ac[1],10):undefined),ab=(ac[2]?parseInt(ac[2],10):undefined);this.change_chrom(ae,ad,ab)},move_fraction:function(ad){var ab=this;var ac=ab.high-ab.low;this.move_delta(ad*ac)},move_delta:function(ae){var ab=this;var ad=ab.high-ab.low;if(ab.low-ae<ab.max_low){ab.low=ab.max_low;ab.high=ab.max_low+ad}else{if(ab.high-ae>ab.max_high){ab.high=ab.max_high;ab.low=ab.max_high-ad}else{ab.high-=ae;ab.low-=ae}}ab.request_redraw({data_fetch:false});if(this.redraw_on_move_fn){clearTimeout(this.redraw_on_move_fn)}this.redraw_on_move_fn=setTimeout(function(){ab.request_redraw()},200);var ac=ab.chrom_select.val();this.trigger_navigate(ac,ab.low,ab.high,true)},add_drawable:function(ab){y.prototype.add_drawable.call(this,ab);ab.init();this.changed();this.update_intro_div()},add_label_track:function(ab){ab.view=this;ab.init();this.label_tracks.push(ab)},remove_drawable:function(ad,ac){y.prototype.remove_drawable.call(this,ad);if(ac){var ab=this;ad.container_div.hide(0,function(){$(this).remove();ab.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(ac,ad){var ab=this,ae=(ad?[ad]:ab.drawables);aa.each(ae,function(af){var ag=aa.find(ab.tracks_to_be_redrawn,function(ah){return ah[0]===af});if(ag){ag[1]=ac}else{ab.tracks_to_be_redrawn.push([af,ac])}});if(!this.requested_redraw){requestAnimationFrame(function(){ab._redraw()});this.requested_redraw=true}},_redraw:function(){this.requested_redraw=false;var ab=this.low,af=this.high;if(ab<this.max_low){ab=this.max_low}if(af>this.max_high){af=this.max_high}var ac=this.high-this.low;if(this.high!==0&&ac<this.min_separation){af=ab+this.min_separation}this.low=Math.floor(ab);this.high=Math.ceil(af);this.update_location(this.low,this.high);this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var ae=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ag=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ad=13;this.overview_box.css({left:ae,width:Math.max(ad,ag)}).show();if(ag<ad){this.overview_box.css("left",ae-(ad-ag)/2)}if(this.overview_highlight){this.overview_highlight.css({left:ae,width:ag})}aa.each(this.tracks_to_be_redrawn,function(aj){var ah=aj[0],ai=aj[1];if(ah){ah._draw(ai)}});this.tracks_to_be_redrawn=[];aa.each(this.label_tracks,function(ah){ah._draw()})},zoom_in:function(ac,ad){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var ae=this.high-this.low,af=ae/2+this.low,ab=(ae/this.zoom_factor)/2;if(ac){af=ac/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(af-ab);this.high=Math.round(af+ab);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ac=this.high-this.low,ad=ac/2+this.low,ab=(ac*this.zoom_factor)/2;this.low=Math.round(ad-ab);this.high=Math.round(ad+ab);this.changed();this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.request_redraw()},set_overview:function(ad){if(this.overview_drawable){if(this.overview_drawable.dataset.id===ad.dataset.id){return}this.overview_viewport.find(".track").remove()}var ac=ad.copy({content_div:this.overview_viewport}),ab=this;ac.header_div.hide();ac.is_overview=true;ab.overview_drawable=ac;this.overview_drawable.postdraw_actions=function(){ab.overview_highlight.show().height(ab.overview_drawable.content_div.height());ab.overview_viewport.height(ab.overview_drawable.content_div.height()+ab.overview_box.outerHeight());ab.overview_close.show();ab.resize_window()};ab.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".tooltip").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var q=P.Tool.extend({defaults:{track:null},initialize:function(ab){P.Tool.prototype.initialize.call(this,ab);var ac=true;if(ab.tool_state!==undefined&&ab.tool_state.hidden!==undefined){ac=ab.tool_state.hidden}this.set("hidden",ac);this.remove_inputs(["data","hidden_data","conditional"])},state_dict:function(ab){return aa.extend(this.get_inputs_dict(),{hidden:!this.is_visible()})}});var t=Backbone.View.extend({events:{"change input":"update_value"},render:function(){var ad=this.$el.addClass("param-row"),ae=this.model;var ab=$("<div>").addClass("param-label").text(ae.get("label")).appendTo(ad);var ac=$("<div/>").addClass("param-input").html(ae.get("html")).appendTo(ad);ac.find(":input").val(ae.get("value"));$("<div style='clear: both;'/>").appendTo(ad)},update_value:function(ab){this.model.set_value($(ab.target).val())}});var Z=Backbone.View.extend({initialize:function(ab){this.model.on("change:hidden",this.set_visible,this)},render:function(){var ac=this;tool=this.model,parent_div=this.$el.addClass("dynamic-tool").hide();parent_div.bind("drag",function(ag){ag.stopPropagation()}).click(function(ag){ag.stopPropagation()}).bind("dblclick",function(ag){ag.stopPropagation()}).keydown(function(ag){ag.stopPropagation()});var ad=$("<div class='tool-name'>").appendTo(parent_div).text(tool.get("name"));tool.get("inputs").each(function(ah){var ag=new t({model:ah});ag.render();parent_div.append(ag.$el)});parent_div.find("input").click(function(){$(this).select()});var ae=$("<div>").addClass("param-row").appendTo(parent_div);var af=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(ae);var ab=$("<input type='submit'>").attr("value","Run on visible region").appendTo(ae);ab.click(function(){ac.run_on_region()});af.click(function(){ac.run_on_dataset()});if(tool.is_visible()){this.$el.show()}},set_visible:function(){if(this.model.is_visible()){this.$el.show()}else{this.$el.hide()}},update_params:function(){for(var ab=0;ab<this.params.length;ab++){this.params[ab].update_value()}},run_on_dataset:function(){var ab=this.model;this.run({target_dataset_id:this.model.get("track").dataset.id,action:"rerun",tool_id:ab.id},null,function(ac){Galaxy.modal.show({title:ab.get("name")+" is Running",body:ab.get("name")+" is running on the complete dataset. Tool outputs are in dataset's history.",buttons:{Close:function(){Galaxy.modal.hide()}}})})},run_on_region:function(){var ac=this.model.get("track"),af=this.model,ah=new w.GenomeRegion({chrom:ac.view.chrom,start:ac.view.low,end:ac.view.high}),ai={target_dataset_id:ac.dataset.id,action:"rerun",tool_id:af.id,regions:[ah.toJSON()]},ag=ac,ak=af.get("name")+ag.tool_region_and_parameters_str(ah),ab;if(ag.container===view){var aj=new N(view,view,{name:ac.config.get_value("name")});var ae=ag.container.replace_drawable(ag,aj,false);aj.container_div.insertBefore(ag.view.content_div.children()[ae]);aj.add_drawable(ag);ag.container_div.appendTo(aj.content_div);ab=aj}else{ab=ag.container}var ad=new ag.constructor(view,ab,{name:ak,hda_ldda:"hda"});ad.init_for_tool_data();ad.change_mode(ag.mode);ad.set_filters_manager(ag.filters_manager.copy(ad));ad.update_icons();ab.add_drawable(ad);ad.tiles_div.text("Starting job.");this.run(ai,ad,function(al){ad.set_dataset(new W.Dataset(al));ad.tiles_div.text("Running job.");ad.init()})},run:function(ab,ad,ae){ab.inputs=this.model.get_inputs_dict();var ac=new j.ServerStateDeferred({ajax_settings:{url:galaxy_config.root+"api/tools",data:JSON.stringify(ab),dataType:"json",contentType:"application/json",type:"POST"},interval:2000,success_fn:function(af){return af!=="pending"}});$.when(ac.go()).then(function(af){if(af==="no converter"){ad.container_div.addClass("error");ad.content_div.text(H)}else{if(af.error){ad.container_div.addClass("error");ad.content_div.text(x+af.message)}else{ae(af)}}})}});var C=function(ab,ac){J.Scaler.call(this,ac);this.filter=ab};C.prototype.gen_val=function(ab){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(ab[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var b=function(ab,ae,af,ac,ad){this.track=ab;this.region=ae;this.low=ae.get("start");this.high=ae.get("end");this.w_scale=af;this.canvas=ac;this.html_elt=$("<div class='track-tile'/>").append(ac);this.data=ad;this.stale=false};b.prototype.predisplay_actions=function(){};var K=function(ab,ae,af,ac,ad){b.call(this,ab,ae,af,ac,ad)};K.prototype.predisplay_actions=function(){};var M=function(ae,al,an,ad,ag,ah,ao,ac,ak){b.call(this,ae,al,an,ad,ag);this.mode=ah;this.all_slotted=ac;this.feature_mapper=ak;this.has_icons=false;if(ao){this.has_icons=true;var ai=this;ad=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:D,width:ad.width}).prependTo(this.html_elt);var aj=new w.GenomeRegion({chrom:ae.view.chrom,start:this.low,end:this.high}),am=ag.length,af=$("<a/>").addClass("icon more-down").attr("title","For speed, only the first "+am+" features in this region were obtained from server. Click to get more data including depth").tooltip().appendTo(message_div),ab=$("<a/>").addClass("icon more-across").attr("title","For speed, only the first "+am+" features in this region were obtained from server. Click to get more data excluding depth").tooltip().appendTo(message_div);af.click(function(){ai.stale=true;ae.data_manager.get_more_data(aj,ae.mode,1/ai.w_scale,{},ae.data_manager.DEEP_DATA_REQ);$(".tooltip").hide();ae.request_draw()}).dblclick(function(ap){ap.stopPropagation()});ab.click(function(){ai.stale=true;ae.data_manager.get_more_data(aj,ae.mode,1/ai.w_scale,{},ae.data_manager.BROAD_DATA_REQ);$(".tooltip").hide();ae.request_draw()}).dblclick(function(ap){ap.stopPropagation()})}};o(M.prototype,b.prototype);M.prototype.predisplay_actions=function(){var ac=this,ab={};if(ac.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(an){if(!this.hovered){return}var ai=$(this).offset(),am=an.pageX-ai.left,al=an.pageY-ai.top,ar=ac.feature_mapper.get_feature_data(am,al),aj=(ar?ar[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!aj||$(this).attr("id")!==aj.toString()){$(this).remove()}});if(ar){var ae=ab[aj];if(!ae){var ao={name:ar[3],start:ar[1],end:ar[2],strand:ar[4]},ah=ac.track.filters_manager.filters,ag;for(var ak=0;ak<ah.length;ak++){ag=ah[ak];ao[ag.name]=ar[ag.index]}ae=$("<div/>").attr("id",aj).addClass("feature-popup");var at=$("<table/>"),aq,ap,au;for(aq in ao){ap=ao[aq];au=$("<tr/>").appendTo(at);$("<th/>").appendTo(au).text(aq);$("<td/>").attr("align","left").appendTo(au).text(typeof(ap)==="number"?U(ap,2):ap)}ae.append($("<div class='feature-popup-inner'>").append(at));ab[aj]=ae}ae.appendTo($(this).parents(".track-content").children(".overlay"));var af=am+parseInt(ac.html_elt.css("left"),10)-ae.width()/2,ad=al+parseInt(ac.html_elt.css("top"),10)+7;ae.css("left",af+"px").css("top",ad+"px")}else{if(!an.isPropagationStopped()){an.stopPropagation();$(this).siblings().each(function(){$(this).trigger(an)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var f=function(ac,ab,ad){o(ad,{drag_handle_class:"draghandle"});p.call(this,ac,ab,ad);this.dataset=null;if(ad.dataset){this.dataset=(ad.dataset instanceof Backbone.Model?ad.dataset:new W.Dataset(ad.dataset))}this.dataset_check_type="converted_datasets_state";this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in ad?ad.data_query_wait:I);this.data_manager=("data_manager" in ad?ad.data_manager:new w.GenomeDataManager({dataset:this.dataset,genome:new w.Genome({key:ac.dbkey,chroms_info:{chrom_info:ac.chrom_data}}),data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=this.config.get_value("height");this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in ad)||ad.resize){this.add_resize_handle()}}};o(f.prototype,p.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},p.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"application-dock-270",on_click_fn:function(ab){ab.view.set_overview(ab)}},p.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"ui-slider-050",on_click_fn:function(ab){if(ab.filters_manager.visible()){ab.filters_manager.clear_filters()}else{ab.filters_manager.init_filters()}ab.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(ab){ab.tool.toggle();if(ab.tool.is_visible()){ab.set_name(ab.config.get_value("name")+ab.tool_region_and_parameters_str())}else{ab.revert_name()}$(".tooltip").remove()}},{name:"param_space_viz_icon",title:"Tool parameter space visualization",css_class:"arrow-split",on_click_fn:function(ab){var ae='<strong>Tool</strong>: <%= track.tool.get("name") %><br/><strong>Dataset</strong>: <%= track.config.get("name") %><br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>',ad=aa.template(ae,{track:ab});var ag=function(){Galaxy.modal.hide();$(window).unbind("keypress.check_enter_esc")},ac=function(){var ai=$('select[name="regions"] option:selected').val(),ak,ah=new w.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),aj=aa.map($(".bookmark"),function(al){return new w.GenomeRegion({from_str:$(al).children(".position").text()})});if(ai==="cur"){ak=[ah]}else{if(ai==="bookmarks"){ak=aj}else{ak=[ah].concat(aj)}}Galaxy.modal.hide();window.location.href=galaxy_config.root+"visualization/sweepster?"+$.param({dataset_id:ab.dataset.id,hda_ldda:ab.dataset.get("hda_ldda"),regions:JSON.stringify(new Backbone.Collection(ak).toJSON())})},af=function(ah){if((ah.keyCode||ah.which)===27){ag()}else{if((ah.keyCode||ah.which)===13){ac()}}};Galaxy.modal.show({title:"Visualize tool parameter space and output from different parameter settings?",body:ad,buttons:{No:ag,Yes:ac}})}},p.prototype.action_icons_def[2]],can_draw:function(){return this.dataset&&p.prototype.can_draw.call(this)},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id)},build_header_div:function(){var ab=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(ab)}this.name_div=$("<div/>").addClass("track-name").appendTo(ab).text(this.config.get_value("name")).attr("id",this.config.get_value("name").replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ab},set_dataset:function(ab){this.dataset=ab;this.data_manager.set("dataset",ab)},on_resize:function(){this.request_draw({clear_tile_cache:true})},add_resize_handle:function(){var ab=this;var ae=false;var ad=false;var ac=$("<div class='track-resize'>");$(ab.container_div).hover(function(){if(ab.config.get_value("content_visible")){ae=true;ac.show()}},function(){ae=false;if(!ad){ac.hide()}});ac.hide().bind("dragstart",function(af,ag){ad=true;ag.original_height=$(ab.content_div).height()}).bind("drag",function(ag,ah){var af=Math.min(Math.max(ah.original_height+ah.deltaY,ab.min_height_px),ab.max_height_px);$(ab.tiles_div).css("height",af);ab.visible_height_px=(ab.max_height_px===af?0:af);ab.on_resize()}).bind("dragend",function(af,ag){ab.tile_cache.clear();ad=false;if(!ae){ac.hide()}ab.config.set("height",ab.visible_height_px);ab.changed()}).appendTo(ab.container_div)},set_display_modes:function(ae,ah){this.display_modes=ae;this.mode=(ah?ah:(this.config&&this.config.get_value("mode")?this.config.get_value("mode"):this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ac=this,af={};for(var ad=0,ab=ac.display_modes.length;ad<ab;ad++){var ag=ac.display_modes[ad];af[ag]=function(ai){return function(){ac.change_mode(ai);ac.icons_div.show();ac.container_div.mouseleave(function(){ac.icons_div.hide()})}}(ag)}make_popupmenu(this.action_icons.mode_icon,af)},build_action_icons:function(){p.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof V){return"LabelTrack"}else{if(this instanceof B){return"ReferenceTrack"}else{if(this instanceof g){return"LineTrack"}else{if(this instanceof S){return"ReadTrack"}else{if(this instanceof Y){return"VariantTrack"}else{if(this instanceof e){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},show_message:function(ab){this.tiles_div.remove();return $("<span/>").addClass("message").html(ab).appendTo(this.content_div)},init:function(ad){var ac=this;ac.enabled=false;ac.tile_cache.clear();ac.data_manager.clear();ac.content_div.children().remove();ac.container_div.removeClass("nodata error pending");ac.tiles_div=$("<div/>").addClass("tiles").appendTo(ac.content_div);if(!ac.dataset.id){return}var ab=$.Deferred(),ae={hda_ldda:ac.dataset.get("hda_ldda"),data_type:this.dataset_check_type,chrom:ac.view.chrom,retry:ad};$.getJSON(this.dataset.url(),ae,function(af){if(!af||af==="error"||af.kind==="error"){ac.container_div.addClass("error");var ag=ac.show_message(m);if(af.message){ag.append($("<a href='javascript:void(0);'></a>").text("View error").click(function(){Galaxy.modal.show({title:"Trackster Error",body:"<pre>"+af.message+"</pre>",buttons:{Close:function(){Galaxy.modal.hide()}}})}));ag.append($("<span/>").text(" "));ag.append($("<a href='javascript:void(0);'></a>").text("Try again").click(function(){ac.init(true)}))}}else{if(af==="no converter"){ac.container_div.addClass("error");ac.show_message(H)}else{if(af==="no data"||(af.data!==undefined&&(af.data===null||af.data.length===0))){ac.container_div.addClass("nodata");ac.show_message(E)}else{if(af==="pending"){ac.container_div.addClass("pending");ac.show_message(u);setTimeout(function(){ac.init()},ac.data_query_wait)}else{if(af==="data"||af.status==="data"){if(af.valid_chroms){ac.valid_chroms=af.valid_chroms;ac.update_icons()}ac.tiles_div.text(T);if(ac.view.chrom){ac.tiles_div.text("");ac.tiles_div.css("height",ac.visible_height_px+"px");ac.enabled=true;$.when.apply($,ac.predraw_init()).done(function(){ab.resolve();ac.container_div.removeClass("nodata error pending");ac.request_draw()})}else{ab.resolve()}}}}}}});this.update_icons();return ab},predraw_init:function(){var ab=this;return $.getJSON(ab.dataset.url(),{data_type:"data",stats:true,chrom:ab.view.chrom,low:0,high:ab.view.max_high,hda_ldda:ab.dataset.get("hda_ldda")},function(ac){var ae=ac.data;if(ae&&ae.min&&ae.max){var ad=ae.min,af=ae.max;ad=Math.floor(Math.min(0,Math.max(ad,ae.mean-2*ae.sd)));af=Math.ceil(Math.max(0,Math.min(af,ae.mean+2*ae.sd)));ab.config.set_default_value("min_value",ad);ab.config.set_default_value("max_value",af);ab.config.set_value("min_value",ad);ab.config.set_value("max_value",af)}})},get_drawables:function(){return this}});var L=function(ad,ac,af){f.call(this,ad,ac,af);var ab=this;k(ab.container_div,ab.drag_handle_class,".group",ab);this.filters_manager=new h.FiltersManager(this,("filters" in af?af.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=(af.tool?new q(aa.extend(af.tool,{track:this,tool_state:af.tool_state})):null);this.tile_cache=new w.Cache(O);this.left_offset=0;if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){var ae=new Z({model:this.tool});ae.render();this.dynamic_tool_div=ae.$el;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);if(!this.config.get_value("content_visible")){this.tiles_div.hide()}this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(af.mode){this.change_mode(af.mode)}};o(L.prototype,p.prototype,f.prototype,{action_icons_def:f.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ab){$(".tooltip").remove();ab.slotters[ab.view.resolution_px_b].max_rows*=2;ab.request_draw({clear_tile_cache:true})},hide:true}]),copy:function(ab){var ac=this.to_dict();o(ac,{data_manager:this.data_manager});var ad=new this.constructor(this.view,ab,ac);ad.change_mode(this.mode);ad.enabled=this.enabled;return ad},set_filters_manager:function(ab){this.filters_manager=ab;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){console.log(this.config.to_key_value_dict());return{track_type:this.get_type(),dataset:{id:this.dataset.id,hda_ldda:this.dataset.get("hda_ldda")},prefs:this.config.to_key_value_dict(),mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},set_min_max:function(){var ab=this;return $.getJSON(ab.dataset.url(),{data_type:"data",stats:true,chrom:ab.view.chrom,low:0,high:ab.view.max_high,hda_ldda:ab.dataset.get("hda_ldda")},function(ac){var ae=ac.data;if(isNaN(parseFloat(ab.config.get_value("min_value")))||isNaN(parseFloat(ab.config.get_value("max_value")))){var ad=ae.min,af=ae.max;ad=Math.floor(Math.min(0,Math.max(ad,ae.mean-2*ae.sd)));af=Math.ceil(Math.max(0,Math.min(af,ae.mean+2*ae.sd)));ab.config.set_value("min_value",ad);ab.config.set_value("max_value",af)}})},change_mode:function(ac){var ab=this;ab.mode=ac;ab.config.set_value("mode",ac);if(ac==="Auto"){this.data_manager.clear()}ab.request_draw({clear_tile_cache:true});this.action_icons.mode_icon.attr("title","Set display mode (now: "+ab.mode+")");return ab},update_icons:function(){var ab=this;if(ab.filters_available){ab.action_icons.filters_icon.show()}else{ab.action_icons.filters_icon.hide()}if(ab.tool){ab.action_icons.tools_icon.show();ab.action_icons.param_space_viz_icon.show()}else{ab.action_icons.tools_icon.hide();ab.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(ac,ab){return ac+"_"+ab},request_draw:function(ab){if(ab&&ab.clear_tile_cache){this.tile_cache.clear()}this.view.request_redraw(ab,this)},before_draw:function(){this.max_height_px=0},_draw:function(ap){if(!this.can_draw()){return}var am=ap&&ap.clear_after,ak=this.view.low,af=this.view.high,ai=af-ak,ac=this.view.container.width(),ao=this.view.resolution_px_b,ae=1/ao;if(this.is_overview){ak=this.view.max_low;af=this.view.max_high;ao=ac/(view.max_high-view.max_low);ae=1/ao}this.before_draw();this.tiles_div.children().addClass("remove");var ab=Math.floor(ak/(ae*Q)),aj,al,ag,ah=[],an=[];while((ab*Q*ae)<af){aj=Math.floor(ab*Q*ae);al=new w.GenomeRegion({chrom:this.view.chrom,start:aj,end:Math.min(aj+Math.ceil(Q*ae),this.view.max_high)});ag=this.draw_helper(al,ao,ap);ah.push(ag);$.when(ag).then(function(aq){an.push(aq)});ab+=1}if(!am){this.tiles_div.children(".remove").removeClass("remove").remove()}var ad=this;$.when.apply($,ah).then(function(){ad.tiles_div.children(".remove").remove();an=aa.filter(an,function(aq){return aq!==null});if(an.length!==0){ad.postdraw_actions(an,ac,ao,am)}})},_add_yaxis_label:function(ae,ag){var ac=this,af=(ae==="max"?"top":"bottom"),ah=(ae==="max"?"max":"min"),ab=(ae==="max"?"max_value":"min_value"),ad=this.container_div.find(".yaxislabel."+af);ag=ag||function(){ac.request_draw({clear_tile_cache:true})};if(ad.length!==0){ad.text(ac.config.get_value(ab))}else{ad=$("<div/>").text(ac.config.get_value(ab)).make_text_editable({num_cols:12,on_finish:function(ai){$(".tooltip").remove();ac.config.set_param_value(ab,ai);ag()},help_text:"Set "+ah+" value"}).addClass("yaxislabel "+af).css("color",this.config.get_value("label_color"));this.container_div.prepend(ad)}},postdraw_actions:function(ae,af,ah,ab){var ad=aa.filter(ae,function(ai){return(ai instanceof K)});if(ad.length>0){this.max_height_px=0;var ac=this;aa.each(ae,function(ai){if(!(ai instanceof K)){ai.html_elt.remove();ac.draw_helper(ai.region,ah,{force:true,mode:"Coverage"})}});ac._add_yaxis_label("max")}else{this.container_div.find(".yaxislabel").remove();var ag=aa.find(ae,function(ai){return ai.has_icons});if(ag){aa.each(ae,function(ai){if(!ai.has_icons){ai.html_elt.css("padding-top",D)}})}}},get_mode:function(ab){return this.mode},update_auto_mode:function(ab){},_get_drawables:function(){return[this]},draw_helper:function(ak,am,an){if(!an){an={}}var ac=an.force,ah=an.mode||this.mode,ae=1/am,ad=this,af=this._get_drawables(),al=this._gen_tile_cache_key(am,ak),ag=function(ao){return(ao&&"track" in ao)};var ai=(ac?undefined:ad.tile_cache.get_elt(al));if(ai){if(ag(ai)){ad.show_tile(ai,am)}return ai}if(an.data_fetch===false){return null}var aj=function(){var ao=(aa.find(A,function(aq){return aq===ah})?"Coverage":ah);var ap=aa.map(af,function(aq){return aq.data_manager.get_data(ak,ao,ae,ad.data_url_extra_params)});if(view.reference_track){ap.push(view.reference_track.data_manager.get_data(ak,ah,ae,view.reference_track.data_url_extra_params))}return ap};var ab=$.Deferred();ad.tile_cache.set_elt(al,ab);$.when.apply($,aj()).then(function(){var ao=aj(),au=ao,aA;if(view.reference_track){aA=view.reference_track.data_manager.subset_entry(ao.pop(),ak)}var av=[],ar=[];aa.each(af,function(aF,aC){var aE=aF.mode,aD=au[aC];if(aE==="Auto"){aE=aF.get_mode(aD);aF.update_auto_mode(aE)}av.push(aE);ar.push(aF.get_canvas_height(aD,aE,am,ap))});var at=ad.view.canvas_manager.new_canvas(),aw=ak.get("start"),aB=ak.get("end"),aq=0,ap=Math.ceil((aB-aw)*am)+ad.left_offset,ay=aa.max(ar),ax;at.width=ap;at.height=(an.height||ay);var az=at.getContext("2d");az.translate(ad.left_offset,0);if(af.length>1){az.globalAlpha=0.5;az.globalCompositeOperation="source-over"}aa.each(af,function(aD,aC){ax=aD.draw_tile(au[aC],az,av[aC],ak,am,aA)});if(ax!==undefined){ad.tile_cache.set_elt(al,ax);ad.show_tile(ax,am)}ab.resolve(ax)});return ab},get_canvas_height:function(ab,ad,ae,ac){return this.visible_height_px},_draw_line_track_tile:function(ab,ad,ag,af,ah){var ae=ad.canvas,ac=new J.LinePainter(ab.data,af.get("start"),af.get("end"),this.config.to_key_value_dict(),ag);ac.draw(ad,ae.width,ae.height,ah);return new K(this,af,ah,ae,ab.data)},draw_tile:function(ab,ac,af,ae,ag,ad){},show_tile:function(ad,ag){var ac=this,ab=ad.html_elt;ad.predisplay_actions();var af=(ad.low-(this.is_overview?this.view.max_low:this.view.low))*ag;if(this.left_offset){af-=this.left_offset}ab.css("left",af);if(ab.hasClass("remove")){ab.removeClass("remove")}else{this.tiles_div.append(ab)}ab.css("height","auto");this.max_height_px=Math.max(this.max_height_px,ab.height()-2);ab.parent().children().css("height",this.max_height_px+"px");var ae=this.max_height_px;if(this.visible_height_px!==0){ae=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",ae+"px")},tool_region_and_parameters_str:function(ae){var ab=this,ad=(ae!==undefined?ae.toString():"all"),ac=aa.values(ab.tool.get_inputs_dict()).join(", ");return" - region=["+ad+"], parameters=["+ac+"]"},data_and_mode_compatible:function(ab,ac){if(ac==="Auto"){return true}else{if(ac==="Coverage"){return ab.dataset_type==="bigwig"}else{if(ab.dataset_type==="bigwig"||ab.extra_info==="no_detail"){return false}else{return true}}}},can_subset:function(ab){if(ab.message||ab.extra_info==="no_detail"){return false}else{if(ab.dataset_type==="bigwig"){return(ab.data[1][0]-ab.data[0][0]===1)}}return true},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data");this.data_query_wait=1000;this.dataset_check_type="state"}});var V=function(ac,ab){var ad={resize:false};f.call(this,ac,ab,ad);this.container_div.addClass("label-track")};o(V.prototype,f.prototype,{build_header_div:function(){},init:function(){this.enabled=true},predraw_init:function(){},_draw:function(af){var ad=this.view,ae=ad.high-ad.low,ai=Math.floor(Math.pow(10,Math.floor(Math.log(ae)/Math.log(10)))),ab=Math.floor(ad.low/ai)*ai,ag=this.view.container.width(),ac=$("<div/>").addClass("label-container");while(ab<ad.high){var ah=(ab-ad.low)/ae*ag;ac.append($("<div/>").addClass("label").text(commatize(ab)).css({left:ah}));ab+=ai}this.content_div.children(":first").remove();this.content_div.append(ac)}});var e=function(ac,ab,af){L.call(this,ac,ab,af);this.drawables=[];if("drawables" in af){var ae;for(var ad=0;ad<af.drawables.length;ad++){ae=af.drawables[ad];this.drawables[ad]=n(ae,ac,null);if(ae.left_offset>this.left_offset){this.left_offset=ae.left_offset}}this.enabled=true}aa.each(this.drawables,function(ag){if(ag instanceof c||ag instanceof S){ag.change_mode("Coverage")}});this.update_icons();this.obj_type="CompositeTrack"};o(e.prototype,L.prototype,{display_modes:A,action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ab){$(".tooltip").remove();ab.show_group()}}].concat(L.prototype.action_icons_def),to_dict:y.prototype.to_dict,add_drawable:y.prototype.add_drawable,unpack_drawables:y.prototype.unpack_drawables,change_mode:function(ab){L.prototype.change_mode.call(this,ab);for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac].change_mode(ab)}},init:function(){var ad=[];for(var ac=0;ac<this.drawables.length;ac++){ad.push(this.drawables[ac].init())}var ab=this;$.when.apply($,ad).then(function(){ab.enabled=true;ab.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide();this.action_icons.param_space_viz_icon.hide()},can_draw:p.prototype.can_draw,_get_drawables:function(){return this.drawables},show_group:function(){var ae=new N(this.view,this.container,{name:this.config.get_value("name")}),ab;for(var ad=0;ad<this.drawables.length;ad++){ab=this.drawables[ad];ab.update_icons();ae.add_drawable(ab);ab.container=ae;ae.content_div.append(ab.container_div)}var ac=this.container.replace_drawable(this,ae,true);ae.request_draw({clear_tile_cache:true})},before_draw:function(){L.prototype.before_draw.call(this);var ac=aa.min(aa.map(this.drawables,function(ad){return ad.config.get_value("min_value")})),ab=aa.max(aa.map(this.drawables,function(ad){return ad.config.get_value("max_value")}));this.config.set_value("min_value",ac);this.config.set_value("max_value",ab);aa.each(this.drawables,function(ad){ad.config.set_value("min_value",ac);ad.config.set_value("max_value",ab)})},update_all_min_max:function(){var ac=this,ab=this.config.get_value("min_value"),ad=this.config.get_value("max_value");aa.each(this.drawables,function(ae){ae.config.set_value("min_value",ab);ae.config.set_value("max_value",ad)});this.request_draw({clear_tile_cache:true})},postdraw_actions:function(ah,ab,ak,ag){L.prototype.postdraw_actions.call(this,ah,ab,ak,ag);var af=-1,ad;for(ad=0;ad<ah.length;ad++){var ai=ah[ad].html_elt.find("canvas").height();if(ai>af){af=ai}}for(ad=0;ad<ah.length;ad++){var ae=ah[ad];if(ae.html_elt.find("canvas").height()!==af){this.draw_helper(ae.region,ak,{force:true,height:af});ae.html_elt.remove()}}var ac=this,aj=function(){ac.update_all_min_max()};this._add_yaxis_label("min",aj);this._add_yaxis_label("max",aj)}});var B=function(ab){L.call(this,ab,{content_div:ab.top_labeltrack},{resize:false});this.left_offset=ab.canvas_manager.char_width_px;this.container_div.addClass("reference-track");this.data_url=galaxy_config.root+"api/genomes/"+this.view.dbkey;this.data_url_extra_params={reference:true};this.data_manager=new w.GenomeReferenceDataManager({data_url:this.data_url,can_subset:this.can_subset});this.hide_contents()};o(B.prototype,p.prototype,L.prototype,{config_params:aa.union(p.prototype.config_params,[{key:"height",type:"int",default_value:13,hidden:true}]),build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},predraw_init:function(){},can_draw:p.prototype.can_draw,draw_helper:function(ac,ad,ab){if(ad>this.view.canvas_manager.char_width_px){this.tiles_div.show();return L.prototype.draw_helper.call(this,ac,ad,ab)}else{this.tiles_div.hide();return null}},can_subset:function(ab){return true},draw_tile:function(ad,aj,ae,ag,ak){var ac=this.data_manager.subset_entry(ad,ag),ai=ac.data;var ab=aj.canvas;aj.font=aj.canvas.manager.default_font;aj.textAlign="center";for(var af=0,ah=ai.length;af<ah;af++){aj.fillStyle=this.view.get_base_color(ai[af]);aj.fillText(ai[af],Math.floor(af*ak),10)}return new b(this,ag,ak,ab,ac)}});var g=function(ac,ab,ad){this.mode="Histogram";L.call(this,ac,ab,ad)};o(g.prototype,p.prototype,L.prototype,{display_modes:A,config_params:aa.union(p.prototype.config_params,[{key:"color",label:"Color",type:"color"},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:30,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},before_draw:function(){},draw_tile:function(ab,ac,ae,ad,af){return this._draw_line_track_tile(ab,ac,ae,ad,af)},can_subset:function(ab){return(ab.data[1][0]-ab.data[0][0]===1)},postdraw_actions:function(ac,ad,ae,ab){this._add_yaxis_label("max");this._add_yaxis_label("min")}});var r=function(ac,ab,ad){this.mode="Heatmap";L.call(this,ac,ab,ad)};o(r.prototype,p.prototype,L.prototype,{display_modes:["Heatmap"],config_params:aa.union(p.prototype.config_params,[{key:"pos_color",label:"Positive Color",type:"color",default_value:"#FF8C00"},{key:"neg_color",label:"Negative Color",type:"color",default_value:"#4169E1"},{key:"min_value",label:"Min Value",type:"float",default_value:-1},{key:"max_value",label:"Max Value",type:"float",default_value:1},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:500,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},draw_tile:function(ab,ad,ag,af,ah){var ae=ad.canvas,ac=new J.DiagonalHeatmapPainter(ab.data,af.get("start"),af.get("end"),this.config.to_key_value_dict(),ag);ac.draw(ad,ae.width,ae.height,ah);return new b(this,af,ah,ae,ab.data)}});var c=function(ac,ab,ad){L.call(this,ac,ab,ad);this.container_div.addClass("feature-track");this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};o(c.prototype,p.prototype,L.prototype,{display_modes:["Auto","Coverage","Dense","Squish","Pack"],config_params:aa.union(p.prototype.config_params,[{key:"block_color",label:"Block color",type:"color"},{key:"reverse_strand_color",label:"Antisense strand color",type:"color"},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.set_painter_from_config();this.request_draw({clear_tile_cache:true})},set_painter_from_config:function(){if(this.config.get("values").connector_style==="arcs"){this.painter=J.ArcLinkedFeaturePainter}else{this.painter=J.LinkedFeaturePainter}},postdraw_actions:function(al,ad,am,ak){L.prototype.postdraw_actions.call(this,al,ad,am,ak);var af=this,ag;if(af.filters_manager){var ae=af.filters_manager.filters,ai;for(ai=0;ai<ae.length;ai++){ae[ai].update_ui_elt()}var ah=false,aj,ac;for(ag=0;ag<al.length;ag++){if(al[ag].data.length){aj=al[ag].data[0];for(ai=0;ai<ae.length;ai++){ac=ae[ai];if(ac.applies_to(aj)&&ac.min!==ac.max){ah=true;break}}}}if(af.filters_available!==ah){af.filters_available=ah;if(!af.filters_available){af.filters_manager.hide()}af.update_icons()}}if(al[0] instanceof M){var ab=true;for(ag=0;ag<al.length;ag++){if(!al[ag].all_slotted){ab=false;break}}if(!ab){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(ab){if(this.mode==="Auto"){if(ab==="no_detail"){ab="feature spans"}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+ab+")")}},incremental_slots:function(af,ab,ae){var ac=this.view.canvas_manager.dummy_context,ad=this.slotters[af];if(!ad||(ad.mode!==ae)){ad=new (s.FeatureSlotter)(af,ae,z,function(ag){return ac.measureText(ag)});this.slotters[af]=ad}return ad.slot_features(ab)},get_mode:function(ab){if(ab.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>G){mode="Squish"}else{mode="Pack"}}return mode},get_canvas_height:function(ab,af,ag,ac){if(af==="Coverage"||ab.dataset_type==="bigwig"){return this.summary_draw_height}else{var ae=this.incremental_slots(ag,ab.data,af);var ad=new (this.painter)(null,null,null,this.config.to_key_value_dict(),af);return Math.max(this.min_height_px,ad.get_required_height(ae,ac))}},draw_tile:function(al,ap,an,ae,ai,ad){var ao=this,ac=ap.canvas,aw=ae.get("start"),ab=ae.get("end"),af=this.left_offset;if(al.dataset_type==="bigwig"){return this._draw_line_track_tile(al,ap,an,ae,ai)}var ah=[],am=this.slotters[ai].slots;all_slotted=true;if(al.data){var aj=this.filters_manager.filters;for(var aq=0,at=al.data.length;aq<at;aq++){var ag=al.data[aq];var ar=false;var ak;for(var av=0,aA=aj.length;av<aA;av++){ak=aj[av];ak.update_attrs(ag);if(!ak.keep(ag)){ar=true;break}}if(!ar){ah.push(ag);if(!(ag[0] in am)){all_slotted=false}}}}var az=(this.filters_manager.alpha_filter?new C(this.filters_manager.alpha_filter):null),ax=(this.filters_manager.height_filter?new C(this.filters_manager.height_filter):null),ay=new (this.painter)(ah,aw,ab,this.config.to_key_value_dict(),an,az,ax,ad,function(aB){return ao.view.get_base_color(aB)});var au=null;ap.fillStyle=this.config.get_value("block_color");ap.font=ap.canvas.manager.default_font;ap.textAlign="right";if(al.data){au=ay.draw(ap,ac.width,ac.height,ai,am);au.translation=-af}return new M(ao,ae,ai,ac,al.data,an,al.message,all_slotted,au)}});var Y=function(ac,ab,ad){L.call(this,ac,ab,ad);this.painter=J.VariantPainter;this.summary_draw_height=30;this.left_offset=30};o(Y.prototype,p.prototype,L.prototype,{display_modes:["Auto","Coverage","Dense","Squish","Pack"],config_params:aa.union(p.prototype.config_params,[{key:"color",label:"Histogram color",type:"color"},{key:"show_sample_data",label:"Show sample data",type:"bool",default_value:true},{key:"show_labels",label:"Show summary and sample labels",type:"bool",default_value:true},{key:"summary_height",label:"Locus summary height",type:"float",default_value:20},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})},draw_tile:function(ab,ae,ag,af,ah){if(ab.dataset_type==="bigwig"){return this._draw_line_track_tile(ab,ae,"Histogram",af,ah)}else{var ad=this.view,ac=new (this.painter)(ab.data,af.get("start"),af.get("end"),this.config.to_key_value_dict(),ag,function(ai){return ad.get_base_color(ai)});ac.draw(ae,ae.canvas.width,ae.canvas.height,ah);return new b(this,af,ah,ae.canvas,ab.data)}},get_canvas_height:function(ab,af,ag,ad){if(ab.dataset_type==="bigwig"){return this.summary_draw_height}else{var ac=(this.dataset.get_metadata("sample_names")?this.dataset.get_metadata("sample_names").length:0);if(ac===0&&ab.data.length!==0){ac=ab.data[0][7].match(/,/g);if(ac===null){ac=1}else{ac=ac.length+1}}var ae=new (this.painter)(null,null,null,this.config.to_key_value_dict(),af);return ae.get_required_height(ac)}},predraw_init:function(){var ab=[f.prototype.predraw_init.call(this)];if(!this.dataset.get_metadata("sample_names")){ab.push(this.dataset.fetch())}return ab},postdraw_actions:function(af,ag,ai,ac){L.prototype.postdraw_actions.call(this,af,ag,ai,ac);var ae=aa.filter(af,function(aj){return(aj instanceof K)});var ad=this.dataset.get_metadata("sample_names");if(ae.length===0&&this.config.get_value("show_labels")&&ad&&ad.length>1){var ab;if(this.container_div.find(".yaxislabel.variant").length===0){ab=this.config.get_value("summary_height")/2;this.tiles_div.prepend($("<div/>").text("Summary").addClass("yaxislabel variant top").css({"font-size":ab+"px",top:(this.config.get_value("summary_height")-ab)/2+"px"}));if(this.config.get_value("show_sample_data")){var ah=ad.join("<br/>");this.tiles_div.prepend($("<div/>").html(ah).addClass("yaxislabel variant top sample").css({top:this.config.get_value("summary_height")}))}}ab=(this.mode==="Squish"?5:10)+"px";$(this.tiles_div).find(".sample").css({"font-size":ab,"line-height":ab});$(this.tiles_div).find(".yaxislabel").css("color",this.config.get_value("label_color"))}else{this.container_div.find(".yaxislabel.variant").remove()}}});var S=function(ac,ab,ad){c.call(this,ac,ab,ad);this.painter=(ac.reference_track?J.RefBasedReadPainter:J.ReadPainter);this.update_icons()};o(S.prototype,p.prototype,L.prototype,c.prototype,{config_params:aa.union(p.prototype.config_params,[{key:"block_color",label:"Block and sense strand color",type:"color"},{key:"reverse_strand_color",label:"Antisense strand color",type:"color"},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"min_value",label:"Histogram minimum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"max_value",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"height",type:"int",default_value:0,hidden:true}]),config_onchange:function(){this.set_name(this.config.get_value("name"));this.request_draw({clear_tile_cache:true})}});var d={CompositeTrack:e,DrawableGroup:N,DiagonalHeatmapTrack:r,FeatureTrack:c,LineTrack:g,ReadTrack:S,VariantTrack:Y,VcfTrack:Y};var n=function(ad,ac,ab){if("copy" in ad){return ad.copy(ab)}else{var ae=ad.obj_type;if(!ae){ae=ad.track_type}return new d[ae](ac,ab,ad)}};return{TracksterView:X,DrawableGroup:N,LineTrack:g,FeatureTrack:c,DiagonalHeatmapTrack:r,ReadTrack:S,VariantTrack:Y,CompositeTrack:e,object_from_template:n}});
\ No newline at end of file
diff -r a59ce5d9dd54eb2e2c7145c149aea7f92cb04d8b -r e826721ef957d2d331331663153a509bb6f0ebed static/scripts/packed/viz/trackster_ui.js
--- a/static/scripts/packed/viz/trackster_ui.js
+++ b/static/scripts/packed/viz/trackster_ui.js
@@ -1,1 +1,1 @@
-define(["base","libs/underscore","viz/trackster/slotting","viz/trackster/painters","viz/trackster/tracks","viz/visualization"],function(a,f,e,c,d,h){var j=d.object_from_template;var b=function(l,k){if(!k){k={}}var m=new IconButtonCollection(f.map(l,function(n){return new IconButton(f.extend(n,k))}));return new IconButtonMenuView({collection:m})};var g=a.Base.extend({initialize:function(k){this.baseURL=k},createButtonMenu:function(){var k=this,l=b([{icon_class:"plus-button",title:"Add tracks",on_click:function(){h.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":view.dbkey},function(m){f.each(m,function(n){view.add_drawable(j(n,view,view))})})}},{icon_class:"block--plus",title:"Add group",on_click:function(){view.add_drawable(new d.DrawableGroup(view,view,{name:"New Group"}))}},{icon_class:"bookmarks",title:"Bookmarks",on_click:function(){force_right_panel(($("div#right").css("right")=="0px"?"hide":"show"))}},{icon_class:"globe",title:"Circster",on_click:function(){window.location=k.baseURL+"visualization/circster?id="+view.vis_id}},{icon_class:"disk--arrow",title:"Save",on_click:function(){Galaxy.modal.show({title:"Saving...",body:"progress"});var m=[];$(".bookmark").each(function(){m.push({position:$(this).children(".position").text(),annotation:$(this).children(".annotation").text()})});var n=(view.overview_drawable?view.overview_drawable.prefs.name:null),o={view:view.to_dict(),viewport:{chrom:view.chrom,start:view.low,end:view.high,overview:n},bookmarks:m};$.ajax({url:galaxy_config.root+"visualization/save",type:"POST",dataType:"json",data:{id:view.vis_id,title:view.prefs.name,dbkey:view.dbkey,type:"trackster",vis_json:JSON.stringify(o)}}).success(function(p){Galaxy.modal.hide();view.vis_id=p.vis_id;view.has_changes=false;window.history.pushState({},"",p.url+window.location.hash)}).error(function(){Galaxy.modal.show({title:"Could Not Save",body:"Could not save visualization. Please try again later.",buttons:{Cancel:function(){Galaxy.modal.hide()}}})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=galaxy_config.root+"visualization"}}],{tooltip_config:{placement:"bottom"}});this.buttonMenu=l;return l},add_bookmarks:function(){var k=this,l=this.baseURL;Galaxy.modal.show({title:"Select dataset for new bookmarks",body:"progress"});$.ajax({url:this.baseURL+"/visualization/list_histories",data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(m){Galaxy.modal.show({title:"Select dataset for new bookmarks",body:m,buttons:{Cancel:function(){Galaxy.modal.hide()},Insert:function(){$("input[name=id]:checked,input[name=ldda_ids]:checked").first().each(function(){var n,o=$(this).val();if($(this).attr("name")==="id"){n={hda_id:o}}else{n={ldda_id:o}}$.ajax({url:this.baseURL+"/visualization/bookmarks_from_dataset",data:n,dataType:"json"}).then(function(p){for(i=0;i<p.data.length;i++){var q=p.data[i];k.add_bookmark(q[0],q[1])}})});Galaxy.modal.hide()}}})}})},add_bookmark:function(o,m,k){var q=$("#right .unified-panel-body"),s=$("<div/>").addClass("bookmark").appendTo(q);var t=$("<div/>").addClass("position").appendTo(s),p=$("<a href=''/>").text(o).appendTo(t).click(function(){view.go_to(o);return false}),n=$("<div/>").text(m).appendTo(s);if(k){var r=$("<div/>").addClass("delete-icon-container").prependTo(s).click(function(){s.slideUp("fast");s.remove();view.has_changes=true;return false}),l=$("<a href=''/>").addClass("icon-button delete").appendTo(r);n.make_text_editable({num_rows:3,use_textarea:true,help_text:"Edit bookmark note"}).addClass("annotation")}view.has_changes=true;return s},create_visualization:function(p,k,o,q,n){var m=this,l=new d.TracksterView(p);l.editor=true;$.when(l.load_chroms_deferred).then(function(B){if(k){var z=k.chrom,r=k.start,w=k.end,t=k.overview;if(z&&(r!==undefined)&&w){l.change_chrom(z,r,w)}else{l.change_chrom(B[0].chrom)}}else{l.change_chrom(B[0].chrom)}if(o){var u,s,v;for(var x=0;x<o.length;x++){l.add_drawable(j(o[x],l,l))}}l.update_intro_div();var A;for(var x=0;x<l.drawables.length;x++){if(l.drawables[x].prefs.name===t){l.set_overview(l.drawables[x]);break}}if(q){var y;for(var x=0;x<q.length;x++){y=q[x];m.add_bookmark(y.position,y.annotation,n)}}l.has_changes=false});return l},init_keyboard_nav:function(k){$(document).keyup(function(l){if($(l.srcElement).is(":input")){return}switch(l.which){case 37:k.move_fraction(0.25);break;case 38:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()-20);break;case 39:k.move_fraction(-0.25);break;case 40:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()+20);break}})}});return{object_from_template:j,TracksterUI:g}});
\ No newline at end of file
+define(["base","libs/underscore","viz/trackster/slotting","viz/trackster/painters","viz/trackster/tracks","viz/visualization"],function(a,f,e,c,d,h){var j=d.object_from_template;var b=function(l,k){if(!k){k={}}var m=new IconButtonCollection(f.map(l,function(n){return new IconButton(f.extend(n,k))}));return new IconButtonMenuView({collection:m})};var g=a.Base.extend({initialize:function(k){this.baseURL=k},createButtonMenu:function(){var k=this,l=b([{icon_class:"plus-button",title:"Add tracks",on_click:function(){h.select_datasets(galaxy_config.root+"visualization/list_current_history_datasets",galaxy_config.root+"api/datasets",{"f-dbkey":view.dbkey},function(m){f.each(m,function(n){view.add_drawable(j(n,view,view))})})}},{icon_class:"block--plus",title:"Add group",on_click:function(){view.add_drawable(new d.DrawableGroup(view,view,{name:"New Group"}))}},{icon_class:"bookmarks",title:"Bookmarks",on_click:function(){force_right_panel(($("div#right").css("right")=="0px"?"hide":"show"))}},{icon_class:"globe",title:"Circster",on_click:function(){window.location=k.baseURL+"visualization/circster?id="+view.vis_id}},{icon_class:"disk--arrow",title:"Save",on_click:function(){Galaxy.modal.show({title:"Saving...",body:"progress"});var m=[];$(".bookmark").each(function(){m.push({position:$(this).children(".position").text(),annotation:$(this).children(".annotation").text()})});var n=(view.overview_drawable?view.overview_drawable.config.get_value("name"):null),o={view:view.to_dict(),viewport:{chrom:view.chrom,start:view.low,end:view.high,overview:n},bookmarks:m};$.ajax({url:galaxy_config.root+"visualization/save",type:"POST",dataType:"json",data:{id:view.vis_id,title:view.config.get_value("name"),dbkey:view.dbkey,type:"trackster",vis_json:JSON.stringify(o)}}).success(function(p){Galaxy.modal.hide();view.vis_id=p.vis_id;view.has_changes=false;window.history.pushState({},"",p.url+window.location.hash)}).error(function(){Galaxy.modal.show({title:"Could Not Save",body:"Could not save visualization. Please try again later.",buttons:{Cancel:function(){Galaxy.modal.hide()}}})})}},{icon_class:"cross-circle",title:"Close",on_click:function(){window.location=galaxy_config.root+"visualization"}}],{tooltip_config:{placement:"bottom"}});this.buttonMenu=l;return l},add_bookmarks:function(){var k=this,l=this.baseURL;Galaxy.modal.show({title:"Select dataset for new bookmarks",body:"progress"});$.ajax({url:this.baseURL+"/visualization/list_histories",data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(m){Galaxy.modal.show({title:"Select dataset for new bookmarks",body:m,buttons:{Cancel:function(){Galaxy.modal.hide()},Insert:function(){$("input[name=id]:checked,input[name=ldda_ids]:checked").first().each(function(){var n,o=$(this).val();if($(this).attr("name")==="id"){n={hda_id:o}}else{n={ldda_id:o}}$.ajax({url:this.baseURL+"/visualization/bookmarks_from_dataset",data:n,dataType:"json"}).then(function(p){for(i=0;i<p.data.length;i++){var q=p.data[i];k.add_bookmark(q[0],q[1])}})});Galaxy.modal.hide()}}})}})},add_bookmark:function(o,m,k){var q=$("#right .unified-panel-body"),s=$("<div/>").addClass("bookmark").appendTo(q);var t=$("<div/>").addClass("position").appendTo(s),p=$("<a href=''/>").text(o).appendTo(t).click(function(){view.go_to(o);return false}),n=$("<div/>").text(m).appendTo(s);if(k){var r=$("<div/>").addClass("delete-icon-container").prependTo(s).click(function(){s.slideUp("fast");s.remove();view.has_changes=true;return false}),l=$("<a href=''/>").addClass("icon-button delete").appendTo(r);n.make_text_editable({num_rows:3,use_textarea:true,help_text:"Edit bookmark note"}).addClass("annotation")}view.has_changes=true;return s},create_visualization:function(p,k,o,q,n){var m=this,l=new d.TracksterView(p);l.editor=true;$.when(l.load_chroms_deferred).then(function(B){if(k){var z=k.chrom,r=k.start,w=k.end,t=k.overview;if(z&&(r!==undefined)&&w){l.change_chrom(z,r,w)}else{l.change_chrom(B[0].chrom)}}else{l.change_chrom(B[0].chrom)}if(o){var u,s,v;for(var x=0;x<o.length;x++){l.add_drawable(j(o[x],l,l))}}l.update_intro_div();var A;for(var x=0;x<l.drawables.length;x++){if(l.drawables[x].config.get_value("name")===t){l.set_overview(l.drawables[x]);break}}if(q){var y;for(var x=0;x<q.length;x++){y=q[x];m.add_bookmark(y.position,y.annotation,n)}}l.has_changes=false});return l},init_keyboard_nav:function(k){$(document).keyup(function(l){if($(l.srcElement).is(":input")){return}switch(l.which){case 37:k.move_fraction(0.25);break;case 38:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()-20);break;case 39:k.move_fraction(-0.25);break;case 40:var m=Math.round(k.viewport_container.height()/15);k.viewport_container.scrollTop(k.viewport_container.scrollTop()+20);break}})}});return{object_from_template:j,TracksterUI:g}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Fix to tag_autocomplete_data when q is default (None)
by commits-noreply@bitbucket.org 04 Mar '14
by commits-noreply@bitbucket.org 04 Mar '14
04 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a59ce5d9dd54/
Changeset: a59ce5d9dd54
User: carlfeberhard
Date: 2014-03-04 17:20:17
Summary: Fix to tag_autocomplete_data when q is default (None)
Affected #: 1 file
diff -r ed9228eb4c05ffba9c4c4dd9b496596c5725d306 -r a59ce5d9dd54eb2e2c7145c149aea7f92cb04d8b lib/galaxy/webapps/galaxy/controllers/tag.py
--- a/lib/galaxy/webapps/galaxy/controllers/tag.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tag.py
@@ -89,6 +89,7 @@
item = self._get_item( trans, item_class, trans.security.decode_id( item_id ) )
user = trans.user
item_class = self.get_class( item_class )
+ q = '' if q is None else q
q = q.encode( 'utf-8' )
if q.find( ":" ) == -1:
return self._get_tag_autocomplete_names( trans, q, limit, timestamp, user, item, item_class )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/b1b8acdf8c72/
Changeset: b1b8acdf8c72
Branch: stable
User: jmchilton
Date: 2014-03-04 14:47:58
Summary: Fix Torque CLI runner to recognize complete ('C') state as 'ok'.
Affected #: 1 file
diff -r 7f7da7248d4de5d9f039639db77721d0b22fa6c5 -r b1b8acdf8c7268dfded0a358acfe31308196381a lib/galaxy/jobs/runners/cli_job/torque.py
--- a/lib/galaxy/jobs/runners/cli_job/torque.py
+++ b/lib/galaxy/jobs/runners/cli_job/torque.py
@@ -131,4 +131,5 @@
def __get_job_state(self, state):
return { 'E' : job_states.RUNNING,
'R' : job_states.RUNNING,
- 'Q' : job_states.QUEUED }.get(state, state)
+ 'Q' : job_states.QUEUED,
+ 'C' : job_states.OK }.get(state, state)
https://bitbucket.org/galaxy/galaxy-central/commits/ed9228eb4c05/
Changeset: ed9228eb4c05
User: jmchilton
Date: 2014-03-04 14:48:28
Summary: Merge stable.
Affected #: 1 file
diff -r d66016004bca809cb3bc1fcd630c6d336bf92a9e -r ed9228eb4c05ffba9c4c4dd9b496596c5725d306 lib/galaxy/jobs/runners/cli_job/torque.py
--- a/lib/galaxy/jobs/runners/cli_job/torque.py
+++ b/lib/galaxy/jobs/runners/cli_job/torque.py
@@ -131,4 +131,5 @@
def __get_job_state(self, state):
return { 'E' : job_states.RUNNING,
'R' : job_states.RUNNING,
- 'Q' : job_states.QUEUED }.get(state, state)
+ 'Q' : job_states.QUEUED,
+ 'C' : job_states.OK }.get(state, state)
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
4 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e662438a3641/
Changeset: e662438a3641
User: nsoranzo
Date: 2014-02-26 19:27:17
Summary: Workflow API: add tool version and parameters for each step when showing a workflow.
Otherwise to get this information an extra API call to download the JSON
representation, followed by a tricky mapping of the step ids are necessary.
Affected #: 1 file
diff -r 451b411b4b19ca34ed154352b07b3f49983d1197 -r e662438a3641ec18fdb396348f190664832d80ef lib/galaxy/webapps/galaxy/api/workflows.py
--- a/lib/galaxy/webapps/galaxy/api/workflows.py
+++ b/lib/galaxy/webapps/galaxy/api/workflows.py
@@ -79,7 +79,7 @@
user=trans.user ).join( 'stored_workflow' ).filter(
trans.app.model.StoredWorkflow.deleted == False ).order_by(
desc( trans.app.model.StoredWorkflow.update_time ) ).all():
- item = wf_sa.stored_workflow.to_dict( value_mapper={ 'id': trans.security.encode_id })
+ item = wf_sa.stored_workflow.to_dict( value_mapper={ 'id': trans.security.encode_id } )
encoded_id = trans.security.encode_id(wf_sa.stored_workflow.id)
item['url'] = url_for( 'workflow', id=encoded_id )
rval.append(item)
@@ -127,6 +127,8 @@
steps[step.id] = {'id': step.id,
'type': step.type,
'tool_id': step.tool_id,
+ 'tool_version': step.tool_version,
+ 'tool_inputs': step.tool_inputs,
'input_steps': {}}
for conn in step.input_connections:
steps[step.id]['input_steps'][conn.input_name] = {'source_step': conn.output_step_id,
@@ -139,7 +141,7 @@
"""
POST /api/workflows
- We're not creating workflows from the api. Just execute for now.
+ We're not creating workflows from the api. Just execute for now.
However, we will import them if installed_repository_file is specified
"""
@@ -210,7 +212,7 @@
else:
trans.response.status = 400
return "Unknown dataset source '%s' specified." % ds_map[k]['src']
- if add_to_history and hda.history != history:
+ if add_to_history and hda.history != history:
hda = hda.copy()
history.add_dataset(hda)
ds_map[k]['hda'] = hda
@@ -260,7 +262,7 @@
trans.response.status = 400
return "Workflow cannot be run because of step upgrade messages: %s" % step.upgrade_messages
else:
- # This is an input step. Make sure we have an available input.
+ # This is an input step. Make sure we have an available input.
if step.type == 'data_input' and str(step.id) not in ds_map:
trans.response.status = 400
return "Workflow cannot be run because an expected input step '%s' has no input dataset." % step.id
@@ -387,7 +389,7 @@
"""
# Pull parameters out of payload.
workflow_id = payload.get('workflow_id', None)
- if workflow_id == None:
+ if workflow_id is None:
raise exceptions.ObjectAttributeMissingException( "Missing required parameter 'workflow_id'." )
try:
stored_workflow = self.get_stored_workflow( trans, workflow_id, check_ownership=False )
@@ -452,7 +454,7 @@
if trans.sa_session.query(trans.app.model.StoredWorkflowUserShareAssociation).filter_by(user=trans.user, stored_workflow=stored_workflow).count() == 0:
raise exceptions.ItemOwnershipException()
results = trans.sa_session.query(self.app.model.WorkflowInvocation).filter(self.app.model.WorkflowInvocation.workflow_id==stored_workflow.latest_workflow_id)
- results = results.filter(self.app.model.WorkflowInvocation.id == trans.security.decode_id(usage_id))
+ results = results.filter(self.app.model.WorkflowInvocation.id == trans.security.decode_id(usage_id))
out = results.first()
if out is not None:
return self.encode_all_ids( trans, out.to_dict('element'), True)
https://bitbucket.org/galaxy/galaxy-central/commits/7a74c3b3fa1e/
Changeset: 7a74c3b3fa1e
User: nsoranzo
Date: 2014-02-26 19:38:43
Summary: Add documentation to workflows API create().
Affected #: 1 file
diff -r e662438a3641ec18fdb396348f190664832d80ef -r 7a74c3b3fa1e8ea52882fbfde4d3037519789390 lib/galaxy/webapps/galaxy/api/workflows.py
--- a/lib/galaxy/webapps/galaxy/api/workflows.py
+++ b/lib/galaxy/webapps/galaxy/api/workflows.py
@@ -143,7 +143,28 @@
We're not creating workflows from the api. Just execute for now.
- However, we will import them if installed_repository_file is specified
+ However, we will import them if installed_repository_file is specified.
+
+ :param installed_repository_file The path of a workflow to import. Either workflow_id or installed_repository_file must be specified
+ :type installed_repository_file str
+
+ :param workflow_id: an existing workflow id. Either workflow_id or installed_repository_file must be specified
+ :type workflow_id: str
+
+ :param parameters: See _update_step_parameters()
+ :type parameters: dict
+
+ :param ds_map: A dictionary mapping each input step id to a dictionary with 2 keys: 'src' (which can be 'ldda', 'ld' or 'hda') and 'id' (which should be the id of a LibraryDatasetDatasetAssociation, LibraryDataset or HistoryDatasetAssociation respectively)
+ :type ds_map: dict
+
+ :param no_add_to_history: if present in the payload with any value, the input datasets will not be added to the selected history
+ :type no_add_to_history: str
+
+ :param history: Either the name of a new history or "hist_id=HIST_ID" where HIST_ID is the id of an existing history
+ :type history: str
+
+ :param replacement_params: A dictionary used when renaming datasets
+ :type replacement_params: dict
"""
# Pull parameters out of payload.
https://bitbucket.org/galaxy/galaxy-central/commits/eb8a8eddb1fc/
Changeset: eb8a8eddb1fc
User: nsoranzo
Date: 2014-02-26 19:54:11
Summary: Workflows API: really allow import with installed_repository_file by not crashing if workflow_id, ds_map or history are not specified.
Reorder code so that a new history is created after checking the sanity checks.
Affected #: 1 file
diff -r 7a74c3b3fa1e8ea52882fbfde4d3037519789390 -r eb8a8eddb1fc79913a10832ca73be049a719949e lib/galaxy/webapps/galaxy/api/workflows.py
--- a/lib/galaxy/webapps/galaxy/api/workflows.py
+++ b/lib/galaxy/webapps/galaxy/api/workflows.py
@@ -168,11 +168,11 @@
"""
# Pull parameters out of payload.
- workflow_id = payload['workflow_id']
+ workflow_id = payload.get('workflow_id', None)
param_map = payload.get('parameters', {})
- ds_map = payload['ds_map']
+ ds_map = payload.get('ds_map', {})
add_to_history = 'no_add_to_history' not in payload
- history_param = payload['history']
+ history_param = payload.get('history', '')
# Get/create workflow.
if not workflow_id:
@@ -198,6 +198,20 @@
return("Workflow is not owned by or shared with current user")
workflow = stored_workflow.latest_workflow
+ # Sanity checks.
+ if not workflow:
+ trans.response.status = 400
+ return "Workflow not found."
+ if len( workflow.steps ) == 0:
+ trans.response.status = 400
+ return "Workflow cannot be run because it does not have any steps"
+ if workflow.has_cycles:
+ trans.response.status = 400
+ return "Workflow cannot be run because it contains cycles"
+ if workflow.has_errors:
+ trans.response.status = 400
+ return "Workflow cannot be run because of validation errors in some steps"
+
# Get target history.
if history_param.startswith('hist_id='):
#Passing an existing history to use.
@@ -241,22 +255,7 @@
trans.response.status = 400
return "Invalid Dataset '%s' Specified" % ds_map[k]['id']
- # Sanity checks.
- if not workflow:
- trans.response.status = 400
- return "Workflow not found."
- if len( workflow.steps ) == 0:
- trans.response.status = 400
- return "Workflow cannot be run because it does not have any steps"
- if workflow.has_cycles:
- trans.response.status = 400
- return "Workflow cannot be run because it contains cycles"
- if workflow.has_errors:
- trans.response.status = 400
- return "Workflow cannot be run because of validation errors in some steps"
-
# Build the state for each step
- rval = {}
for step in workflow.steps:
step_errors = None
input_connections_by_name = {}
@@ -291,12 +290,7 @@
step.state = step.module.get_runtime_state()
# Run each step, connecting outputs to inputs
- outputs = util.odict.odict()
- rval['history'] = trans.security.encode_id(history.id)
- rval['outputs'] = []
-
replacement_dict = payload.get('replacement_params', {})
-
outputs = invoke(
trans=trans,
workflow=workflow,
@@ -308,6 +302,9 @@
# Build legacy output - should probably include more information from
# outputs.
+ rval = {}
+ rval['history'] = trans.security.encode_id(history.id)
+ rval['outputs'] = []
for step in workflow.steps:
if step.type == 'tool' or step.type is None:
for v in outputs[ step.id ].itervalues():
https://bitbucket.org/galaxy/galaxy-central/commits/d66016004bca/
Changeset: d66016004bca
User: jmchilton
Date: 2014-03-04 14:39:06
Summary: Merged in nsoranzo/galaxy-central (pull request #341)
Workflows API enhancements (pull request #337 corrected)
Affected #: 1 file
diff -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 -r d66016004bca809cb3bc1fcd630c6d336bf92a9e lib/galaxy/webapps/galaxy/api/workflows.py
--- a/lib/galaxy/webapps/galaxy/api/workflows.py
+++ b/lib/galaxy/webapps/galaxy/api/workflows.py
@@ -79,7 +79,7 @@
user=trans.user ).join( 'stored_workflow' ).filter(
trans.app.model.StoredWorkflow.deleted == False ).order_by(
desc( trans.app.model.StoredWorkflow.update_time ) ).all():
- item = wf_sa.stored_workflow.to_dict( value_mapper={ 'id': trans.security.encode_id })
+ item = wf_sa.stored_workflow.to_dict( value_mapper={ 'id': trans.security.encode_id } )
encoded_id = trans.security.encode_id(wf_sa.stored_workflow.id)
item['url'] = url_for( 'workflow', id=encoded_id )
rval.append(item)
@@ -127,6 +127,8 @@
steps[step.id] = {'id': step.id,
'type': step.type,
'tool_id': step.tool_id,
+ 'tool_version': step.tool_version,
+ 'tool_inputs': step.tool_inputs,
'input_steps': {}}
for conn in step.input_connections:
steps[step.id]['input_steps'][conn.input_name] = {'source_step': conn.output_step_id,
@@ -139,17 +141,38 @@
"""
POST /api/workflows
- We're not creating workflows from the api. Just execute for now.
+ We're not creating workflows from the api. Just execute for now.
- However, we will import them if installed_repository_file is specified
+ However, we will import them if installed_repository_file is specified.
+
+ :param installed_repository_file The path of a workflow to import. Either workflow_id or installed_repository_file must be specified
+ :type installed_repository_file str
+
+ :param workflow_id: an existing workflow id. Either workflow_id or installed_repository_file must be specified
+ :type workflow_id: str
+
+ :param parameters: See _update_step_parameters()
+ :type parameters: dict
+
+ :param ds_map: A dictionary mapping each input step id to a dictionary with 2 keys: 'src' (which can be 'ldda', 'ld' or 'hda') and 'id' (which should be the id of a LibraryDatasetDatasetAssociation, LibraryDataset or HistoryDatasetAssociation respectively)
+ :type ds_map: dict
+
+ :param no_add_to_history: if present in the payload with any value, the input datasets will not be added to the selected history
+ :type no_add_to_history: str
+
+ :param history: Either the name of a new history or "hist_id=HIST_ID" where HIST_ID is the id of an existing history
+ :type history: str
+
+ :param replacement_params: A dictionary used when renaming datasets
+ :type replacement_params: dict
"""
# Pull parameters out of payload.
- workflow_id = payload['workflow_id']
+ workflow_id = payload.get('workflow_id', None)
param_map = payload.get('parameters', {})
- ds_map = payload['ds_map']
+ ds_map = payload.get('ds_map', {})
add_to_history = 'no_add_to_history' not in payload
- history_param = payload['history']
+ history_param = payload.get('history', '')
# Get/create workflow.
if not workflow_id:
@@ -175,6 +198,20 @@
return("Workflow is not owned by or shared with current user")
workflow = stored_workflow.latest_workflow
+ # Sanity checks.
+ if not workflow:
+ trans.response.status = 400
+ return "Workflow not found."
+ if len( workflow.steps ) == 0:
+ trans.response.status = 400
+ return "Workflow cannot be run because it does not have any steps"
+ if workflow.has_cycles:
+ trans.response.status = 400
+ return "Workflow cannot be run because it contains cycles"
+ if workflow.has_errors:
+ trans.response.status = 400
+ return "Workflow cannot be run because of validation errors in some steps"
+
# Get target history.
if history_param.startswith('hist_id='):
#Passing an existing history to use.
@@ -210,7 +247,7 @@
else:
trans.response.status = 400
return "Unknown dataset source '%s' specified." % ds_map[k]['src']
- if add_to_history and hda.history != history:
+ if add_to_history and hda.history != history:
hda = hda.copy()
history.add_dataset(hda)
ds_map[k]['hda'] = hda
@@ -218,22 +255,7 @@
trans.response.status = 400
return "Invalid Dataset '%s' Specified" % ds_map[k]['id']
- # Sanity checks.
- if not workflow:
- trans.response.status = 400
- return "Workflow not found."
- if len( workflow.steps ) == 0:
- trans.response.status = 400
- return "Workflow cannot be run because it does not have any steps"
- if workflow.has_cycles:
- trans.response.status = 400
- return "Workflow cannot be run because it contains cycles"
- if workflow.has_errors:
- trans.response.status = 400
- return "Workflow cannot be run because of validation errors in some steps"
-
# Build the state for each step
- rval = {}
for step in workflow.steps:
step_errors = None
input_connections_by_name = {}
@@ -260,7 +282,7 @@
trans.response.status = 400
return "Workflow cannot be run because of step upgrade messages: %s" % step.upgrade_messages
else:
- # This is an input step. Make sure we have an available input.
+ # This is an input step. Make sure we have an available input.
if step.type == 'data_input' and str(step.id) not in ds_map:
trans.response.status = 400
return "Workflow cannot be run because an expected input step '%s' has no input dataset." % step.id
@@ -268,12 +290,7 @@
step.state = step.module.get_runtime_state()
# Run each step, connecting outputs to inputs
- outputs = util.odict.odict()
- rval['history'] = trans.security.encode_id(history.id)
- rval['outputs'] = []
-
replacement_dict = payload.get('replacement_params', {})
-
outputs = invoke(
trans=trans,
workflow=workflow,
@@ -285,6 +302,9 @@
# Build legacy output - should probably include more information from
# outputs.
+ rval = {}
+ rval['history'] = trans.security.encode_id(history.id)
+ rval['outputs'] = []
for step in workflow.steps:
if step.type == 'tool' or step.type is None:
for v in outputs[ step.id ].itervalues():
@@ -387,7 +407,7 @@
"""
# Pull parameters out of payload.
workflow_id = payload.get('workflow_id', None)
- if workflow_id == None:
+ if workflow_id is None:
raise exceptions.ObjectAttributeMissingException( "Missing required parameter 'workflow_id'." )
try:
stored_workflow = self.get_stored_workflow( trans, workflow_id, check_ownership=False )
@@ -452,7 +472,7 @@
if trans.sa_session.query(trans.app.model.StoredWorkflowUserShareAssociation).filter_by(user=trans.user, stored_workflow=stored_workflow).count() == 0:
raise exceptions.ItemOwnershipException()
results = trans.sa_session.query(self.app.model.WorkflowInvocation).filter(self.app.model.WorkflowInvocation.workflow_id==stored_workflow.latest_workflow_id)
- results = results.filter(self.app.model.WorkflowInvocation.id == trans.security.decode_id(usage_id))
+ results = results.filter(self.app.model.WorkflowInvocation.id == trans.security.decode_id(usage_id))
out = results.first()
if out is not None:
return self.encode_all_ids( trans, out.to_dict('element'), True)
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
8 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/318e2dc3c8f3/
Changeset: 318e2dc3c8f3
User: jmchilton
Date: 2014-02-28 01:06:36
Summary: Initial work on test cases for history contents API.
Affected #: 2 files
diff -r 95517f976cca49f984b89c9fdd5b9208b1a11fcb -r 318e2dc3c8f388c1f07937f9c0cda11b60022e1e test/base/interactor.py
--- a/test/base/interactor.py
+++ b/test/base/interactor.py
@@ -461,16 +461,21 @@
def post_request( url, data, files={} ):
return __multipart_request( url, data, files, verb="POST" )
- def put_request( url ):
- return __urllib_request( url, 'PUT' )
+ def put_request( url, data=None ):
+ if isinstance( data, dict ):
+ assert False, "This test will fail, Galaxy's webob dependency does not parse out urlencoded PUT/PATCH entity data, API will receive empty payload."
+ return __urllib_request( url, 'PUT', json_str=data )
def delete_request( url ):
return __urllib_request( url, 'DELETE' )
- def __urllib_request( url, verb ):
+ def __urllib_request( url, verb, json_str=None ):
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(url)
request.get_method = lambda: verb
+ if json_str:
+ request.add_header( "content-type", "application/json" )
+ request.add_data(json_str)
try:
response = opener.open(request)
return RequestsLikeResponse( response.read(), status_code=response.getcode() )
diff -r 95517f976cca49f984b89c9fdd5b9208b1a11fcb -r 318e2dc3c8f388c1f07937f9c0cda11b60022e1e test/functional/api/test_history_contents.py
--- /dev/null
+++ b/test/functional/api/test_history_contents.py
@@ -0,0 +1,104 @@
+from base import api
+import json
+
+from .helpers import TestsDatasets
+from .helpers import LibraryPopulator
+from base.interactor import (
+ put_request,
+ delete_request,
+)
+
+
+# TODO: Test anonymous access.
+class HistoryContentsApiTestCase( api.ApiTestCase, TestsDatasets ):
+
+ def setUp( self ):
+ super( HistoryContentsApiTestCase, self ).setUp()
+ self.history_id = self._new_history()
+
+ def test_index_hda_summary( self ):
+ hda1 = self._new_dataset( self.history_id )
+ contents_response = self._get( "histories/%s/contents" % self.history_id )
+ hda_summary = self.__check_for_hda( contents_response, hda1 )
+ assert "display_types" not in hda_summary # Quick summary, not full details
+
+ def test_index_hda_all_details( self ):
+ hda1 = self._new_dataset( self.history_id )
+ contents_response = self._get( "histories/%s/contents?details=all" % self.history_id )
+ hda_details = self.__check_for_hda( contents_response, hda1 )
+ self.__assert_hda_has_full_details( hda_details )
+
+ def test_index_hda_detail_by_id( self ):
+ hda1 = self._new_dataset( self.history_id )
+ contents_response = self._get( "histories/%s/contents?details=%s" % ( self.history_id, hda1[ "id" ] ) )
+ hda_details = self.__check_for_hda( contents_response, hda1 )
+ self.__assert_hda_has_full_details( hda_details )
+
+ def test_show_hda( self ):
+ hda1 = self._new_dataset( self.history_id )
+ show_response = self.__show( hda1 )
+ self._assert_status_code_is( show_response, 200 )
+ self.__assert_matches_hda( hda1, show_response.json() )
+
+ def test_hda_copy( self ):
+ hda1 = self._new_dataset( self.history_id )
+ create_data = dict(
+ source='hda',
+ content=hda1[ "id" ],
+ )
+ second_history_id = self._new_history()
+ assert self.__count_contents( second_history_id ) == 0
+ create_response = self._post( "histories/%s/contents" % second_history_id, create_data )
+ self._assert_status_code_is( create_response, 200 )
+ assert self.__count_contents( second_history_id ) == 1
+
+ # TODO
+ #def test_lda_copy( self ):
+ # pass
+
+ def test_update( self ):
+ hda1 = self._new_dataset( self.history_id )
+ self._wait_for_history( self.history_id )
+ assert str( hda1[ "deleted" ] ).lower() == "false"
+ update_url = self._api_url( "histories/%s/contents/%s" % ( self.history_id, hda1[ "id" ] ), use_key=True )
+ # Awkward json.dumps required here because of https://trello.com/c/CQwmCeG6
+ body = json.dumps( dict( deleted=True ) )
+ update_response = put_request( update_url, data=body )
+ self._assert_status_code_is( update_response, 200 )
+ show_response = self.__show( hda1 )
+ assert str( show_response.json()[ "deleted" ] ).lower() == "true"
+
+ def test_delete( self ):
+ hda1 = self._new_dataset( self.history_id )
+ self._wait_for_history( self.history_id )
+ assert str( self.__show( hda1 ).json()[ "deleted" ] ).lower() == "false"
+ url = self._api_url( "histories/%s/contents/%s" % ( self.history_id, hda1["id" ] ), use_key=True )
+ delete_response = delete_request( url )
+ assert delete_response.status_code < 300 # Something in the 200s :).
+ assert str( self.__show( hda1 ).json()[ "deleted" ] ).lower() == "true"
+
+ def __show( self, hda ):
+ show_response = self._get( "histories/%s/contents/%s" % ( self.history_id, hda[ "id" ] ) )
+ return show_response
+
+ def __count_contents( self, history_id=None, **kwds ):
+ if history_id == None:
+ history_id = self.history_id
+ contents_response = self._get( "histories/%s/contents" % history_id, kwds )
+ return len( contents_response.json() )
+
+ def __assert_hda_has_full_details( self, hda_details ):
+ self._assert_has_keys( hda_details, "display_types", "display_apps" )
+
+ def __check_for_hda( self, contents_response, hda ):
+ self._assert_status_code_is( contents_response, 200 )
+ contents = contents_response.json()
+ assert len( contents ) == 1
+ hda_summary = contents[ 0 ]
+ self.__assert_matches_hda( hda, hda_summary )
+ return hda_summary
+
+ def __assert_matches_hda( self, input_hda, query_hda ):
+ self._assert_has_keys( query_hda, "id", "name" )
+ assert input_hda[ "name" ] == query_hda[ "name" ]
+ assert input_hda[ "id" ] == query_hda[ "id" ]
https://bitbucket.org/galaxy/galaxy-central/commits/deefb8ee758d/
Changeset: deefb8ee758d
User: jmchilton
Date: 2014-02-28 01:06:36
Summary: Include root folder id in output when creating library.
This prevents the need to hit the API one more time to get the root folder ID before creating datasets/folders/etc... in the library.
Affected #: 1 file
diff -r 318e2dc3c8f388c1f07937f9c0cda11b60022e1e -r deefb8ee758d334b8e558a54f284463cba938f54 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -130,6 +130,7 @@
new_library['description'] = description
new_library['synopsis'] = synopsis
new_library['id'] = encoded_id
+ new_library['root_folder_id'] = trans.security.encode_id( root_folder.id )
return new_library
def edit( self, trans, encoded_id, payload, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/10d0dd63522f/
Changeset: 10d0dd63522f
User: jmchilton
Date: 2014-02-28 01:06:36
Summary: Include LDDA state in library dataset to_dict.
Needed to monitor library dataset uploads (in particular for API tests, but really any monitoring will benefit from this).
Affected #: 1 file
diff -r deefb8ee758d334b8e558a54f284463cba938f54 -r 10d0dd63522fa5c777d743bcd58ba5e58aa5eb25 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -2127,6 +2127,7 @@
parent_library_id = self.folder.parent_library.id,
folder_id = self.folder_id,
model_class = self.__class__.__name__,
+ state = ldda.state,
name = ldda.name,
file_name = ldda.file_name,
uploaded_by = ldda.user.email,
https://bitbucket.org/galaxy/galaxy-central/commits/1741dfeaa06c/
Changeset: 1741dfeaa06c
User: jmchilton
Date: 2014-02-28 01:06:36
Summary: Infrastructure for library API testing.
Doesn't really test libraries extensively, but adds enough infrastructure to enable a library dataset to history copy test via API.
Affected #: 3 files
diff -r 10d0dd63522fa5c777d743bcd58ba5e58aa5eb25 -r 1741dfeaa06c3f63f27da96c12ab92950eda7286 test/functional/api/helpers.py
--- a/test/functional/api/helpers.py
+++ b/test/functional/api/helpers.py
@@ -1,6 +1,6 @@
import time
-from json import dumps
-from json import loads
+import json
+import StringIO
from pkg_resources import resource_string
# Simple workflow that takes an input and call cat wrapper on it.
@@ -10,6 +10,8 @@
workflow_random_x2_str = resource_string( __name__, "test_workflow_2.ga" )
+# TODO: Rework this so it is a stand-alone object like workflow
+# populator below instead of mixin.
class TestsDatasets:
def _new_dataset( self, history_id, content='TestData123', **kwds ):
@@ -19,15 +21,7 @@
return run_response.json()["outputs"][0]
def _wait_for_history( self, history_id, assert_ok=False ):
- while True:
- history_details_response = self._get( "histories/%s" % history_id )
- self._assert_status_code_is( history_details_response, 200 )
- history_state = history_details_response.json()[ "state" ]
- if history_state not in [ "running", "queued" ]:
- break
- time.sleep( .1 )
- if assert_ok:
- self.assertEquals( history_state, 'ok' )
+ wait_on_state( lambda: self._get( "histories/%s" % history_id ), assert_ok=assert_ok )
def _new_history( self, **kwds ):
name = kwds.get( "name", "API Test History" )
@@ -60,7 +54,7 @@
def _run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
return dict(
tool_id=tool_id,
- inputs=dumps(inputs),
+ inputs=json.dumps(inputs),
history_id=history_id,
**kwds
)
@@ -73,7 +67,7 @@
self.api_test_case = api_test_case
def load_workflow( self, name, content=workflow_str, add_pja=False ):
- workflow = loads( content )
+ workflow = json.loads( content )
workflow[ "name" ] = name
if add_pja:
tool_step = workflow[ "steps" ][ "2" ]
@@ -93,9 +87,99 @@
def create_workflow( self, workflow, **create_kwds ):
data = dict(
- workflow=dumps( workflow ),
+ workflow=json.dumps( workflow ),
**create_kwds
)
upload_response = self.api_test_case._post( "workflows/upload", data=data )
uploaded_workflow_id = upload_response.json()[ "id" ]
return uploaded_workflow_id
+
+
+class LibraryPopulator( object ):
+
+ def __init__( self, api_test_case ):
+ self.api_test_case = api_test_case
+ self.galaxy_interactor = api_test_case.galaxy_interactor
+
+ def new_private_library( self, name ):
+ library = self.new_library( name )
+ library_id = library[ "id" ]
+
+ role_id = self.user_private_role_id()
+ self.set_permissions( library_id, role_id )
+ return library
+
+ def new_library( self, name ):
+ data = dict( name=name )
+ create_response = self.galaxy_interactor.post( "libraries", data=data, admin=True )
+ return create_response.json()
+
+ def set_permissions( self, library_id, role_id=None ):
+ if role_id:
+ perm_list = json.dumps( role_id )
+ else:
+ perm_list = json.dumps( [] )
+
+ permissions = dict(
+ LIBRARY_ACCESS_in=perm_list,
+ LIBRARY_MODIFY_in=perm_list,
+ LIBRARY_ADD_in=perm_list,
+ LIBRARY_MANAGE_in=perm_list,
+ )
+ self.galaxy_interactor.post( "libraries/%s/permissions" % library_id, data=permissions, admin=True )
+
+ def user_email( self ):
+ users_response = self.galaxy_interactor.get( "users" )
+ users = users_response.json()
+ assert len( users ) == 1
+ return users[ 0 ][ "email" ]
+
+ def user_private_role_id( self ):
+ user_email = self.user_email()
+ roles_response = self.api_test_case.galaxy_interactor.get( "roles", admin=True )
+ users_roles = [ r for r in roles_response.json() if r[ "name" ] == user_email ]
+ assert len( users_roles ) == 1
+ return users_roles[ 0 ][ "id" ]
+
+ def create_dataset_request( self, library, **kwds ):
+ create_data = {
+ "folder_id": kwds.get( "folder_id", library[ "root_folder_id" ] ),
+ "create_type": "file",
+ "files_0|NAME": kwds.get( "name", "NewFile" ),
+ "upload_option": kwds.get( "upload_option", "upload_file" ),
+ "file_type": kwds.get( "file_type", "auto" ),
+ "db_key": kwds.get( "db_key", "?" ),
+ }
+ files = {
+ "files_0|file_data": kwds.get( "file", StringIO.StringIO( kwds.get( "contents", "TestData" ) ) ),
+ }
+ return create_data, files
+
+ def new_library_dataset( self, name, **create_dataset_kwds ):
+ library = self.new_private_library( name )
+ payload, files = self.create_dataset_request( library, **create_dataset_kwds )
+ url_rel = "libraries/%s/contents" % ( library[ "id" ] )
+ dataset = self.api_test_case.galaxy_interactor.post( url_rel, payload, files=files ).json()[0]
+
+ def show():
+ return self.api_test_case.galaxy_interactor.get( "libraries/%s/contents/%s" % ( library[ "id" ], dataset[ "id" ] ) )
+
+ wait_on_state(show)
+ return show().json()
+
+
+def wait_on_state( state_func, assert_ok=False, timeout=5 ):
+ delta = .1
+ iteration = 0
+ while True:
+ if (delta * iteration) > timeout:
+ assert False, "Timed out waiting on state."
+ iteration += 1
+ response = state_func()
+ assert response.status_code == 200, "Failed to fetch state update while waiting."
+ state = response.json()[ "state" ]
+ if state not in [ "running", "queued", "new" ]:
+ break
+ time.sleep( delta )
+ if assert_ok:
+ assert state == "ok", "Final state - %s - not okay." % state
diff -r 10d0dd63522fa5c777d743bcd58ba5e58aa5eb25 -r 1741dfeaa06c3f63f27da96c12ab92950eda7286 test/functional/api/test_history_contents.py
--- a/test/functional/api/test_history_contents.py
+++ b/test/functional/api/test_history_contents.py
@@ -52,9 +52,16 @@
self._assert_status_code_is( create_response, 200 )
assert self.__count_contents( second_history_id ) == 1
- # TODO
- #def test_lda_copy( self ):
- # pass
+ def test_library_copy( self ):
+ ld = LibraryPopulator( self ).new_library_dataset( "lda_test_library" )
+ create_data = dict(
+ source='library',
+ content=ld[ "id" ],
+ )
+ assert self.__count_contents( self.history_id ) == 0
+ create_response = self._post( "histories/%s/contents" % self.history_id, create_data )
+ self._assert_status_code_is( create_response, 200 )
+ assert self.__count_contents( self.history_id ) == 1
def test_update( self ):
hda1 = self._new_dataset( self.history_id )
diff -r 10d0dd63522fa5c777d743bcd58ba5e58aa5eb25 -r 1741dfeaa06c3f63f27da96c12ab92950eda7286 test/functional/api/test_libraries.py
--- /dev/null
+++ b/test/functional/api/test_libraries.py
@@ -0,0 +1,54 @@
+from base import api
+from .helpers import TestsDatasets
+from .helpers import LibraryPopulator
+from .helpers import wait_on_state
+
+
+class LibrariesApiTestCase( api.ApiTestCase, TestsDatasets ):
+
+ def setUp( self ):
+ super( LibrariesApiTestCase, self ).setUp()
+ self.library_populator = LibraryPopulator( self )
+
+ def test_create( self ):
+ data = dict( name="CreateTestLibrary" )
+ create_response = self._post( "libraries", data=data, admin=True )
+ self._assert_status_code_is( create_response, 200 )
+ library = create_response.json()
+ self._assert_has_keys( library, "name" )
+ assert library[ "name" ] == "CreateTestLibrary"
+
+ def test_create_private_library_permissions( self ):
+ library = self.library_populator.new_library( "PermissionTestLibrary" )
+ library_id = library[ "id" ]
+
+ role_id = self.library_populator.user_private_role_id()
+ self.library_populator.set_permissions( library_id, role_id )
+ create_response = self._create_folder( library )
+ self._assert_status_code_is( create_response, 200 )
+
+ def test_create_dataset( self ):
+ library = self.library_populator.new_private_library( "ForCreateDatasets" )
+ payload, files = self.library_populator.create_dataset_request( library, file_type="txt", contents="create_test" )
+ create_response = self._post( "libraries/%s/contents" % library[ "id" ], payload, files=files )
+ self._assert_status_code_is( create_response, 200 )
+ library_datasets = create_response.json()
+ assert len( library_datasets ) == 1
+ library_dataset = library_datasets[ 0 ]
+
+ def show():
+ return self._get( "libraries/%s/contents/%s" % ( library[ "id" ], library_dataset[ "id" ] ) )
+
+ wait_on_state( show, assert_ok=True )
+ library_dataset = show().json()
+ self._assert_has_keys( library_dataset, "peek", "data_type" )
+ assert library_dataset[ "peek" ].find("create_test") >= 0
+ assert library_dataset[ "data_type" ] == "txt"
+
+ def _create_folder( self, library ):
+ create_data = dict(
+ folder_id=library[ "root_folder_id" ],
+ create_type="folder",
+ name="New Folder",
+ )
+ return self._post( "libraries/%s/contents" % library[ "id" ], data=create_data )
https://bitbucket.org/galaxy/galaxy-central/commits/cb6e53fe3c9f/
Changeset: cb6e53fe3c9f
User: jmchilton
Date: 2014-02-28 01:06:36
Summary: Start rework of API test mixin TestsDataset into an plain object.
Mirroring the newer WorkflowPopulator and LibraryPopulator.
Affected #: 1 file
diff -r 1741dfeaa06c3f63f27da96c12ab92950eda7286 -r cb6e53fe3c9fda4de94f3f34b6fa87ed2e218a54 test/functional/api/helpers.py
--- a/test/functional/api/helpers.py
+++ b/test/functional/api/helpers.py
@@ -10,27 +10,46 @@
workflow_random_x2_str = resource_string( __name__, "test_workflow_2.ga" )
-# TODO: Rework this so it is a stand-alone object like workflow
-# populator below instead of mixin.
+# Deprecated mixin, use dataset populator instead.
+# TODO: Rework existing tests to target DatasetPopulator in a setup method instead.
class TestsDatasets:
def _new_dataset( self, history_id, content='TestData123', **kwds ):
- payload = self._upload_payload( history_id, content, **kwds )
- run_response = self._post( "tools", data=payload )
- self._assert_status_code_is( run_response, 200 )
+ return DatasetPopulator( self.galaxy_interactor ).new_dataset( history_id, content=content, **kwds)
+
+ def _wait_for_history( self, history_id, assert_ok=False ):
+ return DatasetPopulator( self.galaxy_interactor ).wait_for_history( history_id, assert_ok=assert_ok )
+
+ def _new_history( self, **kwds ):
+ return DatasetPopulator( self.galaxy_interactor ).new_history( **kwds )
+
+ def _upload_payload( self, history_id, content, **kwds ):
+ return DatasetPopulator( self.galaxy_interactor ).upload_payload( history_id, content, **kwds )
+
+ def _run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
+ return DatasetPopulator( self.galaxy_interactor ).run_tool_payload( tool_id, inputs, history_id, **kwds )
+
+
+class DatasetPopulator( object ):
+
+ def __init__( self, galaxy_interactor ):
+ self.galaxy_interactor = galaxy_interactor
+
+ def new_dataset( self, history_id, content='TestData123', **kwds ):
+ payload = self.upload_payload( history_id, content, **kwds )
+ run_response = self.galaxy_interactor.post( "tools", data=payload )
return run_response.json()["outputs"][0]
- def _wait_for_history( self, history_id, assert_ok=False ):
- wait_on_state( lambda: self._get( "histories/%s" % history_id ), assert_ok=assert_ok )
+ def wait_for_history( self, history_id, assert_ok=False ):
+ wait_on_state( lambda: self.galaxy_interactor.get( "histories/%s" % history_id ), assert_ok=assert_ok )
- def _new_history( self, **kwds ):
+ def new_history( self, **kwds ):
name = kwds.get( "name", "API Test History" )
- create_history_response = self._post( "histories", data=dict( name=name ) )
- self._assert_status_code_is( create_history_response, 200 )
+ create_history_response = self.galaxy_interactor.post( "histories", data=dict( name=name ) )
history_id = create_history_response.json()[ "id" ]
return history_id
- def _upload_payload( self, history_id, content, **kwds ):
+ def upload_payload( self, history_id, content, **kwds ):
name = kwds.get( "name", "Test Dataset" )
dbkey = kwds.get( "dbkey", "?" )
file_type = kwds.get( "file_type", 'txt' )
@@ -44,14 +63,14 @@
upload_params[ "files_0|to_posix_lines"] = kwds[ "to_posix_lines" ]
if "space_to_tab" in kwds:
upload_params[ "files_0|space_to_tab" ] = kwds[ "space_to_tab" ]
- return self._run_tool_payload(
+ return self.run_tool_payload(
tool_id='upload1',
inputs=upload_params,
history_id=history_id,
upload_type='upload_dataset'
)
- def _run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
+ def run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
return dict(
tool_id=tool_id,
inputs=json.dumps(inputs),
https://bitbucket.org/galaxy/galaxy-central/commits/94e9f2c1ea68/
Changeset: 94e9f2c1ea68
User: jmchilton
Date: 2014-02-28 01:06:36
Summary: Rework history.contents_iter to potentially support multiple types.
This is utilized downstream in collections work to allow a similar API to be used for loading HistoryDatasetCollectionAssociations as HistoryDatasetAssociations.
Affected #: 3 files
diff -r cb6e53fe3c9fda4de94f3f34b6fa87ed2e218a54 -r 94e9f2c1ea688ed5b66fbfc8766b50d053efed1b lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -19,6 +19,7 @@
import time
from string import Template
from itertools import ifilter
+from itertools import chain
import galaxy.datatypes
import galaxy.datatypes.registry
@@ -964,24 +965,37 @@
"""
Fetch filtered list of contents of history.
"""
- python_filter = None
+ default_contents_types = [
+ 'dataset',
+ ]
+ types = kwds.get('types', default_contents_types)
+ iters = []
+ if 'dataset' in types:
+ iters.append( self.__dataset_contents_iter( **kwds ) )
+ return galaxy.util.merge_sorted_iterables( operator.attrgetter( "hid" ), *iters )
+
+ def __dataset_contents_iter(self, **kwds):
+ return self.__filter_contents( HistoryDatasetAssociation, **kwds )
+
+ def __filter_contents( self, content_class, **kwds ):
db_session = object_session( self )
assert db_session != None
- query = db_session.query( HistoryDatasetAssociation ).filter( HistoryDatasetAssociation.table.c.history_id == self.id )
- query = query.order_by( HistoryDatasetAssociation.table.c.hid.asc() )
+ query = db_session.query( content_class ).filter( content_class.table.c.history_id == self.id )
+ query = query.order_by( content_class.table.c.hid.asc() )
+ python_filter = None
deleted = galaxy.util.string_as_bool_or_none( kwds.get( 'deleted', None ) )
if deleted is not None:
- query = query.filter( HistoryDatasetAssociation.deleted == deleted )
+ query = query.filter( content_class.deleted == deleted )
visible = galaxy.util.string_as_bool_or_none( kwds.get( 'visible', None ) )
if visible is not None:
- query = query.filter( HistoryDatasetAssociation.visible == visible )
+ query = query.filter( content_class.visible == visible )
if 'ids' in kwds:
ids = kwds['ids']
max_in_filter_length = kwds.get('max_in_filter_length', MAX_IN_FILTER_LENGTH)
if len(ids) < max_in_filter_length:
- query = query.filter( HistoryDatasetAssociation.id.in_(ids) )
+ query = query.filter( content_class.id.in_(ids) )
else:
- python_filter = lambda hda: hda.id in ids
+ python_filter = lambda content: content.id in ids
if python_filter:
return ifilter(python_filter, query)
else:
diff -r cb6e53fe3c9fda4de94f3f34b6fa87ed2e218a54 -r 94e9f2c1ea688ed5b66fbfc8766b50d053efed1b lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -427,6 +427,59 @@
return os.path.commonprefix( [ file, directory ] ) == directory
+def merge_sorted_iterables( operator, *iterables ):
+ """
+
+ >>> operator = lambda x: x
+ >>> list( merge_sorted_iterables( operator, [1,2,3], [4,5] ) )
+ [1, 2, 3, 4, 5]
+ >>> list( merge_sorted_iterables( operator, [4, 5], [1,2,3] ) )
+ [1, 2, 3, 4, 5]
+ >>> list( merge_sorted_iterables( operator, [1, 4, 5], [2], [3] ) )
+ [1, 2, 3, 4, 5]
+ """
+ first_iterable = iterables[ 0 ]
+ if len( iterables ) == 1:
+ for el in first_iterable:
+ yield el
+ else:
+ for el in __merge_two_sorted_iterables(
+ operator,
+ iter( first_iterable ),
+ merge_sorted_iterables( operator, *iterables[ 1: ] )
+ ):
+ yield el
+
+
+def __merge_two_sorted_iterables( operator, iterable1, iterable2 ):
+ unset = object()
+ continue_merge = True
+ next_1 = unset
+ next_2 = unset
+ while continue_merge:
+ try:
+ if next_1 is unset:
+ next_1 = next( iterable1 )
+ if next_2 is unset:
+ next_2 = next( iterable2 )
+ if operator( next_2 ) < operator( next_1 ):
+ yield next_2
+ next_2 = unset
+ else:
+ yield next_1
+ next_1 = unset
+ except StopIteration:
+ continue_merge = False
+ if next_1 is not unset:
+ yield next_1
+ if next_2 is not unset:
+ yield next_2
+ for el in iterable1:
+ yield el
+ for el in iterable2:
+ yield el
+
+
class Params( object ):
"""
Stores and 'sanitizes' parameters. Alphanumeric characters and the
diff -r cb6e53fe3c9fda4de94f3f34b6fa87ed2e218a54 -r 94e9f2c1ea688ed5b66fbfc8766b50d053efed1b lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -51,7 +51,13 @@
else:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
- contents_kwds = {}
+ types = kwd.get( 'types', None ) or []
+ if types:
+ types = util.listify(types)
+ else:
+ types = ['datasets']
+
+ contents_kwds = {'types': types}
if ids:
ids = map( lambda id: trans.security.decode_id( id ), ids.split( ',' ) )
contents_kwds[ 'ids' ] = ids
https://bitbucket.org/galaxy/galaxy-central/commits/eb02193da452/
Changeset: eb02193da452
User: jmchilton
Date: 2014-02-28 01:06:36
Summary: Rework history_contents API to allow room for multiple 'type's of history contents.
Downstream dataset collections are being added and can be associated with histories. They have hids, can be deleted, etc... many of the same properties as HDAs. This refactoring "makes room" for these in the history contents API by allowing type to be passed in.
Since HDAs and HDCAs can have the same id - the path history/{history_id}/contents/{contents_id} is somewhat ambigious - so this changeset adds the path history/{history_id}/contents/datasets/{history_dataset_id} while a matching resource path downstream history/{history_id}/contents/dataset_collections/{history_dataset_collection_id} has been added.
The vanilla contents path remains but should be considered deprecated IMO.
Adding type parameter to history/{history_id}/contents index route - this will always default to dataset for backward compatibility - but downstream can be specified as either dataset, dataset_collection, or "dataset,dataset_collection".
Affected #: 2 files
diff -r 94e9f2c1ea688ed5b66fbfc8766b50d053efed1b -r eb02193da4526756703f94ac14ceb189fd9461db lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -33,6 +33,9 @@
:param history_id: encoded id string of the HDA's History
:type ids: str
:param ids: (optional) a comma separated list of encoded `HDA` ids
+ :param types: (optional) kinds of contents to index (currently just
+ dataset, but dataset_collection will be added shortly).
+ :type types: str
:rtype: list
:returns: dictionaries containing summary or detailed HDA information
@@ -51,11 +54,14 @@
else:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
- types = kwd.get( 'types', None ) or []
+ # Allow passing in type or types - for continuity rest of methods
+ # take in type - but this one can be passed multiple types and
+ # type=dataset,dataset_collection is a bit silly.
+ types = kwd.get( 'type', kwd.get( 'types', None ) ) or []
if types:
types = util.listify(types)
else:
- types = ['datasets']
+ types = [ 'dataset' ]
contents_kwds = {'types': types}
if ids:
@@ -72,13 +78,14 @@
if details and details != 'all':
details = util.listify( details )
- for hda in history.contents_iter( **contents_kwds ):
- encoded_hda_id = trans.security.encode_id( hda.id )
- detailed = details == 'all' or ( encoded_hda_id in details )
- if detailed:
- rval.append( self._detailed_hda_dict( trans, hda ) )
- else:
- rval.append( self._summary_hda_dict( trans, history_id, hda ) )
+ for content in history.contents_iter( **contents_kwds ):
+ if isinstance(content, trans.app.model.HistoryDatasetAssociation):
+ encoded_content_id = trans.security.encode_id( content.id )
+ detailed = details == 'all' or ( encoded_content_id in details )
+ if detailed:
+ rval.append( self._detailed_hda_dict( trans, content ) )
+ else:
+ rval.append( self._summary_hda_dict( trans, history_id, content ) )
except Exception, e:
# for errors that are not specific to one hda (history lookup or summary list)
rval = "Error in history API at listing contents: " + str( e )
@@ -144,6 +151,13 @@
:returns: dictionary containing detailed HDA information
.. seealso:: :func:`galaxy.web.base.controller.UsesHistoryDatasetAssociationMixin.get_hda_dict`
"""
+ contents_type = kwd.get('type', 'dataset')
+ if contents_type == 'dataset':
+ return self.__show_dataset( trans, id, history_id, **kwd )
+ else:
+ return self.__handle_unknown_contents_type( trans, contents_type )
+
+ def __show_dataset( self, trans, id, history_id, **kwd ):
try:
hda = self.get_history_dataset_association_from_ids( trans, id, history_id )
hda_dict = self.get_hda_dict( trans, hda )
@@ -184,11 +198,6 @@
#TODO: copy existing, accessible hda - dataset controller, copy_datasets
#TODO: convert existing, accessible hda - model.DatasetInstance(or hda.datatype).get_converter_types
# check parameters
- source = payload.get('source', None)
- content = payload.get('content', None)
- if source not in ['library', 'hda'] or content is None:
- trans.response.status = 400
- return "Please define the source ('library' or 'hda') and the content."
# retrieve history
try:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=False )
@@ -196,6 +205,18 @@
# no way to tell if it failed bc of perms or other (all MessageExceptions)
trans.response.status = 500
return str( e )
+ type = payload.get('type', 'dataset')
+ if type == 'dataset':
+ return self.__create_dataset( trans, history, payload, **kwd )
+ else:
+ return self.__handle_unknown_contents_type( trans, type )
+
+ def __create_dataset( self, trans, history, payload, **kwd ):
+ source = payload.get('source', None)
+ content = payload.get('content', None)
+ if source not in ['library', 'hda'] or content is None:
+ trans.response.status = 400
+ return "Please define the source ('library' or 'hda') and the content."
# copy from library dataset
if source == 'library':
# get library data set
@@ -227,7 +248,7 @@
return str( msg_exc )
except Exception, exc:
trans.response.status = 500
- log.exception( "history: %s, source: %s, content: %s", history_id, source, content )
+ log.exception( "history: %s, source: %s, content: %s", trans.security.encode_id(history.id), source, content )
return str( exc )
data_copy=hda.copy( copy_children=True )
result=history.add_dataset( data_copy )
@@ -261,6 +282,13 @@
any values that were different from the original and, therefore, updated
"""
#TODO: PUT /api/histories/{encoded_history_id} payload = { rating: rating } (w/ no security checks)
+ contents_type = kwd.get('type', 'dataset')
+ if contents_type == "dataset":
+ return self.__update_dataset( trans, history_id, id, payload, **kwd )
+ else:
+ return self.__handle_unknown_contents_type( contents_type )
+
+ def __update_dataset( self, trans, history_id, id, payload, **kwd ):
changed = {}
try:
# anon user
@@ -329,6 +357,13 @@
* deleted: if the history was marked as deleted,
* purged: if the history was purged
"""
+ contents_type = kwd.get('type', 'dataset')
+ if contents_type == "dataset":
+ return self.__delete_dataset( trans, history_id, id, purge=purge, **kwd )
+ else:
+ return self.__handle_unknown_contents_type( trans, contents_type )
+
+ def __delete_dataset( self, trans, history_id, id, purge, **kwd ):
# get purge from the query or from the request body payload (a request body is optional here)
purge = util.string_as_bool( purge )
if kwd.get( 'payload', None ):
@@ -413,3 +448,8 @@
pass
#log.warn( 'unknown key: %s', str( key ) )
return validated_payload
+
+ def __handle_unknown_contents_type( self, trans, contents_type ):
+ # TODO: raise a message exception instead of setting status and returning dict.
+ trans.response.status = 400
+ return { 'error': 'Unknown contents type %s' % type }
diff -r 94e9f2c1ea688ed5b66fbfc8766b50d053efed1b -r eb02193da4526756703f94ac14ceb189fd9461db lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -75,6 +75,19 @@
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
+ valid_history_contents_types = [
+ 'dataset',
+ ]
+ # This must come before history contents below.
+ # Accesss HDA details via histories/:history_id/contents/datasets/:hda_id
+ webapp.mapper.resource( "typed_content",
+ "{type:%s}s" % "|".join( valid_history_contents_types ),
+ name_prefix="history_content_",
+ controller='history_contents',
+ path_prefix='/api/histories/:history_id/contents',
+ parent_resources=dict( member_name='history', collection_name='histories' ),
+ )
+ # Legacy access to HDA details via histories/:history_id/contents/:hda_id
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
https://bitbucket.org/galaxy/galaxy-central/commits/24f45ea024e4/
Changeset: 24f45ea024e4
User: jmchilton
Date: 2014-03-04 14:25:07
Summary: Merged in jmchilton/galaxy-central-fork-1 (pull request #342)
Refactor History Contents to Support Multiple Types
Affected #: 9 files
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -19,6 +19,7 @@
import time
from string import Template
from itertools import ifilter
+from itertools import chain
import galaxy.datatypes
import galaxy.datatypes.registry
@@ -964,24 +965,37 @@
"""
Fetch filtered list of contents of history.
"""
- python_filter = None
+ default_contents_types = [
+ 'dataset',
+ ]
+ types = kwds.get('types', default_contents_types)
+ iters = []
+ if 'dataset' in types:
+ iters.append( self.__dataset_contents_iter( **kwds ) )
+ return galaxy.util.merge_sorted_iterables( operator.attrgetter( "hid" ), *iters )
+
+ def __dataset_contents_iter(self, **kwds):
+ return self.__filter_contents( HistoryDatasetAssociation, **kwds )
+
+ def __filter_contents( self, content_class, **kwds ):
db_session = object_session( self )
assert db_session != None
- query = db_session.query( HistoryDatasetAssociation ).filter( HistoryDatasetAssociation.table.c.history_id == self.id )
- query = query.order_by( HistoryDatasetAssociation.table.c.hid.asc() )
+ query = db_session.query( content_class ).filter( content_class.table.c.history_id == self.id )
+ query = query.order_by( content_class.table.c.hid.asc() )
+ python_filter = None
deleted = galaxy.util.string_as_bool_or_none( kwds.get( 'deleted', None ) )
if deleted is not None:
- query = query.filter( HistoryDatasetAssociation.deleted == deleted )
+ query = query.filter( content_class.deleted == deleted )
visible = galaxy.util.string_as_bool_or_none( kwds.get( 'visible', None ) )
if visible is not None:
- query = query.filter( HistoryDatasetAssociation.visible == visible )
+ query = query.filter( content_class.visible == visible )
if 'ids' in kwds:
ids = kwds['ids']
max_in_filter_length = kwds.get('max_in_filter_length', MAX_IN_FILTER_LENGTH)
if len(ids) < max_in_filter_length:
- query = query.filter( HistoryDatasetAssociation.id.in_(ids) )
+ query = query.filter( content_class.id.in_(ids) )
else:
- python_filter = lambda hda: hda.id in ids
+ python_filter = lambda content: content.id in ids
if python_filter:
return ifilter(python_filter, query)
else:
@@ -2127,6 +2141,7 @@
parent_library_id = self.folder.parent_library.id,
folder_id = self.folder_id,
model_class = self.__class__.__name__,
+ state = ldda.state,
name = ldda.name,
file_name = ldda.file_name,
uploaded_by = ldda.user.email,
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 lib/galaxy/util/__init__.py
--- a/lib/galaxy/util/__init__.py
+++ b/lib/galaxy/util/__init__.py
@@ -427,6 +427,59 @@
return os.path.commonprefix( [ file, directory ] ) == directory
+def merge_sorted_iterables( operator, *iterables ):
+ """
+
+ >>> operator = lambda x: x
+ >>> list( merge_sorted_iterables( operator, [1,2,3], [4,5] ) )
+ [1, 2, 3, 4, 5]
+ >>> list( merge_sorted_iterables( operator, [4, 5], [1,2,3] ) )
+ [1, 2, 3, 4, 5]
+ >>> list( merge_sorted_iterables( operator, [1, 4, 5], [2], [3] ) )
+ [1, 2, 3, 4, 5]
+ """
+ first_iterable = iterables[ 0 ]
+ if len( iterables ) == 1:
+ for el in first_iterable:
+ yield el
+ else:
+ for el in __merge_two_sorted_iterables(
+ operator,
+ iter( first_iterable ),
+ merge_sorted_iterables( operator, *iterables[ 1: ] )
+ ):
+ yield el
+
+
+def __merge_two_sorted_iterables( operator, iterable1, iterable2 ):
+ unset = object()
+ continue_merge = True
+ next_1 = unset
+ next_2 = unset
+ while continue_merge:
+ try:
+ if next_1 is unset:
+ next_1 = next( iterable1 )
+ if next_2 is unset:
+ next_2 = next( iterable2 )
+ if operator( next_2 ) < operator( next_1 ):
+ yield next_2
+ next_2 = unset
+ else:
+ yield next_1
+ next_1 = unset
+ except StopIteration:
+ continue_merge = False
+ if next_1 is not unset:
+ yield next_1
+ if next_2 is not unset:
+ yield next_2
+ for el in iterable1:
+ yield el
+ for el in iterable2:
+ yield el
+
+
class Params( object ):
"""
Stores and 'sanitizes' parameters. Alphanumeric characters and the
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 lib/galaxy/webapps/galaxy/api/history_contents.py
--- a/lib/galaxy/webapps/galaxy/api/history_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/history_contents.py
@@ -33,6 +33,9 @@
:param history_id: encoded id string of the HDA's History
:type ids: str
:param ids: (optional) a comma separated list of encoded `HDA` ids
+ :param types: (optional) kinds of contents to index (currently just
+ dataset, but dataset_collection will be added shortly).
+ :type types: str
:rtype: list
:returns: dictionaries containing summary or detailed HDA information
@@ -51,7 +54,16 @@
else:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=True )
- contents_kwds = {}
+ # Allow passing in type or types - for continuity rest of methods
+ # take in type - but this one can be passed multiple types and
+ # type=dataset,dataset_collection is a bit silly.
+ types = kwd.get( 'type', kwd.get( 'types', None ) ) or []
+ if types:
+ types = util.listify(types)
+ else:
+ types = [ 'dataset' ]
+
+ contents_kwds = {'types': types}
if ids:
ids = map( lambda id: trans.security.decode_id( id ), ids.split( ',' ) )
contents_kwds[ 'ids' ] = ids
@@ -66,13 +78,14 @@
if details and details != 'all':
details = util.listify( details )
- for hda in history.contents_iter( **contents_kwds ):
- encoded_hda_id = trans.security.encode_id( hda.id )
- detailed = details == 'all' or ( encoded_hda_id in details )
- if detailed:
- rval.append( self._detailed_hda_dict( trans, hda ) )
- else:
- rval.append( self._summary_hda_dict( trans, history_id, hda ) )
+ for content in history.contents_iter( **contents_kwds ):
+ if isinstance(content, trans.app.model.HistoryDatasetAssociation):
+ encoded_content_id = trans.security.encode_id( content.id )
+ detailed = details == 'all' or ( encoded_content_id in details )
+ if detailed:
+ rval.append( self._detailed_hda_dict( trans, content ) )
+ else:
+ rval.append( self._summary_hda_dict( trans, history_id, content ) )
except Exception, e:
# for errors that are not specific to one hda (history lookup or summary list)
rval = "Error in history API at listing contents: " + str( e )
@@ -138,6 +151,13 @@
:returns: dictionary containing detailed HDA information
.. seealso:: :func:`galaxy.web.base.controller.UsesHistoryDatasetAssociationMixin.get_hda_dict`
"""
+ contents_type = kwd.get('type', 'dataset')
+ if contents_type == 'dataset':
+ return self.__show_dataset( trans, id, history_id, **kwd )
+ else:
+ return self.__handle_unknown_contents_type( trans, contents_type )
+
+ def __show_dataset( self, trans, id, history_id, **kwd ):
try:
hda = self.get_history_dataset_association_from_ids( trans, id, history_id )
hda_dict = self.get_hda_dict( trans, hda )
@@ -178,11 +198,6 @@
#TODO: copy existing, accessible hda - dataset controller, copy_datasets
#TODO: convert existing, accessible hda - model.DatasetInstance(or hda.datatype).get_converter_types
# check parameters
- source = payload.get('source', None)
- content = payload.get('content', None)
- if source not in ['library', 'hda'] or content is None:
- trans.response.status = 400
- return "Please define the source ('library' or 'hda') and the content."
# retrieve history
try:
history = self.get_history( trans, history_id, check_ownership=True, check_accessible=False )
@@ -190,6 +205,18 @@
# no way to tell if it failed bc of perms or other (all MessageExceptions)
trans.response.status = 500
return str( e )
+ type = payload.get('type', 'dataset')
+ if type == 'dataset':
+ return self.__create_dataset( trans, history, payload, **kwd )
+ else:
+ return self.__handle_unknown_contents_type( trans, type )
+
+ def __create_dataset( self, trans, history, payload, **kwd ):
+ source = payload.get('source', None)
+ content = payload.get('content', None)
+ if source not in ['library', 'hda'] or content is None:
+ trans.response.status = 400
+ return "Please define the source ('library' or 'hda') and the content."
# copy from library dataset
if source == 'library':
# get library data set
@@ -221,7 +248,7 @@
return str( msg_exc )
except Exception, exc:
trans.response.status = 500
- log.exception( "history: %s, source: %s, content: %s", history_id, source, content )
+ log.exception( "history: %s, source: %s, content: %s", trans.security.encode_id(history.id), source, content )
return str( exc )
data_copy=hda.copy( copy_children=True )
result=history.add_dataset( data_copy )
@@ -255,6 +282,13 @@
any values that were different from the original and, therefore, updated
"""
#TODO: PUT /api/histories/{encoded_history_id} payload = { rating: rating } (w/ no security checks)
+ contents_type = kwd.get('type', 'dataset')
+ if contents_type == "dataset":
+ return self.__update_dataset( trans, history_id, id, payload, **kwd )
+ else:
+ return self.__handle_unknown_contents_type( contents_type )
+
+ def __update_dataset( self, trans, history_id, id, payload, **kwd ):
changed = {}
try:
# anon user
@@ -323,6 +357,13 @@
* deleted: if the history was marked as deleted,
* purged: if the history was purged
"""
+ contents_type = kwd.get('type', 'dataset')
+ if contents_type == "dataset":
+ return self.__delete_dataset( trans, history_id, id, purge=purge, **kwd )
+ else:
+ return self.__handle_unknown_contents_type( trans, contents_type )
+
+ def __delete_dataset( self, trans, history_id, id, purge, **kwd ):
# get purge from the query or from the request body payload (a request body is optional here)
purge = util.string_as_bool( purge )
if kwd.get( 'payload', None ):
@@ -407,3 +448,8 @@
pass
#log.warn( 'unknown key: %s', str( key ) )
return validated_payload
+
+ def __handle_unknown_contents_type( self, trans, contents_type ):
+ # TODO: raise a message exception instead of setting status and returning dict.
+ trans.response.status = 400
+ return { 'error': 'Unknown contents type %s' % type }
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -130,6 +130,7 @@
new_library['description'] = description
new_library['synopsis'] = synopsis
new_library['id'] = encoded_id
+ new_library['root_folder_id'] = trans.security.encode_id( root_folder.id )
return new_library
def edit( self, trans, encoded_id, payload, **kwd ):
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -75,6 +75,19 @@
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
+ valid_history_contents_types = [
+ 'dataset',
+ ]
+ # This must come before history contents below.
+ # Accesss HDA details via histories/:history_id/contents/datasets/:hda_id
+ webapp.mapper.resource( "typed_content",
+ "{type:%s}s" % "|".join( valid_history_contents_types ),
+ name_prefix="history_content_",
+ controller='history_contents',
+ path_prefix='/api/histories/:history_id/contents',
+ parent_resources=dict( member_name='history', collection_name='histories' ),
+ )
+ # Legacy access to HDA details via histories/:history_id/contents/:hda_id
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 test/base/interactor.py
--- a/test/base/interactor.py
+++ b/test/base/interactor.py
@@ -461,16 +461,21 @@
def post_request( url, data, files={} ):
return __multipart_request( url, data, files, verb="POST" )
- def put_request( url ):
- return __urllib_request( url, 'PUT' )
+ def put_request( url, data=None ):
+ if isinstance( data, dict ):
+ assert False, "This test will fail, Galaxy's webob dependency does not parse out urlencoded PUT/PATCH entity data, API will receive empty payload."
+ return __urllib_request( url, 'PUT', json_str=data )
def delete_request( url ):
return __urllib_request( url, 'DELETE' )
- def __urllib_request( url, verb ):
+ def __urllib_request( url, verb, json_str=None ):
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(url)
request.get_method = lambda: verb
+ if json_str:
+ request.add_header( "content-type", "application/json" )
+ request.add_data(json_str)
try:
response = opener.open(request)
return RequestsLikeResponse( response.read(), status_code=response.getcode() )
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 test/functional/api/helpers.py
--- a/test/functional/api/helpers.py
+++ b/test/functional/api/helpers.py
@@ -1,6 +1,6 @@
import time
-from json import dumps
-from json import loads
+import json
+import StringIO
from pkg_resources import resource_string
# Simple workflow that takes an input and call cat wrapper on it.
@@ -10,33 +10,46 @@
workflow_random_x2_str = resource_string( __name__, "test_workflow_2.ga" )
+# Deprecated mixin, use dataset populator instead.
+# TODO: Rework existing tests to target DatasetPopulator in a setup method instead.
class TestsDatasets:
def _new_dataset( self, history_id, content='TestData123', **kwds ):
- payload = self._upload_payload( history_id, content, **kwds )
- run_response = self._post( "tools", data=payload )
- self._assert_status_code_is( run_response, 200 )
+ return DatasetPopulator( self.galaxy_interactor ).new_dataset( history_id, content=content, **kwds)
+
+ def _wait_for_history( self, history_id, assert_ok=False ):
+ return DatasetPopulator( self.galaxy_interactor ).wait_for_history( history_id, assert_ok=assert_ok )
+
+ def _new_history( self, **kwds ):
+ return DatasetPopulator( self.galaxy_interactor ).new_history( **kwds )
+
+ def _upload_payload( self, history_id, content, **kwds ):
+ return DatasetPopulator( self.galaxy_interactor ).upload_payload( history_id, content, **kwds )
+
+ def _run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
+ return DatasetPopulator( self.galaxy_interactor ).run_tool_payload( tool_id, inputs, history_id, **kwds )
+
+
+class DatasetPopulator( object ):
+
+ def __init__( self, galaxy_interactor ):
+ self.galaxy_interactor = galaxy_interactor
+
+ def new_dataset( self, history_id, content='TestData123', **kwds ):
+ payload = self.upload_payload( history_id, content, **kwds )
+ run_response = self.galaxy_interactor.post( "tools", data=payload )
return run_response.json()["outputs"][0]
- def _wait_for_history( self, history_id, assert_ok=False ):
- while True:
- history_details_response = self._get( "histories/%s" % history_id )
- self._assert_status_code_is( history_details_response, 200 )
- history_state = history_details_response.json()[ "state" ]
- if history_state not in [ "running", "queued" ]:
- break
- time.sleep( .1 )
- if assert_ok:
- self.assertEquals( history_state, 'ok' )
+ def wait_for_history( self, history_id, assert_ok=False ):
+ wait_on_state( lambda: self.galaxy_interactor.get( "histories/%s" % history_id ), assert_ok=assert_ok )
- def _new_history( self, **kwds ):
+ def new_history( self, **kwds ):
name = kwds.get( "name", "API Test History" )
- create_history_response = self._post( "histories", data=dict( name=name ) )
- self._assert_status_code_is( create_history_response, 200 )
+ create_history_response = self.galaxy_interactor.post( "histories", data=dict( name=name ) )
history_id = create_history_response.json()[ "id" ]
return history_id
- def _upload_payload( self, history_id, content, **kwds ):
+ def upload_payload( self, history_id, content, **kwds ):
name = kwds.get( "name", "Test Dataset" )
dbkey = kwds.get( "dbkey", "?" )
file_type = kwds.get( "file_type", 'txt' )
@@ -50,17 +63,17 @@
upload_params[ "files_0|to_posix_lines"] = kwds[ "to_posix_lines" ]
if "space_to_tab" in kwds:
upload_params[ "files_0|space_to_tab" ] = kwds[ "space_to_tab" ]
- return self._run_tool_payload(
+ return self.run_tool_payload(
tool_id='upload1',
inputs=upload_params,
history_id=history_id,
upload_type='upload_dataset'
)
- def _run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
+ def run_tool_payload( self, tool_id, inputs, history_id, **kwds ):
return dict(
tool_id=tool_id,
- inputs=dumps(inputs),
+ inputs=json.dumps(inputs),
history_id=history_id,
**kwds
)
@@ -73,7 +86,7 @@
self.api_test_case = api_test_case
def load_workflow( self, name, content=workflow_str, add_pja=False ):
- workflow = loads( content )
+ workflow = json.loads( content )
workflow[ "name" ] = name
if add_pja:
tool_step = workflow[ "steps" ][ "2" ]
@@ -93,9 +106,99 @@
def create_workflow( self, workflow, **create_kwds ):
data = dict(
- workflow=dumps( workflow ),
+ workflow=json.dumps( workflow ),
**create_kwds
)
upload_response = self.api_test_case._post( "workflows/upload", data=data )
uploaded_workflow_id = upload_response.json()[ "id" ]
return uploaded_workflow_id
+
+
+class LibraryPopulator( object ):
+
+ def __init__( self, api_test_case ):
+ self.api_test_case = api_test_case
+ self.galaxy_interactor = api_test_case.galaxy_interactor
+
+ def new_private_library( self, name ):
+ library = self.new_library( name )
+ library_id = library[ "id" ]
+
+ role_id = self.user_private_role_id()
+ self.set_permissions( library_id, role_id )
+ return library
+
+ def new_library( self, name ):
+ data = dict( name=name )
+ create_response = self.galaxy_interactor.post( "libraries", data=data, admin=True )
+ return create_response.json()
+
+ def set_permissions( self, library_id, role_id=None ):
+ if role_id:
+ perm_list = json.dumps( role_id )
+ else:
+ perm_list = json.dumps( [] )
+
+ permissions = dict(
+ LIBRARY_ACCESS_in=perm_list,
+ LIBRARY_MODIFY_in=perm_list,
+ LIBRARY_ADD_in=perm_list,
+ LIBRARY_MANAGE_in=perm_list,
+ )
+ self.galaxy_interactor.post( "libraries/%s/permissions" % library_id, data=permissions, admin=True )
+
+ def user_email( self ):
+ users_response = self.galaxy_interactor.get( "users" )
+ users = users_response.json()
+ assert len( users ) == 1
+ return users[ 0 ][ "email" ]
+
+ def user_private_role_id( self ):
+ user_email = self.user_email()
+ roles_response = self.api_test_case.galaxy_interactor.get( "roles", admin=True )
+ users_roles = [ r for r in roles_response.json() if r[ "name" ] == user_email ]
+ assert len( users_roles ) == 1
+ return users_roles[ 0 ][ "id" ]
+
+ def create_dataset_request( self, library, **kwds ):
+ create_data = {
+ "folder_id": kwds.get( "folder_id", library[ "root_folder_id" ] ),
+ "create_type": "file",
+ "files_0|NAME": kwds.get( "name", "NewFile" ),
+ "upload_option": kwds.get( "upload_option", "upload_file" ),
+ "file_type": kwds.get( "file_type", "auto" ),
+ "db_key": kwds.get( "db_key", "?" ),
+ }
+ files = {
+ "files_0|file_data": kwds.get( "file", StringIO.StringIO( kwds.get( "contents", "TestData" ) ) ),
+ }
+ return create_data, files
+
+ def new_library_dataset( self, name, **create_dataset_kwds ):
+ library = self.new_private_library( name )
+ payload, files = self.create_dataset_request( library, **create_dataset_kwds )
+ url_rel = "libraries/%s/contents" % ( library[ "id" ] )
+ dataset = self.api_test_case.galaxy_interactor.post( url_rel, payload, files=files ).json()[0]
+
+ def show():
+ return self.api_test_case.galaxy_interactor.get( "libraries/%s/contents/%s" % ( library[ "id" ], dataset[ "id" ] ) )
+
+ wait_on_state(show)
+ return show().json()
+
+
+def wait_on_state( state_func, assert_ok=False, timeout=5 ):
+ delta = .1
+ iteration = 0
+ while True:
+ if (delta * iteration) > timeout:
+ assert False, "Timed out waiting on state."
+ iteration += 1
+ response = state_func()
+ assert response.status_code == 200, "Failed to fetch state update while waiting."
+ state = response.json()[ "state" ]
+ if state not in [ "running", "queued", "new" ]:
+ break
+ time.sleep( delta )
+ if assert_ok:
+ assert state == "ok", "Final state - %s - not okay." % state
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 test/functional/api/test_history_contents.py
--- /dev/null
+++ b/test/functional/api/test_history_contents.py
@@ -0,0 +1,111 @@
+from base import api
+import json
+
+from .helpers import TestsDatasets
+from .helpers import LibraryPopulator
+from base.interactor import (
+ put_request,
+ delete_request,
+)
+
+
+# TODO: Test anonymous access.
+class HistoryContentsApiTestCase( api.ApiTestCase, TestsDatasets ):
+
+ def setUp( self ):
+ super( HistoryContentsApiTestCase, self ).setUp()
+ self.history_id = self._new_history()
+
+ def test_index_hda_summary( self ):
+ hda1 = self._new_dataset( self.history_id )
+ contents_response = self._get( "histories/%s/contents" % self.history_id )
+ hda_summary = self.__check_for_hda( contents_response, hda1 )
+ assert "display_types" not in hda_summary # Quick summary, not full details
+
+ def test_index_hda_all_details( self ):
+ hda1 = self._new_dataset( self.history_id )
+ contents_response = self._get( "histories/%s/contents?details=all" % self.history_id )
+ hda_details = self.__check_for_hda( contents_response, hda1 )
+ self.__assert_hda_has_full_details( hda_details )
+
+ def test_index_hda_detail_by_id( self ):
+ hda1 = self._new_dataset( self.history_id )
+ contents_response = self._get( "histories/%s/contents?details=%s" % ( self.history_id, hda1[ "id" ] ) )
+ hda_details = self.__check_for_hda( contents_response, hda1 )
+ self.__assert_hda_has_full_details( hda_details )
+
+ def test_show_hda( self ):
+ hda1 = self._new_dataset( self.history_id )
+ show_response = self.__show( hda1 )
+ self._assert_status_code_is( show_response, 200 )
+ self.__assert_matches_hda( hda1, show_response.json() )
+
+ def test_hda_copy( self ):
+ hda1 = self._new_dataset( self.history_id )
+ create_data = dict(
+ source='hda',
+ content=hda1[ "id" ],
+ )
+ second_history_id = self._new_history()
+ assert self.__count_contents( second_history_id ) == 0
+ create_response = self._post( "histories/%s/contents" % second_history_id, create_data )
+ self._assert_status_code_is( create_response, 200 )
+ assert self.__count_contents( second_history_id ) == 1
+
+ def test_library_copy( self ):
+ ld = LibraryPopulator( self ).new_library_dataset( "lda_test_library" )
+ create_data = dict(
+ source='library',
+ content=ld[ "id" ],
+ )
+ assert self.__count_contents( self.history_id ) == 0
+ create_response = self._post( "histories/%s/contents" % self.history_id, create_data )
+ self._assert_status_code_is( create_response, 200 )
+ assert self.__count_contents( self.history_id ) == 1
+
+ def test_update( self ):
+ hda1 = self._new_dataset( self.history_id )
+ self._wait_for_history( self.history_id )
+ assert str( hda1[ "deleted" ] ).lower() == "false"
+ update_url = self._api_url( "histories/%s/contents/%s" % ( self.history_id, hda1[ "id" ] ), use_key=True )
+ # Awkward json.dumps required here because of https://trello.com/c/CQwmCeG6
+ body = json.dumps( dict( deleted=True ) )
+ update_response = put_request( update_url, data=body )
+ self._assert_status_code_is( update_response, 200 )
+ show_response = self.__show( hda1 )
+ assert str( show_response.json()[ "deleted" ] ).lower() == "true"
+
+ def test_delete( self ):
+ hda1 = self._new_dataset( self.history_id )
+ self._wait_for_history( self.history_id )
+ assert str( self.__show( hda1 ).json()[ "deleted" ] ).lower() == "false"
+ url = self._api_url( "histories/%s/contents/%s" % ( self.history_id, hda1["id" ] ), use_key=True )
+ delete_response = delete_request( url )
+ assert delete_response.status_code < 300 # Something in the 200s :).
+ assert str( self.__show( hda1 ).json()[ "deleted" ] ).lower() == "true"
+
+ def __show( self, hda ):
+ show_response = self._get( "histories/%s/contents/%s" % ( self.history_id, hda[ "id" ] ) )
+ return show_response
+
+ def __count_contents( self, history_id=None, **kwds ):
+ if history_id == None:
+ history_id = self.history_id
+ contents_response = self._get( "histories/%s/contents" % history_id, kwds )
+ return len( contents_response.json() )
+
+ def __assert_hda_has_full_details( self, hda_details ):
+ self._assert_has_keys( hda_details, "display_types", "display_apps" )
+
+ def __check_for_hda( self, contents_response, hda ):
+ self._assert_status_code_is( contents_response, 200 )
+ contents = contents_response.json()
+ assert len( contents ) == 1
+ hda_summary = contents[ 0 ]
+ self.__assert_matches_hda( hda, hda_summary )
+ return hda_summary
+
+ def __assert_matches_hda( self, input_hda, query_hda ):
+ self._assert_has_keys( query_hda, "id", "name" )
+ assert input_hda[ "name" ] == query_hda[ "name" ]
+ assert input_hda[ "id" ] == query_hda[ "id" ]
diff -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e -r 24f45ea024e47f0e6ee8c427c7450e1f79d0c525 test/functional/api/test_libraries.py
--- /dev/null
+++ b/test/functional/api/test_libraries.py
@@ -0,0 +1,54 @@
+from base import api
+from .helpers import TestsDatasets
+from .helpers import LibraryPopulator
+from .helpers import wait_on_state
+
+
+class LibrariesApiTestCase( api.ApiTestCase, TestsDatasets ):
+
+ def setUp( self ):
+ super( LibrariesApiTestCase, self ).setUp()
+ self.library_populator = LibraryPopulator( self )
+
+ def test_create( self ):
+ data = dict( name="CreateTestLibrary" )
+ create_response = self._post( "libraries", data=data, admin=True )
+ self._assert_status_code_is( create_response, 200 )
+ library = create_response.json()
+ self._assert_has_keys( library, "name" )
+ assert library[ "name" ] == "CreateTestLibrary"
+
+ def test_create_private_library_permissions( self ):
+ library = self.library_populator.new_library( "PermissionTestLibrary" )
+ library_id = library[ "id" ]
+
+ role_id = self.library_populator.user_private_role_id()
+ self.library_populator.set_permissions( library_id, role_id )
+ create_response = self._create_folder( library )
+ self._assert_status_code_is( create_response, 200 )
+
+ def test_create_dataset( self ):
+ library = self.library_populator.new_private_library( "ForCreateDatasets" )
+ payload, files = self.library_populator.create_dataset_request( library, file_type="txt", contents="create_test" )
+ create_response = self._post( "libraries/%s/contents" % library[ "id" ], payload, files=files )
+ self._assert_status_code_is( create_response, 200 )
+ library_datasets = create_response.json()
+ assert len( library_datasets ) == 1
+ library_dataset = library_datasets[ 0 ]
+
+ def show():
+ return self._get( "libraries/%s/contents/%s" % ( library[ "id" ], library_dataset[ "id" ] ) )
+
+ wait_on_state( show, assert_ok=True )
+ library_dataset = show().json()
+ self._assert_has_keys( library_dataset, "peek", "data_type" )
+ assert library_dataset[ "peek" ].find("create_test") >= 0
+ assert library_dataset[ "data_type" ] == "txt"
+
+ def _create_folder( self, library ):
+ create_data = dict(
+ folder_id=library[ "root_folder_id" ],
+ create_type="folder",
+ name="New Folder",
+ )
+ return self._post( "libraries/%s/contents" % library[ "id" ], data=create_data )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: Delimit Biostar tags by commas.
by commits-noreply@bitbucket.org 03 Mar '14
by commits-noreply@bitbucket.org 03 Mar '14
03 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6956c4b2d9cd/
Changeset: 6956c4b2d9cd
User: dan
Date: 2014-03-03 23:21:50
Summary: Delimit Biostar tags by commas.
Affected #: 1 file
diff -r b6138d0f8a753cfcc76881d18c0e4cac6298a921 -r 6956c4b2d9cdf2244604dd2782c4c5667c52868e lib/galaxy/webapps/galaxy/controllers/biostar.py
--- a/lib/galaxy/webapps/galaxy/controllers/biostar.py
+++ b/lib/galaxy/webapps/galaxy/controllers/biostar.py
@@ -135,6 +135,6 @@
# Tool specific information for payload
payload = { 'title':'Need help with "%s" tool' % ( tool.name ),
'content': '<br /><hr /><p>Tool name: %s</br>Tool version: %s</br>Tool ID: %s</p>' % ( tool.name, tool.version, tool.id ),
- 'tag_val': 'galaxy ' + tag_for_tool( tool ) }
+ 'tag_val': ','.join( [ 'galaxy', tag_for_tool( tool ) ] ) }
# Pass on to regular question method
return self.biostar_question_redirect( trans, payload )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: davebgx: Update setup_r_environment to work with the elimination of fabric.
by commits-noreply@bitbucket.org 03 Mar '14
by commits-noreply@bitbucket.org 03 Mar '14
03 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/b6138d0f8a75/
Changeset: b6138d0f8a75
User: davebgx
Date: 2014-03-03 21:35:26
Summary: Update setup_r_environment to work with the elimination of fabric.
Affected #: 1 file
diff -r 957e0c7548626c2532af6fd00387ec1568b7dce0 -r b6138d0f8a753cfcc76881d18c0e4cac6298a921 lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
@@ -296,7 +296,7 @@
retain control over the process. This method is named "complex" because it uses queues and
threads to execute a command while capturing and displaying the output.
"""
- wrapped_command = shlex.split( "/bin/sh -c '%s'" % str( command ) )
+ wrapped_command = shlex.split( '/bin/sh -c "%s"' % str( command ) )
# Launch the command as subprocess. A bufsize of 1 means line buffered.
process_handle = subprocess.Popen( wrapped_command,
stdout=subprocess.PIPE,
@@ -607,7 +607,7 @@
with settings( warn_only=True ):
for tarball_name in tarball_names:
cmd = '''PATH=$PATH:$R_HOME/bin; export PATH; R_LIBS=$INSTALL_DIR; export R_LIBS;
- Rscript -e "install.packages(c('%s'),lib='$INSTALL_DIR', repos=NULL, dependencies=FALSE)"''' % ( str( tarball_name ) )
+ Rscript -e \\"install.packages(c('%s'),lib='$INSTALL_DIR', repos=NULL, dependencies=FALSE)\\"''' % ( str( tarball_name ) )
cmd = install_environment.build_command( td_common_util.evaluate_template( cmd, install_dir ) )
return_code = handle_command( app, tool_dependency, install_dir, cmd )
if return_code:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jeremy goecks: Extend TabularDataTableView to support full page view and an embedded view.
by commits-noreply@bitbucket.org 03 Mar '14
by commits-noreply@bitbucket.org 03 Mar '14
03 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/957e0c754862/
Changeset: 957e0c754862
User: jeremy goecks
Date: 2014-03-03 20:04:09
Summary: Extend TabularDataTableView to support full page view and an embedded view.
Affected #: 3 files
diff -r 96bf0719d590025a818828245e66e3b08de9f828 -r 957e0c7548626c2532af6fd00387ec1568b7dce0 static/scripts/mvc/data.js
--- a/static/scripts/mvc/data.js
+++ b/static/scripts/mvc/data.js
@@ -108,13 +108,15 @@
});
/**
- * Provides table-based, dynamic view of a tabular dataset.
- * NOTE: view's el must be in DOM already and provided when
- * createing the view so that scrolling event can be attached
- * to the correct container.
+ * Provides a base for table-based, dynamic view of a tabular dataset.
+ * Do not instantiate directly; use either TopLevelTabularDatasetChunkedView
+ * or EmbeddedTabularDatasetChunkedView.
*/
var TabularDatasetChunkedView = Backbone.View.extend({
+ /**
+ * Initialize view and, importantly, set a scroll element.
+ */
initialize: function(options) {
// Row count for rendering.
this.row_count = 0;
@@ -147,25 +149,15 @@
}
// -- Show new chunks during scrolling. --
-
+
var self = this,
- // Element that does the scrolling.
- scroll_elt = _.find(this.$el.parents(), function(p) {
- return $(p).css('overflow') === 'auto';
- }),
// Flag to ensure that only one chunk is loaded at a time.
loading_chunk = false;
- // If no scrolling element found, use window.
- if (!scroll_elt) { scroll_elt = window; }
-
- // Wrap scrolling element for easy access.
- scroll_elt = $(scroll_elt);
-
// Set up chunk loading when scrolling using the scrolling element.
- scroll_elt.scroll(function() {
+ this.scroll_elt.scroll(function() {
// If not already loading a chunk and have scrolled to the bottom of this element, get next chunk.
- if ( !loading_chunk && (self.$el.height() - scroll_elt.scrollTop() - scroll_elt.height() <= 0) ) {
+ if ( !loading_chunk && self.scrolled_to_bottom() ) {
loading_chunk = true;
$.when(self.model.get_next_chunk()).then(function(result) {
if (result) {
@@ -182,6 +174,13 @@
});
},
+ /**
+ * Returns true if user has scrolled to the bottom of the view.
+ */
+ scrolled_to_bottom: function() {
+ return false;
+ },
+
// -- Helper functions. --
_renderCell: function(cell_contents, index, colspan) {
@@ -246,6 +245,61 @@
}
});
+/**
+ * Tabular view that is placed at the top level of page. Scrolling occurs
+ * view top-level elements outside of view.
+ */
+var TopLevelTabularDatasetChunkedView = TabularDatasetChunkedView.extend({
+
+ initialize: function(options) {
+ TabularDatasetChunkedView.prototype.initialize.call(this, options);
+
+ // Scrolling happens in top-level elements.
+ scroll_elt = _.find(this.$el.parents(), function(p) {
+ return $(p).css('overflow') === 'auto';
+ });
+
+ // If no scrolling element found, use window.
+ if (!scroll_elt) { scroll_elt = window; }
+
+ // Wrap scrolling element for easy access.
+ this.scroll_elt = $(scroll_elt);
+ },
+
+ /**
+ * Returns true if user has scrolled to the bottom of the view.
+ */
+ scrolled_to_bottom: function() {
+ return (this.$el.height() - this.scroll_elt.scrollTop() - this.scroll_elt.height() <= 0);
+ }
+
+});
+
+/**
+ * Tabular view tnat is embedded in a page. Scrolling occurs in view's el.
+ */
+var EmbeddedTabularDatasetChunkedView = TabularDatasetChunkedView.extend({
+
+ initialize: function(options) {
+ TabularDatasetChunkedView.prototype.initialize.call(this, options);
+
+ // Because view is embedded, set up div to do scrolling.
+ this.scroll_elt = this.$el.css({
+ position: 'relative',
+ overflow: 'scroll',
+ height: this.options.height || '500px'
+ });
+ },
+
+ /**
+ * Returns true if user has scrolled to the bottom of the view.
+ */
+ scrolled_to_bottom: function() {
+ return this.$el.scrollTop() + this.$el.innerHeight() >= this.el.scrollHeight;
+ }
+
+});
+
// button for trackster visualization
var TabularButtonTracksterView = Backbone.View.extend(
{
@@ -513,15 +567,28 @@
* Create a tabular dataset chunked view (and requisite tabular dataset model)
* and appends to parent_elt.
*/
-var createTabularDatasetChunkedView = function(dataset_config, parent_elt) {
- // Create view element and add to parent.
- var view_div = $('<div/>').appendTo(parent_elt);
+var createTabularDatasetChunkedView = function(options) {
+ // Create and set model.
+ options.model = new TabularDataset(options.dataset_config);
- // default viewer
- return new TabularDatasetChunkedView({
- el: view_div,
- model: new TabularDataset(dataset_config)
- }).render();
+ var parent_elt = options.parent_elt;
+ var embedded = options.embedded;
+
+ // Clean up options so that only needed options are passed to view.
+ delete options.embedded;
+ delete options.parent_elt;
+ delete options.dataset_config;
+
+ // Create and set up view.
+ var view = (embedded ? new EmbeddedTabularDatasetChunkedView(options) :
+ new TopLevelTabularDatasetChunkedView(options));
+ view.render();
+
+ if (parent_elt) {
+ parent_elt.append(view.$el);
+ }
+
+ return view;
};
return {
diff -r 96bf0719d590025a818828245e66e3b08de9f828 -r 957e0c7548626c2532af6fd00387ec1568b7dce0 templates/webapps/galaxy/dataset/display.mako
--- a/templates/webapps/galaxy/dataset/display.mako
+++ b/templates/webapps/galaxy/dataset/display.mako
@@ -23,18 +23,18 @@
//
$('.page-body').children().remove();
- data.createTabularDatasetChunkedView(
- // Dataset config. TODO: encode id.
- _.extend( ${h.to_json_string( item.to_dict() )},
- {
- chunk_url: "${h.url_for( controller='/dataset', action='display',
- dataset_id=trans.security.encode_id( item.id ))}",
- first_data_chunk: ${first_chunk}
- }
- ),
- // Append view to body.
- $('.page-body')
- );
+ data.createTabularDatasetChunkedView({
+ // TODO: encode id.
+ dataset_config:
+ _.extend( ${h.to_json_string( item.to_dict() )},
+ {
+ chunk_url: "${h.url_for( controller='/dataset', action='display',
+ dataset_id=trans.security.encode_id( item.id ))}",
+ first_data_chunk: ${first_chunk}
+ }
+ ),
+ parent_elt: $('.page-body')
+ });
});
</script>
diff -r 96bf0719d590025a818828245e66e3b08de9f828 -r 957e0c7548626c2532af6fd00387ec1568b7dce0 templates/webapps/galaxy/dataset/tabular_chunked.mako
--- a/templates/webapps/galaxy/dataset/tabular_chunked.mako
+++ b/templates/webapps/galaxy/dataset/tabular_chunked.mako
@@ -17,8 +17,8 @@
});
require(['mvc/data'], function(data) {
- data.createTabularDatasetChunkedView(
- _.extend( ${h.to_json_string( trans.security.encode_dict_ids( dataset.to_dict() ) )},
+ data.createTabularDatasetChunkedView({
+ dataset_config: _.extend( ${h.to_json_string( trans.security.encode_dict_ids( dataset.to_dict() ) )},
{
url_viz: "${h.url_for( controller='/visualization')}",
chunk_url: "${h.url_for( controller='/dataset', action='display',
@@ -26,9 +26,8 @@
first_data_chunk: ${chunk}
}
),
- // Append view to body.
- $('body')
- );
+ parent_elt: $('body')
+ });
});
</script></%def>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: afgane: Add kwd to get_dbkeys method signature to fix the API call that includes the actual API key in the request
by commits-noreply@bitbucket.org 03 Mar '14
by commits-noreply@bitbucket.org 03 Mar '14
03 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/96bf0719d590/
Changeset: 96bf0719d590
User: afgane
Date: 2014-03-03 17:39:40
Summary: Add kwd to get_dbkeys method signature to fix the API call that includes the actual API key in the request
Affected #: 1 file
diff -r edd17b90c126d73451fe0a080ba4c75cff5a92e5 -r 96bf0719d590025a818828245e66e3b08de9f828 lib/galaxy/visualization/genomes.py
--- a/lib/galaxy/visualization/genomes.py
+++ b/lib/galaxy/visualization/genomes.py
@@ -210,7 +210,7 @@
rval = self.genomes[ dbkey ]
return rval
- def get_dbkeys( self, trans, chrom_info=False ):
+ def get_dbkeys( self, trans, chrom_info=False, **kwd ):
""" Returns all known dbkeys. If chrom_info is True, only dbkeys with
chromosome lengths are returned. """
dbkeys = []
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: Use qualified url for biostar_url_redirect due to javascript always prepending galaxy base prefix (e.g. usually /) unless url contains //
by commits-noreply@bitbucket.org 03 Mar '14
by commits-noreply@bitbucket.org 03 Mar '14
03 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/edd17b90c126/
Changeset: edd17b90c126
User: dan
Date: 2014-03-03 16:09:19
Summary: Use qualified url for biostar_url_redirect due to javascript always prepending galaxy base prefix (e.g. usually /) unless url contains //
Affected #: 1 file
diff -r 4da230085f06db45563ab3ee27d07848dd8950de -r edd17b90c126d73451fe0a080ba4c75cff5a92e5 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -40,7 +40,7 @@
'enable_cloud_launch' : app.config.get_bool('enable_cloud_launch', False),
'lims_doc_url' : app.config.get("lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts"),
'biostar_url' : app.config.biostar_url,
- 'biostar_url_redirect' : h.url_for(controller='biostar', action='biostar_redirect', biostar_action='show_tag_galaxy'),
+ 'biostar_url_redirect' : h.url_for( controller='biostar', action='biostar_redirect', biostar_action='show_tag_galaxy', qualified=True ),
'support_url' : app.config.get("support_url", "http://wiki.galaxyproject.org/Support"),
'search_url' : app.config.get("search_url", "http://galaxyproject.org/search/usegalaxy/"),
'mailing_lists' : app.config.get("mailing_lists", "http://wiki.galaxyproject.org/MailingLists"),
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: Fixes for biostar show galaxy tag redirect
by commits-noreply@bitbucket.org 03 Mar '14
by commits-noreply@bitbucket.org 03 Mar '14
03 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4da230085f06/
Changeset: 4da230085f06
User: dan
Date: 2014-03-03 15:57:31
Summary: Fixes for biostar show galaxy tag redirect
Affected #: 2 files
diff -r 538745257362eebda5caf25c8c16793caa90fec7 -r 4da230085f06db45563ab3ee27d07848dd8950de lib/galaxy/webapps/galaxy/controllers/biostar.py
--- a/lib/galaxy/webapps/galaxy/controllers/biostar.py
+++ b/lib/galaxy/webapps/galaxy/controllers/biostar.py
@@ -17,8 +17,9 @@
_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?(a)\[\\\]^_`{|},.]+')
BIOSTAR_ACTIONS = {
+ None: '',
'new': 'p/new/post/',
- None: ''
+ 'show_tag_galaxy': 't/galaxy/'
}
diff -r 538745257362eebda5caf25c8c16793caa90fec7 -r 4da230085f06db45563ab3ee27d07848dd8950de templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -40,7 +40,7 @@
'enable_cloud_launch' : app.config.get_bool('enable_cloud_launch', False),
'lims_doc_url' : app.config.get("lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts"),
'biostar_url' : app.config.biostar_url,
- 'biostar_url_redirect' : h.url_for(controller='biostar', action='biostar_redirect', biostar_action='show/tag/galaxy'),
+ 'biostar_url_redirect' : h.url_for(controller='biostar', action='biostar_redirect', biostar_action='show_tag_galaxy'),
'support_url' : app.config.get("support_url", "http://wiki.galaxyproject.org/Support"),
'search_url' : app.config.get("search_url", "http://galaxyproject.org/search/usegalaxy/"),
'mailing_lists' : app.config.get("mailing_lists", "http://wiki.galaxyproject.org/MailingLists"),
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: Update Biostar interaction to use new cookie-based method.
by commits-noreply@bitbucket.org 03 Mar '14
by commits-noreply@bitbucket.org 03 Mar '14
03 Mar '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/538745257362/
Changeset: 538745257362
User: dan
Date: 2014-03-03 15:11:38
Summary: Update Biostar interaction to use new cookie-based method.
Affected #: 1 file
diff -r eafb4208db665dfeed7d8f3e6f0d2b61ce2f4fc8 -r 538745257362eebda5caf25c8c16793caa90fec7 lib/galaxy/webapps/galaxy/controllers/biostar.py
--- a/lib/galaxy/webapps/galaxy/controllers/biostar.py
+++ b/lib/galaxy/webapps/galaxy/controllers/biostar.py
@@ -7,6 +7,7 @@
import base64
from galaxy.util import json
import hmac
+import urlparse
# Slugifying from Armin Ronacher (http://flask.pocoo.org/snippets/5/)
@@ -15,6 +16,11 @@
_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?(a)\[\\\]^_`{|},.]+')
+BIOSTAR_ACTIONS = {
+ 'new': 'p/new/post/',
+ None: ''
+}
+
def slugify(text, delim=u'-'):
"""Generates an slightly worse ASCII-only slug."""
@@ -28,12 +34,9 @@
# Biostar requires all keys to be present, so we start with a template
DEFAULT_PAYLOAD = {
- 'email': "",
- 'title': "",
- 'tags': 'galaxy',
- 'tool_name': '',
- 'tool_version': '',
- 'tool_id': ''
+ 'title': '',
+ 'tag_val': 'galaxy',
+ 'content': '',
}
@@ -49,10 +52,32 @@
def tag_for_tool( tool ):
"""
- Generate a reasonavle biostar tag for a tool.
+ Generate a reasonable biostar tag for a tool.
"""
return slugify( unicode( tool.name ) )
+def determine_cookie_domain( galaxy_hostname, biostar_hostname ):
+ if galaxy_hostname == biostar_hostname:
+ return galaxy_hostname
+
+ sub_biostar_hostname = biostar_hostname.split( '.', 1 )[-1]
+ if sub_biostar_hostname == galaxy_hostname:
+ return galaxy_hostname
+
+ sub_galaxy_hostname = galaxy_hostname.split( '.', 1 )[-1]
+ if sub_biostar_hostname == sub_galaxy_hostname:
+ return sub_galaxy_hostname
+
+ return galaxy_hostname
+
+def create_cookie( trans, key_name, key, email ):
+ digest = hmac.new( key, email ).hexdigest()
+ value = "%s:%s" % (email, digest)
+ trans.set_cookie( value, name=key_name, path='/', age=90, version='1' )
+ #We need to explicitly set the domain here, in order to allow for biostar in a subdomain to work
+ galaxy_hostname = urlparse.urlsplit( url_for( '/', qualified=True ) ).hostname
+ biostar_hostname = urlparse.urlsplit( trans.app.config.biostar_url ).hostname
+ trans.response.cookies[ key_name ][ 'domain' ] = determine_cookie_domain( galaxy_hostname, biostar_hostname )
class BiostarController( BaseUIController ):
"""
@@ -65,26 +90,22 @@
Generate a redirect to a Biostar site using external authentication to
pass Galaxy user information and information about a specific tool.
"""
- payload = payload or {}
# Ensure biostar integration is enabled
if not trans.app.config.biostar_url:
return error( "Biostar integration is not enabled" )
+ if biostar_action not in BIOSTAR_ACTIONS:
+ return error( "Invalid action specified (%s)." % ( biostar_action ) )
+
# Start building up the payload
+ payload = payload or {}
payload = dict( DEFAULT_PAYLOAD, **payload )
# Do the best we can of providing user information for the payload
if trans.user:
- payload['username'] = "user-" + trans.security.encode_id( trans.user.id )
- payload['email'] = trans.user.email
- if trans.user.username:
- payload['display_name'] = trans.user.username
- else:
- payload['display_name'] = trans.user.email.split( "@" )[0]
+ email = trans.user.email
else:
- encoded = trans.security.encode_id( trans.galaxy_session.id )
- payload['username'] = "anon-" + encoded
- payload['display_name'] = "Anonymous Galaxy User"
- data, digest = encode_data( trans.app.config.biostar_key, payload )
- return trans.response.send_redirect( url_for( trans.app.config.biostar_url, data=data, digest=digest, name=trans.app.config.biostar_key_name, action=biostar_action ) )
+ email = "anon-%s" % ( trans.security.encode_id( trans.galaxy_session.id ) )
+ create_cookie( trans, trans.app.config.biostar_key_name, trans.app.config.biostar_key, email )
+ return trans.response.send_redirect( url_for( urlparse.urljoin( trans.app.config.biostar_url, BIOSTAR_ACTIONS[ biostar_action ] ), **payload ) )
@web.expose
def biostar_question_redirect( self, trans, payload=None ):
@@ -111,9 +132,8 @@
if not tool:
return error( "No tool found matching '%s'" % tool_id )
# Tool specific information for payload
- payload = { 'tool_name': tool.name,
- 'tool_version': tool.version,
- 'tool_id': tool.id,
- 'tags': 'galaxy ' + tag_for_tool( tool ) }
+ payload = { 'title':'Need help with "%s" tool' % ( tool.name ),
+ 'content': '<br /><hr /><p>Tool name: %s</br>Tool version: %s</br>Tool ID: %s</p>' % ( tool.name, tool.version, tool.id ),
+ 'tag_val': 'galaxy ' + tag_for_tool( tool ) }
# Pass on to regular question method
return self.biostar_question_redirect( trans, payload )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5dac6a566615/
Changeset: 5dac6a566615
User: jeremy goecks
Date: 2014-02-28 22:18:05
Summary: Add header and line demarcation to tabular chunked data view.
Affected #: 1 file
diff -r edff37d76463a468753477080f65886d4a53619e -r 5dac6a566615bf747a8515f72ec21be15957647f static/scripts/mvc/data.js
--- a/static/scripts/mvc/data.js
+++ b/static/scripts/mvc/data.js
@@ -115,23 +115,29 @@
*/
var TabularDatasetChunkedView = Backbone.View.extend({
- initialize: function(options)
- {
+ initialize: function(options) {
+ // Row count for rendering.
+ this.row_count = 0;
+
+ // CSS colors used in table.
+ this.header_color = '#AAA';
+ this.dark_row_color = '#DDD';
+
// load trackster button
new TabularButtonTracksterView(options);
},
- render: function()
- {
+ render: function() {
// Add data table and header.
var data_table = $('<table/>').attr({
id: 'content_table',
cellpadding: 0
});
this.$el.append(data_table);
- var column_names = this.model.get_metadata('column_names');
+ var column_names = this.model.get_metadata('column_names'),
+ header_row = $('<tr/>').css('background-color', this.header_color).appendTo(data_table);
if (column_names) {
- data_table.append('<tr><th>' + column_names.join('</th><th>') + '</th></tr>');
+ header_row.append('<th>' + column_names.join('</th><th>') + '</th>');
}
// Add first chunk.
@@ -199,6 +205,11 @@
var cells = line.split('\t'),
row = $('<tr>'),
num_columns = this.model.get_metadata('columns');
+
+ if (this.row_count % 2 !== 0) {
+ row.css('background-color', this.dark_row_color);
+ }
+
if (cells.length === num_columns) {
_.each(cells, function(cell_contents, index) {
row.append(this._renderCell(cell_contents, index));
@@ -222,6 +233,8 @@
// Comment line, just return the one cell.
row.append(this._renderCell(line, 0, num_columns));
}
+
+ this.row_count++;
return row;
},
https://bitbucket.org/galaxy/galaxy-central/commits/eafb4208db66/
Changeset: eafb4208db66
User: jeremy goecks
Date: 2014-02-28 23:03:52
Summary: Automated merge
Affected #: 2 files
diff -r 5dac6a566615bf747a8515f72ec21be15957647f -r eafb4208db665dfeed7d8f3e6f0d2b61ce2f4fc8 test/unit/tools/test_select_parameters.py
--- a/test/unit/tools/test_select_parameters.py
+++ b/test/unit/tools/test_select_parameters.py
@@ -48,6 +48,24 @@
self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
assert self.param.need_late_validation( self.trans, { "series": [ { "my_name": "42", "input_bam": basic.RuntimeValue() } ] } )
+ def test_filter_param_value( self ):
+ self.options_xml = '''<options from_data_table="test_table"><filter type="param_value" ref="input_bam" column="0" /></options>'''
+ assert ("testname1", "testpath1", False) in self.param.get_options( self.trans, { "input_bam": "testname1" } )
+ assert ("testname2", "testpath2", False) in self.param.get_options( self.trans, { "input_bam": "testname2" } )
+ assert len( self.param.get_options( self.trans, { "input_bam": "testname3" } ) ) == 0
+
+ def test_filter_param_value2( self ):
+ # Same test as above, but filtering on a different column.
+ self.options_xml = '''<options from_data_table="test_table"><filter type="param_value" ref="input_bam" column="1" /></options>'''
+ assert ("testname1", "testpath1", False) in self.param.get_options( self.trans, { "input_bam": "testpath1" } )
+ assert ("testname2", "testpath2", False) in self.param.get_options( self.trans, { "input_bam": "testpath2" } )
+ assert len( self.param.get_options( self.trans, { "input_bam": "testpath3" } ) ) == 0
+
+ def test_filter_nested( self ):
+ # Test filtering a parameter inside a conditional (not currently supported.)
+ self.options_xml = '''<options from_data_table="test_table"><filter type="param_value" ref="input_bam" column="1" /></options>'''
+ assert ("testname1", "testpath1", False) in self.param.get_options( self.trans, {"condtional1" : { "input_bam": "testpath1", "my_name": 42 } } )
+
# TODO: Good deal of overlap here with DataToolParameterTestCase,
# refactor.
def setUp( self ):
@@ -59,6 +77,7 @@
self.test_history = model.History()
self.app.model.context.add( self.test_history )
self.app.model.context.flush()
+ self.app.tool_data_tables[ "test_table" ] = MockToolDataTable()
self.trans = bunch.Bunch(
app=self.app,
get_history=lambda: self.test_history,
@@ -91,3 +110,16 @@
self._param = basic.SelectToolParameter( self.mock_tool, self.param_xml )
return self._param
+
+
+class MockToolDataTable( object ):
+
+ def __init__( self ):
+ self.columns = dict(
+ name=0,
+ value=1,
+ )
+ self.missing_index_file = None
+
+ def get_fields( self ):
+ return [ [ "testname1", "testpath1" ], [ "testname2", "testpath2" ] ]
diff -r 5dac6a566615bf747a8515f72ec21be15957647f -r eafb4208db665dfeed7d8f3e6f0d2b61ce2f4fc8 test/unit/tools_support.py
--- a/test/unit/tools_support.py
+++ b/test/unit/tools_support.py
@@ -121,6 +121,7 @@
from galaxy.security import GalaxyRBACAgent
self.job_queue = NoopQueue()
self.security_agent = GalaxyRBACAgent( self.model )
+ self.tool_data_tables = {}
class MockContext(object):
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jmchilton: Test cases for filtering tool parameters on other parameter values.
by commits-noreply@bitbucket.org 28 Feb '14
by commits-noreply@bitbucket.org 28 Feb '14
28 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/01933aab375c/
Changeset: 01933aab375c
User: jmchilton
Date: 2014-02-28 21:49:46
Summary: Test cases for filtering tool parameters on other parameter values.
Includes test case that is broken without JJ's contributions in pull request #343.
Affected #: 2 files
diff -r edff37d76463a468753477080f65886d4a53619e -r 01933aab375c87302106e851a77e425e51d0394a test/unit/tools/test_select_parameters.py
--- a/test/unit/tools/test_select_parameters.py
+++ b/test/unit/tools/test_select_parameters.py
@@ -48,6 +48,24 @@
self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
assert self.param.need_late_validation( self.trans, { "series": [ { "my_name": "42", "input_bam": basic.RuntimeValue() } ] } )
+ def test_filter_param_value( self ):
+ self.options_xml = '''<options from_data_table="test_table"><filter type="param_value" ref="input_bam" column="0" /></options>'''
+ assert ("testname1", "testpath1", False) in self.param.get_options( self.trans, { "input_bam": "testname1" } )
+ assert ("testname2", "testpath2", False) in self.param.get_options( self.trans, { "input_bam": "testname2" } )
+ assert len( self.param.get_options( self.trans, { "input_bam": "testname3" } ) ) == 0
+
+ def test_filter_param_value2( self ):
+ # Same test as above, but filtering on a different column.
+ self.options_xml = '''<options from_data_table="test_table"><filter type="param_value" ref="input_bam" column="1" /></options>'''
+ assert ("testname1", "testpath1", False) in self.param.get_options( self.trans, { "input_bam": "testpath1" } )
+ assert ("testname2", "testpath2", False) in self.param.get_options( self.trans, { "input_bam": "testpath2" } )
+ assert len( self.param.get_options( self.trans, { "input_bam": "testpath3" } ) ) == 0
+
+ def test_filter_nested( self ):
+ # Test filtering a parameter inside a conditional (not currently supported.)
+ self.options_xml = '''<options from_data_table="test_table"><filter type="param_value" ref="input_bam" column="1" /></options>'''
+ assert ("testname1", "testpath1", False) in self.param.get_options( self.trans, {"condtional1" : { "input_bam": "testpath1", "my_name": 42 } } )
+
# TODO: Good deal of overlap here with DataToolParameterTestCase,
# refactor.
def setUp( self ):
@@ -59,6 +77,7 @@
self.test_history = model.History()
self.app.model.context.add( self.test_history )
self.app.model.context.flush()
+ self.app.tool_data_tables[ "test_table" ] = MockToolDataTable()
self.trans = bunch.Bunch(
app=self.app,
get_history=lambda: self.test_history,
@@ -91,3 +110,16 @@
self._param = basic.SelectToolParameter( self.mock_tool, self.param_xml )
return self._param
+
+
+class MockToolDataTable( object ):
+
+ def __init__( self ):
+ self.columns = dict(
+ name=0,
+ value=1,
+ )
+ self.missing_index_file = None
+
+ def get_fields( self ):
+ return [ [ "testname1", "testpath1" ], [ "testname2", "testpath2" ] ]
diff -r edff37d76463a468753477080f65886d4a53619e -r 01933aab375c87302106e851a77e425e51d0394a test/unit/tools_support.py
--- a/test/unit/tools_support.py
+++ b/test/unit/tools_support.py
@@ -121,6 +121,7 @@
from galaxy.security import GalaxyRBACAgent
self.job_queue = NoopQueue()
self.security_agent = GalaxyRBACAgent( self.model )
+ self.tool_data_tables = {}
class MockContext(object):
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jeremy goecks: Trackster: replace custom config settings model and view with generic model/view from config.js. Improvements for generic config model/view. Whitespace fixes.
by commits-noreply@bitbucket.org 28 Feb '14
by commits-noreply@bitbucket.org 28 Feb '14
28 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/edff37d76463/
Changeset: edff37d76463
User: jeremy goecks
Date: 2014-02-28 21:27:59
Summary: Trackster: replace custom config settings model and view with generic model/view from config.js. Improvements for generic config model/view. Whitespace fixes.
Affected #: 4 files
diff -r e059dbc01b575b856509c2e2baccac3750b6eeb5 -r edff37d76463a468753477080f65886d4a53619e static/scripts/utils/config.js
--- a/static/scripts/utils/config.js
+++ b/static/scripts/utils/config.js
@@ -16,9 +16,14 @@
this.set(_.extend({}, defaults, options));
}
- // If type color and no value, get random color.
- if (this.get('type') === 'color' && !this.get('value')) {
- this.set('value', util_mod.get_random_color());
+ if (!this.get('value')) {
+ if (this.get('type') === 'color') {
+ // For color setting, set random color.
+ this.set('value', util_mod.get_random_color());
+ }
+ else {
+ this.set_value(this.get('default_value'));
+ }
}
},
@@ -90,23 +95,49 @@
}
return undefined;
- }
+ },
+
+ /**
+ * Set value for a setting.
+ */
+ set_value: function(key, value) {
+ var s = this.get(key);
+ if (s) {
+ return s.set_value(value);
+ }
+
+ return undefined;
+ },
+
+ /**
+ * Set default value for a setting.
+ */
+ set_default_value: function(key, default_value) {
+ var s = this.get(key);
+ if (s) {
+ return s.set('default_value', default_value);
+ }
+
+ return undefined;
+ }
},
{
/**
- * Utility function that creates a ConfigSettingsCollection from a standard dictionary
- * with configuration key-value pairs.
+ * Utility function that creates a ConfigSettingsCollection from a set of models
+ * and a saved_values dictionary.
*/
- from_config_dict: function(config_dict) {
- // Create a list of key-value dicts suitable for sending to a collection.
- var settings_list = _.map(_.keys(config_dict), function(key) {
- return {
- key: key,
- value: config_dict[key]
- };
- });
-
- return new ConfigSettingCollection(settings_list);
+ from_models_and_saved_values: function(models, saved_values) {
+ // Update models with saved values.
+ if (saved_values) {
+ _.each(models, function(m) {
+ if (saved_values[m.key]) {
+ // Found saved value, so update model.
+ m.value = saved_values[m.key];
+ }
+ });
+ }
+
+ return new ConfigSettingCollection(models);
}
});
@@ -265,7 +296,7 @@
var id = 'param_' + index;
var value = self.$el.find( '#' + id ).val();
if ( setting.get('type') === 'bool' ) {
- value = container.find( '#' + id ).is( ':checked' );
+ value = self.$el.find( '#' + id ).is( ':checked' );
}
setting.set_value(value);
}
diff -r e059dbc01b575b856509c2e2baccac3750b6eeb5 -r edff37d76463a468753477080f65886d4a53619e static/scripts/viz/trackster.js
--- a/static/scripts/viz/trackster.js
+++ b/static/scripts/viz/trackster.js
@@ -199,7 +199,7 @@
init_editor : function ()
{
// set title
- $("#center .unified-panel-title").text(view.prefs.name + " (" + view.dbkey + ")");
+ $("#center .unified-panel-title").text(view.config.get_value('name') + " (" + view.dbkey + ")");
// add dataset
if (galaxy_config.app.add_dataset)
@@ -208,7 +208,7 @@
data: { hda_ldda: 'hda', data_type: 'track_config' },
dataType: "json",
success: function(track_data) { view.add_drawable( trackster_ui.object_from_template(track_data, view, view) ); }
- });
+ });
// initialize icons
$("#add-bookmark-button").click(function()
diff -r e059dbc01b575b856509c2e2baccac3750b6eeb5 -r edff37d76463a468753477080f65886d4a53619e static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1,7 +1,7 @@
define( ["libs/underscore", "viz/visualization", "viz/trackster/util",
"viz/trackster/slotting", "viz/trackster/painters", "viz/trackster/filters",
- "mvc/data", "mvc/tools" ],
- function(_, visualization, util, slotting, painters, filters_mod, data, tools_mod) {
+ "mvc/data", "mvc/tools", "utils/config" ],
+ function(_, visualization, util, slotting, painters, filters_mod, data, tools_mod, config_mod) {
var extend = _.extend;
@@ -207,26 +207,23 @@
// -- Set up drawable configuration. --
- this.config = new Config({
- params: this.config_params,
- onchange: this.config_onchange,
- saved_values: obj_dict.prefs
- });
- this.prefs = this.config.get('values');
-
+ this.config = config_mod.ConfigSettingCollection.from_models_and_saved_values(this.config_params, obj_dict.prefs);
+ console.log(obj_dict.prefs);
+ this.config.each(function(s) { console.log(s.id, s.get('value')) })
+
// If there's no saved name, use object name.
- if (!this.prefs.name) {
- this.prefs.name = obj_dict.name;
+ if (!this.config.get_value('name')) {
+ this.config.set_value('name', obj_dict.name);
}
if (this.config_onchange) {
- this.config.on('change:values', this.config_onchange, this);
+ this.config.on('change', this.config_onchange, this);
}
// Build Drawable HTML and behaviors.
this.container_div = this.build_container_div();
this.header_div = this.build_header_div();
- if (this.header_div) {
+ if (this.header_div) {
this.container_div.append(this.header_div);
// Icons container.
@@ -257,14 +254,14 @@
title: "Hide/show content",
css_class: "toggle",
on_click_fn: function(drawable) {
- if ( drawable.prefs.content_visible ) {
+ if ( drawable.config.get_value('content_visible') ) {
drawable.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");
drawable.hide_contents();
- drawable.prefs.content_visible = false;
- }
+ drawable.config.set_value('content_visible', false);
+ }
else {
drawable.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");
- drawable.prefs.content_visible = true;
+ drawable.config.set_value('content_visible', true);
drawable.show_contents();
}
}
@@ -275,10 +272,10 @@
title: "Edit settings",
css_class: "gear",
on_click_fn: function(drawable) {
- var view = new ConfigView({
- model: drawable.config
+ var view = new config_mod.ConfigSettingCollectionView({
+ collection: drawable.config
});
- view.render();
+ view.render_in_modal('Configure Track');
}
},
// Remove.
@@ -295,7 +292,7 @@
];
extend(Drawable.prototype, {
- config_params: [
+ config_params: [
{ key: 'name', label: 'Name', type: 'text', default_value: '' },
{ key: 'content_visible', type: 'bool', default_value: true, hidden: true }
],
@@ -311,7 +308,7 @@
},
can_draw: function() {
- if (this.enabled && this.prefs.content_visible) {
+ if (this.enabled && this.config.get_value('content_visible')) {
return true;
}
@@ -330,11 +327,11 @@
/**
* Set drawable name.
- */
+ */
set_name: function(new_name) {
- this.old_name = this.prefs.name;
- this.prefs.name = new_name;
- this.name_div.text(this.prefs.name);
+ this.old_name = this.config.get_value('name');
+ this.config.set_value('name', new_name);
+ this.name_div.text(new_name);
},
/**
@@ -342,8 +339,8 @@
*/
revert_name: function() {
if (this.old_name) {
- this.prefs.name = this.old_name;
- this.name_div.text(this.prefs.name);
+ this.config.set_value('name', this.old_name);
+ this.name_div.text(this.old_name);
}
},
@@ -355,7 +352,7 @@
this.container.remove_drawable(this);
var view = this.view;
- this.container_div.hide(0, function() {
+ this.container_div.hide(0, function() {
$(this).remove();
// HACK: is there a better way to update the view?
view.update_intro_div();
@@ -364,7 +361,7 @@
/**
* Build drawable's container div; this is the parent div for all drawable's elements.
- */
+ */
build_container_div: function() {},
/**
@@ -473,7 +470,7 @@
dictified_drawables.push(this.drawables[i].to_dict());
}
return {
- prefs: this.prefs,
+ prefs: this.config.to_key_value_dict(),
obj_type: this.obj_type,
drawables: dictified_drawables
};
@@ -668,7 +665,7 @@
build_header_div: function() {
var header_div = $("<div/>").addClass("track-header");
header_div.append($("<div/>").addClass(this.drag_handle_class));
- this.name_div = $("<div/>").addClass("track-name").text(this.prefs.name).appendTo(header_div);
+ this.name_div = $("<div/>").addClass("track-name").text(this.config.get_value('name')).appendTo(header_div);
return header_div;
},
@@ -829,7 +826,7 @@
*/
show_composite_track: function() {
var composite_track = new CompositeTrack(this.view, this.view, {
- name: this.prefs.name,
+ name: this.config.get_value('name'),
drawables: this.drawables
});
var index = this.container.replace_drawable(this, composite_track, true);
@@ -898,19 +895,15 @@
this.reset();
// Define track configuration
- this.config = new Config( {
- track: this,
- params: [
+ this.config = new config_mod.ConfigSettingCollection({
+ models: [
{ key: 'a_color', label: 'A Color', type: 'color', default_value: "#FF0000" },
{ key: 'c_color', label: 'C Color', type: 'color', default_value: "#00FF00" },
{ key: 'g_color', label: 'G Color', type: 'color', default_value: "#0000FF" },
{ key: 't_color', label: 'T Color', type: 'color', default_value: "#FF00FF" },
{ key: 'n_color', label: 'N Color', type: 'color', default_value: "#AAAAAA" }
- ],
+ ],
saved_values: obj_dict.prefs,
- onchange: function() {
- track.request_redraw({ clear_tile_cache: true });
- }
});
},
@@ -1819,7 +1812,7 @@
// already in group, add track to group.
if (current_track.container === view) {
// Create new group.
- var group = new DrawableGroup(view, view, { name: track.prefs.name });
+ var group = new DrawableGroup(view, view, { name: track.config.get_value('name') });
// Replace track with group.
var index = current_track.container.replace_drawable(current_track, group, false);
@@ -1923,241 +1916,6 @@
};
/**
- * Configuration object.
- */
-var Config = Backbone.Model.extend({
- initialize: function(options) {
- // values is a simple param_key-to-value dictionary used to store
- // param values.
- var values = {};
-
- // Set default values.
- _.each(options.params, function(p) {
- // For color parameters without a default value, assign a random color.
- values[p.key] = (p.type === 'color' && !p.default_value ? util.get_random_color() : p.default_value );
-
- });
-
- // Restore saved values.
- if (options.saved_values) {
- _.each( this.get('params'), function(p) {
- if (p.key in options.saved_values) {
- values[p.key] = options.saved_values[p.key];
- }
- });
- }
-
- this.set('values', values);
- },
-
- /**
- * Set default value for parameter.
- */
- set_param_default_value: function(key, default_value) {
- var param = _.find(this.get('params'), function(p) {
- return p.key === key;
- });
-
- if (param) {
- param.default_value = default_value;
- }
- },
-
- /**
- * Set a parameter's value. Returns true if value changed, false if value changed.
- */
- set_param_value: function(key, value) {
- // Find param.
- var param = _.find(this.get('params'), function(p) {
- return p.key === key;
- });
-
- if (param) {
- // Parse value from string.
- if (typeof value === "string" || value instanceof String) {
- // Parse string.
- if (value.trim() === '') {
- // If empty value, use default.
- value = param.default_value;
- } else if ( param.type === 'float' ) {
- value = parseFloat( value );
- } else if ( param.type === 'int' ) {
- value = parseInt( value, 10 );
- }
- }
- }
-
- // Set value and return true if changed, false otherwise.
- if (this.get('values')[key] !== value) {
- this.get('values')[key] = value;
- // HACK: custom event trigger needed to indicate that values were updated.
- this.trigger('change:values');
- return true;
- }
- else {
- return false;
- }
- }
-});
-
-var ConfigView = Backbone.View.extend({
-
- /**
- * Build and show form for modifying configuration. Currently uses Galaxy modals.
- */
- render: function() {
- //
- // Build config selection UI.
- //
-
- var track_config = this.model;
-
- // Prevent propagation of keydown events to viz to avoid inadvertent navigation.
- var container = $("<div/>").keydown(function(e) {
- e.stopPropagation();
- });
- var param;
- // Function to process parameters recursively
- function handle_params( params, container ) {
- for ( var index = 0; index < params.length; index++ ) {
- param = params[index];
- // Hidden params have no representation in the form
- if ( param.hidden ) { continue; }
- // Build row for param
- var id = 'param_' + index;
- var value = track_config.get('values')[ param.key ];
- var row = $("<div class='form-row' />").appendTo( container );
- row.append( $('<label />').attr("for", id ).text( param.label + ":" ) );
- // Draw parameter as checkbox
- if ( param.type === 'bool' ) {
- row.append( $('<input type="checkbox" />').attr("id", id ).attr("name", id ).attr( 'checked', value ) );
- // Draw parameter as textbox
- }
- else if ( param.type === 'text' ) {
- row.append( $('<input type="text"/>').attr("id", id ).val(value).click( function() { $(this).select(); }));
- // Draw paramter as select area
- }
- else if ( param.type === 'select' ) {
- var select = $('<select />').attr("id", id);
- for ( var i = 0; i < param.options.length; i++ ) {
- $("<option/>").text( param.options[i].label ).attr( "value", param.options[i].value ).appendTo( select );
- }
- select.val( value );
- row.append( select );
- // Draw parameter as color picker
- }
- else if ( param.type === 'color' ) {
- var
- container_div = $("<div/>").appendTo(row),
- input = $('<input/>').attr({
- id: id,
- name: id
- }).val( value ).addClass('color-input')
- .appendTo(container_div).click(function(e) {
- // Hide other pickers.
- $(".tooltip").removeClass( "in" );
-
- // Show input's color picker.
- var tip = $(this).siblings(".tooltip").addClass( "in" );
- tip.css( {
- // left: $(this).position().left + ( $(input).width() / 2 ) - 60,
- // top: $(this).position().top + $(this.height)
- left: $(this).position().left + $(this).width() + 5,
- top: $(this).position().top + Galaxy.modal.scrollTop() - ( $(tip).height() / 2 ) + ( $(this).height() / 2 )
- } ).show();
- // Click management:
-
- // Keep showing tip if clicking in tip.
- tip.click(function(e) {
- e.stopPropagation();
- });
-
- // Hide tip if clicking outside of tip.
- $(document).bind( "click.color-picker", function() {
- tip.hide();
- $(document).unbind( "click.color-picker" );
- });
-
- // No propagation to avoid triggering document click (and tip hiding) above.
- e.stopPropagation();
- }),
- // Icon for setting a new random color; behavior set below.
- new_color_icon = $("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(container_div)
- .attr("title", "Set new random color").tooltip(),
- // Color picker in tool tip style.
- tip = $( "<div class='tooltip right' style='position: absolute;' />" ).appendTo(container_div).hide(),
- // Inner div for padding purposes
- tip_inner = $("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(tip),
- tip_arrow = $("<div class='tooltip-arrow'></div>").appendTo(tip),
- farb_obj = $.farbtastic(tip_inner, { width: 100, height: 100, callback: input, color: value });
-
- // Clear floating.
- container_div.append( $("<div/>").css("clear", "both"));
-
- // Use function to fix farb_obj value.
- (function(fixed_farb_obj) {
- new_color_icon.click(function() {
- fixed_farb_obj.setColor(util.get_random_color());
- });
- })(farb_obj);
-
- }
- else {
- row.append( $('<input />').attr("id", id ).attr("name", id ).val( value ) );
- }
- // Help text
- if ( param.help ) {
- row.append( $("<div class='help'/>").text( param.help ) );
- }
- }
- }
- // Handle top level parameters in order
- handle_params( track_config.get('params'), container );
-
- //
- // Use Galaxy modal to display config.
- //
-
- var self = this,
- cancel_fn = function() { Galaxy.modal.hide(); $(window).unbind("keypress.check_enter_esc"); },
- ok_fn = function() {
- self.update_from_form( $(Galaxy.modal.el) );
- Galaxy.modal.hide();
- $(window).unbind("keypress.check_enter_esc");
- },
- check_enter_esc = function(e) {
- if ((e.keyCode || e.which) === 27) { // Escape key
- cancel_fn();
- } else if ((e.keyCode || e.which) === 13) { // Enter key
- ok_fn();
- }
- };
-
- $(window).bind("keypress.check_enter_esc", check_enter_esc);
- Galaxy.modal.show({title: "Configure", body: container, buttons : {'Cancel' : cancel_fn, 'Ok' : ok_fn } });
- },
-
- /**
- * Update configuration from form.
- */
- update_from_form: function( container ) {
- var model = this.model;
- var changed = false;
- _.each(model.get('params'), function(param, index) {
- if ( !param.hidden ) {
- // Get value from form element.
- var id = 'param_' + index;
- var value = container.find( '#' + id ).val();
- if ( param.type === 'bool' ) {
- value = container.find( '#' + id ).is( ':checked' );
- }
- changed = model.set_param_value(param.key, value) || changed;
- }
- });
- }
-});
-
-/**
* Tiles drawn by tracks.
*/
var Tile = function(track, region, w_scale, canvas, data) {
@@ -2376,8 +2134,8 @@
this.data_url_extra_params = {};
this.data_query_wait = ('data_query_wait' in obj_dict ? obj_dict.data_query_wait : DEFAULT_DATA_QUERY_WAIT);
// A little ugly creating data manager right now due to transition to Backbone-based objects.
- this.data_manager = ('data_manager' in obj_dict ?
- obj_dict.data_manager :
+ this.data_manager = ('data_manager' in obj_dict ?
+ obj_dict.data_manager :
new visualization.GenomeDataManager({
dataset: this.dataset,
// HACK: simulate 'genome' attributes from view for now.
@@ -2395,13 +2153,13 @@
// Height attributes: min height, max height, and visible height.
this.min_height_px = 16;
this.max_height_px = 800;
- this.visible_height_px = this.prefs.height;
+ this.visible_height_px = this.config.get_value('height');
//
// Create content div, which is where track is displayed, and add to container if available.
//
this.content_div = $("<div class='track-content'>").appendTo(this.container_div);
- if (this.container) {
+ if (this.container) {
this.container.content_div.append(this.container_div);
if ( !("resize" in obj_dict) || obj_dict.resize ) {
this.add_resize_handle();
@@ -2459,7 +2217,7 @@
// Update track name.
if (track.tool.is_visible()) {
- track.set_name(track.prefs.name + track.tool_region_and_parameters_str());
+ track.set_name(track.config.get_value('name') + track.tool_region_and_parameters_str());
}
else {
track.revert_name();
@@ -2475,8 +2233,8 @@
css_class: "arrow-split",
on_click_fn: function(track) {
var template =
- '<strong>Tool</strong>: <%= track.tool.get("name") %><br/>' +
- '<strong>Dataset</strong>: <%= track.prefs.name %><br/>' +
+ '<strong>Tool</strong>: <%= track.tool.get("name") %><br/>' +
+ '<strong>Dataset</strong>: <%= track.config.get("name") %><br/>' +
'<strong>Region(s)</strong>: <select name="regions">' +
'<option value="cur">current viewing area</option>' +
'<option value="bookmarks">bookmarks</option>' +
@@ -2492,7 +2250,7 @@
start: view.low,
end: view.high
}),
- bookmarked_regions = _.map($(".bookmark"), function(elt) {
+ bookmarked_regions = _.map($(".bookmark"), function(elt) {
return new visualization.GenomeRegion({from_str: $(elt).children(".position").text()});
});
@@ -2548,8 +2306,8 @@
build_header_div: function() {
var header_div = $("<div class='track-header'/>");
if (this.view.editor) { this.drag_div = $("<div/>").addClass(this.drag_handle_class).appendTo(header_div); }
- this.name_div = $("<div/>").addClass("track-name").appendTo(header_div).text(this.prefs.name)
- .attr( "id", this.prefs.name.replace(/\s+/g,'-').replace(/[^a-zA-Z0-9\-]/g,'').toLowerCase() );
+ this.name_div = $("<div/>").addClass("track-name").appendTo(header_div).text(this.config.get_value('name'))
+ .attr( "id", this.config.get_value('name').replace(/\s+/g,'-').replace(/[^a-zA-Z0-9\-]/g,'').toLowerCase() );
return header_div;
},
@@ -2578,7 +2336,7 @@
var drag_control = $( "<div class='track-resize'>" );
// Control shows on hover over track, stays while dragging
$(track.container_div).hover( function() {
- if ( track.prefs.content_visible ) {
+ if ( track.config.get_value('content_visible') ) {
in_handle = true;
drag_control.show();
}
@@ -2597,10 +2355,10 @@
track.visible_height_px = (track.max_height_px === new_height ? 0 : new_height);
track.on_resize();
}).bind( "dragend", function( e, d ) {
- track.tile_cache.clear();
+ track.tile_cache.clear();
in_drag = false;
if (!in_handle) { drag_control.hide(); }
- track.config.get('values').height = track.visible_height_px;
+ track.config.set('height', track.visible_height_px);
track.changed();
}).appendTo(track.container_div);
},
@@ -2611,16 +2369,15 @@
set_display_modes: function(new_modes, init_mode) {
// Set modes, init mode.
this.display_modes = new_modes;
- this.mode = (init_mode ? init_mode :
- (this.config && this.config.get('values').mode ?
- this.config.get('values').mode : this.display_modes[0])
+ this.mode = (init_mode ? init_mode :
+ (this.config && this.config.get_value('mode') ?
+ this.config.get_value('mode') : this.display_modes[0])
);
this.action_icons.mode_icon.attr("title", "Set display mode (now: " + this.mode + ")");
// Setup popup menu for changing modes.
- var
- track = this,
+ var track = this,
mode_mapping = {};
for (var i = 0, len = track.display_modes.length; i < len; i++) {
var mode = track.display_modes[i];
@@ -2822,10 +2579,10 @@
min_value = Math.floor( Math.min( 0, Math.max( min_value, data.mean - 2 * data.sd ) ) );
max_value = Math.ceil( Math.max( 0, Math.min( max_value, data.mean + 2 * data.sd ) ) );
// Update config, prefs
- track.config.set_param_default_value('min_value', min_value);
- track.config.set_param_default_value('max_value', max_value);
- track.prefs.min_value = min_value;
- track.prefs.max_value = max_value;
+ track.config.set_default_value('min_value', min_value);
+ track.config.set_default_value('max_value', max_value);
+ track.config.set_value('min_value', min_value);
+ track.config.set_value('max_value', max_value);
}
});
},
@@ -2879,7 +2636,7 @@
// Add tiles_div, overlay_div to content_div.
this.tiles_div = $("<div/>").addClass("tiles").appendTo(this.content_div);
- if (!this.prefs.content_visible) {
+ if (!this.config.get_value('content_visible')) {
this.tiles_div.hide();
}
this.overlay_div = $("<div/>").addClass("overlay").appendTo(this.content_div);
@@ -2934,13 +2691,14 @@
* Use from_dict to recreate object.
*/
to_dict: function() {
+ console.log(this.config.to_key_value_dict());
return {
track_type: this.get_type(),
dataset: {
id: this.dataset.id,
hda_ldda: this.dataset.get('hda_ldda')
},
- prefs: this.prefs,
+ prefs: this.config.to_key_value_dict(),
mode: this.mode,
filters: this.filters_manager.to_dict(),
tool_state: (this.tool ? this.tool.state_dict() : {})
@@ -2958,7 +2716,8 @@
high: track.view.max_high, hda_ldda: track.dataset.get('hda_ldda') },
function(result) {
var data = result.data;
- if ( isNaN(parseFloat(track.prefs.min_value)) || isNaN(parseFloat(track.prefs.max_value)) ) {
+ if ( isNaN(parseFloat(track.config.get_value('min_value'))) ||
+ isNaN(parseFloat(track.config.get_value('max_value'))) ) {
// Compute default minimum and maximum values
var min_value = data.min,
max_value = data.max;
@@ -2967,8 +2726,8 @@
min_value = Math.floor( Math.min( 0, Math.max( min_value, data.mean - 2 * data.sd ) ) );
max_value = Math.ceil( Math.max( 0, Math.min( max_value, data.mean + 2 * data.sd ) ) );
// Update the prefs
- track.prefs.min_value = min_value;
- track.prefs.max_value = max_value;
+ track.config.set_value('min_value', min_value);
+ track.config.set_value('max_value', max_value);
}
});
},
@@ -2980,7 +2739,7 @@
var track = this;
// TODO: is it necessary to store the mode in two places (.mode and track_config)?
track.mode = new_mode;
- track.config.get('values').mode = new_mode;
+ track.config.set_value('mode', new_mode);
// FIXME: find a better way to get Auto data w/o clearing cache; using mode in the
// data manager would work if Auto data were checked for compatibility when a specific
// mode is chosen.
@@ -3151,11 +2910,11 @@
if (label.length !== 0) {
// Label already exists, so update value.
- label.text(track.prefs[pref_name]);
+ label.text(track.config.get_value(pref_name));
}
else {
// Add label.
- label = $("<div/>").text(track.prefs[pref_name]).make_text_editable({
+ label = $("<div/>").text(track.config.get_value(pref_name)).make_text_editable({
num_cols: 12,
on_finish: function(new_val) {
$(".tooltip").remove();
@@ -3163,7 +2922,7 @@
on_change();
},
help_text: "Set " + text + " value"
- }).addClass('yaxislabel ' + css_class).css("color", this.prefs.label_color);
+ }).addClass('yaxislabel ' + css_class).css("color", this.config.get_value('label_color'));
this.container_div.prepend(label);
}
},
@@ -3376,7 +3135,7 @@
*/
_draw_line_track_tile: function(result, ctx, mode, region, w_scale) {
var canvas = ctx.canvas,
- painter = new painters.LinePainter(result.data, region.get('start'), region.get('end'), this.prefs, mode);
+ painter = new painters.LinePainter(result.data, region.get('start'), region.get('end'), this.config.to_key_value_dict(), mode);
painter.draw(ctx, canvas.width, canvas.height, w_scale);
return new LineTrackTile(this, region, w_scale, canvas, result.data);
@@ -3691,7 +3450,7 @@
show_group: function() {
// Create group with individual tracks.
var group = new DrawableGroup(this.view, this.container, {
- name: this.prefs.name
+ name: this.config.get_value('name')
}),
track;
for (var i = 0; i < this.drawables.length; i++) {
@@ -3718,16 +3477,16 @@
//
// Get smallest min, biggest max.
- var min = _.min(_.map(this.drawables, function(d) { return d.prefs.min_value; })),
- max = _.max(_.map(this.drawables, function(d) { return d.prefs.max_value; }));
+ var min = _.min(_.map(this.drawables, function(d) { return d.config.get_value('min_value'); })),
+ max = _.max(_.map(this.drawables, function(d) { return d.config.get_value('max_value') }));
- this.prefs.min_value = min;
- this.prefs.max_value = max;
+ this.config.set_value('min_value', min);
+ this.config.set_value('max_value', max);
// Set all tracks to smallest min, biggest max.
_.each(this.drawables, function(d) {
- d.prefs.min_value = min;
- d.prefs.max_value = max;
+ d.config.set_value('min_value', min);
+ d.config.set_value('max_value', max);
});
},
@@ -3735,10 +3494,12 @@
* Update minimum, maximum for component tracks.
*/
update_all_min_max: function() {
- var track = this;
+ var track = this,
+ min_value = this.config.get_value('min_value'),
+ max_value = this.config.get_value('max_value');
_.each(this.drawables, function(d) {
- d.prefs.min_value = track.prefs.min_value;
- d.prefs.max_value = track.prefs.max_value;
+ d.config.set_value('min_value', min_value);
+ d.config.set_value('max_value', max_value);
});
this.request_draw({ clear_tile_cache: true });
},
@@ -3861,16 +3622,16 @@
display_modes: CONTINUOUS_DATA_MODES,
config_params: _.union( Drawable.prototype.config_params, [
- { key: 'color', label: 'Color', type: 'color' },
- { key: 'min_value', label: 'Min Value', type: 'float', default_value: undefined },
- { key: 'max_value', label: 'Max Value', type: 'float', default_value: undefined },
- { key: 'mode', type: 'string', default_value: this.mode, hidden: true },
- { key: 'height', type: 'int', default_value: 30, hidden: true }
+ { key: 'color', label: 'Color', type: 'color' },
+ { key: 'min_value', label: 'Min Value', type: 'float', default_value: undefined },
+ { key: 'max_value', label: 'Max Value', type: 'float', default_value: undefined },
+ { key: 'mode', type: 'string', default_value: this.mode, hidden: true },
+ { key: 'height', type: 'int', default_value: 30, hidden: true }
] ),
config_onchange: function() {
- this.set_name(this.prefs.name);
- this.request_draw({ clear_tile_cache: true });
+ this.set_name(this.config.get_value('name'));
+ this.request_draw({ clear_tile_cache: true });
},
/**
@@ -3924,7 +3685,7 @@
] ),
config_onchange: function() {
- this.set_name(this.prefs.name);
+ this.set_name(this.config.get_value('name'));
this.request_draw({ clear_tile_cache: true });
},
@@ -3934,7 +3695,7 @@
draw_tile: function(result, ctx, mode, region, w_scale) {
// Paint onto canvas.
var canvas = ctx.canvas,
- painter = new painters.DiagonalHeatmapPainter(result.data, region.get('start'), region.get('end'), this.prefs, mode);
+ painter = new painters.DiagonalHeatmapPainter(result.data, region.get('start'), region.get('end'), this.config.to_key_value_dict(), mode);
painter.draw(ctx, canvas.width, canvas.height, w_scale);
return new Tile(this, region, w_scale, canvas, result.data);
@@ -3973,7 +3734,7 @@
] ),
config_onchange: function() {
- this.set_name(this.prefs.name);
+ this.set_name(this.config.get_value('name'));
this.set_painter_from_config();
this.request_draw({ clear_tile_cache: true });
},
@@ -4158,7 +3919,7 @@
var rows_required = this.incremental_slots(w_scale, result.data, mode);
// HACK: use dummy painter to get required height. Painter should be extended so that get_required_height
// works as a static function.
- var dummy_painter = new (this.painter)(null, null, null, this.prefs, mode);
+ var dummy_painter = new (this.painter)(null, null, null, this.config.to_key_value_dict(), mode);
return Math.max(this.min_height_px, dummy_painter.get_required_height(rows_required, canvas_width) );
}
},
@@ -4219,14 +3980,14 @@
// Create painter.
var filter_alpha_scaler = (this.filters_manager.alpha_filter ? new FilterScaler(this.filters_manager.alpha_filter) : null),
filter_height_scaler = (this.filters_manager.height_filter ? new FilterScaler(this.filters_manager.height_filter) : null),
- painter = new (this.painter)(filtered, tile_low, tile_high, this.prefs, mode, filter_alpha_scaler, filter_height_scaler,
+ painter = new (this.painter)(filtered, tile_low, tile_high, this.config.to_key_value_dict(), mode, filter_alpha_scaler, filter_height_scaler,
// HACK: ref_seq only be defined for ReadTracks, and only the ReadPainter accepts that argument
ref_seq,
// Only the ReadPainer will use this function
function(b) { return track.view.get_base_color(b); });
var feature_mapper = null;
- ctx.fillStyle = this.prefs.block_color;
+ ctx.fillStyle = this.config.get_value('block_color');
ctx.font = ctx.canvas.manager.default_font;
ctx.textAlign = "right";
@@ -4262,11 +4023,11 @@
{ key: 'show_labels', label: 'Show summary and sample labels', type: 'bool', default_value: true },
{ key: 'summary_height', label: 'Locus summary height', type: 'float', default_value: 20 },
{ key: 'mode', type: 'string', default_value: this.mode, hidden: true },
- { key: 'height', type: 'int', default_value: 0, hidden: true }
+ { key: 'height', type: 'int', default_value: 32, hidden: true }
] ),
config_onchange: function() {
- this.set_name(this.prefs.name);
+ this.set_name(this.config.get_value('name'));
this.request_draw({ clear_tile_cache: true });
},
@@ -4280,7 +4041,7 @@
}
else { // result.dataset_type === 'variant'
var view = this.view,
- painter = new (this.painter)(result.data, region.get('start'), region.get('end'), this.prefs, mode,
+ painter = new (this.painter)(result.data, region.get('start'), region.get('end'), this.config.to_key_value_dict(), mode,
function(b) { return view.get_base_color(b); });
painter.draw(ctx, ctx.canvas.width, ctx.canvas.height, w_scale);
return new Tile(this, region, w_scale, ctx.canvas, result.data);
@@ -4312,7 +4073,7 @@
}
}
- var dummy_painter = new (this.painter)(null, null, null, this.prefs, mode);
+ var dummy_painter = new (this.painter)(null, null, null, this.config.to_key_value_dict(), mode);
return dummy_painter.get_required_height(num_samples);
}
},
@@ -4343,7 +4104,7 @@
// Add summary/sample labels if needed and not already included.
var sample_names = this.dataset.get_metadata('sample_names');
- if (line_track_tiles.length === 0 && this.prefs.show_labels && sample_names && sample_names.length > 1) {
+ if (line_track_tiles.length === 0 && this.config.get_value('show_labels') && sample_names && sample_names.length > 1) {
var font_size;
// Add and/or style labels.
@@ -4351,21 +4112,21 @@
// Add summary and sample labels.
// Add summary label to middle of summary area.
- font_size = this.prefs.summary_height / 2;
+ font_size = this.config.get_value('summary_height') / 2;
this.tiles_div.prepend(
$("<div/>").text('Summary').addClass('yaxislabel variant top').css({
'font-size': font_size + 'px',
- 'top': (this.prefs.summary_height - font_size) / 2 + 'px'
+ 'top': (this.config.get_value('summary_height') - font_size) / 2 + 'px'
})
);
// Show sample labels.
- if (this.prefs.show_sample_data) {
+ if (this.config.get_value('show_sample_data')) {
var samples_div_html = sample_names.join('<br/>');
this.tiles_div.prepend(
$("<div/>").html(samples_div_html).addClass('yaxislabel variant top sample').css({
- 'top': this.prefs.summary_height,
+ 'top': this.config.get_value('summary_height')
})
);
}
@@ -4380,7 +4141,7 @@
'line-height': font_size
});
// Color labels to preference color.
- $(this.tiles_div).find('.yaxislabel').css('color', this.prefs.label_color);
+ $(this.tiles_div).find('.yaxislabel').css('color', this.config.get_value('label_color'));
}
else {
// Remove all labels.
@@ -4414,7 +4175,7 @@
] ),
config_onchange: function() {
- this.set_name(this.prefs.name);
+ this.set_name(this.config.get_value('name'));
this.request_draw({ clear_tile_cache: true });
}
});
diff -r e059dbc01b575b856509c2e2baccac3750b6eeb5 -r edff37d76463a468753477080f65886d4a53619e static/scripts/viz/trackster_ui.js
--- a/static/scripts/viz/trackster_ui.js
+++ b/static/scripts/viz/trackster_ui.js
@@ -75,7 +75,7 @@
});
// FIXME: give unique IDs to Drawables and save overview as ID.
- var overview_track_name = (view.overview_drawable ? view.overview_drawable.prefs.name : null),
+ var overview_track_name = (view.overview_drawable ? view.overview_drawable.config.get_value('name') : null),
viz_config = {
'view': view.to_dict(),
'viewport': { 'chrom': view.chrom, 'start': view.low , 'end': view.high, 'overview': overview_track_name },
@@ -86,9 +86,9 @@
url: galaxy_config.root + "visualization/save",
type: "POST",
dataType: "json",
- data: {
+ data: {
'id' : view.vis_id,
- 'title' : view.prefs.name,
+ 'title' : view.config.get_value('name'),
'dbkey' : view.dbkey,
'type' : 'trackster',
'vis_json' : JSON.stringify(viz_config)
@@ -265,7 +265,7 @@
// Set overview.
var overview_drawable;
for (var i = 0; i < view.drawables.length; i++) {
- if (view.drawables[i].prefs.name === overview_drawable_name) {
+ if (view.drawables[i].config.get_value('name') === overview_drawable_name) {
view.set_overview(view.drawables[i]);
break;
}
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dan: UI fixes for Configure workflow menu.
by commits-noreply@bitbucket.org 28 Feb '14
by commits-noreply@bitbucket.org 28 Feb '14
28 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e059dbc01b57/
Changeset: e059dbc01b57
User: dan
Date: 2014-02-28 21:02:07
Summary: UI fixes for Configure workflow menu.
Affected #: 2 files
diff -r 410a13e7e8ddabf9a00ffb8ba3bc75d4948447e4 -r e059dbc01b575b856509c2e2baccac3750b6eeb5 lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -1557,23 +1557,28 @@
m.stored_workflow = q.get( id )
user.stored_workflow_menu_entries.append( m )
sess.flush()
- return trans.show_message( "Menu updated", refresh_frames=['tools'] )
+ message = "Menu updated"
+ refresh_frames = ['tools']
else:
- user = trans.get_user()
- ids_in_menu = set( [ x.stored_workflow_id for x in user.stored_workflow_menu_entries ] )
- workflows = trans.sa_session.query( model.StoredWorkflow ) \
- .filter_by( user=user, deleted=False ) \
- .order_by( desc( model.StoredWorkflow.table.c.update_time ) ) \
- .all()
- shared_by_others = trans.sa_session \
- .query( model.StoredWorkflowUserShareAssociation ) \
- .filter_by( user=user ) \
- .filter( model.StoredWorkflow.deleted == False ) \
- .all()
- return trans.fill_template( "workflow/configure_menu.mako",
- workflows=workflows,
- shared_by_others=shared_by_others,
- ids_in_menu=ids_in_menu )
+ message = None
+ refresh_frames = []
+ user = trans.get_user()
+ ids_in_menu = set( [ x.stored_workflow_id for x in user.stored_workflow_menu_entries ] )
+ workflows = trans.sa_session.query( model.StoredWorkflow ) \
+ .filter_by( user=user, deleted=False ) \
+ .order_by( desc( model.StoredWorkflow.table.c.update_time ) ) \
+ .all()
+ shared_by_others = trans.sa_session \
+ .query( model.StoredWorkflowUserShareAssociation ) \
+ .filter_by( user=user ) \
+ .filter( model.StoredWorkflow.deleted == False ) \
+ .all()
+ return trans.fill_template( "workflow/configure_menu.mako",
+ workflows=workflows,
+ shared_by_others=shared_by_others,
+ ids_in_menu=ids_in_menu,
+ message=message,
+ refresh_frames=['tools'] )
def _workflow_to_svg_canvas( self, trans, stored ):
workflow = stored.latest_workflow
diff -r 410a13e7e8ddabf9a00ffb8ba3bc75d4948447e4 -r e059dbc01b575b856509c2e2baccac3750b6eeb5 templates/webapps/galaxy/workflow/configure_menu.mako
--- a/templates/webapps/galaxy/workflow/configure_menu.mako
+++ b/templates/webapps/galaxy/workflow/configure_menu.mako
@@ -1,7 +1,19 @@
-<%inherit file="/base.mako"/>
+<%inherit file="/webapps/galaxy/base_panels.mako"/>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.active_view="workflow"
+ self.message_box_visible=False
+%>
+</%def><%def name="title()">Configure workflow menu</%def>
+<%def name="center_panel()">
+ <div style="overflow: auto; height: 100%;">
+ <div class="page-container" style="padding: 10px;">
%if message:
<%
try:
@@ -69,9 +81,18 @@
%endif
+%if not workflows and not shared_by_others:
+ <tr>
+ <td colspan="4">You do not have any accessible workflows.</td>
+ </tr>
+%endif
+
</table><p />
-<input type="Submit" />
+<input type="Submit" value="Save" /></form>
+ </div>
+ </div>
+</%def>
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jmchilton: Add test cases for need_late_validation of select parameters.
by commits-noreply@bitbucket.org 28 Feb '14
by commits-noreply@bitbucket.org 28 Feb '14
28 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/410a13e7e8dd/
Changeset: 410a13e7e8dd
User: jmchilton
Date: 2014-02-28 19:42:30
Summary: Add test cases for need_late_validation of select parameters.
Two of these fail without JJ's work in pull request 336, additionally these verify 336 doesn't break GATK support for instance.
Affected #: 1 file
diff -r 3981b0070af02af108ce81091866d531e66b8fd1 -r 410a13e7e8ddabf9a00ffb8ba3bc75d4948447e4 test/unit/tools/test_select_parameters.py
--- /dev/null
+++ b/test/unit/tools/test_select_parameters.py
@@ -0,0 +1,93 @@
+from unittest import TestCase
+from galaxy.util import bunch
+from galaxy import model
+from galaxy.tools.parameters import basic
+from elementtree.ElementTree import XML
+
+import tools_support
+
+
+class SelectToolParameterTestCase( TestCase, tools_support.UsesApp ):
+
+ def test_dep_dummy_datasets_need_late_validation( self ):
+ self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
+ assert self.param.need_late_validation( self.trans, { "input_bam": basic.DummyDataset() } )
+
+ def test_dep_runtime_values_need_late_validation( self ):
+ self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
+ assert self.param.need_late_validation( self.trans, { "input_bam": basic.RuntimeValue() } )
+
+ def test_dep_unvalidated_values_need_late_validation( self ):
+ self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
+ assert self.param.need_late_validation( self.trans, { "input_bam": basic.UnvalidatedValue( "42" ) } )
+
+ def test_dep_real_datasets_no_late_validation( self ):
+ self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
+ assert not self.param.need_late_validation( self.trans, { "input_bam": model.HistoryDatasetAssociation() } )
+
+ def test_nested_context_validation_needed( self ):
+ self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
+ # Data ref currently must be same level, if not at top level.
+ assert self.param.need_late_validation( self.trans, { "reference_source": { "my_name": "42", "input_bam": basic.RuntimeValue() } } )
+
+ def test_nested_context_validation_not_needed( self ):
+ self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
+ # Data ref currently must be same level, if not at top level.
+ assert not self.param.need_late_validation( self.trans, { "reference_source": { "my_name": "42", "input_bam": model.HistoryDatasetAssociation() } } )
+
+ # Following test will fail due to bug in Galaxy.
+ def test_nested_context_validation_needed_in_repeat( self ):
+ self.type = "data_column"
+ self.set_ref_text = True
+ self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
+ assert not self.param.need_late_validation( self.trans, { "series": [ { "my_name": "42", "input_bam": model.HistoryDatasetAssociation() } ] } )
+
+ def test_nested_context_validation_not_needed_in_repeat( self ):
+ self.type = "data_column"
+ self.set_ref_text = True
+ self.options_xml = '''<options><filter type="data_meta" ref="input_bam" key="dbkey"/></options>'''
+ assert self.param.need_late_validation( self.trans, { "series": [ { "my_name": "42", "input_bam": basic.RuntimeValue() } ] } )
+
+ # TODO: Good deal of overlap here with DataToolParameterTestCase,
+ # refactor.
+ def setUp( self ):
+ self.setup_app( mock_model=False )
+ self.mock_tool = bunch.Bunch(
+ app=self.app,
+ tool_type="default",
+ )
+ self.test_history = model.History()
+ self.app.model.context.add( self.test_history )
+ self.app.model.context.flush()
+ self.trans = bunch.Bunch(
+ app=self.app,
+ get_history=lambda: self.test_history,
+ get_current_user_roles=lambda: [],
+ workflow_building_mode=False,
+ webapp=bunch.Bunch( name="galaxy" ),
+ )
+ self.type = "select"
+ self.set_data_ref = False
+ self.multiple = False
+ self.optional = False
+ self.options_xml = ""
+ self._param = None
+
+ @property
+ def param( self ):
+ if not self._param:
+ multi_text = ""
+ if self.multiple:
+ multi_text = 'multiple="True"'
+ optional_text = ""
+ if self.optional:
+ optional_text = 'optional="True"'
+ options_text = self.options_xml
+ data_ref_text = ""
+ if self.set_data_ref:
+ data_ref_text = 'data_ref="input_bam"'
+ template_xml = '''<param name="my_name" type="%s" %s %s %s>%s</param>'''
+ self.param_xml = XML( template_xml % ( self.type, data_ref_text, multi_text, optional_text, options_text ) )
+ self._param = basic.SelectToolParameter( self.mock_tool, self.param_xml )
+
+ return self._param
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
5 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/82c43e74d316/
Changeset: 82c43e74d316
Branch: jjohnson/allow-jobs-to-be-filtered-by-tool_id-and-1391011677231
User: jjohnson
Date: 2014-01-29 17:09:12
Summary: Allow jobs to be filtered by tool_id and history_id as well as by state. The tool_id is compared with the like operator.
Affected #: 1 file
diff -r 07dceba219c315e2857f105abfdaf030efa1f16c -r 82c43e74d31617ca3efda965d6c069c7e99ae63b lib/galaxy/webapps/galaxy/api/jobs.py
--- a/lib/galaxy/webapps/galaxy/api/jobs.py
+++ b/lib/galaxy/webapps/galaxy/api/jobs.py
@@ -22,7 +22,7 @@
@expose_api
def index( self, trans, **kwd ):
"""
- index( trans, state=None )
+ index( trans, state=None, tool_id=None, history_id=None )
* GET /api/jobs:
return jobs for current user
@@ -31,6 +31,12 @@
Valid Galaxy job states include:
'new', 'upload', 'waiting', 'queued', 'running', 'ok', 'error', 'paused', 'deleted', 'deleted_new'
+ :type tool_id: string or list
+ :param tool_id: limit listing of jobs to those that match one of the included tool_ids. If none, all are returned.
+
+ :type history_id: string
+ :param history_id: limit listing of jobs to those that match the history_id. If none, all are returned.
+
:rtype: list
:returns: list of dictionaries containing summary job information
"""
@@ -48,6 +54,24 @@
t.append( trans.app.model.Job.state == s )
query = query.filter( or_( *t ) )
+ tool_id = kwd.get( 'tool_id', None )
+ if tool_id is not None:
+ if isinstance( tool_id, basestring ):
+ query = query.filter( trans.app.model.Job.tool_id.like('%' + tool_id + '%') )
+ elif isinstance( tool_id, list ):
+ t = []
+ for s in tool_id:
+ t.append( trans.app.model.Job.tool_id.like('%' + s + '%') )
+ query = query.filter( or_( *t ) )
+
+ history_id = kwd.get( 'history_id', None )
+ if history_id is not None:
+ try:
+ decoded_history_id = trans.security.decode_id(history_id)
+ query = query.filter( trans.app.model.Job.history_id == decoded_history_id )
+ except:
+ raise exceptions.ObjectAttributeInvalidException()
+
out = []
for job in query.order_by(
trans.app.model.Job.update_time.desc()
https://bitbucket.org/galaxy/galaxy-central/commits/b6cc7ffa9f4c/
Changeset: b6cc7ffa9f4c
Branch: jjohnson/allow-jobs-to-be-filtered-by-tool_id-and-1391011677231
User: jmchilton
Date: 2014-02-28 16:57:53
Summary: Reduce a little code dup in api/jobs.py.
Affected #: 1 file
diff -r 82c43e74d31617ca3efda965d6c069c7e99ae63b -r b6cc7ffa9f4c615b0724d5a3a52270ae5087e649 lib/galaxy/webapps/galaxy/api/jobs.py
--- a/lib/galaxy/webapps/galaxy/api/jobs.py
+++ b/lib/galaxy/webapps/galaxy/api/jobs.py
@@ -12,6 +12,7 @@
from galaxy.web.base.controller import UsesHistoryDatasetAssociationMixin
from galaxy.web.base.controller import UsesLibraryMixinItems
from galaxy import exceptions
+from galaxy import util
import logging
log = logging.getLogger( __name__ )
@@ -45,24 +46,22 @@
query = trans.sa_session.query( trans.app.model.Job ).filter(
trans.app.model.Job.user == trans.user
)
- if state is not None:
- if isinstance( state, basestring ):
- query = query.filter( trans.app.model.Job.state == state )
- elif isinstance( state, list ):
- t = []
- for s in state:
- t.append( trans.app.model.Job.state == s )
- query = query.filter( or_( *t ) )
+
+ def build_and_apply_filters( query, objects, filter_func ):
+ if objects is not None:
+ if isinstance( objects, basestring ):
+ query = query.filter( filter_func( objects ) )
+ elif isinstance( objects, list ):
+ t = []
+ for obj in objects:
+ t.append( filter_func( obj ) )
+ query = query.filter( or_( *t ) )
+ return query
+
+ query = build_and_apply_filters( query, state, lambda s: trans.app.model.Job.state == s )
tool_id = kwd.get( 'tool_id', None )
- if tool_id is not None:
- if isinstance( tool_id, basestring ):
- query = query.filter( trans.app.model.Job.tool_id.like('%' + tool_id + '%') )
- elif isinstance( tool_id, list ):
- t = []
- for s in tool_id:
- t.append( trans.app.model.Job.tool_id.like('%' + s + '%') )
- query = query.filter( or_( *t ) )
+ query = build_and_apply_filters( query, tool_id, lambda t: trans.app.model.Job.tool_id.like('%' + t + '%') )
history_id = kwd.get( 'history_id', None )
if history_id is not None:
https://bitbucket.org/galaxy/galaxy-central/commits/7bdfa44e7edc/
Changeset: 7bdfa44e7edc
Branch: jjohnson/allow-jobs-to-be-filtered-by-tool_id-and-1391011677231
User: jmchilton
Date: 2014-02-28 17:02:29
Summary: Modify jobs index to allow exact or inexact tool id filtering.
Discussed this with JJ and Dannon. Formerly this was only inexact matchings, I was a little worried about API users expected exact tool id matches when using a parameter called tool_id. JJ's original intended behavior can be accessed using tool_id_like.
Affected #: 1 file
diff -r b6cc7ffa9f4c615b0724d5a3a52270ae5087e649 -r 7bdfa44e7edcb7a299dcd31943be7c55babfaedc lib/galaxy/webapps/galaxy/api/jobs.py
--- a/lib/galaxy/webapps/galaxy/api/jobs.py
+++ b/lib/galaxy/webapps/galaxy/api/jobs.py
@@ -60,8 +60,8 @@
query = build_and_apply_filters( query, state, lambda s: trans.app.model.Job.state == s )
- tool_id = kwd.get( 'tool_id', None )
- query = build_and_apply_filters( query, tool_id, lambda t: trans.app.model.Job.tool_id.like('%' + t + '%') )
+ query = build_and_apply_filters( query, kwd.get( 'tool_id', None ), lambda t: trans.app.model.Job.tool_id == t )
+ query = build_and_apply_filters( query, kwd.get( 'tool_id_like', None ), lambda t: trans.app.model.Job.tool_id.like(t) )
history_id = kwd.get( 'history_id', None )
if history_id is not None:
https://bitbucket.org/galaxy/galaxy-central/commits/3981b0070af0/
Changeset: 3981b0070af0
User: jmchilton
Date: 2014-02-28 17:08:26
Summary: Merge JJ's pull request #315 (with minor modifications discussed with JJ).
Affected #: 1 file
diff -r 6a633b37c4c47bba640d05fef72fd73f41ae5086 -r 3981b0070af02af108ce81091866d531e66b8fd1 lib/galaxy/webapps/galaxy/api/jobs.py
--- a/lib/galaxy/webapps/galaxy/api/jobs.py
+++ b/lib/galaxy/webapps/galaxy/api/jobs.py
@@ -12,6 +12,7 @@
from galaxy.web.base.controller import UsesHistoryDatasetAssociationMixin
from galaxy.web.base.controller import UsesLibraryMixinItems
from galaxy import exceptions
+from galaxy import util
import logging
log = logging.getLogger( __name__ )
@@ -22,7 +23,7 @@
@expose_api
def index( self, trans, **kwd ):
"""
- index( trans, state=None )
+ index( trans, state=None, tool_id=None, history_id=None )
* GET /api/jobs:
return jobs for current user
@@ -31,6 +32,12 @@
Valid Galaxy job states include:
'new', 'upload', 'waiting', 'queued', 'running', 'ok', 'error', 'paused', 'deleted', 'deleted_new'
+ :type tool_id: string or list
+ :param tool_id: limit listing of jobs to those that match one of the included tool_ids. If none, all are returned.
+
+ :type history_id: string
+ :param history_id: limit listing of jobs to those that match the history_id. If none, all are returned.
+
:rtype: list
:returns: list of dictionaries containing summary job information
"""
@@ -39,14 +46,30 @@
query = trans.sa_session.query( trans.app.model.Job ).filter(
trans.app.model.Job.user == trans.user
)
- if state is not None:
- if isinstance( state, basestring ):
- query = query.filter( trans.app.model.Job.state == state )
- elif isinstance( state, list ):
- t = []
- for s in state:
- t.append( trans.app.model.Job.state == s )
- query = query.filter( or_( *t ) )
+
+ def build_and_apply_filters( query, objects, filter_func ):
+ if objects is not None:
+ if isinstance( objects, basestring ):
+ query = query.filter( filter_func( objects ) )
+ elif isinstance( objects, list ):
+ t = []
+ for obj in objects:
+ t.append( filter_func( obj ) )
+ query = query.filter( or_( *t ) )
+ return query
+
+ query = build_and_apply_filters( query, state, lambda s: trans.app.model.Job.state == s )
+
+ query = build_and_apply_filters( query, kwd.get( 'tool_id', None ), lambda t: trans.app.model.Job.tool_id == t )
+ query = build_and_apply_filters( query, kwd.get( 'tool_id_like', None ), lambda t: trans.app.model.Job.tool_id.like(t) )
+
+ history_id = kwd.get( 'history_id', None )
+ if history_id is not None:
+ try:
+ decoded_history_id = trans.security.decode_id(history_id)
+ query = query.filter( trans.app.model.Job.history_id == decoded_history_id )
+ except:
+ raise exceptions.ObjectAttributeInvalidException()
out = []
for job in query.order_by(
https://bitbucket.org/galaxy/galaxy-central/commits/6d799bd3e8c8/
Changeset: 6d799bd3e8c8
Branch: jjohnson/allow-jobs-to-be-filtered-by-tool_id-and-1391011677231
User: jmchilton
Date: 2014-02-28 17:08:46
Summary: Close branch.
Affected #: 0 files
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Minor fixes for installing tool dependencies and counting repositories processed during the tool shed's install and test runs.
by commits-noreply@bitbucket.org 28 Feb '14
by commits-noreply@bitbucket.org 28 Feb '14
28 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6a633b37c4c4/
Changeset: 6a633b37c4c4
User: greg
Date: 2014-02-28 14:52:52
Summary: Minor fixes for installing tool dependencies and counting repositories processed during the tool shed's install and test runs.
Affected #: 3 files
diff -r 95517f976cca49f984b89c9fdd5b9208b1a11fcb -r 6a633b37c4c47bba640d05fef72fd73f41ae5086 lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
@@ -15,7 +15,7 @@
log = logging.getLogger( __name__ )
# Set no activity timeout to 20 minutes.
-NO_OUTPUT_TIMEOUT = 1200
+NO_OUTPUT_TIMEOUT = 1200.0
class CompressedFile( object ):
@@ -473,7 +473,7 @@
( str( download_url ), NO_OUTPUT_TIMEOUT )
raise Exception( err_msg )
except Exception, e:
- err_msg = err_msg = 'Error downloading from URL %s: %s' % ( str( download_url ), NO_OUTPUT_TIMEOUT )
+ err_msg = err_msg = 'Error downloading from URL\n%s:\n%s' % ( str( download_url ), str( e ) )
raise Exception( err_msg )
finally:
if src:
diff -r 95517f976cca49f984b89c9fdd5b9208b1a11fcb -r 6a633b37c4c47bba640d05fef72fd73f41ae5086 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -181,6 +181,7 @@
changeset_revision = str( repository_dict.get( 'changeset_revision', '' ) )
print "Processing revision %s of repository %s owned by %s..." % ( changeset_revision, name, owner )
repository_identifier_tup = ( name, owner, changeset_revision )
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] += 1
# Retrieve the stored list of tool_test_results_dicts.
tool_test_results_dicts, error_message = \
install_and_test_base_util.get_tool_test_results_dicts( install_and_test_base_util.galaxy_tool_shed_url,
@@ -215,7 +216,6 @@
start_time = time.time()
tool_test_results_dict = install_and_test_base_util.initialize_tool_tests_results_dict( app, tool_test_results_dict )
repository, error_message = install_and_test_base_util.install_repository( app, repository_dict )
- install_and_test_statistics_dict[ 'total_repositories_processed' ] += 1
if error_message:
remove_tests( app, repository )
# The repository installation failed.
diff -r 95517f976cca49f984b89c9fdd5b9208b1a11fcb -r 6a633b37c4c47bba640d05fef72fd73f41ae5086 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -112,6 +112,7 @@
changeset_revision = str( repository_dict.get( 'changeset_revision', '' ) )
print "Processing revision %s of repository %s owned by %s..." % ( changeset_revision, name, owner )
repository_identifier_tup = ( name, owner, changeset_revision )
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] += 1
# Retrieve the stored list of tool_test_results_dicts.
tool_test_results_dicts, error_message = \
install_and_test_base_util.get_tool_test_results_dicts( install_and_test_base_util.galaxy_tool_shed_url,
@@ -146,7 +147,6 @@
start_time = time.time()
tool_test_results_dict = install_and_test_base_util.initialize_tool_tests_results_dict( app, tool_test_results_dict )
repository, error_message = install_and_test_base_util.install_repository( app, repository_dict )
- install_and_test_statistics_dict[ 'total_repositories_processed' ] += 1
if error_message:
# The repository installation failed.
print 'Installation failed for revision %s of repository %s owned by %s.' % ( changeset_revision, name, owner )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/507055a4ddc4/
Changeset: 507055a4ddc4
User: jeremy goecks
Date: 2014-02-27 22:56:03
Summary: Trackster: track mouse with vertical line to support visual alignment.
Affected #: 3 files
diff -r cb00a217028c903a0868bad0b370b325893eb708 -r 507055a4ddc4dc226d976e735e48b77c9151877b static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1091,8 +1091,7 @@
}
});
- // Dragging in the top label track allows selecting a region
- // to zoom in
+ // Dragging in the top label track allows selecting a region to zoom in on selected region.
this.top_labeltrack.bind( "dragstart", function( e, d ) {
return $("<div/>").addClass('zoom-area').css(
"height", view.browser_content_div.height() + view.top_labeltrack.height() + view.nav_labeltrack.height() + 1
@@ -1116,6 +1115,27 @@
$(d.proxy).remove();
view.request_redraw();
});
+
+ // For vertical alignment, track mouse with simple line.
+ var mouse_tracker_div = $('<div/>').addClass('mouse-pos').appendTo(parent_element);
+
+ // Show tracker only when hovering over view.
+ parent_element.hover(
+ function() {
+ mouse_tracker_div.show();
+ parent_element.mousemove(function(e) {
+ mouse_tracker_div.css({
+ // -1 makes it appear next to the mouse w/o obscuring clicking
+ // and dragging on view elements.
+ left: e.pageX - 1
+ });
+ });
+ },
+ function() {
+ parent_element.off('mousemove');
+ mouse_tracker_div.hide();
+ }
+ );
this.add_label_track( new LabelTrack( this, { content_div: this.top_labeltrack } ) );
this.add_label_track( new LabelTrack( this, { content_div: this.nav_labeltrack } ) );
diff -r cb00a217028c903a0868bad0b370b325893eb708 -r 507055a4ddc4dc226d976e735e48b77c9151877b static/style/blue/trackster.css
--- a/static/style/blue/trackster.css
+++ b/static/style/blue/trackster.css
@@ -79,5 +79,6 @@
.feature-popup{position:absolute;z-index:2;padding:5px;font-size:10px;filter:alpha(opacity=80);background-repeat:no-repeat;background-image:url(../images/tipsy.gif);background-position:top center}
.feature-popup-inner{padding:5px 8px 4px 8px;background-color:black;color:white}
.zoom-area{position:absolute;top:0px;background-color:#ccf;opacity:0.5;z-index:2}
+.mouse-pos{position:absolute;top:0px;background-color:black;opacity:0.15;height:100%;width:1px}
.draghandle{margin-top:2px;cursor:move;float:left;background:transparent url(../images/visualization/draggable_horizontal.png) center center no-repeat;width:10px;height:12px}
.dragging{border:1px solid blue}
diff -r cb00a217028c903a0868bad0b370b325893eb708 -r 507055a4ddc4dc226d976e735e48b77c9151877b static/style/src/less/trackster.less
--- a/static/style/src/less/trackster.less
+++ b/static/style/src/less/trackster.less
@@ -531,6 +531,15 @@
z-index: @overlay-index;
}
+.mouse-pos {
+ position: absolute;
+ top: 0px;
+ background-color: black;
+ opacity: 0.15;
+ height: 100%;
+ width: 1px;
+}
+
.draghandle {
margin-top: 2px;
cursor: move;
https://bitbucket.org/galaxy/galaxy-central/commits/95517f976cca/
Changeset: 95517f976cca
User: jeremy goecks
Date: 2014-02-27 22:56:25
Summary: Automated merge
Affected #: 2 files
diff -r 507055a4ddc4dc226d976e735e48b77c9151877b -r 95517f976cca49f984b89c9fdd5b9208b1a11fcb lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
@@ -5,6 +5,7 @@
import os
import Queue
import shutil
+import stat
import subprocess
import sys
import tempfile
@@ -72,10 +73,10 @@
self.install_dir = install_dir
self.return_code = 0
- def append_line( self, skip_if_contained=True, make_executable=True, **kwd ):
+ def append_line( self, make_executable=True, **kwd ):
env_var_dict = dict( **kwd )
env_entry, env_file = self.create_or_update_env_shell_file( self.install_dir, env_var_dict )
- return_code = file_append( env_entry, env_file, skip_if_contained=skip_if_contained, make_executable=make_executable )
+ return_code = file_append( env_entry, env_file, make_executable=make_executable )
self.return_code = self.return_code or return_code
return self.return_code
@@ -194,42 +195,56 @@
stderr_queue.put( output )
stderr_queue.put( None )
-def file_append( text, file_path, skip_if_contained=True, make_executable=True ):
+def file_append( text, file_path, make_executable=True ):
"""
- Append a line to a file unless skip_if_contained is True and the line already exists in the file.
- This method creates the file if it doesn't exist. If make_executable is True, the permissions on
- the file are set to executable by the owner.
+ Append a line to a file unless the line already exists in the file. This method creates the file if
+ it doesn't exist. If make_executable is True, the permissions on the file are set to executable by
+ the owner.
"""
- if not os.path.exists( file_path ):
- return_code = handle_simple_command( 'touch %s' % file_path )
- if return_code:
- return return_code
+ file_dir = os.path.dirname( file_path )
+ if not os.path.exists( file_dir ):
+ try:
+ os.makedirs( file_dir )
+ except Exception, e:
+ log.exception( str( e ) )
+ return 1
+ if os.path.exists( file_path ):
+ try:
+ new_env_file_contents = []
+ env_file_contents = file( file_path, 'a+' ).readlines()
+ # Clean out blank lines from the env.sh file.
+ for line in env_file_contents:
+ line = line.rstrip()
+ if line:
+ new_env_file_contents.append( line )
+ env_file_contents = new_env_file_contents
+ except Exception, e:
+ log.exception( str( e ) )
+ return 1
+ else:
+ env_file_handle = open( file_path, 'w' )
+ env_file_handle.close()
+ env_file_contents = []
if make_executable:
- # Explicitly set the file to the received mode if valid.
- with settings( hide( 'everything' ), warn_only=True ):
- return_code = handle_simple_command( 'chmod +x %s' % file_path )
- if return_code:
- return return_code
+ # Explicitly set the file's executable bits.
+ try:
+ os.chmod( file_path, int( '111', base=8) | os.stat( file_path )[ stat.ST_MODE ] )
+ except Exception, e:
+ log.exception( str( e ) )
+ return 1
return_code = 0
# Convert the received text to a list, in order to support adding one or more lines to the file.
if isinstance( text, basestring ):
text = [ text ]
for line in text:
- # Build a regex to search for the relevant line in env.sh.
- regex = td_common_util.egrep_escape( line )
- if skip_if_contained:
- # If the line exists in the file, egrep will return a success.
- with settings( hide( 'everything' ), warn_only=True ):
- egrep_cmd = 'egrep "^%s$" %s' % ( regex, file_path )
- return_code = handle_simple_command( egrep_cmd )
- if return_code == 0:
- continue
- # Append the current line to the file, escaping any single quotes in the line.
- line = line.replace( "'", r"'\\''" )
- return_code = handle_simple_command( "echo '%s' >> %s" % ( line, file_path ) )
- if return_code:
- # Return upon the first error encountered.
- return return_code
+ line = line.rstrip()
+ if line not in env_file_contents:
+ env_file_contents.append( line )
+ try:
+ file( file_path, 'w' ).write( '\n'.join( env_file_contents ) )
+ except Exception, e:
+ log.exception( str( e ) )
+ return 1
return return_code
def filter_actions_after_binary_installation( actions ):
@@ -278,8 +293,8 @@
"""
Wrap subprocess.Popen in such a way that the stderr and stdout from running a shell command will
be captured and logged in nearly real time. This is similar to fabric.local, but allows us to
- retain control over the process. This method is considered "complex" because it uses queues and
- threads - see handle_simple_command().
+ retain control over the process. This method is named "complex" because it uses queues and
+ threads to execute a command while capturing and displaying the output.
"""
wrapped_command = shlex.split( "/bin/sh -c '%s'" % str( command ) )
# Launch the command as subprocess. A bufsize of 1 means line buffered.
@@ -456,30 +471,6 @@
env_var_dict[ 'value' ] = env_var_value
return env_var_dict
-def handle_simple_command( command ):
- """
- Use this instead of fabric.local to retain control over the process spawned to run the
- received command.
- """
- wrapped_command = shlex.split( "/bin/sh -c '%s'" % str( command ) )
- # Launch the command as subprocess. A bufsize of 1 means line buffered.
- try:
- process_handle = subprocess.Popen( wrapped_command,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- bufsize=1,
- close_fds=False,
- cwd=state.env[ 'lcwd' ] )
- except OSError, e:
- # We don't have a working directory.
- process_handle = subprocess.Popen( wrapped_command,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE )
- stdout, stderr = process_handle.communicate()
- if stderr:
- log.debug( "Problem executing command %s: %s." % ( wrapped_command, stderr ) )
- return process_handle.returncode
-
def install_virtualenv( app, venv_dir ):
if not os.path.exists( venv_dir ):
with make_tmp_dir() as work_dir:
@@ -951,7 +942,10 @@
work_dir = tempfile.mkdtemp( prefix="tmp-toolshed-mtd" )
yield work_dir
if os.path.exists( work_dir ):
- return_code = handle_simple_command( 'rm -rf %s' % work_dir )
+ try:
+ shutil.rmtree( work_dir )
+ except Exception, e:
+ log.exception( str( e ) )
def set_galaxy_environment( galaxy_user, tool_dependency_dir, host='localhost', shell='/bin/bash -l -c' ):
"""General Galaxy environment configuration. This method is not currently used."""
diff -r 507055a4ddc4dc226d976e735e48b77c9151877b -r 95517f976cca49f984b89c9fdd5b9208b1a11fcb lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -884,7 +884,7 @@
if env_var_version == '1.0':
# Create this tool dependency's env.sh file.
env_file_builder = fabric_util.EnvFileBuilder( install_dir )
- return_code = env_file_builder.append_line( skip_if_contained=True, make_executable=True, **env_var_dict )
+ return_code = env_file_builder.append_line( make_executable=True, **env_var_dict )
if return_code:
error_message = 'Error creating env.sh file for tool dependency %s, return_code: %s' % \
( str( tool_dependency.name ), str( return_code ) )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: davebgx: Eliminate the use of shell commands to perform functions that can be done internally with the elimination of fabric.local for tool dependency installation.
by commits-noreply@bitbucket.org 27 Feb '14
by commits-noreply@bitbucket.org 27 Feb '14
27 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/739287adeac8/
Changeset: 739287adeac8
User: davebgx
Date: 2014-02-27 21:46:03
Summary: Eliminate the use of shell commands to perform functions that can be done internally with the elimination of fabric.local for tool dependency installation.
Affected #: 2 files
diff -r cb00a217028c903a0868bad0b370b325893eb708 -r 739287adeac8561c32d6dcf24d7eb9309d992da3 lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
@@ -5,6 +5,7 @@
import os
import Queue
import shutil
+import stat
import subprocess
import sys
import tempfile
@@ -72,10 +73,10 @@
self.install_dir = install_dir
self.return_code = 0
- def append_line( self, skip_if_contained=True, make_executable=True, **kwd ):
+ def append_line( self, make_executable=True, **kwd ):
env_var_dict = dict( **kwd )
env_entry, env_file = self.create_or_update_env_shell_file( self.install_dir, env_var_dict )
- return_code = file_append( env_entry, env_file, skip_if_contained=skip_if_contained, make_executable=make_executable )
+ return_code = file_append( env_entry, env_file, make_executable=make_executable )
self.return_code = self.return_code or return_code
return self.return_code
@@ -194,42 +195,56 @@
stderr_queue.put( output )
stderr_queue.put( None )
-def file_append( text, file_path, skip_if_contained=True, make_executable=True ):
+def file_append( text, file_path, make_executable=True ):
"""
- Append a line to a file unless skip_if_contained is True and the line already exists in the file.
- This method creates the file if it doesn't exist. If make_executable is True, the permissions on
- the file are set to executable by the owner.
+ Append a line to a file unless the line already exists in the file. This method creates the file if
+ it doesn't exist. If make_executable is True, the permissions on the file are set to executable by
+ the owner.
"""
- if not os.path.exists( file_path ):
- return_code = handle_simple_command( 'touch %s' % file_path )
- if return_code:
- return return_code
+ file_dir = os.path.dirname( file_path )
+ if not os.path.exists( file_dir ):
+ try:
+ os.makedirs( file_dir )
+ except Exception, e:
+ log.exception( str( e ) )
+ return 1
+ if os.path.exists( file_path ):
+ try:
+ new_env_file_contents = []
+ env_file_contents = file( file_path, 'a+' ).readlines()
+ # Clean out blank lines from the env.sh file.
+ for line in env_file_contents:
+ line = line.rstrip()
+ if line:
+ new_env_file_contents.append( line )
+ env_file_contents = new_env_file_contents
+ except Exception, e:
+ log.exception( str( e ) )
+ return 1
+ else:
+ env_file_handle = open( file_path, 'w' )
+ env_file_handle.close()
+ env_file_contents = []
if make_executable:
- # Explicitly set the file to the received mode if valid.
- with settings( hide( 'everything' ), warn_only=True ):
- return_code = handle_simple_command( 'chmod +x %s' % file_path )
- if return_code:
- return return_code
+ # Explicitly set the file's executable bits.
+ try:
+ os.chmod( file_path, int( '111', base=8) | os.stat( file_path )[ stat.ST_MODE ] )
+ except Exception, e:
+ log.exception( str( e ) )
+ return 1
return_code = 0
# Convert the received text to a list, in order to support adding one or more lines to the file.
if isinstance( text, basestring ):
text = [ text ]
for line in text:
- # Build a regex to search for the relevant line in env.sh.
- regex = td_common_util.egrep_escape( line )
- if skip_if_contained:
- # If the line exists in the file, egrep will return a success.
- with settings( hide( 'everything' ), warn_only=True ):
- egrep_cmd = 'egrep "^%s$" %s' % ( regex, file_path )
- return_code = handle_simple_command( egrep_cmd )
- if return_code == 0:
- continue
- # Append the current line to the file, escaping any single quotes in the line.
- line = line.replace( "'", r"'\\''" )
- return_code = handle_simple_command( "echo '%s' >> %s" % ( line, file_path ) )
- if return_code:
- # Return upon the first error encountered.
- return return_code
+ line = line.rstrip()
+ if line not in env_file_contents:
+ env_file_contents.append( line )
+ try:
+ file( file_path, 'w' ).write( '\n'.join( env_file_contents ) )
+ except Exception, e:
+ log.exception( str( e ) )
+ return 1
return return_code
def filter_actions_after_binary_installation( actions ):
@@ -278,8 +293,8 @@
"""
Wrap subprocess.Popen in such a way that the stderr and stdout from running a shell command will
be captured and logged in nearly real time. This is similar to fabric.local, but allows us to
- retain control over the process. This method is considered "complex" because it uses queues and
- threads - see handle_simple_command().
+ retain control over the process. This method is named "complex" because it uses queues and
+ threads to execute a command while capturing and displaying the output.
"""
wrapped_command = shlex.split( "/bin/sh -c '%s'" % str( command ) )
# Launch the command as subprocess. A bufsize of 1 means line buffered.
@@ -456,30 +471,6 @@
env_var_dict[ 'value' ] = env_var_value
return env_var_dict
-def handle_simple_command( command ):
- """
- Use this instead of fabric.local to retain control over the process spawned to run the
- received command.
- """
- wrapped_command = shlex.split( "/bin/sh -c '%s'" % str( command ) )
- # Launch the command as subprocess. A bufsize of 1 means line buffered.
- try:
- process_handle = subprocess.Popen( wrapped_command,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- bufsize=1,
- close_fds=False,
- cwd=state.env[ 'lcwd' ] )
- except OSError, e:
- # We don't have a working directory.
- process_handle = subprocess.Popen( wrapped_command,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE )
- stdout, stderr = process_handle.communicate()
- if stderr:
- log.debug( "Problem executing command %s: %s." % ( wrapped_command, stderr ) )
- return process_handle.returncode
-
def install_virtualenv( app, venv_dir ):
if not os.path.exists( venv_dir ):
with make_tmp_dir() as work_dir:
@@ -951,7 +942,10 @@
work_dir = tempfile.mkdtemp( prefix="tmp-toolshed-mtd" )
yield work_dir
if os.path.exists( work_dir ):
- return_code = handle_simple_command( 'rm -rf %s' % work_dir )
+ try:
+ shutil.rmtree( work_dir )
+ except Exception, e:
+ log.exception( str( e ) )
def set_galaxy_environment( galaxy_user, tool_dependency_dir, host='localhost', shell='/bin/bash -l -c' ):
"""General Galaxy environment configuration. This method is not currently used."""
diff -r cb00a217028c903a0868bad0b370b325893eb708 -r 739287adeac8561c32d6dcf24d7eb9309d992da3 lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -884,7 +884,7 @@
if env_var_version == '1.0':
# Create this tool dependency's env.sh file.
env_file_builder = fabric_util.EnvFileBuilder( install_dir )
- return_code = env_file_builder.append_line( skip_if_contained=True, make_executable=True, **env_var_dict )
+ return_code = env_file_builder.append_line( make_executable=True, **env_var_dict )
if return_code:
error_message = 'Error creating env.sh file for tool dependency %s, return_code: %s' % \
( str( tool_dependency.name ), str( return_code ) )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Improve time management and process handling when installing repositories and tool dependencies from the tool shed and completely eliminate the tuse of fabric.local when installing tool dependencies.
by commits-noreply@bitbucket.org 27 Feb '14
by commits-noreply@bitbucket.org 27 Feb '14
27 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/cb00a217028c/
Changeset: cb00a217028c
User: greg
Date: 2014-02-27 18:14:53
Summary: Improve time management and process handling when installing repositories and tool dependencies from the tool shed and completely eliminate the tuse of fabric.local when installing tool dependencies.
Affected #: 2 files
diff -r a3db4f68660384e96212f7ebb0ff1856db40278b -r cb00a217028c903a0868bad0b370b325893eb708 lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py
@@ -29,7 +29,6 @@
from fabric.api import env
from fabric.api import hide
from fabric.api import lcd
-from fabric.api import local
from fabric.api import settings
from fabric.api import prefix
from fabric.operations import _AttributeString
@@ -37,7 +36,6 @@
log = logging.getLogger( __name__ )
INSTALLATION_LOG = 'INSTALLATION.log'
-NO_CMD_OUTPUT_TIMEOUT = 600.0
VIRTUALENV_URL = 'https://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.9.1.tar.gz'
@@ -55,10 +53,13 @@
def run( self ):
"""Read lines and put them on the queue."""
+ thread_lock = threading.Lock()
+ thread_lock.acquire()
for line in iter( self._fd.readline, '' ):
stripped_line = line.rstrip()
self.lines.append( stripped_line )
self._queue.put( stripped_line )
+ thread_lock.release()
def installation_complete( self ):
"""Make sure there is more installation and compilation logging content expected."""
@@ -156,6 +157,24 @@
if int( version.split( "." )[ 0 ] ) < 1:
raise NotImplementedError( "Install Fabric version 1.0 or later." )
+def close_file_descriptor( fd ):
+ """Attempt to close a file descriptor."""
+ start_timer = time.time()
+ error = ''
+ while True:
+ try:
+ fd.close()
+ break
+ except IOError, e:
+ # Undoubtedly close() was called during a concurrent operation on the same file object.
+ log.debug( 'Error closing file descriptor: %s' % str( e ) )
+ time.sleep( .5 )
+ current_wait_time = time.time() - start_timer
+ if current_wait_time >= 600:
+ error = 'Error closing file descriptor: %s' % str( e )
+ break
+ return error
+
def enqueue_output( stdout, stdout_queue, stderr, stderr_queue ):
"""
This method places streamed stdout and stderr into a threaded IPC queue target. Received data
@@ -177,16 +196,20 @@
def file_append( text, file_path, skip_if_contained=True, make_executable=True ):
"""
- Append a line to a file unless skip_if_contained is True and the line already exists in the file. This method creates the file
- if it doesn't exist. If make_executable is True, the permissions on the file are set to executable by the owner. This method
- is similar to a local version of fabric.contrib.files.append.
+ Append a line to a file unless skip_if_contained is True and the line already exists in the file.
+ This method creates the file if it doesn't exist. If make_executable is True, the permissions on
+ the file are set to executable by the owner.
"""
if not os.path.exists( file_path ):
- local( 'touch %s' % file_path )
+ return_code = handle_simple_command( 'touch %s' % file_path )
+ if return_code:
+ return return_code
if make_executable:
# Explicitly set the file to the received mode if valid.
with settings( hide( 'everything' ), warn_only=True ):
- local( 'chmod +x %s' % file_path )
+ return_code = handle_simple_command( 'chmod +x %s' % file_path )
+ if return_code:
+ return return_code
return_code = 0
# Convert the received text to a list, in order to support adding one or more lines to the file.
if isinstance( text, basestring ):
@@ -198,12 +221,12 @@
# If the line exists in the file, egrep will return a success.
with settings( hide( 'everything' ), warn_only=True ):
egrep_cmd = 'egrep "^%s$" %s' % ( regex, file_path )
- contains_line = local( egrep_cmd ).succeeded
- if contains_line:
+ return_code = handle_simple_command( egrep_cmd )
+ if return_code == 0:
continue
# Append the current line to the file, escaping any single quotes in the line.
line = line.replace( "'", r"'\\''" )
- return_code = local( "echo '%s' >> %s" % ( line, file_path ) ).return_code
+ return_code = handle_simple_command( "echo '%s' >> %s" % ( line, file_path ) )
if return_code:
# Return upon the first error encountered.
return return_code
@@ -225,7 +248,7 @@
def handle_command( app, tool_dependency, install_dir, cmd, return_output=False ):
context = app.install_model.context
- output = run_local_command( cmd )
+ output = handle_complex_command( cmd )
log_results( cmd, output, os.path.join( install_dir, INSTALLATION_LOG ) )
stdout = output.stdout
stderr = output.stderr
@@ -251,6 +274,96 @@
return output
return output.return_code
+def handle_complex_command( command ):
+ """
+ Wrap subprocess.Popen in such a way that the stderr and stdout from running a shell command will
+ be captured and logged in nearly real time. This is similar to fabric.local, but allows us to
+ retain control over the process. This method is considered "complex" because it uses queues and
+ threads - see handle_simple_command().
+ """
+ wrapped_command = shlex.split( "/bin/sh -c '%s'" % str( command ) )
+ # Launch the command as subprocess. A bufsize of 1 means line buffered.
+ process_handle = subprocess.Popen( wrapped_command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ bufsize=1,
+ close_fds=False,
+ cwd=state.env[ 'lcwd' ] )
+ pid = process_handle.pid
+ # Launch the asynchronous readers of the process' stdout and stderr.
+ stdout_queue = Queue.Queue()
+ stdout_reader = AsynchronousReader( process_handle.stdout, stdout_queue )
+ stdout_reader.start()
+ stderr_queue = Queue.Queue()
+ stderr_reader = AsynchronousReader( process_handle.stderr, stderr_queue )
+ stderr_reader.start()
+ # Place streamed stdout and stderr into a threaded IPC queue target so it can
+ # be printed and stored for later retrieval when generating the INSTALLATION.log.
+ stdio_thread = threading.Thread( target=enqueue_output,
+ args=( process_handle.stdout,
+ stdout_queue,
+ process_handle.stderr,
+ stderr_queue ) )
+ thread_lock = threading.Lock()
+ thread_lock.acquire()
+ stdio_thread.start()
+ # Check the queues for output until there is nothing more to get.
+ start_timer = time.time()
+ while not stdout_reader.installation_complete() or not stderr_reader.installation_complete():
+ # Show what we received from standard output.
+ while not stdout_queue.empty():
+ try:
+ line = stdout_queue.get()
+ except Queue.Empty:
+ line = None
+ break
+ if line:
+ print line
+ start_timer = time.time()
+ else:
+ break
+ # Show what we received from standard error.
+ while not stderr_queue.empty():
+ try:
+ line = stderr_queue.get()
+ except Queue.Empty:
+ line = None
+ break
+ if line:
+ print line
+ start_timer = time.time()
+ else:
+ stderr_queue.task_done()
+ break
+ # Sleep a bit before asking the readers again.
+ time.sleep( .1 )
+ current_wait_time = time.time() - start_timer
+ if stdout_queue.empty() and stderr_queue.empty() and current_wait_time > td_common_util.NO_OUTPUT_TIMEOUT:
+ err_msg = "\nShutting down process id %s because it generated no output for the defined timeout period of %.1f seconds.\n" % \
+ ( pid, td_common_util.NO_OUTPUT_TIMEOUT )
+ stderr_reader.lines.append( err_msg )
+ process_handle.kill()
+ break
+ thread_lock.release()
+ # Wait until each of the threads we've started terminate. The following calls will block each thread
+ # until it terminates either normally, through an unhandled exception, or until the timeout occurs.
+ stdio_thread.join( td_common_util.NO_OUTPUT_TIMEOUT )
+ stdout_reader.join( td_common_util.NO_OUTPUT_TIMEOUT )
+ stderr_reader.join( td_common_util.NO_OUTPUT_TIMEOUT )
+ # Close subprocess' file descriptors.
+ error = close_file_descriptor( process_handle.stdout )
+ error = close_file_descriptor( process_handle.stderr )
+ stdout = '\n'.join( stdout_reader.lines )
+ stderr = '\n'.join( stderr_reader.lines )
+ # Handle error condition (deal with stdout being None, too)
+ output = _AttributeString( stdout.strip() if stdout else "" )
+ errors = _AttributeString( stderr.strip() if stderr else "" )
+ # Make sure the process has finished.
+ process_handle.poll()
+ output.return_code = process_handle.returncode
+ output.stderr = errors
+ return output
+
def handle_environment_variables( app, tool_dependency, install_dir, env_var_dict, set_prior_environment_commands ):
"""
This method works with with a combination of three tool dependency definition tag sets, which are defined in the tool_dependencies.xml file in the
@@ -343,6 +456,30 @@
env_var_dict[ 'value' ] = env_var_value
return env_var_dict
+def handle_simple_command( command ):
+ """
+ Use this instead of fabric.local to retain control over the process spawned to run the
+ received command.
+ """
+ wrapped_command = shlex.split( "/bin/sh -c '%s'" % str( command ) )
+ # Launch the command as subprocess. A bufsize of 1 means line buffered.
+ try:
+ process_handle = subprocess.Popen( wrapped_command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ bufsize=1,
+ close_fds=False,
+ cwd=state.env[ 'lcwd' ] )
+ except OSError, e:
+ # We don't have a working directory.
+ process_handle = subprocess.Popen( wrapped_command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE )
+ stdout, stderr = process_handle.communicate()
+ if stderr:
+ log.debug( "Problem executing command %s: %s." % ( wrapped_command, stderr ) )
+ return process_handle.returncode
+
def install_virtualenv( app, venv_dir ):
if not os.path.exists( venv_dir ):
with make_tmp_dir() as work_dir:
@@ -623,8 +760,6 @@
# </action>
filtered_actions = [ a for a in actions ]
dir = install_dir
- # We need to be careful in determining if the value of dir is a valid directory because we're dealing with 2 environments, the fabric local
- # environment and the python environment. Checking the path as follows should work.
full_path_to_dir = os.path.abspath( os.path.join( work_dir, dir ) )
if not os.path.exists( full_path_to_dir ):
os.makedirs( full_path_to_dir )
@@ -795,8 +930,7 @@
def log_results( command, fabric_AttributeString, file_path ):
"""
- Write attributes of fabric.operations._AttributeString (which is the output of executing command using fabric's local() method)
- to a specified log file.
+ Write attributes of fabric.operations._AttributeString to a specified log file.
"""
if os.path.exists( file_path ):
logfile = open( file_path, 'ab' )
@@ -817,104 +951,7 @@
work_dir = tempfile.mkdtemp( prefix="tmp-toolshed-mtd" )
yield work_dir
if os.path.exists( work_dir ):
- local( 'rm -rf %s' % work_dir )
-
-def run_local_command( command ):
- """
- Wrap subprocess.Popen in such a way that the stderr and stdout from running a shell command will
- be captured and logged in nearly real time. This is similar to fabric.local, but allows us to
- retain control over the process.
- """
- wrapped_command = shlex.split( "/bin/sh -c '%s'" % str( command ) )
- # Launch the command as subprocess. A bufsize of 1 means line buffered.
- process_handle = subprocess.Popen( wrapped_command,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- bufsize=1,
- close_fds=False,
- cwd=state.env[ 'lcwd' ] )
- pid = process_handle.pid
- # Launch the asynchronous readers of the process' stdout and stderr.
- stdout_queue = Queue.Queue()
- stdout_reader = AsynchronousReader( process_handle.stdout, stdout_queue )
- stdout_reader.start()
- stderr_queue = Queue.Queue()
- stderr_reader = AsynchronousReader( process_handle.stderr, stderr_queue )
- stderr_reader.start()
- # Place streamed stdout and stderr into a threaded IPC queue target so it can
- # be printed and stored for later retrieval when generating the INSTALLATION.log.
- stdio_thread = threading.Thread( target=enqueue_output,
- args=( process_handle.stdout,
- stdout_queue,
- process_handle.stderr,
- stderr_queue ) )
- stdio_thread.start()
- # Check the queues for output until there is nothing more to get.
- start_timer = time.time()
- while not stdout_reader.installation_complete() or not stderr_reader.installation_complete():
- # Show what we received from standard output.
- while not stdout_queue.empty():
- try:
- line = stdout_queue.get()
- except Queue.Empty:
- err_msg = "\nShutting down process id %s because it generated no stdout for the defined timout period of %d seconds.\n" % \
- ( pid, NO_CMD_OUTPUT_TIMEOUT )
- print err_msg
- stdout_reader.lines.append( err_msg )
- # Close subprocess' file descriptors.
- process_handle.stdout.close()
- process_handle.stderr.close()
- process_handle.kill()
- line = None
- break
- if line:
- print line
- start_timer = time.time()
- else:
- break
- # Show what we received from standard error.
- while not stderr_queue.empty():
- try:
- line = stderr_queue.get()
- except Queue.Empty:
- err_msg = "\nShutting down process id %s because it generated no stderr for the defined timout period of %d seconds.\n" % \
- ( pid, NO_CMD_OUTPUT_TIMEOUT )
- print err_msg
- stderr_reader.lines.append( err_msg )
- # Close subprocess' file descriptor.
- process_handle.stdout.close()
- process_handle.stderr.close()
- process_handle.kill()
- line = None
- break
- if line:
- print line
- start_timer = time.time()
- else:
- stderr_queue.task_done()
- break
- # Sleep a bit before asking the readers again.
- time.sleep( .1 )
- current_wait_time = time.time() - start_timer
- if stdout_queue.empty() and stderr_queue.empty() and current_wait_time < NO_CMD_OUTPUT_TIMEOUT:
- break
- # Wait until each of the threads we've started terminate. The following calls will block each thread
- # until it terminates either normally, through an unhandled exception, or until the timeout occurs.
- stdio_thread.join( NO_CMD_OUTPUT_TIMEOUT )
- stdout_reader.join( NO_CMD_OUTPUT_TIMEOUT )
- stderr_reader.join( NO_CMD_OUTPUT_TIMEOUT )
- # Close subprocess' file descriptors.
- process_handle.stdout.close()
- process_handle.stderr.close()
- stdout = '\n'.join( stdout_reader.lines )
- stderr = '\n'.join( stderr_reader.lines )
- # Handle error condition (deal with stdout being None, too)
- output = _AttributeString( stdout.strip() if stdout else "" )
- errors = _AttributeString( stderr.strip() if stderr else "" )
- process_handle.poll()
- output.return_code = process_handle.returncode
- output.stderr = errors
- return output
+ return_code = handle_simple_command( 'rm -rf %s' % work_dir )
def set_galaxy_environment( galaxy_user, tool_dependency_dir, host='localhost', shell='/bin/bash -l -c' ):
"""General Galaxy environment configuration. This method is not currently used."""
@@ -923,5 +960,4 @@
env.host_string = host
env.shell = shell
env.use_sudo = False
- env.safe_cmd = local
return env
diff -r a3db4f68660384e96212f7ebb0ff1856db40278b -r cb00a217028c903a0868bad0b370b325893eb708 lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py
@@ -4,6 +4,7 @@
import shutil
import sys
import tarfile
+import time
import traceback
import urllib2
import zipfile
@@ -13,9 +14,12 @@
log = logging.getLogger( __name__ )
+# Set no activity timeout to 20 minutes.
+NO_OUTPUT_TIMEOUT = 1200
+
class CompressedFile( object ):
-
+
def __init__( self, file_path, mode='r' ):
if istar( file_path ):
self.file_type = 'tar'
@@ -30,7 +34,7 @@
self.archive = getattr( self, method )( file_path, mode )
else:
raise NameError( 'File type %s specified, no open method found.' % self.file_type )
-
+
def extract( self, path ):
'''Determine the path to which the archive should be extracted.'''
contents = self.getmembers()
@@ -58,36 +62,36 @@
os.makedirs( extraction_path )
self.archive.extractall( os.path.join( extraction_path ) )
return os.path.abspath( extraction_path )
-
+
def getmembers_tar( self ):
return self.archive.getmembers()
-
+
def getmembers_zip( self ):
return self.archive.infolist()
-
+
def getname_tar( self, item ):
return item.name
-
+
def getname_zip( self, item ):
return item.filename
-
+
def getmember( self, name ):
for member in self.getmembers():
if self.getname( member ) == name:
return member
-
+
def getmembers( self ):
return getattr( self, 'getmembers_%s' % self.type )()
-
+
def getname( self, member ):
return getattr( self, 'getname_%s' % self.type )( member )
-
+
def isdir( self, member ):
return getattr( self, 'isdir_%s' % self.type )( member )
-
+
def isdir_tar( self, member ):
return member.isdir()
-
+
def isdir_zip( self, member ):
if member.filename.endswith( os.sep ):
return True
@@ -97,7 +101,7 @@
if not self.isdir( member ):
return True
return False
-
+
def open_tar( self, filepath, mode ):
return tarfile.open( filepath, mode, errorlevel=0 )
@@ -146,9 +150,7 @@
return None
def download_binary( url, work_dir ):
- '''
- Download a pre-compiled binary from the specified URL.
- '''
+ """Download a pre-compiled binary from the specified URL."""
downloaded_filename = os.path.split( url )[ -1 ]
dir = url_download( work_dir, downloaded_filename, url, extract=False )
return downloaded_filename
@@ -179,7 +181,6 @@
return os.path.abspath( os.path.join( root, name ) )
return None
-
def get_env_shell_file_paths( app, elem ):
# Currently only the following tag set is supported.
# <repository toolshed="http://localhost:9009/" name="package_numpy_1_7" owner="test" changeset_revision="c84c6a8be056">
@@ -455,6 +456,8 @@
file_path = os.path.join( install_dir, downloaded_file_name )
src = None
dst = None
+ # Set a timer so we don't sit here forever.
+ start_time = time.time()
try:
src = urllib2.urlopen( download_url )
dst = open( file_path, 'wb' )
@@ -464,8 +467,14 @@
dst.write( chunk )
else:
break
- except:
- raise
+ time_taken = time.time() - start_time
+ if time_taken > NO_OUTPUT_TIMEOUT:
+ err_msg = 'Downloading from URL %s took longer than the defined timeout period of %.1f seconds.' % \
+ ( str( download_url ), NO_OUTPUT_TIMEOUT )
+ raise Exception( err_msg )
+ except Exception, e:
+ err_msg = err_msg = 'Error downloading from URL %s: %s' % ( str( download_url ), NO_OUTPUT_TIMEOUT )
+ raise Exception( err_msg )
finally:
if src:
src.close()
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
27 Feb '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a3db4f686603/
Changeset: a3db4f686603
User: davebgx
Date: 2014-02-27 15:20:45
Summary: Fix python 2.6 support.
Affected #: 1 file
diff -r a6fff5c62ceb6b73ffa67eb5ba8854adc8860eb7 -r a3db4f68660384e96212f7ebb0ff1856db40278b lib/tool_shed/util/commit_util.py
--- a/lib/tool_shed/util/commit_util.py
+++ b/lib/tool_shed/util/commit_util.py
@@ -289,8 +289,8 @@
revised = False
toolshed = elem.get( 'toolshed' )
changeset_revision = elem.get( 'changeset_revision' )
- sub_elems = elem.findall( './/' )
- if sub_elems:
+ sub_elems = [ child_elem for child_elem in list( elem ) ]
+ if len( sub_elems ) > 0:
# At this point, a <repository> tag will point only to a package.
# <package name="xorg_macros" version="1.17.1" />
# Coerce the list to an odict().
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a6af49ec80a8/
Changeset: a6af49ec80a8
User: BjoernGruening
Date: 2014-02-27 09:40:35
Summary: strip trailing slashes in tool IDs
Affected #: 1 file
diff -r ce7e90f9576ba51317508de0749a277e8d7f58bb -r a6af49ec80a89548af58750c87c67589ef7a0d9a lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -181,7 +181,7 @@
if tools is not None:
for tool in self.__findall_with_required(tools, 'tool'):
# There can be multiple definitions with identical ids, but different params
- id = tool.get('id').lower()
+ id = tool.get('id').lower().strip('/')
if id not in self.tools:
self.tools[id] = list()
self.tools[id].append(JobToolConfiguration(**dict(tool.items())))
https://bitbucket.org/galaxy/galaxy-central/commits/a6fff5c62ceb/
Changeset: a6fff5c62ceb
User: BjoernGruening
Date: 2014-02-27 11:12:49
Summary: change strip to rstrip as proposed by Nicola
Affected #: 1 file
diff -r a6af49ec80a89548af58750c87c67589ef7a0d9a -r a6fff5c62ceb6b73ffa67eb5ba8854adc8860eb7 lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -181,7 +181,7 @@
if tools is not None:
for tool in self.__findall_with_required(tools, 'tool'):
# There can be multiple definitions with identical ids, but different params
- id = tool.get('id').lower().strip('/')
+ id = tool.get('id').lower().rstrip('/')
if id not in self.tools:
self.tools[id] = list()
self.tools[id].append(JobToolConfiguration(**dict(tool.items())))
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0