galaxy-commits
Threads by month
- ----- 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
September 2012
- 1 participants
- 161 discussions
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/72f1dcdf1c54/
changeset: 72f1dcdf1c54
user: dannon
date: 2012-09-12 19:05:15
summary: Pack scripts.
affected #: 4 files
diff -r 8b301f6af8340f8cd01022035d5765cf0aba3ede -r 72f1dcdf1c542e6d4689305f5de2077c0bfb4035 static/scripts/packed/galaxy.base.js
--- a/static/scripts/packed/galaxy.base.js
+++ b/static/scripts/packed/galaxy.base.js
@@ -1,1 +1,1 @@
-if(!Array.indexOf){Array.prototype.indexOf=function(c){for(var b=0,a=this.length;b<a;b++){if(this[b]==c){return b}}return -1}}function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function make_popupmenu(b,c){var a=(b.data("menu_options"));b.data("menu_options",c);if(a){return}b.bind("click.show_popup",function(d){$(".popmenu-wrapper").remove();setTimeout(function(){var g=$("<ul class='dropdown-menu' id='"+b.attr("id")+"-menu'></ul>");var f=b.data("menu_options");if(obj_length(f)<=0){$("<li>No Options.</li>").appendTo(g)}$.each(f,function(j,i){if(i){g.append($("<li></li>").append($("<a href='#'></a>").html(j).click(i)))}else{g.append($("<li></li>").addClass("head").append($("<a href='#'></a>").html(j)))}});var h=$("<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>").append(g).appendTo("body");var e=d.pageX-h.width()/2;e=Math.min(e,$(document).scrollLeft()+$(window).width()-$(h).width()-5);e=Math.max(e,$(document).scrollLeft()+5);h.css({top:d.pageY,left:e})},10);setTimeout(function(){var f=function(h){$(h).bind("click.close_popup",function(){$(".popmenu-wrapper").remove();h.unbind("click.close_popup")})};f($(window.document));f($(window.top.document));for(var e=window.top.frames.length;e--;){var g=$(window.top.frames[e].document);f(g)}},50);return false})}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var a={};var c=$(this);c.find("a").each(function(){var f=$(this),h=f.get(0);var d=h.getAttribute("confirm"),e=h.getAttribute("href"),g=h.getAttribute("target");if(!e){a[f.text()]=null}else{a[f.text()]=function(){if(!d||confirm(d)){var i;if(g=="_parent"){window.parent.location=e}else{if(g=="_top"){window.top.location=e}else{if(g=="demo"){if(i===undefined||i.closed){i=window.open(e,g);i.creator=self}}else{window.location=e}}}}}}});var b=$("#"+c.attr("popupmenu"));b.find("a").bind("click",function(d){d.stopPropagation();return true});make_popupmenu(b,a);b.addClass("popup");c.remove()})}function naturalSort(j,h){var p=/(-?[0-9\.]+)/g,k=j.toString().toLowerCase()||"",g=h.toString().toLowerCase()||"",l=String.fromCharCode(0),n=k.replace(p,l+"$1"+l).split(l),e=g.replace(p,l+"$1"+l).split(l),d=(new Date(k)).getTime(),o=d?(new Date(g)).getTime():null;if(o){if(d<o){return -1}else{if(d>o){return 1}}}var m,f;for(var i=0,c=Math.max(n.length,e.length);i<c;i++){m=parseFloat(n[i])||n[i];f=parseFloat(e[i])||e[i];if(m<f){return -1}else{if(m>f){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var d=$(this);var g=d.find("option").length;if((g<a)||(g>b)){return}if(d.attr("multiple")==="multiple"){return}if(d.hasClass("no-autocomplete")){return}var m=d.attr("value");var c=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",d.attr("name"));c.attr("id",d.attr("id"));c.click(function(){var n=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(n);$(this).select()});var e=[];var i={};d.children("option").each(function(){var o=$(this).text();var n=$(this).attr("value");e.push(o);i[o]=n;i[n]=n;if(n==m){c.attr("value",o)}});if(m===""||m==="?"){c.attr("value","Click to Search or Select")}if(d.attr("name")=="dbkey"){e=e.sort(naturalSort)}var f={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(e,f);d.replaceWith(c);var k=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){c.attr("value",n)}else{if(m!==""){c.attr("value",m)}else{c.attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_to_values",function(){k()});if(d.attr("refresh_on_change")=="true"){var h=d.attr("refresh_on_change_values"),l=d.attr("last_selected_value");if(h!==undefined){h=h.split(",")}var j=function(){var n=i[c.attr("value")];if(l!==n&&n!==null&&n!==undefined){if(h!==undefined&&$.inArray(n,h)===-1&&$.inArray(l,h)===-1){return}c.attr("value",n);$(window).trigger("refresh_on_change");c.parents("form").submit()}};c.bind("result",j);c.keyup(function(n){if(n.keyCode===13){j()}});c.keydown(function(n){if(n.keyCode===13){return false}})}})}$.fn.make_text_editable=function(g){var d=("num_cols" in g?g.num_cols:30),c=("num_rows" in g?g.num_rows:4),e=("use_textarea" in g?g.use_textarea:false),b=("on_finish" in g?g.on_finish:null),f=("help_text" in g?g.help_text:null);var a=$(this);a.addClass("editable-text").click(function(l){if($(this).children(":input").length>0){return}a.removeClass("editable-text");var i=function(m){a.find(":input").remove();if(m!==""){a.text(m)}else{a.html("<br>")}a.addClass("editable-text");if(b){b(m)}};var h=a.text(),k,j;if(e){k=$("<textarea/>").attr({rows:c,cols:d}).text($.trim(h)).keyup(function(m){if(m.keyCode===27){i(h)}});j=$("<button/>").text("Done").click(function(){i(k.val());return false})}else{k=$("<input type='text'/>").attr({value:$.trim(h),size:d}).blur(function(){i(h)}).keyup(function(m){if(m.keyCode===27){$(this).trigger("blur")}else{if(m.keyCode===13){i($(this).val())}}})}a.text("");a.append(k);if(j){a.append(j)}k.focus();k.select();l.stopPropagation()});if(f){a.attr("title",f).tooltip()}return a};function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!==""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStorage.get("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStorage.deleteKey("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id,h=$(this).children("div.historyItemBody"),i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){var k;if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){k=$.jStorage.get("history_expand_state");if(k){delete k[j];$.jStorage.set("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){k=$.jStorage.get("history_expand_state");if(!k){k={}}k[j]=true;$.jStorage.set("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStorage.get("history_expand_state");if(!h){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStorage.set("history_expand_state",h)}).show()};b()}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length===0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");$(document).trigger("convert_to_values");a.get(0).form.submit()});$(":checkbox[refresh_on_change='true']").click(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tooltip){$(".tooltip").tooltip({placement:"top"})}make_popup_menus();replace_big_select_inputs(20,1500);$("a").click(function(){var b=$(this);var c=(parent.frames&&parent.frames.galaxy_main);if((b.attr("target")=="galaxy_main")&&(!c)){var a=b.attr("href");if(a.indexOf("?")==-1){a+="?"}else{a+="&"}a+="use_panels=True";b.attr("href",a);b.attr("target","_self")}return b})});
\ No newline at end of file
+if(!Array.indexOf){Array.prototype.indexOf=function(c){for(var b=0,a=this.length;b<a;b++){if(this[b]==c){return b}}return -1}}function obj_length(c){if(c.length!==undefined){return c.length}var b=0;for(var a in c){b++}return b}$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function make_popupmenu(b,c){var a=(b.data("menu_options"));b.data("menu_options",c);if(a){return}b.bind("click.show_popup",function(d){$(".popmenu-wrapper").remove();setTimeout(function(){var g=$("<ul class='dropdown-menu' id='"+b.attr("id")+"-menu'></ul>");var f=b.data("menu_options");if(obj_length(f)<=0){$("<li>No Options.</li>").appendTo(g)}$.each(f,function(j,i){if(i){g.append($("<li></li>").append($("<a href='#'></a>").html(j).click(i)))}else{g.append($("<li></li>").addClass("head").append($("<a href='#'></a>").html(j)))}});var h=$("<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>").append(g).appendTo("body");var e=d.pageX-h.width()/2;e=Math.min(e,$(document).scrollLeft()+$(window).width()-$(h).width()-5);e=Math.max(e,$(document).scrollLeft()+5);h.css({top:d.pageY,left:e})},10);setTimeout(function(){var f=function(h){$(h).bind("click.close_popup",function(){$(".popmenu-wrapper").remove();h.unbind("click.close_popup")})};f($(window.document));f($(window.top.document));for(var e=window.top.frames.length;e--;){var g=$(window.top.frames[e].document);f(g)}},50);return false})}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var a={};var c=$(this);c.find("a").each(function(){var f=$(this),h=f.get(0);var d=h.getAttribute("confirm"),e=h.getAttribute("href"),g=h.getAttribute("target");if(!e){a[f.text()]=null}else{a[f.text()]=function(){if(!d||confirm(d)){var i;if(g=="_parent"){window.parent.location=e}else{if(g=="_top"){window.top.location=e}else{if(g=="demo"){if(i===undefined||i.closed){i=window.open(e,g);i.creator=self}}else{window.location=e}}}}}}});var b=$("#"+c.attr("popupmenu"));b.find("a").bind("click",function(d){d.stopPropagation();return true});make_popupmenu(b,a);b.addClass("popup");c.remove()})}function naturalSort(j,h){var p=/(-?[0-9\.]+)/g,k=j.toString().toLowerCase()||"",g=h.toString().toLowerCase()||"",l=String.fromCharCode(0),n=k.replace(p,l+"$1"+l).split(l),e=g.replace(p,l+"$1"+l).split(l),d=(new Date(k)).getTime(),o=d?(new Date(g)).getTime():null;if(o){if(d<o){return -1}else{if(d>o){return 1}}}var m,f;for(var i=0,c=Math.max(n.length,e.length);i<c;i++){m=parseFloat(n[i])||n[i];f=parseFloat(e[i])||e[i];if(m<f){return -1}else{if(m>f){return 1}}}return 0}function replace_big_select_inputs(a,c,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(c===undefined){c=3000}var b=b||$("select");b.each(function(){var e=$(this);var h=e.find("option").length;if((h<a)||(h>c)){return}if(e.attr("multiple")==="multiple"){return}if(e.hasClass("no-autocomplete")){return}var n=e.attr("value");var d=$("<input type='text' class='text-and-autocomplete-select'></input>");d.attr("size",40);d.attr("name",e.attr("name"));d.attr("id",e.attr("id"));d.click(function(){var o=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(o);$(this).select()});var f=[];var j={};e.children("option").each(function(){var p=$(this).text();var o=$(this).attr("value");f.push(p);j[p]=o;j[o]=o;if(o==n){d.attr("value",p)}});if(n===""||n==="?"){d.attr("value","Click to Search or Select")}if(e.attr("name")=="dbkey"){f=f.sort(naturalSort)}var g={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:c,minChars:0,hideForLessThanMinChars:false};d.autocomplete(f,g);e.replaceWith(d);var l=function(){var p=d.attr("value");var o=j[p];if(o!==null&&o!==undefined){d.attr("value",o)}else{if(n!==""){d.attr("value",n)}else{d.attr("value","?")}}};d.parents("form").submit(function(){l()});$(document).bind("convert_to_values",function(){l()});if(e.attr("refresh_on_change")=="true"){var i=e.attr("refresh_on_change_values"),m=e.attr("last_selected_value");if(i!==undefined){i=i.split(",")}var k=function(){var o=j[d.attr("value")];if(m!==o&&o!==null&&o!==undefined){if(i!==undefined&&$.inArray(o,i)===-1&&$.inArray(m,i)===-1){return}d.attr("value",o);$(window).trigger("refresh_on_change");d.parents("form").submit()}};d.bind("result",k);d.keyup(function(o){if(o.keyCode===13){k()}});d.keydown(function(o){if(o.keyCode===13){return false}})}})}$.fn.make_text_editable=function(g){var d=("num_cols" in g?g.num_cols:30),c=("num_rows" in g?g.num_rows:4),e=("use_textarea" in g?g.use_textarea:false),b=("on_finish" in g?g.on_finish:null),f=("help_text" in g?g.help_text:null);var a=$(this);a.addClass("editable-text").click(function(l){if($(this).children(":input").length>0){return}a.removeClass("editable-text");var i=function(m){a.find(":input").remove();if(m!==""){a.text(m)}else{a.html("<br>")}a.addClass("editable-text");if(b){b(m)}};var h=a.text(),k,j;if(e){k=$("<textarea/>").attr({rows:c,cols:d}).text($.trim(h)).keyup(function(m){if(m.keyCode===27){i(h)}});j=$("<button/>").text("Done").click(function(){i(k.val());return false})}else{k=$("<input type='text'/>").attr({value:$.trim(h),size:d}).blur(function(){i(h)}).keyup(function(m){if(m.keyCode===27){$(this).trigger("blur")}else{if(m.keyCode===13){i($(this).val())}}})}a.text("");a.append(k);if(j){a.append(j)}k.focus();k.select();l.stopPropagation()});if(f){a.attr("title",f).tooltip()}return a};function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text($.trim(k))}else{j=$("<input type='text'></input>").attr({value:$.trim(k),size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){if(o!==""){l.text(o)}else{l.html("<em>None</em>")}if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStorage.get("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStorage.deleteKey("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id,h=$(this).children("div.historyItemBody"),i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){var k;if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){k=$.jStorage.get("history_expand_state");if(k){delete k[j];$.jStorage.set("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){k=$.jStorage.get("history_expand_state");if(!k){k={}}k[j]=true;$.jStorage.set("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStorage.get("history_expand_state");if(!h){h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStorage.set("history_expand_state",h)}).show()};b()}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length===0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools")}}var GalaxyAsync=function(a){this.url_dict={};this.log_action=(a===undefined?false:a)};GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("select[refresh_on_change='true']").change(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");$(document).trigger("convert_to_values");a.get(0).form.submit()});$(":checkbox[refresh_on_change='true']").click(function(){var a=$(this),e=a.val(),d=false,c=a.attr("refresh_on_change_values");if(c){c=c.split(",");var b=a.attr("last_selected_value");if($.inArray(e,c)===-1&&$.inArray(b,c)===-1){return}}$(window).trigger("refresh_on_change");a.get(0).form.submit()});$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tooltip){$(".tooltip").tooltip({placement:"top"})}make_popup_menus();replace_big_select_inputs(20,1500);$("a").click(function(){var b=$(this);var c=(parent.frames&&parent.frames.galaxy_main);if((b.attr("target")=="galaxy_main")&&(!c)){var a=b.attr("href");if(a.indexOf("?")==-1){a+="?"}else{a+="&"}a+="use_panels=True";b.attr("href",a);b.attr("target","_self")}return b})});
\ No newline at end of file
diff -r 8b301f6af8340f8cd01022035d5765cf0aba3ede -r 72f1dcdf1c542e6d4689305f5de2077c0bfb4035 static/scripts/packed/mvc/history.js
--- a/static/scripts/packed/mvc/history.js
+++ b/static/scripts/packed/mvc/history.js
@@ -1,1 +1,1 @@
-_=_;var Loggable={logger:null,log:function(){return(this.logger)?(this.logger.debug.apply(this,arguments)):(undefined)}};var LoggingModel=BaseModel.extend(Loggable);var LoggingView=BaseView.extend(Loggable);var Localizable={localizedStrings:{},setLocalizedString:function(b,a){this.localizedStrings[b]=a},localize:function(a){if(a in this.localizedStrings){return this.localizedStrings[a]}return a}};var LocalizableView=LoggingView.extend(Localizable);function linkHTMLTemplate(b,a){if(!b){return"<a></a>"}a=a||"a";var c=["<"+a];for(key in b){var d=b[key];if(d===""){continue}switch(key){case"text":continue;case"classes":key="class";d=(b.classes.join)?(b.classes.join(" ")):(b.classes);default:c.push([" ",key,'="',d,'"'].join(""))}}c.push(">");if("text" in b){c.push(b.text)}c.push("</"+a+">");return c.join("")}var HistoryItem=LoggingModel.extend({defaults:{id:null,name:"",data_type:null,file_size:0,genome_build:null,metadata_data_lines:0,metadata_dbkey:null,metadata_sequences:0,misc_blurb:"",misc_info:"",model_class:"",state:"",deleted:false,purged:false,visible:true,for_editing:true,bodyIsShown:false},initialize:function(){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"))},isEditable:function(){return(!(this.get("deleted")||this.get("purged")))},hasData:function(){return(this.get("file_size")>0)},toString:function(){return"HistoryItem("+(this.get("name")||this.get("id")||"")+")"}});HistoryItem.STATES={NEW:"new",UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",OK:"ok",EMPTY:"empty",ERROR:"error",DISCARDED:"discarded",SETTING_METADATA:"setting_metadata",FAILED_METADATA:"failed_metadata"};var HistoryItemView=LoggingView.extend({logger:console,tagName:"div",className:"historyItemContainer",initialize:function(){this.log(this+".initialize:",this,this.model);return this},render:function(){this.log(this+".model:",this.model);var d=this.model.get("id"),c=this.model.get("state");this.$el.attr("id","historyItemContainer-"+d);var a=$("<div/>").attr("id","historyItem-"+d).addClass("historyItemWrapper").addClass("historyItem").addClass("historyItem-"+c);a.append(this._render_purgedWarning());a.append(this._render_deletionWarning());a.append(this._render_visibleWarning());a.append(this._render_titleBar());this.body=$(this._render_body());a.append(this.body);a.find(".tooltip").tooltip({placement:"bottom"});var b=a.find("[popupmenu]");b.each(function(e,f){f=$(f);make_popupmenu(f)});this.$el.children().remove();return this.$el.append(a)},_render_purgedWarning:function(){var a=null;if(this.model.get("purged")){a=$(HistoryItemView.STRINGS.purgedMsg)}return a},_render_deletionWarning:function(){var b=null;if(this.model.get("deleted")){var a="";if(this.model.get("undelete_url")){a+=HistoryItemView.TEMPLATES.undeleteLink(this.model.attributes)}if(this.model.get("purge_url")){a+=HistoryItemView.TEMPLATES.purgeLink(this.model.attributes)}b=$(HistoryItemView.TEMPLATES.warningMsg({warning:a}))}return b},_render_visibleWarning:function(){var b=null;if(!this.model.get("visible")&&this.model.get("unhide_url")){var a=HistoryItemView.TEMPLATES.hiddenMsg(this.model.attributes);b=$(HistoryItemView.TEMPLATES.warningMsg({warning:a}))}return b},_render_titleBar:function(){var a=$('<div class="historyItemTitleBar" style="overflow: hidden"></div>');a.append(this._render_titleButtons());a.append('<span class="state-icon"></span>');a.append(this._render_titleLink());return a},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>'),b=this.model.get("for_editing");if(this.model.get("state")!==HistoryItem.STATES.UPLOAD){a.append(this._render_displayButton());if(b){a.append(this._render_editButton())}}if(b){a.append(this._render_deleteButton())}return a},_render_displayButton:function(){if(this.model.get("purged")){return $('<span class="icon-button display_disabled tooltip" title="Cannot display datasets removed from disk"></span>')}var b=this.model.get("id"),a={title:"Display data in browser",href:"/datasets/"+b+"/display/?preview=True",target:(this.model.get("for_editing"))?("galaxy_main"):(""),classes:["icon-button","tooltip","display"],dataset_id:b};return $(linkHTMLTemplate(a))},_render_editButton:function(){var c=this.model.get("id"),b=this.model.get("purged"),a=this.model.get("deleted");if(a||b){if(!b){return $('<span class="icon-button edit_disabled tooltip" title="Undelete dataset to edit attributes"></span>')}else{return $('<span class="icon-button edit_disabled tooltip" title="Cannot edit attributes of datasets removed from disk"></span>')}}return $(linkHTMLTemplate({title:"Edit attributes",href:"/datasets/"+c+"/edit",target:"galaxy_main",classes:["icon-button","tooltip","edit"]}))},_render_deleteButton:function(){var c=this.model.get("id"),b=this.model.get("purged"),a=this.model.get("deleted");if(b||a){return $('<span title="Dataset is already deleted" class="icon-button delete_disabled tooltip"></span>')}return $(linkHTMLTemplate({title:"Delete",href:"/datasets/"+c+"/delete?show_deleted_on_refresh=False",target:"galaxy_main",id:"historyItemDeleter-"+c,classes:["icon-button","tooltip","delete"]}))},_render_titleLink:function(){var b=this.model.get("name"),a=this.model.get("hid");title=(a+": "+b);return $(linkHTMLTemplate({href:"javascript:void(0);",text:'<span class="historyItemTitle">'+title+"</span>"}))},_render_body_not_viewable:function(a){a.append($("<div>You do not have permission to view dataset.</div>"))},_render_body_uploading:function(a){a.append($("<div>Dataset is uploading</div>"))},_render_body_queued:function(a){a.append($("<div>Job is waiting to run.</div>"));a.append(this._render_showParamsAndRerun())},_render_body_running:function(a){a.append("<div>Job is currently running.</div>");a.append(this._render_showParamsAndRerun())},_render_body_error:function(a){if(!this.model.get("purged")){a.append($("<div>"+this.model.get("misc_blurb")+"</div>"))}a.append(("An error occurred running this job: <i>"+$.trim(this.model.get("misc_info"))+"</i>"));var b=$(this._render_showParamsAndRerun());if(this.model.get("for_editing")){b.prepend($(linkHTMLTemplate({title:"View or report this error",href:this.model.get("report_errors_url"),target:"galaxy_main",classes:["icon-button","tooltip","bug"]})))}if(this.model.hasData()){b.prepend(this._render_downloadLinks())}a.append(b)},_render_body_discarded:function(a){a.append("<div>The job creating this dataset was cancelled before completion.</div>");a.append(this._render_showParamsAndRerun())},_render_body_setting_metadata:function(a){a.append($("<div>Metadata is being auto-detected.</div>"))},_render_body_empty:function(a){a.append($("<div>No data: <i>"+this.model.get("misc_blurb")+"</i></div>"));a.append(this._render_showParamsAndRerun())},_render_body_failed_metadata:function(b){var c="An error occurred setting the metadata for this dataset.";if(this.model.isEditable()){var a=linkHTMLTemplate({text:"set it manually or retry auto-detection",href:this.model.get("edit_url"),target:"galaxy_main"});c+="You may be able to "+a+"."}b.append($(HistoryItemView.TEMPLATES.warningMsg({warning:c})));this._render_body_ok(b)},_render_body_ok:function(i){i.append(this._render_hdaSummary());if(this.model.get("misc_info")){i.append($('<div class="info">Info: '+this.model.get("misc_info")+"</div>"))}if(this.model.hasData()){var d=$("<div/>");d.append(this._render_downloadLinks());d.append($(linkHTMLTemplate({title:"View details",href:this.model.get("show_params_url"),target:"galaxy_main",classes:["icon-button","tooltip","information"]})));if(this.model.get("for_editing")){d.append($(linkHTMLTemplate({title:"Run this job again",href:this.model.get("rerun_url"),target:"galaxy_main",classes:["icon-button","tooltip","arrow-circle"]})));if(this.model.get("trackster_urls")){var a=this.model.get("trackster_urls");d.append($(linkHTMLTemplate({title:"View in Trackster",href:"javascript:void(0)",classes:["icon-button","tooltip","chart_curve","trackster-add"],"data-url":a["data-url"],"action-url":a["action-url"],"new-url":a["new-url"]})))}if(this.model.get("retag_url")&&this.model.get("annotate_url")){var e=$('<div style="float: right;"></div>');e.append($(linkHTMLTemplate({title:"Edit dataset tags",target:"galaxy_main",href:this.model.get("retag_url"),classes:["icon-button","tooltip","tags"]})));e.append($(linkHTMLTemplate({title:"Edit dataset annotation",target:"galaxy_main",href:this.model.get("annotation_url"),classes:["icon-button","tooltip","annotate"]})));d.append(e);d.append('<div style="clear: both"></div>');var c=$('<div class="tag-area" style="display: none">');c.append("<strong>Tags:</strong>");c.append('<div class="tag-elt"></div>');d.append(c);var f=$(('<div id="${dataset_id}-annotation-area" class="annotation-area" style="display: none">'));f.append("<strong>Annotation:</strong>");f.append(('<div id="${dataset_id}-annotation-elt" style="margin: 1px 0px 1px 0px" class="annotation-elt tooltip editable-text" title="Edit dataset annotation"></div>'));d.append(f)}}d.append('<div style="clear: both;"></div>');i.append(d);var j=$("<div/>");if(this.model.get("display_apps")){var k=this.model.get("display_apps"),b=$("<span/>");for(app_name in k){var h=k[app_name],g=app_name+" ";for(location_name in h){g+=linkHTMLTemplate({text:location_name,href:h[location_name].url,target:h[location_name].target})+" "}b.append(g)}j.append(b)}i.append(j)}else{if(this.model.get("for_editing")){i.append(this._render_showParamsAndRerun())}}i.append(this._render_peek())},_render_body:function(){this.log(this+"_render_body");var b=this.model.get("state"),c=this.model.get("for_editing");this.log("state:",b,"for_editing",c);var a=$("<div/>").attr("id","info-"+this.model.get("id")).addClass("historyItemBody").attr("style","display: block");switch(b){case HistoryItem.STATES.NOT_VIEWABLE:this._render_body_not_viewable(a);break;case HistoryItem.STATES.UPLOAD:this._render_body_uploading(a);break;case HistoryItem.STATES.QUEUED:this._render_body_queued(a);break;case HistoryItem.STATES.RUNNING:this._render_body_running(a);break;case HistoryItem.STATES.ERROR:this._render_body_error(a);break;case HistoryItem.STATES.DISCARDED:this._render_body_discarded(a);break;case HistoryItem.STATES.SETTING_METADATA:this._render_body_setting_metadata(a);break;case HistoryItem.STATES.EMPTY:this._render_body_empty(a);break;case HistoryItem.STATES.FAILED_METADATA:this._render_body_failed_metadata(a);break;case HistoryItem.STATES.OK:this._render_body_ok(a);break;default:a.append($('<div>Error: unknown dataset state "'+b+'".</div>'))}a.append('<div style="clear: both"></div>');if(this.model.get("bodyIsShown")===false){a.hide()}return a},_render_hdaSummary:function(){var a=_.template('<span class="<%= dbkey %>"><%= dbkey %></span>',{dbkey:this.model.get("metadata_dbkey")});if(this.model.get("metadata_dbkey")==="?"&&this.model.isEditable()){a=linkHTMLTemplate({text:this.model.get("metadata_dbkey"),href:this.model.get("edit_url"),target:"galaxy_main"})}return(HistoryItemView.TEMPLATES.hdaSummary(_.extend({dbkeyHTML:a},this.model.attributes)))},_render_showParamsAndRerun:function(){var a=$("<div/>");a.append($(linkHTMLTemplate({title:"View details",href:this.model.get("show_params_url"),target:"galaxy_main",classes:["icon-button","tooltip","information"]})));if(this.model.get("for_editing")){a.append($(linkHTMLTemplate({title:"Run this job again",href:this.model.get("rerun_url"),target:"galaxy_main",classes:["icon-button","tooltip","arrow-circle"]})))}return a},_render_downloadLinks:function(){if(this.model.get("purged")){return null}var a=linkHTMLTemplate({title:"Download",href:this.model.get("download_url"),classes:["icon-button","tooltip","disk"]});var d=this.model.get("download_meta_urls");if(!d){return a}var c=$('<div popupmenu="dataset-'+this.model.get("id")+'-popup"></div>');c.append(linkHTMLTemplate({text:"Download Dataset",title:"Download",href:this.model.get("download_url"),classes:["icon-button","tooltip","disk"]}));c.append("<a>Additional Files</a>");for(file_type in d){c.append(linkHTMLTemplate({text:"Download "+file_type,href:d[file_type],classes:["action-button"]}))}var b=$(('<div style="float:left;" class="menubutton split popup" id="dataset-${dataset_id}-popup"></div>'));b.append(a);c.append(b);return c},_render_peek:function(){if(!this.model.get("peek")){return null}return $("<div/>").append($("<pre/>").attr("id","peek"+this.model.get("id")).addClass("peek").append(this.model.get("peek")))},events:{"click .historyItemTitle":"toggleBodyVisibility"},toggleBodyVisibility:function(){this.log(this+".toggleBodyVisibility");this.$el.find(".historyItemBody").toggle()},toString:function(){var a=(this.model)?(this.model+""):("");return"HistoryItemView("+a+")"}});HistoryItemView.TEMPLATES={};HistoryItemView.TEMPLATES.warningMsg=_.template("<div class=warningmessagesmall><strong><%= warning %></strong></div>");HistoryItemView.TEMPLATES.undeleteLink=_.template('This dataset has been deleted. Click <a href="<%= undelete_url %>" class="historyItemUndelete" id="historyItemUndeleter-{{ id }}" target="galaxy_history">here</a> to undelete it.');HistoryItemView.TEMPLATES.purgeLink=_.template(' or <a href="<%= purge_url %>" class="historyItemPurge" id="historyItemPurger-{{ id }}" target="galaxy_history">here</a> to immediately remove it from disk.');HistoryItemView.TEMPLATES.hiddenMsg=_.template('This dataset has been hidden. Click <a href="<%= unhide_url %>" class="historyItemUnhide" id="historyItemUnhider-{{ id }}" target="galaxy_history">here</a> to unhide it.');HistoryItemView.TEMPLATES.hdaSummary=_.template(["<%= misc_blurb %><br />",'format: <span class="<%= data_type %>"><%= data_type %></span>, ',"database: <%= dbkeyHTML %>"].join(""));HistoryItemView.STRINGS={};HistoryItemView.STRINGS.purgedMsg=HistoryItemView.TEMPLATES.warningMsg({warning:"This dataset has been deleted and removed from disk."});var HistoryCollection=Backbone.Collection.extend({model:HistoryItem,toString:function(){return("HistoryCollection()")}});var History=LoggingModel.extend({logger:console,defaults:{id:"",name:"",state:"",state_details:{discarded:0,empty:0,error:0,failed_metadata:0,ok:0,queued:0,running:0,setting_metadata:0,upload:0}},initialize:function(b,a){this.log(this+".initialize",b,a);this.items=new HistoryCollection()},loadDatasetsAsHistoryItems:function(c){var a=this,b=this.get("id"),d=this.get("state_details");_.each(c,function(f,e){a.log("loading dataset: ",f,e);var h=new HistoryItem(_.extend(f,{history_id:b}));a.log("as History:",h);a.items.add(h);var g=f.state;d[g]+=1});this.set("state_details",d);this._stateFromStateDetails();return this},_stateFromStateDetails:function(){this.set("state","");var a=this.get("state_details");if((a.error>0)||(a.failed_metadata>0)){this.set("state",HistoryItem.STATES.ERROR)}else{if((a.running>0)||(a.setting_metadata>0)){this.set("state",HistoryItem.STATES.RUNNING)}else{if(a.queued>0){this.set("state",HistoryItem.STATES.QUEUED)}else{if(a.ok===this.items.length){this.set("state",HistoryItem.STATES.OK)}else{throw ("_stateFromStateDetails: unable to determine history state from state details: "+this.state_details)}}}}return this},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});var HistoryView=LoggingView.extend({logger:console,el:"body.historyPage",initialize:function(){this.log(this+".initialize");this.itemViews=[];var a=this;this.model.items.each(function(c){var b=new HistoryItemView({model:c});a.itemViews.push(b)})},render:function(){this.log(this+".render");var a=$("<div/>");_.each(this.itemViews,function(b){a.prepend(b.render())});this.$el.append(a.children());a.remove()},toString:function(){var a=this.model.get("name")||"";return"HistoryView("+a+")"}});if(window.USE_MOCK_DATA){mockHistory={};mockHistory.data={template:{id:"a799d38679e985db",name:"template",data_type:"fastq",file_size:226297533,genome_build:"?",metadata_data_lines:0,metadata_dbkey:"?",metadata_sequences:0,misc_blurb:"215.8 MB",misc_info:"uploaded fastq file",model_class:"HistoryDatasetAssociation",download_url:"",state:"ok",visible:true,deleted:false,purged:false,hid:0,for_editing:true,undelete_url:"example.com/undelete",purge_url:"example.com/purge",unhide_url:"example.com/unhide",display_url:"example.com/display",edit_url:"example.com/edit",delete_url:"example.com/delete",show_params_url:"example.com/show_params",rerun_url:"example.com/rerun",retag_url:"example.com/retag",annotate_url:"example.com/annotate",peek:['<table cellspacing="0" cellpadding="3"><tr><th>1.QNAME</th><th>2.FLAG</th><th>3.RNAME</th><th>4.POS</th><th>5.MAPQ</th><th>6.CIGAR</th><th>7.MRNM</th><th>8.MPOS</th><th>9.ISIZE</th><th>10.SEQ</th><th>11.QUAL</th><th>12.OPT</th></tr>','<tr><td colspan="100%">@SQ SN:gi|87159884|ref|NC_007793.1| LN:2872769</td></tr>','<tr><td colspan="100%">@PG ID:bwa PN:bwa VN:0.5.9-r16</td></tr>','<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 73 gi|87159884|ref|NC_007793.1| 2720169 37 101M = 2720169 0 NAATATGACATTATTTTCAAAACAGCTGAAAATTTAGACGTACCGATTTATCTACATCCCGCGCCAGTTAACAGTGACATTTATCAATCATACTATAAAGG !!!!!!!!!!$!!!$!!!!!$!!!!!!$!$!$$$!!$!!$!!!!!!!!!!!$!</td></tr>','<tr><td colspan="100%">!!!$!$!$$!!$$!!$!!!!!!!!!!!!!!!!!!!!!!!!!!$!!$!! XT:A:U NM:i:1 SM:i:37 AM:i:0 X0:i:1 X1:i:0 XM:i:1 XO:i:0 XG:i:0 MD:Z:0A100</td></tr>','<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 133 gi|87159884|ref|NC_007793.1| 2720169 0 * = 2720169 0 NAAACTGTGGCTTCGTTNNNNNNNNNNNNNNNGTGANNNNNNNNNNNNNNNNNNNGNNNNNNNNNNNNNNNNNNNNCNAANNNNNNNNNNNNNNNNNNNNN !!!!!!!!!!!!$!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>','<tr><td colspan="100%">!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>',"</table>"].join("")}};_.extend(mockHistory.data,{deleted:_.extend(_.clone(mockHistory.data.template),{deleted:true}),purgedNotDeleted:_.extend(_.clone(mockHistory.data.template),{purged:true}),notvisible:_.extend(_.clone(mockHistory.data.template),{visible:false}),hasDisplayApps:_.extend(_.clone(mockHistory.data.template),{display_apps:{"display in IGB":{Web:"/display_application/63cd3858d057a6d1/igb_bam/Web",Local:"/display_application/63cd3858d057a6d1/igb_bam/Local"}}}),canTrackster:_.extend(_.clone(mockHistory.data.template),{trackster_urls:{"data-url":"example.com/trackster-data","action-url":"example.com/trackster-action","new-url":"example.com/trackster-new"}}),zeroSize:_.extend(_.clone(mockHistory.data.template),{file_size:0}),hasMetafiles:_.extend(_.clone(mockHistory.data.template),{download_meta_urls:{bam_index:"example.com/bam-index"}}),upload:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.UPLOAD}),queued:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.QUEUED}),running:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.RUNNING}),empty:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.EMPTY}),error:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.ERROR}),discarded:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.DISCARDED}),setting_metadata:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.SETTING_METADATA}),failed_metadata:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.FAILED_METADATA})});$(document).ready(function(){mockHistory.items={};mockHistory.views={};for(key in mockHistory.data){mockHistory.items[key]=new HistoryItem(mockHistory.data[key]);mockHistory.items[key].set("name",key);mockHistory.views[key]=new HistoryItemView({model:mockHistory.items[key]});$("body").append(mockHistory.views[key].render())}})};
\ No newline at end of file
+function linkHTMLTemplate(b,a){if(!b){return"<a></a>"}a=a||"a";var c=["<"+a];for(key in b){var d=b[key];if(d===""){continue}switch(key){case"text":continue;case"classes":key="class";d=(b.classes.join)?(b.classes.join(" ")):(b.classes);default:c.push([" ",key,'="',d,'"'].join(""))}}c.push(">");if("text" in b){c.push(b.text)}c.push("</"+a+">");return c.join("")}var HistoryItem=BaseModel.extend(LoggableMixin).extend({defaults:{id:null,name:"",data_type:null,file_size:0,genome_build:null,metadata_data_lines:0,metadata_dbkey:null,metadata_sequences:0,misc_blurb:"",misc_info:"",model_class:"",state:"",deleted:false,purged:false,visible:true,for_editing:true,bodyIsShown:false},initialize:function(){this.log(this+".initialize",this.attributes);this.log("\tparent history_id: "+this.get("history_id"))},isEditable:function(){return(!(this.get("deleted")||this.get("purged")))},hasData:function(){return(this.get("file_size")>0)},toString:function(){var a=this.get("id")||"";if(this.get("name")){a+=':"'+this.get("name")+'"'}return"HistoryItem("+a+")"}});HistoryItem.STATES={NEW:"new",UPLOAD:"upload",QUEUED:"queued",RUNNING:"running",OK:"ok",EMPTY:"empty",ERROR:"error",DISCARDED:"discarded",SETTING_METADATA:"setting_metadata",FAILED_METADATA:"failed_metadata"};var HistoryItemView=BaseView.extend(LoggableMixin).extend({tagName:"div",className:"historyItemContainer",initialize:function(){this.log(this+".initialize:",this,this.model)},render:function(){this.log(this+".model:",this.model);var d=this.model.get("id"),c=this.model.get("state");this.$el.attr("id","historyItemContainer-"+d);var a=$("<div/>").attr("id","historyItem-"+d).addClass("historyItemWrapper").addClass("historyItem").addClass("historyItem-"+c);a.append(this._render_warnings());a.append(this._render_titleBar());this.body=$(this._render_body());a.append(this.body);a.find(".tooltip").tooltip({placement:"bottom"});var b=a.find("[popupmenu]");b.each(function(e,f){f=$(f);make_popupmenu(f)});this.$el.children().remove();return this.$el.append(a)},_render_warnings:function(){return $(jQuery.trim(HistoryItemView.templates.messages(this.model.toJSON())))},_render_titleBar:function(){var a=$('<div class="historyItemTitleBar" style="overflow: hidden"></div>');a.append(this._render_titleButtons());a.append('<span class="state-icon"></span>');a.append(this._render_titleLink());return a},_render_titleButtons:function(){var a=$('<div class="historyItemButtons"></div>'),b=this.model.get("for_editing");if(this.model.get("state")!==HistoryItem.STATES.UPLOAD){a.append(this._render_displayButton());if(b){a.append(this._render_editButton())}}if(b){a.append(this._render_deleteButton())}return a},_render_displayButton:function(){displayBtnData=(this.model.get("purged"))?({title:"Cannot display datasets removed from disk",icon_class:"display",enabled:false,}):({title:"Display data in browser",href:this.model.get("display_url"),target:(this.model.get("for_editing"))?("galaxy_main"):(null),icon_class:"display",});this.displayButton=new IconButtonView({model:new IconButton(displayBtnData)});return this.displayButton.render().$el},_render_editButton:function(){var d=this.model.get("id"),c=this.model.get("purged"),a=this.model.get("deleted");var b={title:"Edit attributes",href:this.model.get("edit_url"),target:"galaxy_main",icon_class:"edit"};if(a||c){b={enabled:false,icon_class:"edit"};b.title=(!c)?("Undelete dataset to edit attributes"):("Cannot edit attributes of datasets removed from disk")}this.editButton=new IconButtonView({model:new IconButton(b)});return this.editButton.render().$el},_render_deleteButton:function(){if(!this.model.get("for_editing")){return null}var a=(this.model.get("delete_url"))?({title:"Delete",href:this.model.get("delete_url"),target:"galaxy_main",id:"historyItemDeleter-"+this.model.get("id"),icon_class:"delete"}):({title:"Dataset is already deleted",icon_class:"delete",enabled:false});this.deleteButton=new IconButtonView({model:new IconButton(a)});return this.deleteButton.render().$el},_render_titleLink:function(){var b=this.model.get("name"),a=this.model.get("hid");title=(a+": "+b);return $(linkHTMLTemplate({href:"javascript:void(0);",text:'<span class="historyItemTitle">'+title+"</span>"}))},_render_body_not_viewable:function(a){a.append($("<div>You do not have permission to view dataset.</div>"))},_render_body_uploading:function(a){a.append($("<div>Dataset is uploading</div>"))},_render_body_queued:function(a){a.append($("<div>Job is waiting to run.</div>"));a.append(this._render_showParamsAndRerun())},_render_body_running:function(a){a.append("<div>Job is currently running.</div>");a.append(this._render_showParamsAndRerun())},_render_body_error:function(a){if(!this.model.get("purged")){a.append($("<div>"+this.model.get("misc_blurb")+"</div>"))}a.append(("An error occurred running this job: <i>"+$.trim(this.model.get("misc_info"))+"</i>"));var b=$(this._render_showParamsAndRerun());if(this.model.get("for_editing")){b.prepend($(linkHTMLTemplate({title:"View or report this error",href:this.model.get("report_errors_url"),target:"galaxy_main",classes:["icon-button","tooltip","bug"]})))}if(this.model.hasData()){b.prepend(this._render_downloadLinks())}a.append(b)},_render_body_discarded:function(a){a.append("<div>The job creating this dataset was cancelled before completion.</div>");a.append(this._render_showParamsAndRerun())},_render_body_setting_metadata:function(a){a.append($("<div>Metadata is being auto-detected.</div>"))},_render_body_empty:function(a){a.append($("<div>No data: <i>"+this.model.get("misc_blurb")+"</i></div>"));a.append(this._render_showParamsAndRerun())},_render_body_failed_metadata:function(b){var c="An error occurred setting the metadata for this dataset.";if(this.model.isEditable()){var a=linkHTMLTemplate({text:"set it manually or retry auto-detection",href:this.model.get("edit_url"),target:"galaxy_main"});c+="You may be able to "+a+"."}b.append($(HistoryItemView.templates.warningMsg({warning:c})));this._render_body_ok(b)},_render_body_ok:function(h){h.append(this._render_hdaSummary());if(this.model.get("misc_info")){h.append($('<div class="info">Info: '+this.model.get("misc_info")+"</div>"))}if(this.model.hasData()){var c=$("<div/>");c.append(this._render_downloadLinks());c.append($(linkHTMLTemplate({title:"View details",href:this.model.get("show_params_url"),target:"galaxy_main",classes:["icon-button","tooltip","information"]})));if(this.model.get("for_editing")){c.append($(linkHTMLTemplate({title:"Run this job again",href:this.model.get("rerun_url"),target:"galaxy_main",classes:["icon-button","tooltip","arrow-circle"]})));if(this.model.get("trackster_urls")){var a=this.model.get("trackster_urls");c.append($(linkHTMLTemplate({title:"View in Trackster",href:"javascript:void(0)",classes:["icon-button","tooltip","chart_curve","trackster-add"],"data-url":a["data-url"],"action-url":a["action-url"],"new-url":a["new-url"]})))}if(this.model.get("retag_url")&&this.model.get("annotate_url")){var d=$('<div style="float: right;"></div>');d.append($(linkHTMLTemplate({title:"Edit dataset tags",target:"galaxy_main",href:this.model.get("retag_url"),classes:["icon-button","tooltip","tags"]})));d.append($(linkHTMLTemplate({title:"Edit dataset annotation",target:"galaxy_main",href:this.model.get("annotation_url"),classes:["icon-button","tooltip","annotate"]})));c.append(d);c.append('<div style="clear: both"></div>');this.tagArea=$('<div class="tag-area" style="display: none">');this.tagArea.append("<strong>Tags:</strong>");this.tagElt=$('<div class="tag-elt"></div>');c.append(this.tagArea.append(this.tagElt));var e=$(('<div id="${dataset_id}-annotation-area" class="annotation-area" style="display: none">'));this.annotationArea=e;e.append("<strong>Annotation:</strong>");this.annotationElem=$('<div id="'+this.model.get("id")+'-annotation-elt" style="margin: 1px 0px 1px 0px" class="annotation-elt tooltip editable-text" title="Edit dataset annotation"></div>');e.append(this.annotationElem);c.append(e)}}c.append('<div style="clear: both;"></div>');h.append(c);var i=$("<div/>");if(this.model.get("display_apps")){var j=this.model.get("display_apps"),b=$("<span/>");for(app_name in j){var g=j[app_name],f=app_name+" ";for(location_name in g){f+=linkHTMLTemplate({text:location_name,href:g[location_name].url,target:g[location_name].target})+" "}b.append(f)}i.append(b)}h.append(i)}else{if(this.model.get("for_editing")){h.append(this._render_showParamsAndRerun())}}h.append(this._render_peek())},_render_body:function(){var b=this.model.get("state"),c=this.model.get("for_editing");var a=$("<div/>").attr("id","info-"+this.model.get("id")).addClass("historyItemBody").attr("style","display: block");switch(b){case HistoryItem.STATES.NOT_VIEWABLE:this._render_body_not_viewable(a);break;case HistoryItem.STATES.UPLOAD:this._render_body_uploading(a);break;case HistoryItem.STATES.QUEUED:this._render_body_queued(a);break;case HistoryItem.STATES.RUNNING:this._render_body_running(a);break;case HistoryItem.STATES.ERROR:this._render_body_error(a);break;case HistoryItem.STATES.DISCARDED:this._render_body_discarded(a);break;case HistoryItem.STATES.SETTING_METADATA:this._render_body_setting_metadata(a);break;case HistoryItem.STATES.EMPTY:this._render_body_empty(a);break;case HistoryItem.STATES.FAILED_METADATA:this._render_body_failed_metadata(a);break;case HistoryItem.STATES.OK:this._render_body_ok(a);break;default:a.append($('<div>Error: unknown dataset state "'+b+'".</div>'))}a.append('<div style="clear: both"></div>');if(this.model.get("bodyIsShown")===false){a.hide()}return a},_render_hdaSummary:function(){var a=_.template('<span class="<%= dbkey %>"><%= dbkey %></span>',{dbkey:this.model.get("metadata_dbkey")});if(this.model.get("metadata_dbkey")==="?"&&this.model.isEditable()){a=linkHTMLTemplate({text:this.model.get("metadata_dbkey"),href:this.model.get("edit_url"),target:"galaxy_main"})}return(HistoryItemView.TEMPLATES.hdaSummary(_.extend({dbkeyHTML:a},this.model.attributes)))},_render_showParamsAndRerun:function(){var a=$("<div/>");a.append($(linkHTMLTemplate({title:"View details",href:this.model.get("show_params_url"),target:"galaxy_main",classes:["icon-button","tooltip","information"]})));if(this.model.get("for_editing")){a.append($(linkHTMLTemplate({title:"Run this job again",href:this.model.get("rerun_url"),target:"galaxy_main",classes:["icon-button","tooltip","arrow-circle"]})))}return a},_render_downloadLinks:function(){if(this.model.get("purged")){return null}var a=linkHTMLTemplate({title:"Download",href:this.model.get("download_url"),classes:["icon-button","tooltip","disk"]});var d=this.model.get("download_meta_urls");if(!d){return a}var c=$('<div popupmenu="dataset-'+this.model.get("id")+'-popup"></div>');c.append(linkHTMLTemplate({text:"Download Dataset",title:"Download",href:this.model.get("download_url"),classes:["icon-button","tooltip","disk"]}));c.append("<a>Additional Files</a>");for(file_type in d){c.append(linkHTMLTemplate({text:"Download "+file_type,href:d[file_type],classes:["action-button"]}))}var b=$(('<div style="float:left;" class="menubutton split popup" id="dataset-${dataset_id}-popup"></div>'));b.append(a);c.append(b);return c},_render_peek:function(){if(!this.model.get("peek")){return null}return $("<div/>").append($("<pre/>").attr("id","peek"+this.model.get("id")).addClass("peek").append(this.model.get("peek")))},events:{"click .historyItemTitle":"toggleBodyVisibility","click a.icon-button.tags":"loadAndDisplayTags","click a.icon-button.annotate":"loadAndDisplayAnnotation"},loadAndDisplayTags:function(b){this.log(this,".loadAndDisplayTags",b);var c=this.tagArea;var a=this.tagElt;if(c.is(":hidden")){if(!a.html()){$.ajax({url:this.model.get("ajax_get_tag_url"),error:function(){alert("Tagging failed")},success:function(d){this.log("tag_elt_html:",d);a.html(d);a.find(".tooltip").tooltip();c.slideDown("fast")}})}else{c.slideDown("fast")}}else{c.slideUp("fast")}return false},loadAndDisplayAnnotation:function(b){this.log(this,".loadAndDisplayAnnotation",b);var d=this.annotationArea,c=this.annotationElem,a=this.model.get("ajax_set_annotation_url");this.log("annotationArea hidden:",d.is(":hidden"));this.log("annotationElem html:",c.html());if(d.is(":hidden")){if(!c.html()){$.ajax({url:this.model.get("ajax_get_annotation_url"),error:function(){alert("Annotations failed")},success:function(e){if(e===""){e="<em>Describe or add notes to dataset</em>"}c.html(e);d.find(".tooltip").tooltip();async_save_text(c.attr("id"),c.attr("id"),a,"new_annotation",18,true,4);d.slideDown("fast")}})}else{d.slideDown("fast")}}else{d.slideUp("fast")}return false},toggleBodyVisibility:function(){this.log(this+".toggleBodyVisibility");this.$el.find(".historyItemBody").toggle()},toString:function(){var a=(this.model)?(this.model+""):("");return"HistoryItemView("+a+")"}});HistoryItemView.TEMPLATES={};HistoryItemView.TEMPLATES.hdaSummary=_.template(["<%= misc_blurb %><br />",'format: <span class="<%= data_type %>"><%= data_type %></span>, ',"database: <%= dbkeyHTML %>"].join(""));HistoryItemView.templates=CompiledTemplateLoader.getTemplates({"common-templates.html":{warningMsg:"template-warningmessagesmall",},"history-templates.html":{messages:"template-history-warning-messages2",hdaSummary:"template-history-hdaSummary"}});var HistoryCollection=Backbone.Collection.extend({model:HistoryItem,toString:function(){return("HistoryCollection()")}});var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",state_details:{discarded:0,empty:0,error:0,failed_metadata:0,ok:0,queued:0,running:0,setting_metadata:0,upload:0}},initialize:function(b,a){this.log(this+".initialize",b,a);this.items=new HistoryCollection()},loadDatasetsAsHistoryItems:function(c){var a=this,b=this.get("id"),d=this.get("state_details");_.each(c,function(f,e){a.log("loading dataset: ",f,e);var h=new HistoryItem(_.extend(f,{history_id:b}));a.log("as History:",h);a.items.add(h);var g=f.state;d[g]+=1});this.set("state_details",d);this._stateFromStateDetails();return this},_stateFromStateDetails:function(){this.set("state","");var a=this.get("state_details");if((a.error>0)||(a.failed_metadata>0)){this.set("state",HistoryItem.STATES.ERROR)}else{if((a.running>0)||(a.setting_metadata>0)){this.set("state",HistoryItem.STATES.RUNNING)}else{if(a.queued>0){this.set("state",HistoryItem.STATES.QUEUED)}else{if(a.ok===this.items.length){this.set("state",HistoryItem.STATES.OK)}else{throw ("_stateFromStateDetails: unable to determine history state from state details: "+this.state_details)}}}}return this},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});var HistoryView=BaseView.extend(LoggableMixin).extend({el:"body.historyPage",initialize:function(){this.log(this+".initialize");this.itemViews=[];var a=this;this.model.items.each(function(c){var b=new HistoryItemView({model:c});a.itemViews.push(b)})},render:function(){this.log(this+".render");var a=$("<div/>");_.each(this.itemViews,function(b){a.prepend(b.render())});this.$el.append(a.children());a.remove()},toString:function(){var a=this.model.get("name")||"";return"HistoryView("+a+")"}});function createMockHistoryData(){mockHistory={};mockHistory.data={template:{id:"a799d38679e985db",name:"template",data_type:"fastq",file_size:226297533,genome_build:"?",metadata_data_lines:0,metadata_dbkey:"?",metadata_sequences:0,misc_blurb:"215.8 MB",misc_info:"uploaded fastq file",model_class:"HistoryDatasetAssociation",download_url:"",state:"ok",visible:true,deleted:false,purged:false,hid:0,for_editing:true,undelete_url:"example.com/undelete",purge_url:"example.com/purge",unhide_url:"example.com/unhide",display_url:"example.com/display",edit_url:"example.com/edit",delete_url:"example.com/delete",show_params_url:"example.com/show_params",rerun_url:"example.com/rerun",retag_url:"example.com/retag",annotate_url:"example.com/annotate",peek:['<table cellspacing="0" cellpadding="3"><tr><th>1.QNAME</th><th>2.FLAG</th><th>3.RNAME</th><th>4.POS</th><th>5.MAPQ</th><th>6.CIGAR</th><th>7.MRNM</th><th>8.MPOS</th><th>9.ISIZE</th><th>10.SEQ</th><th>11.QUAL</th><th>12.OPT</th></tr>','<tr><td colspan="100%">@SQ SN:gi|87159884|ref|NC_007793.1| LN:2872769</td></tr>','<tr><td colspan="100%">@PG ID:bwa PN:bwa VN:0.5.9-r16</td></tr>','<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 73 gi|87159884|ref|NC_007793.1| 2720169 37 101M = 2720169 0 NAATATGACATTATTTTCAAAACAGCTGAAAATTTAGACGTACCGATTTATCTACATCCCGCGCCAGTTAACAGTGACATTTATCAATCATACTATAAAGG !!!!!!!!!!$!!!$!!!!!$!!!!!!$!$!$$$!!$!!$!!!!!!!!!!!$!</td></tr>','<tr><td colspan="100%">!!!$!$!$$!!$$!!$!!!!!!!!!!!!!!!!!!!!!!!!!!$!!$!! XT:A:U NM:i:1 SM:i:37 AM:i:0 X0:i:1 X1:i:0 XM:i:1 XO:i:0 XG:i:0 MD:Z:0A100</td></tr>','<tr><td colspan="100%">HWUSI-EAS664L:15:64HOJAAXX:1:1:13280:968 133 gi|87159884|ref|NC_007793.1| 2720169 0 * = 2720169 0 NAAACTGTGGCTTCGTTNNNNNNNNNNNNNNNGTGANNNNNNNNNNNNNNNNNNNGNNNNNNNNNNNNNNNNNNNNCNAANNNNNNNNNNNNNNNNNNNNN !!!!!!!!!!!!$!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>','<tr><td colspan="100%">!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td></tr>',"</table>"].join("")}};_.extend(mockHistory.data,{deleted:_.extend(_.clone(mockHistory.data.template),{deleted:true}),purgedNotDeleted:_.extend(_.clone(mockHistory.data.template),{purged:true}),notvisible:_.extend(_.clone(mockHistory.data.template),{visible:false}),hasDisplayApps:_.extend(_.clone(mockHistory.data.template),{display_apps:{"display in IGB":{Web:"/display_application/63cd3858d057a6d1/igb_bam/Web",Local:"/display_application/63cd3858d057a6d1/igb_bam/Local"}}}),canTrackster:_.extend(_.clone(mockHistory.data.template),{trackster_urls:{"data-url":"example.com/trackster-data","action-url":"example.com/trackster-action","new-url":"example.com/trackster-new"}}),zeroSize:_.extend(_.clone(mockHistory.data.template),{file_size:0}),hasMetafiles:_.extend(_.clone(mockHistory.data.template),{download_meta_urls:{bam_index:"example.com/bam-index"}}),upload:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.UPLOAD}),queued:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.QUEUED}),running:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.RUNNING}),empty:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.EMPTY}),error:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.ERROR}),discarded:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.DISCARDED}),setting_metadata:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.SETTING_METADATA}),failed_metadata:_.extend(_.clone(mockHistory.data.template),{state:HistoryItem.STATES.FAILED_METADATA})});$(document).ready(function(){mockHistory.items={};mockHistory.views={};for(key in mockHistory.data){mockHistory.items[key]=new HistoryItem(mockHistory.data[key]);mockHistory.items[key].set("name",key);mockHistory.views[key]=new HistoryItemView({model:mockHistory.items[key]});$("body").append(mockHistory.views[key].render())}})};
\ No newline at end of file
diff -r 8b301f6af8340f8cd01022035d5765cf0aba3ede -r 72f1dcdf1c542e6d4689305f5de2077c0bfb4035 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 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 GalaxyPaths=Backbone.Model.extend({defaults:{root_path:"",image_path:""}});var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,tooltip_config:{}}});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(c){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",c.attributes.title).addClass("icon-button menu-button").addClass(c.attributes.icon_class).appendTo(a.$el).click(c.attributes.on_click);if(c.attributes.tooltip_config){b.tooltip(c.attributes.tooltip_config)}});return this}});var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});
\ No newline at end of file
+var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,tooltip_config:{},isMenuButton:true,id:null,href:null,target:null,enabled:true,visible:true}});var IconButtonView=Backbone.View.extend({tagName:"a",className:"icon-button",hrefVoidFn:["javascript",":void(0)"].join(""),fadeSpeed:0,initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){var a=this.model.attributes.icon_class+"_disabled";if(!this.model.attributes.visible){this.$el.fadeOut(this.fadeSpeed)}if(this.model.attributes.enabled){if(this.$el.hasClass(a)){this.$el.removeClass(a)}this.$el.addClass(this.model.attributes.icon_class)}else{this.$el.removeClass(this.model.attributes.icon_class);this.$el.addClass(a)}if(this.model.attributes.isMenuButton){this.$el.addClass("menu-button")}else{this.$el.removeClass("menu-button")}this.$el.data("tooltip",false);this.$el.attr("data-original-title",null);this.$el.removeClass("tooltip");if(this.model.attributes.title){this.$el.attr("title",this.model.attributes.title).addClass("tooltip");this.$el.tooltip(this.model.attributes.tooltip_config)}this.$el.attr("id",(this.model.attributes.id)?(this.model.attributes.id):(null));this.$el.attr("target",(this.model.attributes.target)?(this.model.attributes.target):(null));this.$el.attr("href",(this.model.attributes.href&&!this.model.attributes.on_click)?(this.model.attributes.href):(this.hrefVoidFn));if(this.model.attributes.visible){this.$el.fadeIn(this.fadeSpeed)}return this},events:{click:"click"},click:function(a){if(this.model.attributes.on_click){this.model.attributes.on_click(a);return false}return true}});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(c){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",c.attributes.title).addClass("icon-button menu-button").addClass(c.attributes.icon_class).appendTo(a.$el).click(c.attributes.on_click);if(c.attributes.tooltip_config){b.tooltip(c.attributes.tooltip_config)}});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 GalaxyPaths=Backbone.Model.extend({defaults:{root_path:"",image_path:""}});var GalaxyLocalization=jQuery.extend({},{aliasName:"_l",localizedStrings:{},setLocalizedString:function(b,a){var c=this;var d=function(f,e){if(f!==e){c.localizedStrings[f]=e}};if(jQuery.type(b)==="string"){d(b,a)}else{if(jQuery.type(b)==="object"){jQuery.each(b,function(e,f){d(e,f)})}else{throw ("Localization.setLocalizedString needs either a string or object as the first argument, given: "+b)}}},localize:function(b){try{return this.localizedStrings[b]}catch(a){return b}},toString:function(){return"GalaxyLocalization"}});window[GalaxyLocalization.aliasName]=function(a){return GalaxyLocalization.localize(a)};
\ No newline at end of file
diff -r 8b301f6af8340f8cd01022035d5765cf0aba3ede -r 72f1dcdf1c542e6d4689305f5de2077c0bfb4035 static/scripts/packed/viz/visualization.js
--- a/static/scripts/packed/viz/visualization.js
+++ b/static/scripts/packed/viz/visualization.js
@@ -1,1 +1,1 @@
-var ServerStateDeferred=Backbone.Model.extend({defaults:{ajax_settings:{},interval:1000,success_fn:function(a){return true}},go:function(){var d=$.Deferred(),c=this,f=c.get("ajax_settings"),e=c.get("success_fn"),b=c.get("interval"),a=function(){$.ajax(f).success(function(g){if(e(g)){d.resolve(g)}else{setTimeout(a,b)}})};a();return d}});var CanvasManager=function(a){this.default_font=a!==undefined?a:"9px Monaco, Lucida Console, monospace";this.dummy_canvas=this.new_canvas();this.dummy_context=this.dummy_canvas.getContext("2d");this.dummy_context.font=this.default_font;this.char_width_px=this.dummy_context.measureText("A").width;this.patterns={};this.load_pattern("right_strand","/visualization/strand_right.png");this.load_pattern("left_strand","/visualization/strand_left.png");this.load_pattern("right_strand_inv","/visualization/strand_right_inv.png");this.load_pattern("left_strand_inv","/visualization/strand_left_inv.png")};_.extend(CanvasManager.prototype,{load_pattern:function(a,e){var b=this.patterns,c=this.dummy_context,d=new Image();d.src=galaxy_paths.attributes.image_path+e;d.onload=function(){b[a]=c.createPattern(d,"repeat")}},get_pattern:function(a){return this.patterns[a]},new_canvas:function(){var a=$("<canvas/>")[0];if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(a)}a.manager=this;return a}});var Cache=Backbone.Model.extend({defaults:{num_elements:20,obj_cache:null,key_ary:null},initialize:function(a){this.clear()},get_elt:function(b){var c=this.attributes.obj_cache,d=this.attributes.key_ary,a=d.indexOf(b);if(a!==-1){if(c[b].stale){d.splice(a,1);delete c[b]}else{this.move_key_to_end(b,a)}}return c[b]},set_elt:function(b,d){var e=this.attributes.obj_cache,f=this.attributes.key_ary,c=this.attributes.num_elements;if(!e[b]){if(f.length>=c){var a=f.shift();delete e[a]}f.push(b)}e[b]=d;return d},move_key_to_end:function(b,a){this.attributes.key_ary.splice(a,1);this.attributes.key_ary.push(b)},clear:function(){this.attributes.obj_cache={};this.attributes.key_ary=[]},size:function(){return this.attributes.key_ary.length}});var GenomeDataManager=Cache.extend({defaults:_.extend({},Cache.prototype.defaults,{dataset:null,filters_manager:null,data_url:null,dataset_state_url:null,feature_search_url:null,genome_wide_summary_data:null,data_mode_compatible:function(a,b){return true},can_subset:function(a){return false}}),data_is_ready:function(){var c=this.get("dataset"),b=$.Deferred(),a=new ServerStateDeferred({ajax_settings:{url:this.get("dataset_state_url"),data:{dataset_id:c.id,hda_ldda:c.get("hda_ldda")},dataType:"json"},interval:5000,success_fn:function(d){return d!=="pending"}});$.when(a.go()).then(function(d){b.resolve(d==="ok"||d==="data")});return b},search_features:function(a){var b=this.get("dataset"),c={query:a,dataset_id:b.id,hda_ldda:b.get("hda_ldda")};return $.getJSON(this.get("feature_search_url"),c)},load_data:function(j,h,b,g){var d={chrom:j.get("chrom"),low:j.get("start"),high:j.get("end"),mode:h,resolution:b},e=this.get("dataset");if(e){d.dataset_id=e.id;d.hda_ldda=e.get("hda_ldda")}$.extend(d,g);var k=this.get("filters_manager");if(k){var l=[];var a=k.filters;for(var f=0;f<a.length;f++){l.push(a[f].name)}d.filter_cols=JSON.stringify(l)}var c=this;return $.getJSON(this.get("data_url"),d,function(i){c.set_data(j,i)})},get_data:function(g,f,c,e){var h=this.get_elt(g);if(h&&(is_deferred(h)||this.get("data_mode_compatible")(h,f))){return h}var j=this.get("key_ary"),b=this.get("obj_cache"),k,a;for(var d=0;d<j.length;d++){k=j[d];a=new GenomeRegion({from_str:k});if(a.contains(g)){h=b[k];if(is_deferred(h)||(this.get("data_mode_compatible")(h,f)&&this.get("can_subset")(h))){this.move_key_to_end(k,d);return h}}}h=this.load_data(g,f,c,e);this.set_data(g,h);return h},set_data:function(b,a){this.set_elt(b,a)},DEEP_DATA_REQ:"deep",BROAD_DATA_REQ:"breadth",get_more_data:function(i,h,d,g,e){var k=this.get_elt(i);if(!(k&&this.get("data_mode_compatible")(k,h))){console.log("ERROR: no current data for: ",dataset,i.toString(),h,d,g);return}k.stale=true;var c=i.get("start");if(e===this.DEEP_DATA_REQ){$.extend(g,{start_val:k.data.length+1})}else{if(e===this.BROAD_DATA_REQ){c=(k.max_high?k.max_high:k.data[k.data.length-1][2])+1}}var j=i.copy().set("start",c);var b=this,f=this.load_data(j,h,d,g),a=$.Deferred();this.set_data(i,a);$.when(f).then(function(l){if(l.data){l.data=k.data.concat(l.data);if(l.max_low){l.max_low=k.max_low}if(l.message){l.message=l.message.replace(/[0-9]+/,l.data.length)}}b.set_data(i,l);a.resolve(l)});return a},get_elt:function(a){return Cache.prototype.get_elt.call(this,a.toString())},set_elt:function(b,a){return Cache.prototype.set_elt.call(this,b.toString(),a)}});var ReferenceTrackDataManager=GenomeDataManager.extend({load_data:function(a,d,e,b,c){if(b>1){return{data:null}}return GenomeDataManager.prototype.load_data.call(this,a,d,e,b,c)}});var Genome=Backbone.Model.extend({defaults:{name:null,key:null,chroms_info:null},get_chroms_info:function(){return this.attributes.chroms_info.chrom_info}});var GenomeRegion=Backbone.RelationalModel.extend({defaults:{chrom:null,start:0,end:0,DIF_CHROMS:1000,BEFORE:1001,CONTAINS:1002,OVERLAP_START:1003,OVERLAP_END:1004,CONTAINED_BY:1005,AFTER:1006},initialize:function(b){if(b.from_str){var d=b.from_str.split(":"),c=d[0],a=d[1].split("-");this.set({chrom:c,start:parseInt(a[0],10),end:parseInt(a[1],10)})}},copy:function(){return new GenomeRegion({chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")})},length:function(){return this.get("end")-this.get("start")},toString:function(){return this.get("chrom")+":"+this.get("start")+"-"+this.get("end")},toJSON:function(){return{chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")}},compute_overlap:function(h){var b=this.get("chrom"),g=h.get("chrom"),f=this.get("start"),d=h.get("start"),e=this.get("end"),c=h.get("end"),a;if(b&&g&&b!==g){return this.get("DIF_CHROMS")}if(f<d){if(e<d){a=this.get("BEFORE")}else{if(e<=c){a=this.get("OVERLAP_START")}else{a=this.get("CONTAINS")}}}else{if(f>c){a=this.get("AFTER")}else{if(e<=c){a=this.get("CONTAINED_BY")}else{a=this.get("OVERLAP_END")}}}return a},contains:function(a){return this.compute_overlap(a)===this.get("CONTAINS")},overlaps:function(a){return _.intersection([this.compute_overlap(a)],[this.get("DIF_CHROMS"),this.get("BEFORE"),this.get("AFTER")]).length===0}});var GenomeRegionCollection=Backbone.Collection.extend({model:GenomeRegion});var BrowserBookmark=Backbone.RelationalModel.extend({defaults:{region:null,note:""},relations:[{type:Backbone.HasOne,key:"region",relatedModel:"GenomeRegion"}]});var BrowserBookmarkCollection=Backbone.Collection.extend({model:BrowserBookmark});var GenomeWideBigWigData=Backbone.Model.extend({defaults:{data:null,min:0,max:0},initialize:function(b){var a=_.flatten(_.map(this.get("data"),function(c){if(c.data.length!==0){return _.map(c.data,function(d){return d[1]})}else{return 0}}));this.set("max",_.max(a));this.set("min",_.min(a))}});var GenomeWideSummaryTreeData=Backbone.RelationalModel.extend({defaults:{data:null,min:0,max:0},initialize:function(b){var a=_.max(this.get("data"),function(c){if(!c||typeof c==="string"){return 0}return c[1]});this.attributes.max=(a&&typeof a!=="string"?a[1]:0)}});var BackboneTrack=Dataset.extend({initialize:function(a){this.set("id",a.dataset_id);var c=this.get("genome_wide_data");if(c){var b=(this.get("track_type")==="LineTrack"?GenomeWideBigWigData:GenomeWideSummaryTreeData);this.set("genome_wide_data",new b(c))}}});var Visualization=Backbone.RelationalModel.extend({defaults:{id:"",title:"",type:"",dbkey:"",tracks:null},relations:[{type:Backbone.HasMany,key:"tracks",relatedModel:"BackboneTrack"}],url:function(){return galaxy_paths.get("visualization_url")},save:function(){return $.ajax({url:this.url(),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(this)}})}});var GenomeVisualization=Visualization.extend({defaults:_.extend({},Visualization.prototype.defaults,{bookmarks:null,viewport:null})});var TrackConfig=Backbone.Model.extend({});var CircsterDataLayout=Backbone.Model.extend({defaults:{genome:null,dataset:null,total_gap:null},chroms_layout:function(){var b=this.attributes.genome.get_chroms_info(),d=d3.layout.pie().value(function(f){return f.len}).sort(null),e=d(b),a=this.attributes.total_gap/b.length,c=_.map(e,function(h,g){var f=h.endAngle-a;h.endAngle=(f>h.startAngle?f:h.startAngle);return h});return c},chrom_data_layout:function(d,c,b,e,a){},genome_data_layout:function(){var b=this,a=this.chroms_layout(),f=this.get("track").get("genome_wide_data"),e=this.get("radius_start"),c=this.get("radius_end"),d=_.zip(a,f.get("data")),g=_.map(d,function(i){var j=i[0],h=i[1];return b.chrom_data_layout(j,h,e,c,f.get("min"),f.get("max"))});return g}});var CircsterSummaryTreeLayout=CircsterDataLayout.extend({chrom_data_layout:function(k,b,h,g,d,i){if(!b||typeof b==="string"){return null}var e=b[0],j=b[3],c=d3.scale.linear().domain([d,i]).range([h,g]),f=d3.layout.pie().value(function(l){return j}).startAngle(k.startAngle).endAngle(k.endAngle),a=f(e);_.each(e,function(l,m){a[m].outerRadius=c(l[1])});return a}});var CircsterBigWigLayout=CircsterDataLayout.extend({chrom_data_layout:function(j,b,h,g,d,i){var e=b.data;if(e.length===0){return}var c=d3.scale.linear().domain([d,i]).range([h,g]),f=d3.layout.pie().value(function(l,k){if(k+1===e.length){return 0}return e[k+1][0]-e[k][0]}).startAngle(j.startAngle).endAngle(j.endAngle),a=f(e);_.each(e,function(k,l){a[l].outerRadius=c(k[1])});return a}});var CircsterView=Backbone.View.extend({className:"circster",initialize:function(a){this.width=a.width;this.height=a.height;this.total_gap=a.total_gap;this.genome=a.genome;this.radius_start=a.radius_start;this.dataset_arc_height=a.dataset_arc_height;this.track_gap=5},render:function(){var b=this,c=this.dataset_arc_height;var a=d3.select(b.$el[0]).append("svg").attr("width",b.width).attr("height",b.height).append("g").attr("transform","translate("+b.width/2+","+b.height/2+")");this.model.get("tracks").each(function(f,l){var g=f.get("genome_wide_data"),k=b.radius_start+l*(c+b.track_gap),h=(g instanceof GenomeWideBigWigData?CircsterBigWigLayout:CircsterSummaryTreeLayout),n=new h({track:f,radius_start:k,radius_end:k+c,genome:b.genome,total_gap:b.total_gap}),d=n.chroms_layout(),i=n.genome_data_layout();var o=a.append("g").attr("id","inner-arc"),m=d3.svg.arc().innerRadius(k).outerRadius(k+c),e=o.selectAll("#inner-arc>path").data(d).enter().append("path").attr("d",m).style("stroke","#ccc").style("fill","#ccc").append("title").text(function(q){return q.data.chrom});var p=f.get("prefs"),j=p.block_color;_.each(i,function(q){if(!q){return}var t=a.append("g"),s=d3.svg.arc().innerRadius(k),r=t.selectAll("path").data(q).enter().append("path").attr("d",s).style("stroke",j).style("fill",j)})})}});var TrackBrowserRouter=Backbone.Router.extend({initialize:function(b){this.view=b.view;this.route(/([\w]+)$/,"change_location");this.route(/([\w]+\:[\d,]+-[\d,]+)$/,"change_location");var a=this;a.view.on("navigate",function(c){a.navigate(c)})},change_location:function(a){this.view.go_to(a)}});var add_datasets=function(a,c,b){$.ajax({url:a,data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(d){show_modal("Select datasets for new tracks",d,{Cancel:function(){hide_modal()},Add:function(){var e=[];$("input[name=id]:checked,input[name=ldda_ids]:checked").each(function(){var f,g=$(this).val();if($(this).attr("name")==="id"){f={hda_id:g}}else{f={ldda_id:g}}e[e.length]=$.ajax({url:c,data:f,dataType:"json"})});$.when.apply($,e).then(function(){var f=(arguments[0] instanceof Array?$.map(arguments,function(g){return g[0]}):[arguments[0]]);b(f)});hide_modal()}})}})};
\ No newline at end of file
+var ServerStateDeferred=Backbone.Model.extend({defaults:{ajax_settings:{},interval:1000,success_fn:function(a){return true}},go:function(){var d=$.Deferred(),c=this,f=c.get("ajax_settings"),e=c.get("success_fn"),b=c.get("interval"),a=function(){$.ajax(f).success(function(g){if(e(g)){d.resolve(g)}else{setTimeout(a,b)}})};a();return d}});var CanvasManager=function(a){this.default_font=a!==undefined?a:"9px Monaco, Lucida Console, monospace";this.dummy_canvas=this.new_canvas();this.dummy_context=this.dummy_canvas.getContext("2d");this.dummy_context.font=this.default_font;this.char_width_px=this.dummy_context.measureText("A").width;this.patterns={};this.load_pattern("right_strand","/visualization/strand_right.png");this.load_pattern("left_strand","/visualization/strand_left.png");this.load_pattern("right_strand_inv","/visualization/strand_right_inv.png");this.load_pattern("left_strand_inv","/visualization/strand_left_inv.png")};_.extend(CanvasManager.prototype,{load_pattern:function(a,e){var b=this.patterns,c=this.dummy_context,d=new Image();d.src=galaxy_paths.attributes.image_path+e;d.onload=function(){b[a]=c.createPattern(d,"repeat")}},get_pattern:function(a){return this.patterns[a]},new_canvas:function(){var a=$("<canvas/>")[0];if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(a)}a.manager=this;return a}});var Cache=Backbone.Model.extend({defaults:{num_elements:20,obj_cache:null,key_ary:null},initialize:function(a){this.clear()},get_elt:function(b){var c=this.attributes.obj_cache,d=this.attributes.key_ary,a=d.indexOf(b);if(a!==-1){if(c[b].stale){d.splice(a,1);delete c[b]}else{this.move_key_to_end(b,a)}}return c[b]},set_elt:function(b,d){var e=this.attributes.obj_cache,f=this.attributes.key_ary,c=this.attributes.num_elements;if(!e[b]){if(f.length>=c){var a=f.shift();delete e[a]}f.push(b)}e[b]=d;return d},move_key_to_end:function(b,a){this.attributes.key_ary.splice(a,1);this.attributes.key_ary.push(b)},clear:function(){this.attributes.obj_cache={};this.attributes.key_ary=[]},size:function(){return this.attributes.key_ary.length}});var GenomeDataManager=Cache.extend({defaults:_.extend({},Cache.prototype.defaults,{dataset:null,filters_manager:null,data_url:null,dataset_state_url:null,feature_search_url:null,genome_wide_summary_data:null,data_mode_compatible:function(a,b){return true},can_subset:function(a){return false}}),data_is_ready:function(){var c=this.get("dataset"),b=$.Deferred(),a=new ServerStateDeferred({ajax_settings:{url:this.get("dataset_state_url"),data:{dataset_id:c.id,hda_ldda:c.get("hda_ldda")},dataType:"json"},interval:5000,success_fn:function(d){return d!=="pending"}});$.when(a.go()).then(function(d){b.resolve(d==="ok"||d==="data")});return b},search_features:function(a){var b=this.get("dataset"),c={query:a,dataset_id:b.id,hda_ldda:b.get("hda_ldda")};return $.getJSON(this.get("feature_search_url"),c)},load_data:function(j,h,b,g){var d={chrom:j.get("chrom"),low:j.get("start"),high:j.get("end"),mode:h,resolution:b},e=this.get("dataset");if(e){d.dataset_id=e.id;d.hda_ldda=e.get("hda_ldda")}$.extend(d,g);var k=this.get("filters_manager");if(k){var l=[];var a=k.filters;for(var f=0;f<a.length;f++){l.push(a[f].name)}d.filter_cols=JSON.stringify(l)}var c=this;return $.getJSON(this.get("data_url"),d,function(i){c.set_data(j,i)})},get_data:function(g,f,c,e){var h=this.get_elt(g);if(h&&(is_deferred(h)||this.get("data_mode_compatible")(h,f))){return h}var j=this.get("key_ary"),b=this.get("obj_cache"),k,a;for(var d=0;d<j.length;d++){k=j[d];a=new GenomeRegion({from_str:k});if(a.contains(g)){h=b[k];if(is_deferred(h)||(this.get("data_mode_compatible")(h,f)&&this.get("can_subset")(h))){this.move_key_to_end(k,d);return h}}}h=this.load_data(g,f,c,e);this.set_data(g,h);return h},set_data:function(b,a){this.set_elt(b,a)},DEEP_DATA_REQ:"deep",BROAD_DATA_REQ:"breadth",get_more_data:function(i,h,d,g,e){var k=this.get_elt(i);if(!(k&&this.get("data_mode_compatible")(k,h))){console.log("ERROR: no current data for: ",dataset,i.toString(),h,d,g);return}k.stale=true;var c=i.get("start");if(e===this.DEEP_DATA_REQ){$.extend(g,{start_val:k.data.length+1})}else{if(e===this.BROAD_DATA_REQ){c=(k.max_high?k.max_high:k.data[k.data.length-1][2])+1}}var j=i.copy().set("start",c);var b=this,f=this.load_data(j,h,d,g),a=$.Deferred();this.set_data(i,a);$.when(f).then(function(l){if(l.data){l.data=k.data.concat(l.data);if(l.max_low){l.max_low=k.max_low}if(l.message){l.message=l.message.replace(/[0-9]+/,l.data.length)}}b.set_data(i,l);a.resolve(l)});return a},get_elt:function(a){return Cache.prototype.get_elt.call(this,a.toString())},set_elt:function(b,a){return Cache.prototype.set_elt.call(this,b.toString(),a)}});var ReferenceTrackDataManager=GenomeDataManager.extend({load_data:function(a,d,e,b,c){if(b>1){return{data:null}}return GenomeDataManager.prototype.load_data.call(this,a,d,e,b,c)}});var Genome=Backbone.Model.extend({defaults:{name:null,key:null,chroms_info:null},get_chroms_info:function(){return this.attributes.chroms_info.chrom_info}});var GenomeRegion=Backbone.RelationalModel.extend({defaults:{chrom:null,start:0,end:0,DIF_CHROMS:1000,BEFORE:1001,CONTAINS:1002,OVERLAP_START:1003,OVERLAP_END:1004,CONTAINED_BY:1005,AFTER:1006},initialize:function(b){if(b.from_str){var d=b.from_str.split(":"),c=d[0],a=d[1].split("-");this.set({chrom:c,start:parseInt(a[0],10),end:parseInt(a[1],10)})}},copy:function(){return new GenomeRegion({chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")})},length:function(){return this.get("end")-this.get("start")},toString:function(){return this.get("chrom")+":"+this.get("start")+"-"+this.get("end")},toJSON:function(){return{chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")}},compute_overlap:function(h){var b=this.get("chrom"),g=h.get("chrom"),f=this.get("start"),d=h.get("start"),e=this.get("end"),c=h.get("end"),a;if(b&&g&&b!==g){return this.get("DIF_CHROMS")}if(f<d){if(e<d){a=this.get("BEFORE")}else{if(e<=c){a=this.get("OVERLAP_START")}else{a=this.get("CONTAINS")}}}else{if(f>c){a=this.get("AFTER")}else{if(e<=c){a=this.get("CONTAINED_BY")}else{a=this.get("OVERLAP_END")}}}return a},contains:function(a){return this.compute_overlap(a)===this.get("CONTAINS")},overlaps:function(a){return _.intersection([this.compute_overlap(a)],[this.get("DIF_CHROMS"),this.get("BEFORE"),this.get("AFTER")]).length===0}});var GenomeRegionCollection=Backbone.Collection.extend({model:GenomeRegion});var BrowserBookmark=Backbone.RelationalModel.extend({defaults:{region:null,note:""},relations:[{type:Backbone.HasOne,key:"region",relatedModel:"GenomeRegion"}]});var BrowserBookmarkCollection=Backbone.Collection.extend({model:BrowserBookmark});var GenomeWideBigWigData=Backbone.Model.extend({defaults:{data:null,min:0,max:0},initialize:function(b){var a=_.flatten(_.map(this.get("data"),function(c){if(c.data.length!==0){return _.map(c.data,function(d){return d[1]})}else{return 0}}));this.set("max",_.max(a));this.set("min",_.min(a))}});var GenomeWideSummaryTreeData=Backbone.RelationalModel.extend({defaults:{data:null,min:0,max:0},initialize:function(b){var a=_.max(this.get("data"),function(c){if(!c||typeof c==="string"){return 0}return c[1]});this.attributes.max=(a&&typeof a!=="string"?a[1]:0)}});var BackboneTrack=Dataset.extend({initialize:function(a){this.set("id",a.dataset_id);var c=this.get("genome_wide_data");if(c){var b=(this.get("track_type")==="LineTrack"?GenomeWideBigWigData:GenomeWideSummaryTreeData);this.set("genome_wide_data",new b(c))}}});var Visualization=Backbone.RelationalModel.extend({defaults:{id:"",title:"",type:"",dbkey:"",tracks:null},relations:[{type:Backbone.HasMany,key:"tracks",relatedModel:"BackboneTrack"}],url:function(){return galaxy_paths.get("visualization_url")},save:function(){return $.ajax({url:this.url(),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(this)}})}});var GenomeVisualization=Visualization.extend({defaults:_.extend({},Visualization.prototype.defaults,{bookmarks:null,viewport:null})});var TrackConfig=Backbone.Model.extend({});var CircsterDataLayout=Backbone.Model.extend({defaults:{genome:null,dataset:null,total_gap:null},chroms_layout:function(){var b=this.attributes.genome.get_chroms_info(),d=d3.layout.pie().value(function(f){return f.len}).sort(null),e=d(b),a=this.attributes.total_gap/b.length,c=_.map(e,function(h,g){var f=h.endAngle-a;h.endAngle=(f>h.startAngle?f:h.startAngle);return h});return c},chrom_data_layout:function(d,c,b,e,a){},genome_data_layout:function(){var b=this,a=this.chroms_layout(),f=this.get("track").get("genome_wide_data"),e=this.get("radius_start"),c=this.get("radius_end"),d=_.zip(a,f.get("data")),g=_.map(d,function(i){var j=i[0],h=i[1];return b.chrom_data_layout(j,h,e,c,f.get("min"),f.get("max"))});return g}});var CircsterSummaryTreeLayout=CircsterDataLayout.extend({chrom_data_layout:function(k,b,h,g,d,i){if(!b||typeof b==="string"){return null}var e=b[0],j=b[3],c=d3.scale.linear().domain([d,i]).range([h,g]),f=d3.layout.pie().value(function(l){return j}).startAngle(k.startAngle).endAngle(k.endAngle),a=f(e);_.each(e,function(l,m){a[m].outerRadius=c(l[1])});return a}});var CircsterBigWigLayout=CircsterDataLayout.extend({chrom_data_layout:function(j,b,h,g,d,i){var e=b.data;if(e.length===0){return}var c=d3.scale.linear().domain([d,i]).range([h,g]),f=d3.layout.pie().value(function(l,k){if(k+1===e.length){return 0}return e[k+1][0]-e[k][0]}).startAngle(j.startAngle).endAngle(j.endAngle),a=f(e);_.each(e,function(k,l){a[l].outerRadius=c(k[1])});return a}});var CircsterView=Backbone.View.extend({className:"circster",initialize:function(a){this.total_gap=a.total_gap;this.genome=a.genome;this.dataset_arc_height=a.dataset_arc_height;this.track_gap=5},render:function(){var c=this,e=this.dataset_arc_height,f=c.$el.width(),a=c.$el.height(),d=(Math.min(f,a)/2-this.model.get("tracks").length*(this.dataset_arc_height+this.track_gap));var b=d3.select(c.$el[0]).append("svg").attr("width",f).attr("height",a).attr("pointer-events","all").append("svg:g").call(d3.behavior.zoom().on("zoom",function(){b.attr("transform","translate("+d3.event.translate+") scale("+d3.event.scale+")")})).attr("transform","translate("+f/2+","+a/2+")").append("svg:g");this.model.get("tracks").each(function(i,o){var j=i.get("genome_wide_data"),n=d+o*(e+c.track_gap),k=(j instanceof GenomeWideBigWigData?CircsterBigWigLayout:CircsterSummaryTreeLayout),q=new k({track:i,radius_start:n,radius_end:n+e,genome:c.genome,total_gap:c.total_gap}),g=q.chroms_layout(),l=q.genome_data_layout();var r=b.append("g").attr("id","inner-arc"),p=d3.svg.arc().innerRadius(n).outerRadius(n+e),h=r.selectAll("#inner-arc>path").data(g).enter().append("path").attr("d",p).style("stroke","#ccc").style("fill","#ccc").append("title").text(function(t){return t.data.chrom});var s=i.get("prefs"),m=s.block_color;_.each(l,function(t){if(!t){return}var w=b.append("g"),v=d3.svg.arc().innerRadius(n),u=w.selectAll("path").data(t).enter().append("path").attr("d",v).style("stroke",m).style("fill",m)})})}});var TrackBrowserRouter=Backbone.Router.extend({initialize:function(b){this.view=b.view;this.route(/([\w]+)$/,"change_location");this.route(/([\w]+\:[\d,]+-[\d,]+)$/,"change_location");var a=this;a.view.on("navigate",function(c){a.navigate(c)})},change_location:function(a){this.view.go_to(a)}});var add_datasets=function(a,c,b){$.ajax({url:a,data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(d){show_modal("Select datasets for new tracks",d,{Cancel:function(){hide_modal()},Add:function(){var e=[];$("input[name=id]:checked,input[name=ldda_ids]:checked").each(function(){var f,g=$(this).val();if($(this).attr("name")==="id"){f={hda_id:g}}else{f={ldda_id:g}}e[e.length]=$.ajax({url:c,data:f,dataType:"json"})});$.when.apply($,e).then(function(){var f=(arguments[0] instanceof Array?$.map(arguments,function(g){return g[0]}):[arguments[0]]);b(f)});hide_modal()}})}})};
\ 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: dannon: Enhance replace_big_select_inputs to accept a list of elements instead of automatically grabbing all selects.
by Bitbucket 12 Sep '12
by Bitbucket 12 Sep '12
12 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8b301f6af834/
changeset: 8b301f6af834
user: dannon
date: 2012-09-12 18:59:19
summary: Enhance replace_big_select_inputs to accept a list of elements instead of automatically grabbing all selects.
affected #: 1 file
diff -r 59e3badd56fd2805b9f89f9b5d2f7dcf6189e74c -r 8b301f6af8340f8cd01022035d5765cf0aba3ede static/scripts/galaxy.base.js
--- a/static/scripts/galaxy.base.js
+++ b/static/scripts/galaxy.base.js
@@ -178,7 +178,7 @@
}
// Replace select box with a text input box + autocomplete.
-function replace_big_select_inputs(min_length, max_length) {
+function replace_big_select_inputs(min_length, max_length, select_elts) {
// To do replace, jQuery's autocomplete plugin must be loaded.
if (!jQuery().autocomplete) {
return;
@@ -191,8 +191,10 @@
if (max_length === undefined) {
max_length = 3000;
}
+
+ var select_elts = select_elts || $('select');
- $('select').each( function() {
+ select_elts.each( function() {
var select_elt = $(this);
// Make sure that options is within range.
var num_options = select_elt.find('option').length;
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: dannon: Cleanup imports, fix reference to 'libraries' in example.
by Bitbucket 12 Sep '12
by Bitbucket 12 Sep '12
12 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/59e3badd56fd/
changeset: 59e3badd56fd
user: dannon
date: 2012-09-12 18:57:42
summary: Cleanup imports, fix reference to 'libraries' in example.
affected #: 1 file
diff -r 81529070d60df960ae226d7d31a422b6f8febb0c -r 59e3badd56fd2805b9f89f9b5d2f7dcf6189e74c lib/galaxy/web/api/history_contents.py
--- a/lib/galaxy/web/api/history_contents.py
+++ b/lib/galaxy/web/api/history_contents.py
@@ -3,8 +3,7 @@
"""
import logging
from galaxy import web
-from galaxy.web.base.controller import *
-from galaxy.model.orm import *
+from galaxy.web.base.controller import BaseAPIController, url_for, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesLibraryMixin, UsesLibraryMixinItems
import pkg_resources
pkg_resources.require( "Routes" )
@@ -73,7 +72,7 @@
@web.expose_api
def create( self, trans, history_id, payload, **kwd ):
"""
- POST /api/libraries/{encoded_history_id}/contents
+ POST /api/histories/{encoded_history_id}/contents
Creates a new history content item (file, aka HistoryDatasetAssociation).
"""
from_ld_id = payload.get( 'from_ld_id', None )
@@ -99,3 +98,4 @@
# TODO: implement other "upload" methods here.
trans.response.status = 403
return "Not implemented."
+
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: dannon: Whitespace cleanup, remove unused sa_session reference.
by Bitbucket 12 Sep '12
by Bitbucket 12 Sep '12
12 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/81529070d60d/
changeset: 81529070d60d
user: dannon
date: 2012-09-12 18:55:01
summary: Whitespace cleanup, remove unused sa_session reference.
affected #: 1 file
diff -r 93a04e500eb464b7dd7c0dba565b60661307e94b -r 81529070d60df960ae226d7d31a422b6f8febb0c lib/galaxy/web/framework/__init__.py
--- a/lib/galaxy/web/framework/__init__.py
+++ b/lib/galaxy/web/framework/__init__.py
@@ -62,7 +62,7 @@
"""
func.exposed = True
return func
-
+
def json( func ):
@wraps(func)
def decorator( self, trans, *args, **kwargs ):
@@ -127,8 +127,8 @@
trans.set_user( provided_key.user )
if trans.request.body:
def extract_payload_from_request(trans, func, kwargs):
- content_type = trans.request.headers['content-type']
- if content_type.startswith('application/x-www-form-urlencoded') or content_type.startswith('multipart/form-data'):
+ content_type = trans.request.headers['content-type']
+ if content_type.startswith('application/x-www-form-urlencoded') or content_type.startswith('multipart/form-data'):
# If the content type is a standard type such as multipart/form-data, the wsgi framework parses the request body
# and loads all field values into kwargs. However, kwargs also contains formal method parameters etc. which
# are not a part of the request body. This is a problem because it's not possible to differentiate between values
@@ -136,7 +136,7 @@
# in the payload. Therefore, the decorated method's formal arguments are discovered through reflection and removed from
# the payload dictionary. This helps to prevent duplicate argument conflicts in downstream methods.
payload = kwargs.copy()
- named_args, _, _, _ = inspect.getargspec(func)
+ named_args, _, _, _ = inspect.getargspec(func)
for arg in named_args:
payload.pop(arg, None)
else:
@@ -145,7 +145,7 @@
# such as multipart/form-data. Leaving it as is for backward compatibility, just in case.
payload = util.recursively_stringify_dictionary_keys( simplejson.loads( trans.request.body ) )
return payload
- try:
+ try:
kwargs['payload'] = extract_payload_from_request(trans, func, kwargs)
except ValueError:
error_status = '400 Bad Request'
@@ -210,7 +210,7 @@
def form( *args, **kwargs ):
return FormBuilder( *args, **kwargs )
-
+
class WebApplication( base.WebApplication ):
def __init__( self, galaxy_app, session_cookie='galaxysession' ):
base.WebApplication.__init__( self )
@@ -322,7 +322,7 @@
except:
event.user = None
try:
- event.session_id = self.galaxy_session.id
+ event.session_id = self.galaxy_session.id
except:
event.session_id = None
self.sa_session.add( event )
@@ -339,23 +339,22 @@
return None
def set_cookie( self, value, name='galaxysession', path='/', age=90, version='1' ):
"""Convenience method for setting a session cookie"""
- # The galaxysession cookie value must be a high entropy 128 bit random number encrypted
+ # The galaxysession cookie value must be a high entropy 128 bit random number encrypted
# using a server secret key. Any other value is invalid and could pose security issues.
self.response.cookies[name] = value
self.response.cookies[name]['path'] = path
self.response.cookies[name]['max-age'] = 3600 * 24 * age # 90 days
tstamp = time.localtime ( time.time() + 3600 * 24 * age )
- self.response.cookies[name]['expires'] = time.strftime( '%a, %d-%b-%Y %H:%M:%S GMT', tstamp )
+ self.response.cookies[name]['expires'] = time.strftime( '%a, %d-%b-%Y %H:%M:%S GMT', tstamp )
self.response.cookies[name]['version'] = version
def _ensure_valid_session( self, session_cookie, create=True):
"""
Ensure that a valid Galaxy session exists and is available as
trans.session (part of initialization)
-
+
Support for universe_session and universe_user cookies has been
removed as of 31 Oct 2008.
"""
- sa_session = self.sa_session
# Try to load an existing session
secure_id = self.get_cookie( name=session_cookie )
galaxy_session = None
@@ -369,7 +368,7 @@
# Decode the cookie value to get the session_key
session_key = self.security.decode_guid( secure_id )
try:
- # Make sure we have a valid UTF-8 string
+ # Make sure we have a valid UTF-8 string
session_key = session_key.encode( 'utf8' )
except UnicodeDecodeError:
# We'll end up creating a new galaxy_session
@@ -384,7 +383,7 @@
if self.app.config.use_remote_user:
assert "HTTP_REMOTE_USER" in self.environ, \
"use_remote_user is set but no HTTP_REMOTE_USER variable"
- remote_user_email = self.environ[ 'HTTP_REMOTE_USER' ]
+ remote_user_email = self.environ[ 'HTTP_REMOTE_USER' ]
if galaxy_session:
# An existing session, make sure correct association exists
if galaxy_session.user is None:
@@ -429,13 +428,13 @@
# FIXME: If prev_session is a proper relation this would not
# be needed.
if prev_galaxy_session:
- self.sa_session.add( prev_galaxy_session )
+ self.sa_session.add( prev_galaxy_session )
self.sa_session.flush()
# If the old session was invalid, get a new history with our new session
if invalidate_existing_session:
self.new_history()
def _ensure_logged_in_user( self, environ, session_cookie ):
- # The value of session_cookie can be one of
+ # The value of session_cookie can be one of
# 'galaxysession' or 'galaxycommunitysession'
# Currently this method does nothing unless session_cookie is 'galaxysession'
if session_cookie == 'galaxysession' and self.galaxy_session.user is None:
@@ -469,7 +468,7 @@
host = None
if host in UCSC_SERVERS:
return
- external_display_path = url_for( controller='dataset', action='display_application' )
+ external_display_path = url_for( controller='dataset', action='display_application' )
if self.request.path.startswith( external_display_path ):
request_path_split = external_display_path.split( '/' )
try:
@@ -484,13 +483,13 @@
Create a new GalaxySession for this request, possibly with a connection
to a previous session (in `prev_galaxy_session`) and an existing user
(in `user_for_new_session`).
-
+
Caller is responsible for flushing the returned session.
"""
session_key = self.security.get_new_guid()
galaxy_session = self.app.model.GalaxySession(
session_key=session_key,
- is_valid=True,
+ is_valid=True,
remote_host = self.request.remote_host,
remote_addr = self.request.remote_addr,
referer = self.request.headers.get( 'Referer', None ) )
@@ -507,7 +506,7 @@
"""
if not self.app.config.use_remote_user:
return None
-
+
user = self.sa_session.query( self.app.model.User ) \
.filter( self.app.model.User.table.c.email==remote_user_email ) \
.first()
@@ -555,7 +554,7 @@
Login a new user (possibly newly created)
- create a new session
- associate new session with user
- - if old session had a history and it was not associated with a user, associate it with the new session,
+ - if old session had a history and it was not associated with a user, associate it with the new session,
otherwise associate the current session's history with the user
- add the disk usage of the current session to the user's total disk usage
"""
@@ -637,7 +636,7 @@
return self.galaxy_session
def get_history( self, create=False ):
"""
- Load the current history, creating a new one only if there is not
+ Load the current history, creating a new one only if there is not
current history and we're told to create"
"""
history = self.galaxy_session.current_history
@@ -709,7 +708,7 @@
return rval
def set_message( self, message, type=None ):
"""
- Convenience method for setting the 'message' and 'message_type'
+ Convenience method for setting the 'message' and 'message_type'
element of the template context.
"""
self.template_context['message'] = message
@@ -724,11 +723,11 @@
def show_message( self, message, type='info', refresh_frames=[], cont=None, use_panels=False, active_view="" ):
"""
Convenience method for displaying a simple page with a single message.
-
+
`type`: one of "error", "warning", "info", or "done"; determines the
type of dialog box and icon displayed with the message
-
- `refresh_frames`: names of frames in the interface that should be
+
+ `refresh_frames`: names of frames in the interface that should be
refreshed when the message is displayed
"""
return self.fill_template( "message.mako", status=type, message=message, refresh_frames=refresh_frames, cont=cont, use_panels=use_panels, active_view=active_view )
@@ -752,7 +751,7 @@
Convenience method for displaying a simple page with a single HTML
form.
"""
- return self.fill_template( template, form=form, header=header, use_panels=( form.use_panels or use_panels ),
+ return self.fill_template( template, form=form, header=header, use_panels=( form.use_panels or use_panels ),
active_view=active_view )
def fill_template(self, filename, **kwargs):
"""
@@ -764,19 +763,19 @@
if filename.endswith( ".mako" ):
return self.fill_template_mako( filename, **kwargs )
else:
- template = Template( file=os.path.join(self.app.config.template_path, filename),
+ template = Template( file=os.path.join(self.app.config.template_path, filename),
searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)] )
return str( template )
def fill_template_mako( self, filename, **kwargs ):
template = self.webapp.mako_template_lookup.get_template( filename )
- template.output_encoding = 'utf-8'
+ template.output_encoding = 'utf-8'
data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
data.update( self.template_context )
data.update( kwargs )
return template.render( **data )
def stream_template_mako( self, filename, **kwargs ):
template = self.webapp.mako_template_lookup.get_template( filename )
- template.output_encoding = 'utf-8'
+ template.output_encoding = 'utf-8'
data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
data.update( self.template_context )
data.update( kwargs )
@@ -808,31 +807,31 @@
dbnames = list()
datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \
.filter_by( deleted=False, history_id=self.history.id, extension="len" )
-
+
for dataset in datasets:
dbnames.append( (dataset.dbkey, dataset.name) )
-
+
user = self.get_user()
if user and 'dbkeys' in user.preferences:
user_keys = from_json_string( user.preferences['dbkeys'] )
for key, chrom_dict in user_keys.iteritems():
dbnames.append((key, "%s (%s) [Custom]" % (chrom_dict['name'], key) ))
-
+
dbnames.extend( util.dbnames )
return dbnames
@property
def ucsc_builds( self ):
return util.dlnames['ucsc']
-
+
@property
def ensembl_builds( self ):
return util.dlnames['ensembl']
-
+
@property
def ncbi_builds( self ):
return util.dlnames['ncbi']
-
+
def db_dataset_for( self, dbkey ):
"""
Returns the db_file dataset associated/needed by `dataset`, or `None`.
@@ -855,7 +854,7 @@
if self.sa_session.query( self.app.model.RequestType ).filter_by( deleted=False ).count() > 0:
return True
return False
-
+
class FormBuilder( object ):
"""
Simple class describing an HTML form
@@ -875,7 +874,7 @@
def add_password( self, name, label, value=None, error=None, help=None ):
return self.add_input( 'password', label, name, value, error, help )
def add_select( self, name, label, value=None, options=[], error=None, help=None, use_label=True ):
- self.inputs.append( SelectInput( name, label, value=value, options=options, error=error, help=help, use_label=use_label ) )
+ self.inputs.append( SelectInput( name, label, value=value, options=options, error=error, help=help, use_label=use_label ) )
return self
class FormInput( object ):
@@ -893,7 +892,7 @@
class GalaxyWebAPITransaction( GalaxyWebTransaction ):
"""
- TODO: Unify this with WebUITransaction, since we allow session auth now.
+ TODO: Unify this with WebUITransaction, since we allow session auth now.
Enable functionality of 'create' parameter in parent _ensure_valid_session
"""
def __init__( self, environ, app, webapp, session_cookie):
@@ -915,7 +914,7 @@
# Decode the cookie value to get the session_key
session_key = self.security.decode_guid( secure_id )
try:
- # Make sure we have a valid UTF-8 string
+ # Make sure we have a valid UTF-8 string
session_key = session_key.encode( 'utf8' )
except UnicodeDecodeError:
# We'll end up creating a new galaxy_session
@@ -991,7 +990,7 @@
dbnames.append((key, "%s (%s) [Custom]" % (chrom_dict['name'], key) ))
dbnames.extend( util.dbnames )
return dbnames
-
+
@property
def ucsc_builds( self ):
return util.dlnames['ucsc']
@@ -1029,7 +1028,7 @@
def __init__( self, name, label, value=None, options=[], error=None, help=None, use_label=True ):
FormInput.__init__( self, "select", name, label, value=value, error=error, help=help, use_label=use_label )
self.options = options
-
+
class FormData( object ):
"""
Class for passing data about a form to a template, very rudimentary, could
@@ -1038,7 +1037,7 @@
def __init__( self ):
self.values = Bunch()
self.errors = Bunch()
-
+
class Bunch( dict ):
"""
Bunch based on a dict
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: dannon: Boto bumped to 2.5.2, async cloudlaunch update with downloadable keys and basic cluster picker.
by Bitbucket 12 Sep '12
by Bitbucket 12 Sep '12
12 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/93a04e500eb4/
changeset: 93a04e500eb4
user: dannon
date: 2012-09-12 18:51:56
summary: Boto bumped to 2.5.2, async cloudlaunch update with downloadable keys and basic cluster picker.
affected #: 3 files
diff -r e2cb958b3f7596744bb08febc858fa25139fd1ac -r 93a04e500eb464b7dd7c0dba565b60661307e94b eggs.ini
--- a/eggs.ini
+++ b/eggs.ini
@@ -33,7 +33,7 @@
[eggs:noplatform]
amqplib = 0.6.1
Beaker = 1.4
-boto = 2.2.2
+boto = 2.5.2
decorator = 3.1.2
docutils = 0.7
drmaa = 0.4b3
diff -r e2cb958b3f7596744bb08febc858fa25139fd1ac -r 93a04e500eb464b7dd7c0dba565b60661307e94b lib/galaxy/web/controllers/cloudlaunch.py
--- a/lib/galaxy/web/controllers/cloudlaunch.py
+++ b/lib/galaxy/web/controllers/cloudlaunch.py
@@ -8,38 +8,77 @@
import datetime
import logging
+import os
+import tempfile
import time
+import boto
+import pkg_resources
from galaxy import eggs
-import pkg_resources
pkg_resources.require('boto')
-import boto
from galaxy import web
from galaxy.web.base.controller import BaseUIController
+from galaxy.util.json import to_json_string
from boto.ec2.regioninfo import RegionInfo
from boto.exception import EC2ResponseError
+from boto.s3.connection import OrdinaryCallingFormat, S3Connection
+
log = logging.getLogger(__name__)
+PKEY_PREFIX = 'gxy_pkey'
+DEFAULT_KEYPAIR = 'deleteme_keypair'
+DEFAULT_AMI = 'ami-da58aab3'
+
class CloudController(BaseUIController):
+
def __init__(self, app):
BaseUIController.__init__(self, app)
@web.expose
def index(self, trans, share_string=None):
- return trans.fill_template("cloud/index.mako", share_string=share_string)
+ return trans.fill_template("cloud/index.mako", default_keypair = DEFAULT_KEYPAIR, share_string=share_string)
@web.expose
- def launch_instance(self, trans, cluster_name, password, key_id, secret, instance_type, share_string):
+ def get_account_info(self, trans, key_id, secret, **kwargs):
+ """
+ Get EC2 Account Info
+ """
+ #Keypairs
+ account_info = {}
+ try:
+ ec2_conn = connect_ec2(key_id, secret)
+ kps = ec2_conn.get_all_key_pairs()
+ except EC2ResponseError, e:
+ log.error("Problem starting an instance: %s\n%s" % (e, e.body))
+ account_info['keypairs'] = [akp.name for akp in kps]
+ #Existing Clusters
+ s3_conn = S3Connection(key_id, secret, calling_format=OrdinaryCallingFormat())
+ buckets = s3_conn.get_all_buckets()
+ clusters = []
+ for bucket in buckets:
+ pd = bucket.get_key('persistent_data.yaml')
+ if pd:
+ # This is a cloudman bucket.
+ # We need to get persistent data, and the cluster name.
+ for key in bucket.list():
+ if key.name.endswith('.clusterName'):
+ clusters.append({'name':key.name.split('.clusterName')[0], 'persistent_data': pd.get_contents_as_string()})
+ account_info['clusters'] = clusters
+ return to_json_string(account_info)
+
+ @web.expose
+ def launch_instance(self, trans, cluster_name, password, key_id, secret, instance_type, share_string, keypair, **kwargs):
ec2_error = None
try:
# Create security group & key pair used when starting an instance
ec2_conn = connect_ec2(key_id, secret)
sg_name = create_cm_security_group(ec2_conn)
- kp_name, kp_material = create_key_pair(ec2_conn)
+ kp_name, kp_material = create_key_pair(ec2_conn, key_name=keypair)
except EC2ResponseError, err:
ec2_error = err.error_message
if ec2_error:
- return trans.fill_template("cloud/run.mako", error = ec2_error)
+ #return trans.fill_template("cloud/run.mako", error = ec2_error)
+ return {'errors':[ec2_error]}
else:
user_provided_data={'cluster_name':cluster_name,
'access_key':key_id,
@@ -62,13 +101,41 @@
instance.update()
ct +=1
time.sleep(1)
- return trans.fill_template("cloud/run.mako",
- instance = rs.instances[0],
- kp_name = kp_name,
- kp_material = kp_material)
+ if kp_material:
+ #We have created a keypair. Save to tempfile for one time retrieval.
+ (fd, fname) = tempfile.mkstemp(prefix=PKEY_PREFIX, dir=trans.app.config.new_file_path)
+ f = os.fdopen(fd, 'wt')
+ f.write(kp_material)
+ f.close()
+ kp_material_tag = fname[fname.rfind(PKEY_PREFIX) + len(PKEY_PREFIX):]
+ else:
+ kp_material_tag = None
+ return to_json_string({
+ 'cluster_name': cluster_name,
+ 'instance_id': rs.instances[0].id,
+ 'image_id': rs.instances[0].image_id,
+ 'public_dns_name': rs.instances[0].public_dns_name,
+ 'kp_name': kp_name,
+ 'kp_material_tag':kp_material_tag
+ })
else:
- return trans.fill_template("cloud/run.mako",
- error = "Instance failure, but no specific error was detected. Please check your AWS Console.")
+ return {'errors':["Instance failure, but no specific error was detected. Please check your AWS Console."]}
+
+ @web.expose
+ def get_pkey(self, trans, kp_material_tag=None):
+ if kp_material_tag:
+ expected_path = os.path.join(trans.app.config.new_file_path, PKEY_PREFIX + kp_material_tag)
+ if os.path.exists(expected_path):
+ f = open(expected_path)
+ kp_material = f.read()
+ f.close()
+ trans.response.headers['Content-Length'] = int( os.stat( expected_path ).st_size )
+ trans.response.set_content_type( "application/octet-stream" ) #force octet-stream so Safari doesn't append mime extensions to filename
+ trans.response.headers["Content-Disposition"] = 'attachment; filename="%s.pem"' % DEFAULT_KEYPAIR
+ os.remove(expected_path)
+ return kp_material
+ trans.response.status = 400
+ return "Invalid identifier"
# ## Cloud interaction methods
def connect_ec2(a_key, s_key):
@@ -150,7 +217,7 @@
return True
return False
-def create_key_pair(ec2_conn, key_name='cloudman_key_pair'):
+def create_key_pair(ec2_conn, key_name=DEFAULT_KEYPAIR):
""" Create a key pair with the provided name.
Return the name of the key or None if there was an error creating the key.
"""
@@ -169,8 +236,8 @@
return None, None
return kp.name, kp.material
-def run_instance(ec2_conn, user_provided_data, image_id='ami-da58aab3',
- kernel_id=None, ramdisk_id=None, key_name='cloudman_key_pair',
+def run_instance(ec2_conn, user_provided_data, image_id=DEFAULT_AMI,
+ kernel_id=None, ramdisk_id=None, key_name=DEFAULT_KEYPAIR,
security_groups=['CloudMan']):
""" Start an instance. If instance start was OK, return the ResultSet object
else return None.
diff -r e2cb958b3f7596744bb08febc858fa25139fd1ac -r 93a04e500eb464b7dd7c0dba565b60661307e94b templates/cloud/index.mako
--- a/templates/cloud/index.mako
+++ b/templates/cloud/index.mako
@@ -27,6 +27,9 @@
#ec_button_container{
float:right;
}
+ #hidden_options{
+ display:none;
+ }
div.toolForm{
margin-top: 10px;
margin-bottom: 10px;
@@ -45,57 +48,215 @@
.workflow-annotation {
margin-bottom: 1em;
}
+ #loading_indicator{
+ position:fixed;
+ top:40px;
+ }
</style></%def>
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ <script type="text/javascript">
+ var ACCOUNT_URL = "${h.url_for( controller='/cloudlaunch', action='get_account_info')}";
+ var PKEY_DL_URL = "${h.url_for( controller='/cloudlaunch', action='get_pkey')}";
+ $(document).ready(function(){
+ $('#id_existing_instance').change(function(){
+ var ei_name = $(this).val();
+ if (ei_name === "New Cluster"){
+ //For new instances, need to see the cluster name field.
+ $('#id_cluster_name').val("New Cluster")
+ $('#cluster_name_wrapper').show('fast');
+ }else{
+ //Hide the Cluster Name field, but set the value
+ $('#id_cluster_name').val($(this).val());
+ $('#cluster_name_wrapper').hide('fast');
+ }
+ });
+ //When id_secret and id_key are complete, submit to get_account_info
+ $("#id_secret, #id_key_id").bind("change paste keyup", function(){
+ secret_el = $("#id_secret");
+ key_el = $("#id_key_id");
+ if (secret_el.val().length === 40 && key_el.val().length === 20){
+ //Submit these to get_account_info, unhide fields, and update as appropriate
+ $.getJSON(ACCOUNT_URL,
+ {key_id: key_el.val(),secret:secret_el.val()},
+ function(result){
+ var kplist = $("#id_keypair");
+ var clusterlist = $("#id_existing_instance");
+ kplist.find('option').remove();
+ clusterlist.find('option').remove();
+ //Update fields with appropriate elements
+ if (_.size(result.clusters) > 0){
+ clusterlist.append($('<option/>').val('New Cluster').text('New Cluster'));
+ _.each(result.clusters, function(cluster, index){
+ clusterlist.append($('<option/>').val(cluster.name).text(cluster.name));
+ });
+ $('#existing_instance_wrapper').show();
+ }
+ if (!_.include(result.keypairs, '${default_keypair}')){
+ kplist.append($('<option/>').val('${default_keypair}').text('Create New - ${default_keypair}'));
+ }
+ _.each(result.keypairs, function(keypair, index){
+ kplist.append($('<option/>').val(keypair).text(keypair));
+ });
+ $('#hidden_options').show('fast');
+ });
+ }
+ });
+ $('#loading_indicator').ajaxStart(function(){
+ $(this).show('fast');
+ }).ajaxStop(function(){
+ $(this).hide('fast');
+ });
+ $('form').ajaxForm({
+ type: 'POST',
+ dataType: 'json',
+ beforeSubmit: function(data){
+ //Hide the form, show pending box with spinner.
+ $('#launchFormContainer').hide('fast');
+ $('#responsePanel').show('fast');
+ },
+ success: function(data){
+ //Success Message, link to key download if required, link to server itself.
+ $('#launchPending').hide('fast');
+ //Check for success/error.
+ if (data.error){
+ //Apologize profusely.
+ $("launchPending").hide();
+ $("#launchError").show();
+ }else{
+ //Set appropriate fields (dns, key, ami) and then display.
+ if(data.kp_material_tag){
+ var kp_download_link = $('<a/>').attr('href', PKEY_DL_URL + '?kp_material_tag=' + data.kp_material_tag)
+ .attr('target','_blank')
+ .text("Download your key now");
+ $('#keypairInfo').append(kp_download_link);
+ $('#keypairInfo').show();
+ }
+ $('.kp_name').text(data.kp_name);
+ $('#instance_id').text(data.instance_id);
+ $('#image_id').text(data.image_id);
+ $('#instance_link').html($('<a/>')
+ .attr('href', 'http://' + data.public_dns_name + '/cloud')
+ .attr('target','_blank')
+ .text(data.public_dns_name + '/cloud'));
+ $('#instance_dns').text(data.public_dns_name);
+ $('#launchSuccess').show('fast');
+ }
+ }
+ });
+ });
+ </script>
+</%def><%def name="center_panel()"><div style="overflow: auto; height: 100%;"><div class="page-container" style="padding: 10px;">
+ <div id="loading_indicator"></div><h2>Launch a Galaxy Cloud Instance</h2>
- <div class="toolForm">
- <form action="${h.url_for( controller='cloudlaunch', action='launch_instance' )}" method="post">
- <div class="form-row">
- <label for="id_cluster_name">Cluster Name</label>
- <input type="text" size="40" name="cluster_name" id="id_cluster_name"/><br/>
- </div>
- <div class="form-row">
- <label for="id_password">Password</label>
- <input type="password" size="40" name="password" id="id_password"/><br/>
- </div>
+ <div id="launchFormContainer" class="toolForm">
+ <form id="cloudlaunch_form" action="${h.url_for( controller='/cloudlaunch', action='launch_instance')}" method="post">
+
+ <p>To launch a Galaxy Cloud Cluster, enter your AWS Secret Key ID, and Secret Key. Galaxy will use these to present appropriate
+options for launching your cluster.</p>
+
<div class="form-row"><label for="id_key_id">Key ID</label>
- <input type="text" size="40" name="key_id" id="id_key_id"/><br/>
+ <input type="text" size="30" maxlength="20" name="key_id" id="id_key_id" value=""/><br/>
+ <div class="toolParamHelp">
+ This is the text string that uniquely identifies your account, found in the <a
+href="https://portal.aws.amazon.com/gp/aws/securityCredentials">Security Credentials section of the AWS
+Console</a>.
+ </div></div>
+
<div class="form-row"><label for="id_secret">Secret Key</label>
- <input type="password" size="120" name="secret" id="id_secret"/><br/>
+ <input type="text" size="50" maxlength="40" name="secret" id="id_secret" value=""/><br/>
+ <div class="toolParamHelp">
+ This is your AWS Secret Key, also found in the <a href="https://portal.aws.amazon.com/gp/aws/securityCredentials">Security
+Credentials section of the AWS Console</a>. </div></div>
- %if share_string:
- <input type='hidden' name='share_string' value='${share_string}'/>
- %else:
- <div class="form-row">
- <label for="id_share_string">Instance Share String (optional)</label>
- <input type="text" size="120" name="share_string" id="id_share_string"/><br/>
+
+ <div id="hidden_options">
+ <div id='existing_instance_wrapper' style="display:none;" class="form-row">
+ <label for="id_existing_instance">Instances in your account</label>
+ <select name="existing_instance" id="id_existing_instance">
+ </select>
+ </div>
+ <div id='cluster_name_wrapper' class="form-row">
+ <label for="id_cluster_name">Cluster Name</label>
+ <input type="text" size="40" class="text-and-autocomplete-select" name="cluster_name" id="id_cluster_name"/><br/>
+ <div class="toolParamHelp">
+ This is the name for your cluster. You'll use this when you want to restart.
+ </div>
+ </div>
+
+ <div class="form-row">
+ <label for="id_password">Cluster Password</label>
+ <input type="password" size="40" name="password" id="id_password"/><br/>
+ </div>
+
+ <div class="form-row">
+ <label for="id_keypair">Key Pair</label>
+ <select name="keypair" id="id_keypair">
+ <option name="Create" value="cloudman_keypair">cloudman_keypair</option>
+ </select>
+ </div>
+
+ %if share_string:
+ <input type='hidden' name='share_string' value='${share_string}'/>
+ %else:
+ <div class="form-row">
+ <label for="id_share_string">Instance Share String (optional)</label>
+ <input type="text" size="120" name="share_string" id="id_share_string"/><br/>
+ </div>
+ %endif
+ <div class="form-row">
+ <label for="id_instance_type">Instance Type</label>
+ <select name="instance_type" id="id_instance_type">
+ <option value="m1.large">Large</option>
+ <option value="m1.xlarge">Extra Large</option>
+ <option value="m2.4xlarge">High-Memory Quadruple Extra Large</option>
+ </select>
+ </div>
+ <div class="form-row">
+ <p>Requesting the instance may take a moment, please be patient. Do not refresh your browser or navigate away from the page</p>
+ <input type="submit" value="Submit" id="id_submit"/>
+ </div></div>
- %endif
- <div class="form-row">
- <label for="id_instance_type">Instance Type</label>
- <select name="instance_type" id="id_instance_type">
- <option value="m1.large">Large</option>
- <option value="t1.micro">Micro</option>
- <option value="m1.xlarge">Extra Large</option>
- <option value="m2.4xlarge">High-Memory Quadruple Extra Large</option>
- </select>
- </div>
- <div class="form-row">
- <p>Requesting the instance may take a moment, please be patient. Do not refresh your browser or navigate away from the page</p>
- <input type="submit" value="Submit" id="id_submit"/>
- </div>
+ <div class="form-row">
+ <div id="loading_indicator" style="position:relative;left:10px;right:0px"></div>
+ </div></form></div>
+ <div id="responsePanel" class="toolForm" style="display:none;">
+ <div id="launchPending">Launch Pending, please be patient.</div>
+ <div id="launchError" style="display:none;">ERROR</div>
+ <div id="launchSuccess" style="display:none;">
+ <div id="keypairInfo" style="display:none;margin-bottom:20px;">
+ <h3>Very Important Key Pair Information</h3>
+ <p>A new key pair named <strong><span class="kp_name">kp_name</span></strong> has been created in your AWS
+ account and will be used to access this instance via ssh. It is
+ <strong>very important</strong> that you save the following private key
+ as it is not saved on this Galaxy instance and will be permanently lost if not saved. Additionally, this link will
+ only allow a single download, after which the key is removed from the Galaxy server permanently.<br/>
+ </div>
+ <div>
+ <h3>Access Information</h3>
+ <ul>
+ <li>Your instance '<span id="instance_id">undefined</span>' has been successfully launched using the
+ '<span id="image_id">undefined</span>' AMI.</li>
+ <li>While it may take a few moments to boot, you will be able to access the cloud control
+ panel at <span id="instance_link">undefined.</span>.</li>
+ <li>SSH access is also available using your private key. From the terminal, you would execute something like:</br> `ssh -i <span class="kp_name">undefined</span>.pem ubuntu@<span
+id="instance_dns">undefined</span>`</li>
+ </ul>
+ </div>
+ </div></div></div></%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
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/ce64f5d64ff4/
changeset: ce64f5d64ff4
user: carlfeberhard
date: 2012-09-12 17:03:17
summary: Client side templates for history panel; IconButtonView in ui.js; copied BaseModel/View into base-mvc.js; stubs for annotations, tags MVC; cleaned up controllers/root.py
affected #: 18 files
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 lib/galaxy/web/controllers/root.py
--- a/lib/galaxy/web/controllers/root.py
+++ b/lib/galaxy/web/controllers/root.py
@@ -120,9 +120,9 @@
show_hidden = util.string_as_bool( show_hidden )
datasets = self.get_history_datasets( trans, history, show_deleted, show_hidden, show_purged )
+ history_panel_template = "root/history.mako"
# history panel -> backbone
- history_panel_template = "root/history.mako"
- #history_panel_template = "root/alternate_history.mako"
+ ##history_panel_template = "root/alternate_history.mako"
return trans.stream_template_mako( history_panel_template,
history = history,
annotation = self.get_item_annotation_str( trans.sa_session, trans.user, history ),
@@ -512,85 +512,3 @@
@web.expose
def generate_error( self, trans ):
raise Exception( "Fake error!" )
-
-
-
-"""
-import galaxy.web.framework.helpers as web_helpers
-from functools import wraps
-def debug_mako_template( template_fn ):
- # Wrap a function that produces a mako template for better debugging
-
- # http://stackoverflow.com/questions/390409/how-do-you-debug-mako-templates
- @wraps( template_fn )
- def wrapper( *args, **kwargs ):
- try:
- log.debug( 'rendering template' )
- return template_fn( *args, **kwargs )
- log.debug( 'done rendering template' )
- except Exception, ex:
- log.error( "Mako Exception: " + str( ex ), exc_info=True )
- return exceptions.html_error_template().render()
- return wrapper
-
-def prep_dataset( hda, trans ):
- states = trans.app.model.Dataset.states
- print states
- STATES_INTERPRETED_AS_QUEUED = [ 'no state', '', None ]
- ## dataset is actually a HistoryDatasetAssociation
- #ported mostly from history_common
- #post: return dictionary form
- #??: move out of templates?
- #??: gather data from model?
-
-
- #TODO: clean up magic strings
-
- hda_dict = hda.get_api_value()
- #TODO: use hda_dict.update to add these (instead of localvars)
- def add_to_hda( **kwargs ):
- hda_dict.update( kwargs )
-
- # trans
- encoded_hda_id = trans.security.encode_id( hda.id )
- add_to_hda( id=encoded_hda_id )
-
- add_to_hda( state=hda.state )
- if hda.state in STATES_INTERPRETED_AS_QUEUED:
- #TODO: magic string
- add_to_hda( state='queued' )
-
- # trans
- current_user_roles = trans.get_current_user_roles()
- add_to_hda( can_edit=( not ( hda.deleted or hda.purged ) ) )
-
- # considered accessible if user can access or user isn't admin
- # trans
- accessible = trans.app.security_agent.can_access_dataset( current_user_roles, hda.dataset )
- accessible = trans.user_is_admin() or accessible
- add_to_hda( accessible=accessible )
-
- #TODO: move urls into js galaxy_paths (decorate)
- deleted = hda.deleted
- purged = hda.purged
- dataset_purged = hda.dataset.purged
- if not ( dataset_purged or purged ) and for_editing:
- undelete_url = web_helpers.url_for( controller='dataset', action='undelete', dataset_id=encoded_hda_id )
- # trans
- if trans.app.config.allow_user_dataset_purge:
- purge_url = web_helpers.url_for( controller='dataset', action='purge', dataset_id=encoded_hda_id )
-
- if not hda.visible:
- unhide_url = web_helpers.url_for( controller='dataset', action='unhide', dataset_id=encoded_hda_id )
-
-
- print 'dataset:', dataset
-
- undelete_url = web_helpers.url_for( controller='dataset', action='undelete', dataset_id=encoded_hda_id )
- purge_url = web_helpers.url_for( controller='dataset', action='purge', dataset_id=encoded_hda_id )
- unhide_url = web_helpers.url_for( controller='dataset', action='unhide', dataset_id=encoded_hda_id )
- print 'urls:', '\n'.join( undelete_url, purge_url, unhide_url )
-
- return hda_dict
-
-"""
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/libs/handlebars.full.js
--- /dev/null
+++ b/static/scripts/libs/handlebars.full.js
@@ -0,0 +1,1550 @@
+// lib/handlebars/base.js
+var Handlebars = {};
+
+Handlebars.VERSION = "1.0.beta.6";
+
+Handlebars.helpers = {};
+Handlebars.partials = {};
+
+Handlebars.registerHelper = function(name, fn, inverse) {
+ if(inverse) { fn.not = inverse; }
+ this.helpers[name] = fn;
+};
+
+Handlebars.registerPartial = function(name, str) {
+ this.partials[name] = str;
+};
+
+Handlebars.registerHelper('helperMissing', function(arg) {
+ if(arguments.length === 2) {
+ return undefined;
+ } else {
+ throw new Error("Could not find property '" + arg + "'");
+ }
+});
+
+var toString = Object.prototype.toString, functionType = "[object Function]";
+
+Handlebars.registerHelper('blockHelperMissing', function(context, options) {
+ var inverse = options.inverse || function() {}, fn = options.fn;
+
+
+ var ret = "";
+ var type = toString.call(context);
+
+ if(type === functionType) { context = context.call(this); }
+
+ if(context === true) {
+ return fn(this);
+ } else if(context === false || context == null) {
+ return inverse(this);
+ } else if(type === "[object Array]") {
+ if(context.length > 0) {
+ for(var i=0, j=context.length; i<j; i++) {
+ ret = ret + fn(context[i]);
+ }
+ } else {
+ ret = inverse(this);
+ }
+ return ret;
+ } else {
+ return fn(context);
+ }
+});
+
+Handlebars.registerHelper('each', function(context, options) {
+ var fn = options.fn, inverse = options.inverse;
+ var ret = "";
+
+ if(context && context.length > 0) {
+ for(var i=0, j=context.length; i<j; i++) {
+ ret = ret + fn(context[i]);
+ }
+ } else {
+ ret = inverse(this);
+ }
+ return ret;
+});
+
+Handlebars.registerHelper('if', function(context, options) {
+ var type = toString.call(context);
+ if(type === functionType) { context = context.call(this); }
+
+ if(!context || Handlebars.Utils.isEmpty(context)) {
+ return options.inverse(this);
+ } else {
+ return options.fn(this);
+ }
+});
+
+Handlebars.registerHelper('unless', function(context, options) {
+ var fn = options.fn, inverse = options.inverse;
+ options.fn = inverse;
+ options.inverse = fn;
+
+ return Handlebars.helpers['if'].call(this, context, options);
+});
+
+Handlebars.registerHelper('with', function(context, options) {
+ return options.fn(context);
+});
+
+Handlebars.registerHelper('log', function(context) {
+ Handlebars.log(context);
+});
+;
+// lib/handlebars/compiler/parser.js
+/* Jison generated parser */
+var handlebars = (function(){
+
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"root":3,"program":4,"EOF":5,"statements":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"OPEN_PARTIAL":24,"params":25,"hash":26,"param":27,"STRING":28,"INTEGER":29,"BOOLEAN":30,"hashSegments":31,"hashSegment":32,"ID":33,"EQUALS":34,"pathSegments":35,"SEP":36,"$accept":0,"$end":1},
+terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"OPEN_PARTIAL",28:"STRING",29:"INTEGER",30:"BOOLEAN",33:"ID",34:"EQUALS",36:"SEP"},
+productions_: [0,[3,2],[4,3],[4,1],[4,0],[6,1],[6,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[7,2],[17,3],[17,2],[17,2],[17,1],[25,2],[25,1],[27,1],[27,1],[27,1],[27,1],[26,1],[31,2],[31,1],[32,3],[32,3],[32,3],[32,3],[21,1],[35,3],[35,1]],
+performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1: return $$[$0-1]
+break;
+case 2: this.$ = new yy.ProgramNode($$[$0-2], $$[$0])
+break;
+case 3: this.$ = new yy.ProgramNode($$[$0])
+break;
+case 4: this.$ = new yy.ProgramNode([])
+break;
+case 5: this.$ = [$$[$0]]
+break;
+case 6: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]
+break;
+case 7: this.$ = new yy.InverseNode($$[$0-2], $$[$0-1], $$[$0])
+break;
+case 8: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0])
+break;
+case 9: this.$ = $$[$0]
+break;
+case 10: this.$ = $$[$0]
+break;
+case 11: this.$ = new yy.ContentNode($$[$0])
+break;
+case 12: this.$ = new yy.CommentNode($$[$0])
+break;
+case 13: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1])
+break;
+case 14: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1])
+break;
+case 15: this.$ = $$[$0-1]
+break;
+case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1])
+break;
+case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true)
+break;
+case 18: this.$ = new yy.PartialNode($$[$0-1])
+break;
+case 19: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1])
+break;
+case 20:
+break;
+case 21: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]]
+break;
+case 22: this.$ = [[$$[$0-1]].concat($$[$0]), null]
+break;
+case 23: this.$ = [[$$[$0-1]], $$[$0]]
+break;
+case 24: this.$ = [[$$[$0]], null]
+break;
+case 25: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
+break;
+case 26: this.$ = [$$[$0]]
+break;
+case 27: this.$ = $$[$0]
+break;
+case 28: this.$ = new yy.StringNode($$[$0])
+break;
+case 29: this.$ = new yy.IntegerNode($$[$0])
+break;
+case 30: this.$ = new yy.BooleanNode($$[$0])
+break;
+case 31: this.$ = new yy.HashNode($$[$0])
+break;
+case 32: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]
+break;
+case 33: this.$ = [$$[$0]]
+break;
+case 34: this.$ = [$$[$0-2], $$[$0]]
+break;
+case 35: this.$ = [$$[$0-2], new yy.StringNode($$[$0])]
+break;
+case 36: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])]
+break;
+case 37: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])]
+break;
+case 38: this.$ = new yy.IdNode($$[$0])
+break;
+case 39: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
+break;
+case 40: this.$ = [$$[$0]]
+break;
+}
+},
+table: [{3:1,4:2,5:[2,4],6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],24:[1,15]},{1:[3]},{5:[1,16]},{5:[2,3],7:17,8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,19],20:[2,3],22:[1,13],23:[1,14],24:[1,15]},{5:[2,5],14:[2,5],15:[2,5],16:[2,5],19:[2,5],20:[2,5],22:[2,5],23:[2,5],24:[2,5]},{4:20,6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],24:[1,15]},{4:21,6:3,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],24:[1,15]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],24:[2,9]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],24:[2,10]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],24:[2,11]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],24:[2,12]},{17:22,21:23,33:[1,25],35:24},{17:26,21:23,33:[1,25],35:24},{17:27,21:23,33:[1,25],35:24},{17:28,21:23,33:[1,25],35:24},{21:29,33:[1,25],35:24},{1:[2,1]},{6:30,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],24:[1,15]},{5:[2,6],14:[2,6],15:[2,6],16:[2,6],19:[2,6],20:[2,6],22:[2,6],23:[2,6],24:[2,6]},{17:22,18:[1,31],21:23,33:[1,25],35:24},{10:32,20:[1,33]},{10:34,20:[1,33]},{18:[1,35]},{18:[2,24],21:40,25:36,26:37,27:38,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,38],28:[2,38],29:[2,38],30:[2,38],33:[2,38],36:[1,46]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],36:[2,40]},{18:[1,47]},{18:[1,48]},{18:[1,49]},{18:[1,50],21:51,33:[1,25],35:24},{5:[2,2],8:18,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,2],22:[1,13],23:[1,14],24:[1,15]},{14:[2,20],15:[2,20],16:[2,20],19:[2,20],22:[2,20],23:[2,20],24:[2,20]},{5:[2,7],14:[2,7],15:[2,7],16:[2,7],19:[2,7],20:[2,7],22:[2,7],23:[2,7],24:[2,7]},{21:52,33:[1,25],35:24},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],24:[2,8]},{14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],24:[2,14]},{18:[2,22],21:40,26:53,27:54,28:[1,41],29:[1,42],30:[1,43],31:39,32:44,33:[1,45],35:24},{18:[2,23]},{18:[2,26],28:[2,26],29:[2,26],30:[2,26],33:[2,26]},{18:[2,31],32:55,33:[1,56]},{18:[2,27],28:[2,27],29:[2,27],30:[2,27],33:[2,27]},{18:[2,28],28:[2,28],29:[2,28],30:[2,28],33:[2,28]},{18:[2,29],28:[2,29],29:[2,29],30:[2,29],33:[2,29]},{18:[2,30],28:[2,30],29:[2,30],30:[2,30],33:[2,30]},{18:[2,33],33:[2,33]},{18:[2,40],28:[2,40],29:[2,40],30:[2,40],33:[2,40],34:[1,57],36:[2,40]},{33:[1,58]},{14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],24:[2,13]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],24:[2,16]},{5:[2,17],14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],24:[2,17]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],24:[2,18]},{18:[1,59]},{18:[1,60]},{18:[2,21]},{18:[2,25],28:[2,25],29:[2,25],30:[2,25],33:[2,25]},{18:[2,32],33:[2,32]},{34:[1,57]},{21:61,28:[1,62],29:[1,63],30:[1,64],33:[1,25],35:24},{18:[2,39],28:[2,39],29:[2,39],30:[2,39],33:[2,39],36:[2,39]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],24:[2,19]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],24:[2,15]},{18:[2,34],33:[2,34]},{18:[2,35],33:[2,35]},{18:[2,36],33:[2,36]},{18:[2,37],33:[2,37]}],
+defaultActions: {16:[2,1],37:[2,23],53:[2,21]},
+parseError: function parseError(str, hash) {
+ throw new Error(str);
+},
+parse: function parse(input) {
+ var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+ this.lexer.setInput(input);
+ this.lexer.yy = this.yy;
+ this.yy.lexer = this.lexer;
+ if (typeof this.lexer.yylloc == "undefined")
+ this.lexer.yylloc = {};
+ var yyloc = this.lexer.yylloc;
+ lstack.push(yyloc);
+ if (typeof this.yy.parseError === "function")
+ this.parseError = this.yy.parseError;
+ function popStack(n) {
+ stack.length = stack.length - 2 * n;
+ vstack.length = vstack.length - n;
+ lstack.length = lstack.length - n;
+ }
+ function lex() {
+ var token;
+ token = self.lexer.lex() || 1;
+ if (typeof token !== "number") {
+ token = self.symbols_[token] || token;
+ }
+ return token;
+ }
+ var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+ while (true) {
+ state = stack[stack.length - 1];
+ if (this.defaultActions[state]) {
+ action = this.defaultActions[state];
+ } else {
+ if (symbol == null)
+ symbol = lex();
+ action = table[state] && table[state][symbol];
+ }
+ if (typeof action === "undefined" || !action.length || !action[0]) {
+ if (!recovering) {
+ expected = [];
+ for (p in table[state])
+ if (this.terminals_[p] && p > 2) {
+ expected.push("'" + this.terminals_[p] + "'");
+ }
+ var errStr = "";
+ if (this.lexer.showPosition) {
+ errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + this.terminals_[symbol] + "'";
+ } else {
+ errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
+ }
+ this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
+ }
+ }
+ if (action[0] instanceof Array && action.length > 1) {
+ throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
+ }
+ switch (action[0]) {
+ case 1:
+ stack.push(symbol);
+ vstack.push(this.lexer.yytext);
+ lstack.push(this.lexer.yylloc);
+ stack.push(action[1]);
+ symbol = null;
+ if (!preErrorSymbol) {
+ yyleng = this.lexer.yyleng;
+ yytext = this.lexer.yytext;
+ yylineno = this.lexer.yylineno;
+ yyloc = this.lexer.yylloc;
+ if (recovering > 0)
+ recovering--;
+ } else {
+ symbol = preErrorSymbol;
+ preErrorSymbol = null;
+ }
+ break;
+ case 2:
+ len = this.productions_[action[1]][1];
+ yyval.$ = vstack[vstack.length - len];
+ yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
+ r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
+ if (typeof r !== "undefined") {
+ return r;
+ }
+ if (len) {
+ stack = stack.slice(0, -1 * len * 2);
+ vstack = vstack.slice(0, -1 * len);
+ lstack = lstack.slice(0, -1 * len);
+ }
+ stack.push(this.productions_[action[1]][0]);
+ vstack.push(yyval.$);
+ lstack.push(yyval._$);
+ newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+ stack.push(newState);
+ break;
+ case 3:
+ return true;
+ }
+ }
+ return true;
+}
+};/* Jison generated lexer */
+var lexer = (function(){
+
+var lexer = ({EOF:1,
+parseError:function parseError(str, hash) {
+ if (this.yy.parseError) {
+ this.yy.parseError(str, hash);
+ } else {
+ throw new Error(str);
+ }
+ },
+setInput:function (input) {
+ this._input = input;
+ this._more = this._less = this.done = false;
+ this.yylineno = this.yyleng = 0;
+ this.yytext = this.matched = this.match = '';
+ this.conditionStack = ['INITIAL'];
+ this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
+ return this;
+ },
+input:function () {
+ var ch = this._input[0];
+ this.yytext+=ch;
+ this.yyleng++;
+ this.match+=ch;
+ this.matched+=ch;
+ var lines = ch.match(/\n/);
+ if (lines) this.yylineno++;
+ this._input = this._input.slice(1);
+ return ch;
+ },
+unput:function (ch) {
+ this._input = ch + this._input;
+ return this;
+ },
+more:function () {
+ this._more = true;
+ return this;
+ },
+pastInput:function () {
+ var past = this.matched.substr(0, this.matched.length - this.match.length);
+ return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+ },
+upcomingInput:function () {
+ var next = this.match;
+ if (next.length < 20) {
+ next += this._input.substr(0, 20-next.length);
+ }
+ return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
+ },
+showPosition:function () {
+ var pre = this.pastInput();
+ var c = new Array(pre.length + 1).join("-");
+ return pre + this.upcomingInput() + "\n" + c+"^";
+ },
+next:function () {
+ if (this.done) {
+ return this.EOF;
+ }
+ if (!this._input) this.done = true;
+
+ var token,
+ match,
+ col,
+ lines;
+ if (!this._more) {
+ this.yytext = '';
+ this.match = '';
+ }
+ var rules = this._currentRules();
+ for (var i=0;i < rules.length; i++) {
+ match = this._input.match(this.rules[rules[i]]);
+ if (match) {
+ lines = match[0].match(/\n.*/g);
+ if (lines) this.yylineno += lines.length;
+ this.yylloc = {first_line: this.yylloc.last_line,
+ last_line: this.yylineno+1,
+ first_column: this.yylloc.last_column,
+ last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
+ this.yytext += match[0];
+ this.match += match[0];
+ this.matches = match;
+ this.yyleng = this.yytext.length;
+ this._more = false;
+ this._input = this._input.slice(match[0].length);
+ this.matched += match[0];
+ token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
+ if (token) return token;
+ else return;
+ }
+ }
+ if (this._input === "") {
+ return this.EOF;
+ } else {
+ this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
+ {text: "", token: null, line: this.yylineno});
+ }
+ },
+lex:function lex() {
+ var r = this.next();
+ if (typeof r !== 'undefined') {
+ return r;
+ } else {
+ return this.lex();
+ }
+ },
+begin:function begin(condition) {
+ this.conditionStack.push(condition);
+ },
+popState:function popState() {
+ return this.conditionStack.pop();
+ },
+_currentRules:function _currentRules() {
+ return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
+ },
+topState:function () {
+ return this.conditionStack[this.conditionStack.length-2];
+ },
+pushState:function begin(condition) {
+ this.begin(condition);
+ }});
+lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+
+var YYSTATE=YY_START
+switch($avoiding_name_collisions) {
+case 0:
+ if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
+ if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
+ if(yy_.yytext) return 14;
+
+break;
+case 1: return 14;
+break;
+case 2: this.popState(); return 14;
+break;
+case 3: return 24;
+break;
+case 4: return 16;
+break;
+case 5: return 20;
+break;
+case 6: return 19;
+break;
+case 7: return 19;
+break;
+case 8: return 23;
+break;
+case 9: return 23;
+break;
+case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
+break;
+case 11: return 22;
+break;
+case 12: return 34;
+break;
+case 13: return 33;
+break;
+case 14: return 33;
+break;
+case 15: return 36;
+break;
+case 16: /*ignore whitespace*/
+break;
+case 17: this.popState(); return 18;
+break;
+case 18: this.popState(); return 18;
+break;
+case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28;
+break;
+case 20: return 30;
+break;
+case 21: return 30;
+break;
+case 22: return 29;
+break;
+case 23: return 33;
+break;
+case 24: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33;
+break;
+case 25: return 'INVALID';
+break;
+case 26: return 5;
+break;
+}
+};
+lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^[^\x00]{2,}?(?=(\{\{))/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[\/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s\/.])/,/^\[[^\]]*\]/,/^./,/^$/];
+lexer.conditions = {"mu":{"rules":[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"INITIAL":{"rules":[0,1,26],"inclusive":true}};return lexer;})()
+parser.lexer = lexer;
+return parser;
+})();
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = handlebars;
+exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
+exports.main = function commonjsMain(args) {
+ if (!args[1])
+ throw new Error('Usage: '+args[0]+' FILE');
+ if (typeof process !== 'undefined') {
+ var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
+ } else {
+ var cwd = require("file").path(require("file").cwd());
+ var source = cwd.join(args[1]).read({charset: "utf-8"});
+ }
+ return exports.parser.parse(source);
+}
+if (typeof module !== 'undefined' && require.main === module) {
+ exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
+}
+};
+;
+// lib/handlebars/compiler/base.js
+Handlebars.Parser = handlebars;
+
+Handlebars.parse = function(string) {
+ Handlebars.Parser.yy = Handlebars.AST;
+ return Handlebars.Parser.parse(string);
+};
+
+Handlebars.print = function(ast) {
+ return new Handlebars.PrintVisitor().accept(ast);
+};
+
+Handlebars.logger = {
+ DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
+
+ // override in the host environment
+ log: function(level, str) {}
+};
+
+Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
+;
+// lib/handlebars/compiler/ast.js
+(function() {
+
+ Handlebars.AST = {};
+
+ Handlebars.AST.ProgramNode = function(statements, inverse) {
+ this.type = "program";
+ this.statements = statements;
+ if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
+ };
+
+ Handlebars.AST.MustacheNode = function(params, hash, unescaped) {
+ this.type = "mustache";
+ this.id = params[0];
+ this.params = params.slice(1);
+ this.hash = hash;
+ this.escaped = !unescaped;
+ };
+
+ Handlebars.AST.PartialNode = function(id, context) {
+ this.type = "partial";
+
+ // TODO: disallow complex IDs
+
+ this.id = id;
+ this.context = context;
+ };
+
+ var verifyMatch = function(open, close) {
+ if(open.original !== close.original) {
+ throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
+ }
+ };
+
+ Handlebars.AST.BlockNode = function(mustache, program, close) {
+ verifyMatch(mustache.id, close);
+ this.type = "block";
+ this.mustache = mustache;
+ this.program = program;
+ };
+
+ Handlebars.AST.InverseNode = function(mustache, program, close) {
+ verifyMatch(mustache.id, close);
+ this.type = "inverse";
+ this.mustache = mustache;
+ this.program = program;
+ };
+
+ Handlebars.AST.ContentNode = function(string) {
+ this.type = "content";
+ this.string = string;
+ };
+
+ Handlebars.AST.HashNode = function(pairs) {
+ this.type = "hash";
+ this.pairs = pairs;
+ };
+
+ Handlebars.AST.IdNode = function(parts) {
+ this.type = "ID";
+ this.original = parts.join(".");
+
+ var dig = [], depth = 0;
+
+ for(var i=0,l=parts.length; i<l; i++) {
+ var part = parts[i];
+
+ if(part === "..") { depth++; }
+ else if(part === "." || part === "this") { this.isScoped = true; }
+ else { dig.push(part); }
+ }
+
+ this.parts = dig;
+ this.string = dig.join('.');
+ this.depth = depth;
+ this.isSimple = (dig.length === 1) && (depth === 0);
+ };
+
+ Handlebars.AST.StringNode = function(string) {
+ this.type = "STRING";
+ this.string = string;
+ };
+
+ Handlebars.AST.IntegerNode = function(integer) {
+ this.type = "INTEGER";
+ this.integer = integer;
+ };
+
+ Handlebars.AST.BooleanNode = function(bool) {
+ this.type = "BOOLEAN";
+ this.bool = bool;
+ };
+
+ Handlebars.AST.CommentNode = function(comment) {
+ this.type = "comment";
+ this.comment = comment;
+ };
+
+})();;
+// lib/handlebars/utils.js
+Handlebars.Exception = function(message) {
+ var tmp = Error.prototype.constructor.apply(this, arguments);
+
+ for (var p in tmp) {
+ if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
+ }
+
+ this.message = tmp.message;
+};
+Handlebars.Exception.prototype = new Error;
+
+// Build out our basic SafeString type
+Handlebars.SafeString = function(string) {
+ this.string = string;
+};
+Handlebars.SafeString.prototype.toString = function() {
+ return this.string.toString();
+};
+
+(function() {
+ var escape = {
+ "<": "<",
+ ">": ">",
+ '"': """,
+ "'": "'",
+ "`": "`"
+ };
+
+ var badChars = /&(?!\w+;)|[<>"'`]/g;
+ var possible = /[&<>"'`]/;
+
+ var escapeChar = function(chr) {
+ return escape[chr] || "&";
+ };
+
+ Handlebars.Utils = {
+ escapeExpression: function(string) {
+ // don't escape SafeStrings, since they're already safe
+ if (string instanceof Handlebars.SafeString) {
+ return string.toString();
+ } else if (string == null || string === false) {
+ return "";
+ }
+
+ if(!possible.test(string)) { return string; }
+ return string.replace(badChars, escapeChar);
+ },
+
+ isEmpty: function(value) {
+ if (typeof value === "undefined") {
+ return true;
+ } else if (value === null) {
+ return true;
+ } else if (value === false) {
+ return true;
+ } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ };
+})();;
+// lib/handlebars/compiler/compiler.js
+Handlebars.Compiler = function() {};
+Handlebars.JavaScriptCompiler = function() {};
+
+(function(Compiler, JavaScriptCompiler) {
+ Compiler.OPCODE_MAP = {
+ appendContent: 1,
+ getContext: 2,
+ lookupWithHelpers: 3,
+ lookup: 4,
+ append: 5,
+ invokeMustache: 6,
+ appendEscaped: 7,
+ pushString: 8,
+ truthyOrFallback: 9,
+ functionOrFallback: 10,
+ invokeProgram: 11,
+ invokePartial: 12,
+ push: 13,
+ assignToHash: 15,
+ pushStringParam: 16
+ };
+
+ Compiler.MULTI_PARAM_OPCODES = {
+ appendContent: 1,
+ getContext: 1,
+ lookupWithHelpers: 2,
+ lookup: 1,
+ invokeMustache: 3,
+ pushString: 1,
+ truthyOrFallback: 1,
+ functionOrFallback: 1,
+ invokeProgram: 3,
+ invokePartial: 1,
+ push: 1,
+ assignToHash: 1,
+ pushStringParam: 1
+ };
+
+ Compiler.DISASSEMBLE_MAP = {};
+
+ for(var prop in Compiler.OPCODE_MAP) {
+ var value = Compiler.OPCODE_MAP[prop];
+ Compiler.DISASSEMBLE_MAP[value] = prop;
+ }
+
+ Compiler.multiParamSize = function(code) {
+ return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]];
+ };
+
+ Compiler.prototype = {
+ compiler: Compiler,
+
+ disassemble: function() {
+ var opcodes = this.opcodes, opcode, nextCode;
+ var out = [], str, name, value;
+
+ for(var i=0, l=opcodes.length; i<l; i++) {
+ opcode = opcodes[i];
+
+ if(opcode === 'DECLARE') {
+ name = opcodes[++i];
+ value = opcodes[++i];
+ out.push("DECLARE " + name + " = " + value);
+ } else {
+ str = Compiler.DISASSEMBLE_MAP[opcode];
+
+ var extraParams = Compiler.multiParamSize(opcode);
+ var codes = [];
+
+ for(var j=0; j<extraParams; j++) {
+ nextCode = opcodes[++i];
+
+ if(typeof nextCode === "string") {
+ nextCode = "\"" + nextCode.replace("\n", "\\n") + "\"";
+ }
+
+ codes.push(nextCode);
+ }
+
+ str = str + " " + codes.join(" ");
+
+ out.push(str);
+ }
+ }
+
+ return out.join("\n");
+ },
+
+ guid: 0,
+
+ compile: function(program, options) {
+ this.children = [];
+ this.depths = {list: []};
+ this.options = options;
+
+ // These changes will propagate to the other compiler components
+ var knownHelpers = this.options.knownHelpers;
+ this.options.knownHelpers = {
+ 'helperMissing': true,
+ 'blockHelperMissing': true,
+ 'each': true,
+ 'if': true,
+ 'unless': true,
+ 'with': true,
+ 'log': true
+ };
+ if (knownHelpers) {
+ for (var name in knownHelpers) {
+ this.options.knownHelpers[name] = knownHelpers[name];
+ }
+ }
+
+ return this.program(program);
+ },
+
+ accept: function(node) {
+ return this[node.type](node);
+ },
+
+ program: function(program) {
+ var statements = program.statements, statement;
+ this.opcodes = [];
+
+ for(var i=0, l=statements.length; i<l; i++) {
+ statement = statements[i];
+ this[statement.type](statement);
+ }
+ this.isSimple = l === 1;
+
+ this.depths.list = this.depths.list.sort(function(a, b) {
+ return a - b;
+ });
+
+ return this;
+ },
+
+ compileProgram: function(program) {
+ var result = new this.compiler().compile(program, this.options);
+ var guid = this.guid++;
+
+ this.usePartial = this.usePartial || result.usePartial;
+
+ this.children[guid] = result;
+
+ for(var i=0, l=result.depths.list.length; i<l; i++) {
+ depth = result.depths.list[i];
+
+ if(depth < 2) { continue; }
+ else { this.addDepth(depth - 1); }
+ }
+
+ return guid;
+ },
+
+ block: function(block) {
+ var mustache = block.mustache;
+ var depth, child, inverse, inverseGuid;
+
+ var params = this.setupStackForMustache(mustache);
+
+ var programGuid = this.compileProgram(block.program);
+
+ if(block.program.inverse) {
+ inverseGuid = this.compileProgram(block.program.inverse);
+ this.declare('inverse', inverseGuid);
+ }
+
+ this.opcode('invokeProgram', programGuid, params.length, !!mustache.hash);
+ this.declare('inverse', null);
+ this.opcode('append');
+ },
+
+ inverse: function(block) {
+ var params = this.setupStackForMustache(block.mustache);
+
+ var programGuid = this.compileProgram(block.program);
+
+ this.declare('inverse', programGuid);
+
+ this.opcode('invokeProgram', null, params.length, !!block.mustache.hash);
+ this.declare('inverse', null);
+ this.opcode('append');
+ },
+
+ hash: function(hash) {
+ var pairs = hash.pairs, pair, val;
+
+ this.opcode('push', '{}');
+
+ for(var i=0, l=pairs.length; i<l; i++) {
+ pair = pairs[i];
+ val = pair[1];
+
+ this.accept(val);
+ this.opcode('assignToHash', pair[0]);
+ }
+ },
+
+ partial: function(partial) {
+ var id = partial.id;
+ this.usePartial = true;
+
+ if(partial.context) {
+ this.ID(partial.context);
+ } else {
+ this.opcode('push', 'depth0');
+ }
+
+ this.opcode('invokePartial', id.original);
+ this.opcode('append');
+ },
+
+ content: function(content) {
+ this.opcode('appendContent', content.string);
+ },
+
+ mustache: function(mustache) {
+ var params = this.setupStackForMustache(mustache);
+
+ this.opcode('invokeMustache', params.length, mustache.id.original, !!mustache.hash);
+
+ if(mustache.escaped && !this.options.noEscape) {
+ this.opcode('appendEscaped');
+ } else {
+ this.opcode('append');
+ }
+ },
+
+ ID: function(id) {
+ this.addDepth(id.depth);
+
+ this.opcode('getContext', id.depth);
+
+ this.opcode('lookupWithHelpers', id.parts[0] || null, id.isScoped || false);
+
+ for(var i=1, l=id.parts.length; i<l; i++) {
+ this.opcode('lookup', id.parts[i]);
+ }
+ },
+
+ STRING: function(string) {
+ this.opcode('pushString', string.string);
+ },
+
+ INTEGER: function(integer) {
+ this.opcode('push', integer.integer);
+ },
+
+ BOOLEAN: function(bool) {
+ this.opcode('push', bool.bool);
+ },
+
+ comment: function() {},
+
+ // HELPERS
+ pushParams: function(params) {
+ var i = params.length, param;
+
+ while(i--) {
+ param = params[i];
+
+ if(this.options.stringParams) {
+ if(param.depth) {
+ this.addDepth(param.depth);
+ }
+
+ this.opcode('getContext', param.depth || 0);
+ this.opcode('pushStringParam', param.string);
+ } else {
+ this[param.type](param);
+ }
+ }
+ },
+
+ opcode: function(name, val1, val2, val3) {
+ this.opcodes.push(Compiler.OPCODE_MAP[name]);
+ if(val1 !== undefined) { this.opcodes.push(val1); }
+ if(val2 !== undefined) { this.opcodes.push(val2); }
+ if(val3 !== undefined) { this.opcodes.push(val3); }
+ },
+
+ declare: function(name, value) {
+ this.opcodes.push('DECLARE');
+ this.opcodes.push(name);
+ this.opcodes.push(value);
+ },
+
+ addDepth: function(depth) {
+ if(depth === 0) { return; }
+
+ if(!this.depths[depth]) {
+ this.depths[depth] = true;
+ this.depths.list.push(depth);
+ }
+ },
+
+ setupStackForMustache: function(mustache) {
+ var params = mustache.params;
+
+ this.pushParams(params);
+
+ if(mustache.hash) {
+ this.hash(mustache.hash);
+ }
+
+ this.ID(mustache.id);
+
+ return params;
+ }
+ };
+
+ JavaScriptCompiler.prototype = {
+ // PUBLIC API: You can override these methods in a subclass to provide
+ // alternative compiled forms for name lookup and buffering semantics
+ nameLookup: function(parent, name, type) {
+ if (/^[0-9]+$/.test(name)) {
+ return parent + "[" + name + "]";
+ } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
+ return parent + "." + name;
+ }
+ else {
+ return parent + "['" + name + "']";
+ }
+ },
+
+ appendToBuffer: function(string) {
+ if (this.environment.isSimple) {
+ return "return " + string + ";";
+ } else {
+ return "buffer += " + string + ";";
+ }
+ },
+
+ initializeBuffer: function() {
+ return this.quotedString("");
+ },
+
+ namespace: "Handlebars",
+ // END PUBLIC API
+
+ compile: function(environment, options, context, asObject) {
+ this.environment = environment;
+ this.options = options || {};
+
+ this.name = this.environment.name;
+ this.isChild = !!context;
+ this.context = context || {
+ programs: [],
+ aliases: { self: 'this' },
+ registers: {list: []}
+ };
+
+ this.preamble();
+
+ this.stackSlot = 0;
+ this.stackVars = [];
+
+ this.compileChildren(environment, options);
+
+ var opcodes = environment.opcodes, opcode;
+
+ this.i = 0;
+
+ for(l=opcodes.length; this.i<l; this.i++) {
+ opcode = this.nextOpcode(0);
+
+ if(opcode[0] === 'DECLARE') {
+ this.i = this.i + 2;
+ this[opcode[1]] = opcode[2];
+ } else {
+ this.i = this.i + opcode[1].length;
+ this[opcode[0]].apply(this, opcode[1]);
+ }
+ }
+
+ return this.createFunctionContext(asObject);
+ },
+
+ nextOpcode: function(n) {
+ var opcodes = this.environment.opcodes, opcode = opcodes[this.i + n], name, val;
+ var extraParams, codes;
+
+ if(opcode === 'DECLARE') {
+ name = opcodes[this.i + 1];
+ val = opcodes[this.i + 2];
+ return ['DECLARE', name, val];
+ } else {
+ name = Compiler.DISASSEMBLE_MAP[opcode];
+
+ extraParams = Compiler.multiParamSize(opcode);
+ codes = [];
+
+ for(var j=0; j<extraParams; j++) {
+ codes.push(opcodes[this.i + j + 1 + n]);
+ }
+
+ return [name, codes];
+ }
+ },
+
+ eat: function(opcode) {
+ this.i = this.i + opcode.length;
+ },
+
+ preamble: function() {
+ var out = [];
+
+ // this register will disambiguate helper lookup from finding a function in
+ // a context. This is necessary for mustache compatibility, which requires
+ // that context functions in blocks are evaluated by blockHelperMissing, and
+ // then proceed as if the resulting value was provided to blockHelperMissing.
+ this.useRegister('foundHelper');
+
+ if (!this.isChild) {
+ var namespace = this.namespace;
+ var copies = "helpers = helpers || " + namespace + ".helpers;";
+ if(this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
+ out.push(copies);
+ } else {
+ out.push('');
+ }
+
+ if (!this.environment.isSimple) {
+ out.push(", buffer = " + this.initializeBuffer());
+ } else {
+ out.push("");
+ }
+
+ // track the last context pushed into place to allow skipping the
+ // getContext opcode when it would be a noop
+ this.lastContext = 0;
+ this.source = out;
+ },
+
+ createFunctionContext: function(asObject) {
+ var locals = this.stackVars;
+ if (!this.isChild) {
+ locals = locals.concat(this.context.registers.list);
+ }
+
+ if(locals.length > 0) {
+ this.source[1] = this.source[1] + ", " + locals.join(", ");
+ }
+
+ // Generate minimizer alias mappings
+ if (!this.isChild) {
+ var aliases = []
+ for (var alias in this.context.aliases) {
+ this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
+ }
+ }
+
+ if (this.source[1]) {
+ this.source[1] = "var " + this.source[1].substring(2) + ";";
+ }
+
+ // Merge children
+ if (!this.isChild) {
+ this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
+ }
+
+ if (!this.environment.isSimple) {
+ this.source.push("return buffer;");
+ }
+
+ var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
+
+ for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
+ params.push("depth" + this.environment.depths.list[i]);
+ }
+
+ if (asObject) {
+ params.push(this.source.join("\n "));
+
+ return Function.apply(this, params);
+ } else {
+ var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + this.source.join("\n ") + '}';
+ Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
+ return functionSource;
+ }
+ },
+
+ appendContent: function(content) {
+ this.source.push(this.appendToBuffer(this.quotedString(content)));
+ },
+
+ append: function() {
+ var local = this.popStack();
+ this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
+ if (this.environment.isSimple) {
+ this.source.push("else { " + this.appendToBuffer("''") + " }");
+ }
+ },
+
+ appendEscaped: function() {
+ var opcode = this.nextOpcode(1), extra = "";
+ this.context.aliases.escapeExpression = 'this.escapeExpression';
+
+ if(opcode[0] === 'appendContent') {
+ extra = " + " + this.quotedString(opcode[1][0]);
+ this.eat(opcode);
+ }
+
+ this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
+ },
+
+ getContext: function(depth) {
+ if(this.lastContext !== depth) {
+ this.lastContext = depth;
+ }
+ },
+
+ lookupWithHelpers: function(name, isScoped) {
+ if(name) {
+ var topStack = this.nextStack();
+
+ this.usingKnownHelper = false;
+
+ var toPush;
+ if (!isScoped && this.options.knownHelpers[name]) {
+ toPush = topStack + " = " + this.nameLookup('helpers', name, 'helper');
+ this.usingKnownHelper = true;
+ } else if (isScoped || this.options.knownHelpersOnly) {
+ toPush = topStack + " = " + this.nameLookup('depth' + this.lastContext, name, 'context');
+ } else {
+ this.register('foundHelper', this.nameLookup('helpers', name, 'helper'));
+ toPush = topStack + " = foundHelper || " + this.nameLookup('depth' + this.lastContext, name, 'context');
+ }
+
+ toPush += ';';
+ this.source.push(toPush);
+ } else {
+ this.pushStack('depth' + this.lastContext);
+ }
+ },
+
+ lookup: function(name) {
+ var topStack = this.topStack();
+ this.source.push(topStack + " = (" + topStack + " === null || " + topStack + " === undefined || " + topStack + " === false ? " +
+ topStack + " : " + this.nameLookup(topStack, name, 'context') + ");");
+ },
+
+ pushStringParam: function(string) {
+ this.pushStack('depth' + this.lastContext);
+ this.pushString(string);
+ },
+
+ pushString: function(string) {
+ this.pushStack(this.quotedString(string));
+ },
+
+ push: function(name) {
+ this.pushStack(name);
+ },
+
+ invokeMustache: function(paramSize, original, hasHash) {
+ this.populateParams(paramSize, this.quotedString(original), "{}", null, hasHash, function(nextStack, helperMissingString, id) {
+ if (!this.usingKnownHelper) {
+ this.context.aliases.helperMissing = 'helpers.helperMissing';
+ this.context.aliases.undef = 'void 0';
+ this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
+ if (nextStack !== id) {
+ this.source.push("else { " + nextStack + " = " + id + "; }");
+ }
+ }
+ });
+ },
+
+ invokeProgram: function(guid, paramSize, hasHash) {
+ var inverse = this.programExpression(this.inverse);
+ var mainProgram = this.programExpression(guid);
+
+ this.populateParams(paramSize, null, mainProgram, inverse, hasHash, function(nextStack, helperMissingString, id) {
+ if (!this.usingKnownHelper) {
+ this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
+ this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
+ }
+ });
+ },
+
+ populateParams: function(paramSize, helperId, program, inverse, hasHash, fn) {
+ var needsRegister = hasHash || this.options.stringParams || inverse || this.options.data;
+ var id = this.popStack(), nextStack;
+ var params = [], param, stringParam, stringOptions;
+
+ if (needsRegister) {
+ this.register('tmp1', program);
+ stringOptions = 'tmp1';
+ } else {
+ stringOptions = '{ hash: {} }';
+ }
+
+ if (needsRegister) {
+ var hash = (hasHash ? this.popStack() : '{}');
+ this.source.push('tmp1.hash = ' + hash + ';');
+ }
+
+ if(this.options.stringParams) {
+ this.source.push('tmp1.contexts = [];');
+ }
+
+ for(var i=0; i<paramSize; i++) {
+ param = this.popStack();
+ params.push(param);
+
+ if(this.options.stringParams) {
+ this.source.push('tmp1.contexts.push(' + this.popStack() + ');');
+ }
+ }
+
+ if(inverse) {
+ this.source.push('tmp1.fn = tmp1;');
+ this.source.push('tmp1.inverse = ' + inverse + ';');
+ }
+
+ if(this.options.data) {
+ this.source.push('tmp1.data = data;');
+ }
+
+ params.push(stringOptions);
+
+ this.populateCall(params, id, helperId || id, fn, program !== '{}');
+ },
+
+ populateCall: function(params, id, helperId, fn, program) {
+ var paramString = ["depth0"].concat(params).join(", ");
+ var helperMissingString = ["depth0"].concat(helperId).concat(params).join(", ");
+
+ var nextStack = this.nextStack();
+
+ if (this.usingKnownHelper) {
+ this.source.push(nextStack + " = " + id + ".call(" + paramString + ");");
+ } else {
+ this.context.aliases.functionType = '"function"';
+ var condition = program ? "foundHelper && " : ""
+ this.source.push("if(" + condition + "typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
+ }
+ fn.call(this, nextStack, helperMissingString, id);
+ this.usingKnownHelper = false;
+ },
+
+ invokePartial: function(context) {
+ params = [this.nameLookup('partials', context, 'partial'), "'" + context + "'", this.popStack(), "helpers", "partials"];
+
+ if (this.options.data) {
+ params.push("data");
+ }
+
+ this.pushStack("self.invokePartial(" + params.join(", ") + ");");
+ },
+
+ assignToHash: function(key) {
+ var value = this.popStack();
+ var hash = this.topStack();
+
+ this.source.push(hash + "['" + key + "'] = " + value + ";");
+ },
+
+ // HELPERS
+
+ compiler: JavaScriptCompiler,
+
+ compileChildren: function(environment, options) {
+ var children = environment.children, child, compiler;
+
+ for(var i=0, l=children.length; i<l; i++) {
+ child = children[i];
+ compiler = new this.compiler();
+
+ this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
+ var index = this.context.programs.length;
+ child.index = index;
+ child.name = 'program' + index;
+ this.context.programs[index] = compiler.compile(child, options, this.context);
+ }
+ },
+
+ programExpression: function(guid) {
+ if(guid == null) { return "self.noop"; }
+
+ var child = this.environment.children[guid],
+ depths = child.depths.list;
+ var programParams = [child.index, child.name, "data"];
+
+ for(var i=0, l = depths.length; i<l; i++) {
+ depth = depths[i];
+
+ if(depth === 1) { programParams.push("depth0"); }
+ else { programParams.push("depth" + (depth - 1)); }
+ }
+
+ if(depths.length === 0) {
+ return "self.program(" + programParams.join(", ") + ")";
+ } else {
+ programParams.shift();
+ return "self.programWithDepth(" + programParams.join(", ") + ")";
+ }
+ },
+
+ register: function(name, val) {
+ this.useRegister(name);
+ this.source.push(name + " = " + val + ";");
+ },
+
+ useRegister: function(name) {
+ if(!this.context.registers[name]) {
+ this.context.registers[name] = true;
+ this.context.registers.list.push(name);
+ }
+ },
+
+ pushStack: function(item) {
+ this.source.push(this.nextStack() + " = " + item + ";");
+ return "stack" + this.stackSlot;
+ },
+
+ nextStack: function() {
+ this.stackSlot++;
+ if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
+ return "stack" + this.stackSlot;
+ },
+
+ popStack: function() {
+ return "stack" + this.stackSlot--;
+ },
+
+ topStack: function() {
+ return "stack" + this.stackSlot;
+ },
+
+ quotedString: function(str) {
+ return '"' + str
+ .replace(/\\/g, '\\\\')
+ .replace(/"/g, '\\"')
+ .replace(/\n/g, '\\n')
+ .replace(/\r/g, '\\r') + '"';
+ }
+ };
+
+ var reservedWords = (
+ "break else new var" +
+ " case finally return void" +
+ " catch for switch while" +
+ " continue function this with" +
+ " default if throw" +
+ " delete in try" +
+ " do instanceof typeof" +
+ " abstract enum int short" +
+ " boolean export interface static" +
+ " byte extends long super" +
+ " char final native synchronized" +
+ " class float package throws" +
+ " const goto private transient" +
+ " debugger implements protected volatile" +
+ " double import public let yield"
+ ).split(" ");
+
+ var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
+
+ for(var i=0, l=reservedWords.length; i<l; i++) {
+ compilerWords[reservedWords[i]] = true;
+ }
+
+ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
+ if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
+ return true;
+ }
+ return false;
+ }
+
+})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
+
+Handlebars.precompile = function(string, options) {
+ options = options || {};
+
+ var ast = Handlebars.parse(string);
+ var environment = new Handlebars.Compiler().compile(ast, options);
+ return new Handlebars.JavaScriptCompiler().compile(environment, options);
+};
+
+Handlebars.compile = function(string, options) {
+ options = options || {};
+
+ var compiled;
+ function compile() {
+ var ast = Handlebars.parse(string);
+ var environment = new Handlebars.Compiler().compile(ast, options);
+ var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
+ return Handlebars.template(templateSpec);
+ }
+
+ // Template is only compiled on first use and cached after that point.
+ return function(context, options) {
+ if (!compiled) {
+ compiled = compile();
+ }
+ return compiled.call(this, context, options);
+ };
+};
+;
+// lib/handlebars/runtime.js
+Handlebars.VM = {
+ template: function(templateSpec) {
+ // Just add water
+ var container = {
+ escapeExpression: Handlebars.Utils.escapeExpression,
+ invokePartial: Handlebars.VM.invokePartial,
+ programs: [],
+ program: function(i, fn, data) {
+ var programWrapper = this.programs[i];
+ if(data) {
+ return Handlebars.VM.program(fn, data);
+ } else if(programWrapper) {
+ return programWrapper;
+ } else {
+ programWrapper = this.programs[i] = Handlebars.VM.program(fn);
+ return programWrapper;
+ }
+ },
+ programWithDepth: Handlebars.VM.programWithDepth,
+ noop: Handlebars.VM.noop
+ };
+
+ return function(context, options) {
+ options = options || {};
+ return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
+ };
+ },
+
+ programWithDepth: function(fn, data, $depth) {
+ var args = Array.prototype.slice.call(arguments, 2);
+
+ return function(context, options) {
+ options = options || {};
+
+ return fn.apply(this, [context, options.data || data].concat(args));
+ };
+ },
+ program: function(fn, data) {
+ return function(context, options) {
+ options = options || {};
+
+ return fn(context, options.data || data);
+ };
+ },
+ noop: function() { return ""; },
+ invokePartial: function(partial, name, context, helpers, partials, data) {
+ options = { helpers: helpers, partials: partials, data: data };
+
+ if(partial === undefined) {
+ throw new Handlebars.Exception("The partial " + name + " could not be found");
+ } else if(partial instanceof Function) {
+ return partial(context, options);
+ } else if (!Handlebars.compile) {
+ throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
+ } else {
+ partials[name] = Handlebars.compile(partial);
+ return partials[name](context, options);
+ }
+ }
+};
+
+Handlebars.template = Handlebars.VM.template;
+;
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/mvc/annotations.js
--- /dev/null
+++ b/static/scripts/mvc/annotations.js
@@ -0,0 +1,35 @@
+/*==============================================================================
+Backbone MV for Annotations
+
+
+TODO:
+
+==============================================================================*/
+/** Single Tag model
+ */
+var Annotation = BaseModel.extend( LoggableMixin ).extend({
+
+ // uncomment this out see log messages
+ logger : console,
+
+ toString : function(){
+ return 'Annotation()';
+ }
+});
+
+//------------------------------------------------------------------------------
+/** Single Tag view
+ */
+var AnnotationView = BaseView.extend( LoggableMixin ).extend({
+
+ // uncomment this out see log messages
+ logger : console,
+
+ toString : function(){
+ return 'AnnotationView()';
+ }
+});
+
+//==============================================================================
+/** YAGNI? A collection of Annotations, mainView?
+ */
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/mvc/base-mvc.js
--- /dev/null
+++ b/static/scripts/mvc/base-mvc.js
@@ -0,0 +1,271 @@
+/**
+ * Simple base model for any visible element. Includes useful attributes and ability
+ * to set and track visibility.
+ */
+var BaseModel = Backbone.RelationalModel.extend({
+ defaults: {
+ name: null,
+ hidden: false
+ },
+
+ show: function() {
+ this.set("hidden", false);
+ },
+
+ hide: function() {
+ this.set("hidden", true);
+ },
+
+ is_visible: function() {
+ return !this.attributes.hidden;
+ }
+});
+
+
+/**
+ * Base view that handles visibility based on model's hidden attribute.
+ */
+var BaseView = Backbone.View.extend({
+
+ initialize: function() {
+ this.model.on("change:hidden", this.update_visible, this);
+ this.update_visible();
+ },
+
+ update_visible: function() {
+ if( this.model.attributes.hidden ){
+ this.$el.hide();
+ } else {
+ this.$el.show();
+ }
+ }
+});
+
+//==============================================================================
+/**
+ * Adds logging capabilities to your Models/Views
+ * can be used with plain browser console (or something more complex like an AJAX logger)
+ *
+ * add to your models/views at the definition using chaining:
+ * var MyModel = BaseModel.extend( LoggableMixin ).extend({ // ... });
+ *
+ * or - more explicitly AFTER the definition:
+ * var MyModel = BaseModel.extend({
+ * logger : console
+ * // ...
+ * this.log( '$#%& it! - broken already...' );
+ * })
+ * _.extend( MyModel.prototype, LoggableMixin )
+ *
+ * NOTE: currently only uses the console.debug log function (as opposed to debug, error, warn, etc.)
+ */
+var LoggableMixin = {
+ // replace null with console (if available) to see all logs
+ logger : null,
+
+ log : function(){
+ return ( this.logger )?( this.logger.log.apply( this, arguments ) )
+ :( undefined );
+ }
+};
+
+
+//==============================================================================
+/**
+ * Base class for template loaders:
+ * The main interface is loader.getTemplates( templatesToLoad )
+ * where templatesToLoad is in the form:
+ * {
+ * remoteTemplateFilename1: {
+ * templateFunctionName1 : templateID1,
+ * templateFunctionName2 : templateID2,
+ * ...
+ * },
+ * remoteTemplateFilename2: {
+ * templateFunctionName3 : templateID3,
+ * templateFunctionName4 : templateID4,
+ * ...
+ * }
+ * }
+ * getTemplates will return a map of the templates in the form:
+ * {
+ * templateFunctionName1 : compiledTemplateFn1(),
+ * templateFunctionName2 : compiledTemplateFn2(),
+ * templateFunctionName3 : compiledTemplateFn3(),
+ * ...
+ * }
+ *
+ * Generally meant to be called for Backbone views, etc like this:
+ * BackboneView.templates = CompiledTemplateLoader( templatesToLoad );
+ */
+var TemplateLoader = _.extend( {}, LoggableMixin, {
+ //TODO: incorporate caching of template functions (for use across objects)
+ //TODO: only require and use 2 level (or some variation) map templatesToLoad for the remote loader
+
+ // comment next line out to suppress logging
+ //logger : console,
+
+ //cachedTemplates : {},
+
+ getTemplateLoadFn : function(){
+ throw( "There is no templateLoadFn. Make sure you're using a subclass of TemplateLoader" );
+ },
+
+ // loop through templatesToLoad assuming it is a map in the form mentioned above
+ getTemplates : function( templatesToLoad, forceReload ){
+ forceReload = forceReload || false;
+ this.log( this, 'getTemplates:', templatesToLoad, ', forceReload:', forceReload );
+
+ //!TODO: cache templates here
+ var templates = {},
+ loader = this,
+ templateLoadFn = this.getTemplateLoadFn();
+
+ if( !templatesToLoad ){ return templates; }
+ jQuery.each( templatesToLoad, function( templateFile, templateData ){
+
+ //TODO: handle flatter map versions of templatesToLoad ({ name : id })
+ jQuery.each( templateData, function( templateName, templateID ){
+ loader.log( loader + ', templateFile:', templateFile,
+ 'templateName:', templateName, ', templateID:', templateID );
+ templates[ templateName ] = templateLoadFn.call( loader, templateFile, templateName, templateID );
+ });
+ });
+ return templates;
+ }
+});
+
+
+//..............................................................................
+/** find the compiled template in Handlebars.templates by templateName
+ * and return the entire, requested templates map
+ */
+var CompiledTemplateLoader = _.extend( {}, TemplateLoader, {
+ getTemplateLoadFn : function(){ return this.loadCompiledHandlebarsTemplate; },
+
+ // override if new compiler
+ loadCompiledHandlebarsTemplate : function( templateFile, templateName, templateID ){
+ //pre: compiled templates should have been loaded with the mako helper h.templates
+ // (although these could be dynamically loaded as well?)
+ this.log( 'getInDomTemplates, templateFile:', templateFile,
+ 'templateName:', templateName, ', templateID:', templateID );
+
+ if( !Handlebars.templates || !Handlebars.templates[ templateID ] ){
+ throw( 'Template not found: Handlebars.' + templateID
+ + '. Check your h.templates() call in the mako file that rendered this page' );
+ }
+ this.log( 'found template function:', templateID );
+ // really this is just a lookup
+ return Handlebars.templates[ templateID ];
+ }
+
+ //TEST: Handlebars.full NOT runtime
+ //TEST: no Handlebars
+ //TEST: bad id
+ //TEST: Handlebars.runtime, good id
+});
+
+//..............................................................................
+/** find the NON-compiled template templateID in the DOM, compile it (using Handlebars),
+ * and return the entire, requested templates map
+ * (Note: for use with Mako.include and multiple templates)
+ */
+var InDomTemplateLoader = _.extend( {}, TemplateLoader, {
+
+ // override or change if a new compiler (Underscore, etc.) is being used
+ compileTemplate : function( templateText ){
+ // we'll need the compiler
+ if( !Handlebars || !Handlebars.compile ){
+ throw( 'No Handlebars.compile found. You may only have Handlebars.runtime loaded.'
+ + 'Include handlebars.full for this to work' );
+ }
+ // copy fn ref to this view under the templateName
+ this.log( 'compiling template:', templateText );
+ return Handlebars.compile( templateText );
+ },
+
+ findTemplateInDom : function( templateFile, templateName, templateID ){
+ // assume the last is best
+ return $( 'script#' + templateID ).last();
+ },
+
+ getTemplateLoadFn : function(){ return this.loadInDomTemplate; },
+
+ loadInDomTemplate : function( templateFile, templateName, templateID ){
+ this.log( 'getInDomTemplate, templateFile:', templateFile,
+ 'templateName:', templateName, ', templateID:', templateID );
+
+ // find it in the dom by the id and compile
+ var template = this.findTemplateInDom( templateFile, templateName, templateID );
+ if( !template || !template.length ){
+ throw( 'Template not found within the DOM: ' + templateID
+ + '. Check that this template has been included in the page' );
+ }
+ this.log( 'found template in dom:', template.html() );
+ return this.compileTemplate( template.html() );
+ }
+
+ //TEST: no compiler
+ //TEST: good url, good id, in DOM
+ //TEST: good url, good id, NOT in DOM
+});
+
+//..............................................................................
+/** HTTP GET the NON-compiled templates, append into the DOM, compile them,
+ * and return the entire, requested templates map
+ * (for use with dynamically loaded views)
+ */
+var RemoteTemplateLoader = _.extend( {}, InDomTemplateLoader, {
+ templateBaseURL : 'static/scripts/templates/',
+
+ getTemplateLoadFn : function(){ return this.loadViaHttpGet; },
+
+ loadViaHttpGet : function( templateFile, templateName, templateID ){
+ var templateBaseURL = 'static/scripts/templates/';
+ this.log( 'loadViaHttpGet, templateFile:', templateFile,
+ 'templateName:', templateName, ', templateID:', templateID,
+ 'templateBaseURL:', this.templateBaseURL );
+
+ //??: possibly not the best pattern here...
+ // try in-dom first (prevent loading the same templateFile for each of its templates)
+ var template = null;
+ try {
+ template = this.loadInDomTemplate( templateFile, templateName, templateID );
+
+ // if that didn't work, load the templateFile via GET,...
+ } catch( exception ){
+ this.log( 'getInDomTemplate exception:' + exception );
+ // handle no compiler exception
+ if( !Handlebars.compile ){ throw( exception ); }
+ //TEST:
+
+ this.log( "Couldn't locate template in DOM: " + templateID );
+ var loader = this;
+ var url = templateBaseURL + templateFile;
+ //??: async : false may cause problems in the long run
+ jQuery.ajax( url, {
+ method : 'GET',
+ async : false,
+ success : function( data ){
+ loader.log( templateFile + ' loaded via GET. Attempting compile...' );
+ //...move the templateFile into the DOM and try that again
+ $( 'body' ).append( data );
+ template = loader.loadInDomTemplate( templateFile, templateName, templateID );
+ },
+ error : function( data, status, xhr ){
+ throw( 'Failed to fetch ' + url + ':' + status );
+ }
+ });
+ }
+ if( !template ){
+ throw( "Couldn't load or fetch template: " + templateID );
+ }
+ return template;
+ }
+
+ //TEST: no compiler
+ //TEST: good url, good id, already local
+ //TEST: good url, good id, remote load
+ //TEST: good url, bad template id
+ //TEST: bad url, error from ajax
+});
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/mvc/history.js
--- a/static/scripts/mvc/history.js
+++ b/static/scripts/mvc/history.js
@@ -1,3 +1,4 @@
+if( console ){ console.debug( 'history mvc loaded' ); }
/*
Backbone.js implementation of history panel
@@ -13,6 +14,8 @@
convert function comments to /** style
complete comments
+ don't draw body until it's first unhide event
+
as always: where does the model end and the view begin?
HistoryPanel
HistoryCollection: (collection of History: 'Saved Histories')
@@ -30,23 +33,12 @@
poly HistoryItemView on: can/cant_edit
*/
-_ = _;
//==============================================================================
-var Loggable = {
- // replace null with console (if available) to see all logs
- logger : null,
-
- log : function(){
- return ( this.logger )?( this.logger.debug.apply( this, arguments ) )
- :( undefined );
- }
-};
-var LoggingModel = BaseModel.extend( Loggable );
-var LoggingView = BaseView.extend( Loggable );
//==============================================================================
//TODO: move to Galaxy obj./namespace, decorate for current page (as GalaxyPaths)
+/*
var Localizable = {
localizedStrings : {},
setLocalizedString : function( str, localizedString ){
@@ -58,6 +50,7 @@
}
};
var LocalizableView = LoggingView.extend( Localizable );
+*/
//TODO: wire up to views
//==============================================================================
@@ -93,7 +86,8 @@
}
//==============================================================================
-var HistoryItem = LoggingModel.extend({
+//TODO: use initialize (or validate) to check purged AND deleted -> purged XOR deleted
+var HistoryItem = BaseModel.extend( LoggableMixin ).extend({
// a single HDA model
// uncomment this out see log messages
@@ -141,7 +135,11 @@
},
toString : function(){
- return 'HistoryItem(' + ( this.get( 'name' ) || this.get( 'id' ) || '' ) + ')';
+ var nameAndId = this.get( 'id' ) || '';
+ if( this.get( 'name' ) ){
+ nameAndId += ':"' + this.get( 'name' ) + '"';
+ }
+ return 'HistoryItem(' + nameAndId + ')';
}
});
@@ -161,20 +159,19 @@
//==============================================================================
-var HistoryItemView = LoggingView.extend({
+var HistoryItemView = BaseView.extend( LoggableMixin ).extend({
// view for HistoryItem model above
// uncomment this out see log messages
- logger : console,
+ //logger : console,
tagName : "div",
className : "historyItemContainer",
-
// ................................................................................ SET UP
initialize : function(){
this.log( this + '.initialize:', this, this.model );
- return this;
+
},
// ................................................................................ RENDER MAIN
@@ -190,12 +187,8 @@
.addClass( 'historyItemWrapper' ).addClass( 'historyItem' )
.addClass( 'historyItem-' + state );
- itemWrapper.append( this._render_purgedWarning() );
- itemWrapper.append( this._render_deletionWarning() );
- itemWrapper.append( this._render_visibleWarning() );
-
+ itemWrapper.append( this._render_warnings() );
itemWrapper.append( this._render_titleBar() );
-
this.body = $( this._render_body() );
itemWrapper.append( this.body );
@@ -215,52 +208,9 @@
},
// ................................................................................ RENDER WARNINGS
- //TODO: refactor into generalized warning widget/view
- //TODO: refactor the three following - too much common ground
- _render_purgedWarning : function(){
- // Render warnings for purged
- //this.log( this + '_render_purgedWarning' );
- var warning = null;
- if( this.model.get( 'purged' ) ){
- warning = $( HistoryItemView.STRINGS.purgedMsg );
- }
- //this.log( 'warning:', warning );
- return warning;
- },
-
- _render_deletionWarning : function(){
- //this.log( this + '_render_deletionWarning' );
- // Render warnings for deleted items (and links: undelete and purge)
- //pre: this.model.purge_url will be undefined if trans.app.config.allow_user_dataset_purge=False
- var warningElem = null;
- if( this.model.get( 'deleted' ) ){
- var warning = '';
-
- if( this.model.get( 'undelete_url' ) ){
- warning += HistoryItemView.TEMPLATES.undeleteLink( this.model.attributes );
- }
- if( this.model.get( 'purge_url' ) ){
- warning += HistoryItemView.TEMPLATES.purgeLink( this.model.attributes );
- }
- // wrap it in the standard warning msg
- warningElem = $( HistoryItemView.TEMPLATES.warningMsg({ warning: warning }) );
- }
- //this.log( 'warning:', warning );
- return warningElem;
- },
-
- _render_visibleWarning : function(){
- //this.log( this + '_render_visibleWarning' );
- // Render warnings for hidden items (and link: unhide)
- var warningElem = null;
- if( !this.model.get( 'visible' ) && this.model.get( 'unhide_url' ) ){
- var warning = HistoryItemView.TEMPLATES.hiddenMsg( this.model.attributes );
-
- // wrap it in the standard warning msg
- warningElem = $( HistoryItemView.TEMPLATES.warningMsg({ warning: warning }) );
- }
- //this.log( 'warning:', warning );
- return warningElem;
+ _render_warnings : function(){
+ // jQ errs on building dom with whitespace - if there are no messages, trim -> ''
+ return $( jQuery.trim( HistoryItemView.templates.messages( this.model.toJSON() ) ) );
},
// ................................................................................ RENDER TITLEBAR
@@ -292,23 +242,21 @@
//TODO: move other data (non-href) into {} in view definition, cycle over those keys in _titlebuttons
//TODO: move disabled span data into view def, move logic into _titlebuttons
_render_displayButton : function(){
- // render the display icon-button
- // show a disabled display if the data's been purged
- if( this.model.get( 'purged' ) ){
- return $( '<span class="icon-button display_disabled tooltip" ' +
- 'title="Cannot display datasets removed from disk"></span>' );
- }
- var id = this.model.get( 'id' ),
- displayBtnData = {
- //TODO: localized title
- title : 'Display data in browser',
- //TODO: need generated url here
- href : '/datasets/' + id + '/display/?preview=True',
- target : ( this.model.get( 'for_editing' ) )?( 'galaxy_main' ):( '' ),
- classes : [ 'icon-button', 'tooltip', 'display' ],
- dataset_id : id
- };
- return $( linkHTMLTemplate( displayBtnData ) );
+ displayBtnData = ( this.model.get( 'purged' ) )?({
+ // show a disabled display if the data's been purged
+ title : 'Cannot display datasets removed from disk',
+ icon_class : 'display',
+ enabled : false,
+ }):({
+ // if not, render the display icon-button with href
+ title : 'Display data in browser',
+ //TODO: need generated url here
+ href : this.model.get( 'display_url' ),
+ target : ( this.model.get( 'for_editing' ) )?( 'galaxy_main' ):( null ),
+ icon_class : 'display',
+ });
+ this.displayButton = new IconButtonView({ model : new IconButton( displayBtnData ) });
+ return this.displayButton.render().$el;
},
_render_editButton : function(){
@@ -317,44 +265,42 @@
purged = this.model.get( 'purged' ),
deleted = this.model.get( 'deleted' );
+ var editBtnData = {
+ title : 'Edit attributes',
+ href : this.model.get( 'edit_url' ),
+ target : 'galaxy_main',
+ icon_class : 'edit'
+ }
+ // disable if purged or deleted and explain why in the tooltip
+ //TODO: if for_editing
if( deleted || purged ){
- if( !purged ){
- return $( '<span class="icon-button edit_disabled tooltip" ' +
- 'title="Undelete dataset to edit attributes"></span>' );
- } else {
- return $( '<span class="icon-button edit_disabled tooltip" ' +
- 'title="Cannot edit attributes of datasets removed from disk"></span>' );
- }
+ editBtnData = {
+ enabled : false,
+ icon_class : 'edit'
+ };
+ editBtnData.title = ( !purged )?( 'Undelete dataset to edit attributes' ):
+ ( 'Cannot edit attributes of datasets removed from disk' );
}
- return $( linkHTMLTemplate({
- title : 'Edit attributes',
- //TODO: need generated url here
- href : '/datasets/' + id + '/edit',
- target : 'galaxy_main',
- classes : [ 'icon-button', 'tooltip', 'edit' ]
- }) );
+ this.editButton = new IconButtonView({ model : new IconButton( editBtnData ) });
+ return this.editButton.render().$el;
},
_render_deleteButton : function(){
- // render the delete icon-button
- var id = this.model.get( 'id' ),
- purged = this.model.get( 'purged' ),
- deleted = this.model.get( 'deleted' );
-
- //??: WHAAAA? can_edit == deleted??
- // yes! : (history_common.mako) can_edit=( not ( data.deleted or data.purged ) )
- if( purged || deleted ){
- return $( '<span title="Dataset is already deleted" ' +
- 'class="icon-button delete_disabled tooltip"></span>' );
- }
- return $( linkHTMLTemplate({
+ if( !this.model.get( 'for_editing' ) ){ return null; }
+
+ var deleteBtnData = ( this.model.get( 'delete_url' ) )?({
title : 'Delete',
- //TODO: need generated url here
- href : '/datasets/' + id + '/delete?show_deleted_on_refresh=False',
+ href : this.model.get( 'delete_url' ),
target : 'galaxy_main',
- id : 'historyItemDeleter-' + id,
- classes : [ 'icon-button', 'tooltip', 'delete' ]
- }));
+ id : 'historyItemDeleter-' + this.model.get( 'id' ),
+ icon_class : 'delete'
+ }):({
+ title : 'Dataset is already deleted',
+ icon_class : 'delete',
+ enabled : false
+ });
+ this.deleteButton = new IconButtonView({ model : new IconButton( deleteBtnData ) });
+ return this.deleteButton.render().$el;
},
_render_titleLink : function(){
@@ -441,7 +387,7 @@
});
warningMsgText += 'You may be able to ' + editLink + '.';
}
- parent.append( $( HistoryItemView.TEMPLATES.warningMsg({ warning: warningMsgText }) ) );
+ parent.append( $( HistoryItemView.templates.warningMsg({ warning: warningMsgText }) ) );
//...render the remaining body as STATES.OK (only diff between these states is the box above)
this._render_body_ok( parent );
@@ -497,7 +443,8 @@
// if trans.user
if( this.model.get( 'retag_url' ) && this.model.get( 'annotate_url' ) ){
- // tags, annotation buttons and display areas
+ // tag, annotate buttons
+ //TODO: move to tag, Annot MV
var tagsAnnotationsBtns = $( '<div style="float: right;"></div>' );
tagsAnnotationsBtns.append( $( linkHTMLTemplate({
title : 'Edit dataset tags',
@@ -514,17 +461,20 @@
actionBtnDiv.append( tagsAnnotationsBtns );
actionBtnDiv.append( '<div style="clear: both"></div>' );
- var tagArea = $( '<div class="tag-area" style="display: none">' );
- tagArea.append( '<strong>Tags:</strong>' );
- tagArea.append( '<div class="tag-elt"></div>' );
- actionBtnDiv.append( tagArea );
+ // tag/annot display areas
+ this.tagArea = $( '<div class="tag-area" style="display: none">' );
+ this.tagArea.append( '<strong>Tags:</strong>' );
+ this.tagElt = $( '<div class="tag-elt"></div>' );
+ actionBtnDiv.append( this.tagArea.append( this.tagElt ) );
var annotationArea = $( ( '<div id="${dataset_id}-annotation-area"'
+ ' class="annotation-area" style="display: none">' ) );
+ this.annotationArea = annotationArea;
annotationArea.append( '<strong>Annotation:</strong>' );
- annotationArea.append( ( '<div id="${dataset_id}-annotation-elt" '
+ this.annotationElem = $( '<div id="' + this.model.get( 'id' ) + '-annotation-elt" '
+ 'style="margin: 1px 0px 1px 0px" class="annotation-elt tooltip editable-text" '
- + 'title="Edit dataset annotation"></div>' ) );
+ + 'title="Edit dataset annotation"></div>' );
+ annotationArea.append( this.annotationElem );
actionBtnDiv.append( annotationArea );
}
}
@@ -564,10 +514,10 @@
},
_render_body : function(){
- this.log( this + '_render_body' );
+ //this.log( this + '_render_body' );
var state = this.model.get( 'state' ),
for_editing = this.model.get( 'for_editing' );
- this.log( 'state:', state, 'for_editing', for_editing );
+ //this.log( 'state:', state, 'for_editing', for_editing );
//TODO: incorrect id (encoded - use hid?)
var body = $( '<div/>' )
@@ -708,10 +658,86 @@
// ................................................................................ EVENTS
events : {
- 'click .historyItemTitle' : 'toggleBodyVisibility'
+ 'click .historyItemTitle' : 'toggleBodyVisibility',
+ 'click a.icon-button.tags' : 'loadAndDisplayTags',
+ 'click a.icon-button.annotate' : 'loadAndDisplayAnnotation'
},
// ................................................................................ STATE CHANGES / MANIPULATION
+ loadAndDisplayTags : function( event ){
+ //TODO: this is a drop in from history.mako - should use MV as well
+ this.log( this, '.loadAndDisplayTags', event );
+ var tagArea = this.tagArea;
+ var tagElt = this.tagElt;
+
+ // Show or hide tag area; if showing tag area and it's empty, fill it.
+ if( tagArea.is( ":hidden" ) ){
+ if( !tagElt.html() ){
+ // Need to fill tag element.
+ $.ajax({
+ url: this.model.get( 'ajax_get_tag_url' ),
+ error: function() { alert( "Tagging failed" ) },
+ success: function(tag_elt_html) {
+ console.debug( 'tag_elt_html:', tag_elt_html );
+ tagElt.html(tag_elt_html);
+ tagElt.find(".tooltip").tooltip();
+ tagArea.slideDown("fast");
+ }
+ });
+ } else {
+ // Tag element is filled; show.
+ tagArea.slideDown("fast");
+ }
+
+ } else {
+ // Hide.
+ tagArea.slideUp("fast");
+ }
+ return false;
+ },
+
+ loadAndDisplayAnnotation : function( event ){
+ //TODO: this is a drop in from history.mako - should use MV as well
+ this.log( this, '.loadAndDisplayAnnotation', event );
+ var annotationArea = this.annotationArea,
+ annotationElem = this.annotationElem,
+ setAnnotationUrl = this.model.get( 'ajax_set_annotation_url' );
+
+ // Show or hide annotation area; if showing annotation area and it's empty, fill it.
+ this.log( 'annotationArea hidden:', annotationArea.is( ":hidden" ) );
+ this.log( 'annotationElem html:', annotationElem.html() );
+ if ( annotationArea.is( ":hidden" ) ){
+ if( !annotationElem.html() ){
+ // Need to fill annotation element.
+ $.ajax({
+ url: this.model.get( 'ajax_get_annotation_url' ),
+ error: function(){ alert( "Annotations failed" ) },
+ success: function( htmlFromAjax ){
+ if( htmlFromAjax === "" ){
+ htmlFromAjax = "<em>Describe or add notes to dataset</em>";
+ }
+ annotationElem.html( htmlFromAjax );
+ annotationArea.find(".tooltip").tooltip();
+
+ async_save_text(
+ annotationElem.attr("id"), annotationElem.attr("id"),
+ setAnnotationUrl,
+ "new_annotation", 18, true, 4
+ );
+ annotationArea.slideDown("fast");
+ }
+ });
+ } else {
+ annotationArea.slideDown("fast");
+ }
+
+ } else {
+ // Hide.
+ annotationArea.slideUp("fast");
+ }
+ return false;
+ },
+
toggleBodyVisibility : function(){
this.log( this + '.toggleBodyVisibility' );
this.$el.find( '.historyItemBody' ).toggle();
@@ -723,30 +749,8 @@
return 'HistoryItemView(' + modelString + ')';
}
});
-
-//------------------------------------------------------------------------------
HistoryItemView.TEMPLATES = {};
-//TODO: move next one out - doesn't belong
-HistoryItemView.TEMPLATES.warningMsg =
- _.template( '<div class=warningmessagesmall><strong><%= warning %></strong></div>' );
-
-
-//??TODO: move into functions?
-HistoryItemView.TEMPLATES.undeleteLink = _.template(
- 'This dataset has been deleted. ' +
- 'Click <a href="<%= undelete_url %>" class="historyItemUndelete" id="historyItemUndeleter-{{ id }}" ' +
- ' target="galaxy_history">here</a> to undelete it.' );
-
-HistoryItemView.TEMPLATES.purgeLink = _.template(
- ' or <a href="<%= purge_url %>" class="historyItemPurge" id="historyItemPurger-{{ id }}"' +
- ' target="galaxy_history">here</a> to immediately remove it from disk.' );
-
-HistoryItemView.TEMPLATES.hiddenMsg = _.template(
- 'This dataset has been hidden. ' +
- 'Click <a href="<%= unhide_url %>" class="historyItemUnhide" id="historyItemUnhider-{{ id }}" ' +
- ' target="galaxy_history">here</a> to unhide it.' );
-
//TODO: contains localized strings
HistoryItemView.TEMPLATES.hdaSummary = _.template([
'<%= misc_blurb %><br />',
@@ -754,13 +758,18 @@
'database: <%= dbkeyHTML %>'
].join( '' ));
-
-//------------------------------------------------------------------------------
-HistoryItemView.STRINGS = {};
-HistoryItemView.STRINGS.purgedMsg = HistoryItemView.TEMPLATES.warningMsg(
- { warning: 'This dataset has been deleted and removed from disk.' });
-
+//==============================================================================
+//HistoryItemView.templates = InDomTemplateLoader.getTemplates({
+HistoryItemView.templates = CompiledTemplateLoader.getTemplates({
+ 'common-templates.html' : {
+ warningMsg : 'template-warningmessagesmall',
+ },
+ 'history-templates.html' : {
+ messages : 'template-history-warning-messages2',
+ hdaSummary : 'template-history-hdaSummary'
+ }
+});
//==============================================================================
var HistoryCollection = Backbone.Collection.extend({
@@ -773,10 +782,10 @@
//==============================================================================
-var History = LoggingModel.extend({
+var History = BaseModel.extend( LoggableMixin ).extend({
// uncomment this out see log messages
- logger : console,
+ //logger : console,
// values from api (may need more)
defaults : {
@@ -867,11 +876,11 @@
});
//------------------------------------------------------------------------------
-var HistoryView = LoggingView.extend({
+var HistoryView = BaseView.extend( LoggableMixin ).extend({
// view for the HistoryCollection (as per current right hand panel)
// uncomment this out see log messages
- logger : console,
+ //logger : console,
// direct attachment to existing element
el : 'body.historyPage',
@@ -908,8 +917,7 @@
//==============================================================================
-//USE_MOCK_DATA = true;
-if( window.USE_MOCK_DATA ){
+function createMockHistoryData(){
mockHistory = {};
mockHistory.data = {
@@ -986,6 +994,7 @@
notvisible :
_.extend( _.clone( mockHistory.data.template ),
{ visible : false }),
+
hasDisplayApps :
_.extend( _.clone( mockHistory.data.template ),
{ display_apps : {
@@ -1041,7 +1050,8 @@
failed_metadata :
_.extend( _.clone( mockHistory.data.template ),
{ state : HistoryItem.STATES.FAILED_METADATA })
-
+/*
+*/
});
$( document ).ready( function(){
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/mvc/tags.js
--- /dev/null
+++ b/static/scripts/mvc/tags.js
@@ -0,0 +1,66 @@
+/*==============================================================================
+Backbone MV for Tags
+
+
+TODO:
+move rendering from tagging_common.py
+move functionality from controllers/tag.py
+?? - polymorph on class or simply class as attr?
+
+==============================================================================*/
+/** Single Tag model
+ */
+var Tag = BaseModel.extend( LoggableMixin ).extend({
+
+ // uncomment this out see log messages
+ logger : console,
+
+ defaults : {
+ id : null,
+ itemClass : null
+ },
+
+ toString : function(){
+ return 'Tag()';
+ }
+});
+
+//------------------------------------------------------------------------------
+/** Single Tag view
+ */
+var TagView = BaseView.extend( LoggableMixin ).extend({
+
+ // uncomment this out see log messages
+ logger : console,
+
+ toString : function(){
+ return 'TagView()';
+ }
+});
+
+//==============================================================================
+/** A collection of Tags
+ */
+var TagCollection = Backbone.Collection.extend( LoggableMixin ).extend({
+ model : Tag,
+
+ // uncomment this out see log messages
+ logger : console,
+
+ toString : function(){
+ return 'TagCollection()';
+ }
+});
+
+//------------------------------------------------------------------------------
+/** View for a TagCollection (and it's controls) - as per an hda's tag controls on the history panel
+ */
+var TagList = BaseView.extend( LoggableMixin ).extend({
+
+ // uncomment this out see log messages
+ logger : console,
+
+ toString : function(){
+ return 'TagList()';
+ }
+});
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -1,41 +1,12 @@
/**
* -- Functions for creating large UI elements. --
*/
-
-/**
- * Returns an IconButtonMenuView for the provided configuration.
- * Configuration is a list of dictionaries where each dictionary
- * defines an icon button. Each dictionary must have the following
- * elements: icon_class, title, and on_click.
- */
-var create_icon_buttons_menu = function(config, global_config) {
- if (!global_config) { global_config = {}; }
-
- // Create and initialize menu.
- var buttons = new IconButtonCollection(
- _.map(config, function(button_config) {
- return new IconButton(_.extend(button_config, global_config));
- })
- );
-
- return new IconButtonMenuView( {collection: buttons} );
-};
-
+// =============================================================================
/**
* -- Utility models and views for Galaxy objects. --
*/
/**
- * Necessary Galaxy paths.
- */
-var GalaxyPaths = Backbone.Model.extend({
- defaults: {
- root_path: "",
- image_path: ""
- }
-});
-
-/**
* Clickable button represented as an icon.
*/
var IconButton = Backbone.Model.extend({
@@ -43,14 +14,107 @@
title: "",
icon_class: "",
on_click: null,
- tooltip_config: {}
+ tooltip_config: {},
+
+ isMenuButton : true,
+ id : null,
+ href : null,
+ target : null,
+ enabled : true,
+ visible : true
+ }
+
+ //validate : function( attributes ){
+ //TODO: validate href or on_click
+ //TODO: validate icon_class
+ //}
+});
+
+
+/**
+ *
+ */
+var IconButtonView = Backbone.View.extend({
+ tagName : 'a',
+ className : 'icon-button',
+
+ // jsHint.shut( up )
+ hrefVoidFn : [ 'javascript', ':void(0)' ].join( '' ),
+ fadeSpeed : 0,
+
+ initialize : function(){
+ // better rendering this way (for me anyway)
+ this.model.attributes.tooltip_config = { placement : 'bottom' };
+ this.model.bind( 'change', this.render, this );
+ },
+
+ render : function(){
+ //NOTE: this is meant to be rerendered on a model change
+ // - since most of the rendering is attributes, it gets complicated
+ //TODO: template would be faster, more concise - more PITA
+ var disabled_class = this.model.attributes.icon_class + '_disabled';
+
+ if( !this.model.attributes.visible ){
+ //TODO: fancy - but may not work as you expect
+ this.$el.fadeOut( this.fadeSpeed );
+ }
+
+ if( this.model.attributes.enabled ){
+ if( this.$el.hasClass( disabled_class ) ){
+ this.$el.removeClass( disabled_class );
+ }
+ this.$el.addClass( this.model.attributes.icon_class );
+ } else {
+ this.$el.removeClass( this.model.attributes.icon_class );
+ this.$el.addClass( disabled_class );
+ }
+
+ if( this.model.attributes.isMenuButton ){
+ this.$el.addClass( 'menu-button' );
+ } else {
+ this.$el.removeClass( 'menu-button' );
+ }
+
+ this.$el.data( 'tooltip', false );
+ this.$el.attr( 'data-original-title', null );
+ this.$el.removeClass( 'tooltip' );
+ if( this.model.attributes.title ){
+ this.$el.attr( 'title', this.model.attributes.title ).addClass( 'tooltip' );
+ this.$el.tooltip( this.model.attributes.tooltip_config );
+ }
+
+ this.$el.attr( 'id', ( this.model.attributes.id )?( this.model.attributes.id ):( null ) );
+ this.$el.attr( 'target', ( this.model.attributes.target )?( this.model.attributes.target ):( null ) );
+ this.$el.attr( 'href', ( this.model.attributes.href && !this.model.attributes.on_click )?
+ ( this.model.attributes.href ):( this.hrefVoidFn ) );
+
+ if( this.model.attributes.visible ){
+ this.$el.fadeIn( this.fadeSpeed );
+ }
+ return this;
+ },
+
+ events : {
+ 'click' : 'click'
+ },
+
+ click : function( event ){
+ // if on_click pass to that function
+ if( this.model.attributes.on_click ){
+ this.model.attributes.on_click( event );
+ return false;
+ }
+ // otherwise, bubble up (to href or whatever)
+ return true;
}
});
+
var IconButtonCollection = Backbone.Collection.extend({
model: IconButton
});
+//------------------------------------------------------------------------------
/**
* Menu with multiple icon buttons. Views are not needed nor used for individual buttons.
*/
@@ -82,6 +146,27 @@
});
/**
+ * Returns an IconButtonMenuView for the provided configuration.
+ * Configuration is a list of dictionaries where each dictionary
+ * defines an icon button. Each dictionary must have the following
+ * elements: icon_class, title, and on_click.
+ */
+var create_icon_buttons_menu = function(config, global_config) {
+ if (!global_config) { global_config = {}; }
+
+ // Create and initialize menu.
+ var buttons = new IconButtonCollection(
+ _.map(config, function(button_config) {
+ return new IconButton(_.extend(button_config, global_config));
+ })
+ );
+
+ return new IconButtonMenuView( {collection: buttons} );
+};
+
+
+// =============================================================================
+/**
*
*/
var Grid = Backbone.Collection.extend({
@@ -95,4 +180,101 @@
});
+// =============================================================================
+/**
+ * Necessary Galaxy paths.
+ */
+var GalaxyPaths = Backbone.Model.extend({
+ defaults: {
+ root_path: "",
+ image_path: ""
+ }
+});
+// =============================================================================
+/** Global string localization object (and global short form alias)
+ * set with either:
+ * GalaxyLocalization.setLocalizedString( original, localized )
+ * GalaxyLocalization.setLocalizedString({ original1 : localized1, original2 : localized2 })
+ * get with either:
+ * _l( original )
+ */
+//TODO: move to Galaxy.Localization
+var GalaxyLocalization = jQuery.extend({}, {
+ aliasName : '_l',
+ localizedStrings : {},
+
+ setLocalizedString : function( str_or_obj, localizedString ){
+ // pass in either two strings (english, translated) or an obj (map) of english : translated attributes
+ //console.debug( this + '.setLocalizedString:', str_or_obj, localizedString );
+ var self = this;
+
+ // DRY non-duplicate assignment function
+ var setStringIfNotDuplicate = function( original, localized ){
+ // do not set if identical - strcmp expensive but should only happen once per page per word
+ if( original !== localized ){
+ self.localizedStrings[ original ] = localized;
+ }
+ };
+
+ if( jQuery.type( str_or_obj ) === "string" ){
+ setStringIfNotDuplicate( str_or_obj, localizedString );
+
+ } else if( jQuery.type( str_or_obj ) === "object" ){
+ jQuery.each( str_or_obj, function( key, val ){
+ //console.debug( 'key=>val', key, '=>', val );
+ // could recurse here but no reason
+ setStringIfNotDuplicate( key, val );
+ });
+
+ } else {
+ throw( 'Localization.setLocalizedString needs either a string or object as the first argument,' +
+ ' given: ' + str_or_obj );
+ }
+ },
+
+ localize : function( strToLocalize ){
+ //console.debug( this + '.localize:', strToLocalize );
+ // return the localized version if it's there, the strToLocalize if not
+ // try/catch cheaper than if in
+ try {
+ //var localized = this.localizedStrings[ strToLocalize ];
+ //return localized;
+ return this.localizedStrings[ strToLocalize ];
+ } catch( err ){
+ //TODO??: potentially problematic catch all
+ //console.error( err );
+ return strToLocalize;
+ }
+ },
+
+ toString : function(){ return 'GalaxyLocalization'; }
+});
+
+// global localization alias
+window[ GalaxyLocalization.aliasName ] = function( str ){ return GalaxyLocalization.localize( str ); };
+
+//TEST: setLocalizedString( string, string ), _l( string )
+//TEST: setLocalizedString( hash ), _l( string )
+//TEST: setLocalizedString( string === string ), _l( string )
+//TEST: _l( non assigned string )
+
+
+// =============================================================================
+/** UI icon-button (Backbone.View only - no model)
+ *
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/common-templates.html
--- /dev/null
+++ b/static/scripts/templates/common-templates.html
@@ -0,0 +1,3 @@
+<script type="text/template" class="template-common" id="template-warningmessagesmall">
+ <div class=warningmessagesmall><strong>{{{warning}}}</strong></div>
+</script>
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/compile_templates.py
--- a/static/scripts/templates/compile_templates.py
+++ b/static/scripts/templates/compile_templates.py
@@ -3,24 +3,274 @@
# Script requires handlebars compiler be installed; use node package manager
# to install handlebars.
+"""%prog [options] [ specific .handlebars or .html template file to compile ]
+ Compiles handlebars templates into javascript for better load times and performance.
+
+ Handlebars compilation errors will print to stderr.
+ NOTE!: you will need node.js, npm, and handlebars (installed from npm) in order to compile templates
+ See http://handlebarsjs.com/precompilation.html for more info.
+
+ Should be called from the $GLX_ROOT/static/scripts/templates directory.
+ Compiled templates will be written to the compiled/ directory.
+
+ If the --multi-ext option is passed a file extension string, the script will:
+ * search for *.<extension> in the current directory,
+ * extract each '<script type="text/template"> from the file and write them into a new handlebars file
+ named using the id of the script tag (eg. id="undeleteMsg" -> undeleteMsg.handlebars)
+ * these new handlebars scripts will be compiled along with any other handlebars templates in the dir
+
+ additionally for each of those files, the script will:
+ * extract each '<script type="text/javascript"> from the file and write them ALL into a single js file
+ named using the prefix 'helpers-' and the basename of the enclosing file
+ (eg. common-templates.html -> helpers-common-templates.js )
+ * these new handlebars scripts will be compiled along with any other handlebars templates in the dir
+ These files can be included as if they were templates (eg. h.templates( 'helpers-common-templates' ) )
+
+ This hopefully will allow both multiple templates and their associated Handlebars helper functions to be
+ included in one file.
+
+ Currently, this works (best?) if:
+ * the multiple templates are within one html file
+ * each template is enclosed in a <script> tag with type="text/template" and some id attribute
+ * each helper fn is enclosed in a <script> tag with type="text/javascript" and some id attribute
+ e.g. <script type="text/template" id="history-deletedMsg" class="template">Deleted {{ msg }}</script>
+
+"""
+# ------------------------------------------------------------------------------
+
+TODO = """
+"""
+
import sys
from glob import glob
from subprocess import call
from shutil import copyfile
from os import path
+from optparse import OptionParser
+from HTMLParser import HTMLParser
-cmd = "handlebars %s -f compiled/%s.js"
+import logging
+log = logging.getLogger( __name__ )
-# If specific scripts specified on command line, just pack them, otherwise pack
-# all.
+COMPILED_DIR = 'compiled'
+COMPILED_EXT = '.js'
+COMPILE_CMD_STR = "handlebars %s -f %s"
+COMPILE_MINIMIZE_SWITCH = ' -m'
-if len( sys.argv ) > 1:
- to_pack = sys.argv[1:]
-else:
- to_pack = glob( "*.handlebars" )
+# both of these are off by default for backward compat
+DEFAULT_MINIMIZATION = False
+DEFAULT_MULTI_EXT = None #'.html'
+
+# ------------------------------------------------------------------------------
+class HTMLMultiTemplateParser( HTMLParser ):
+ """Parses multiple templates from an HTML file, saving them to a map of:
+ { id : template_text, ... }
+
+ Templates must:
+ * be within the TEMPLATE_TAG
+ * TEMPLATE_TAG must have a type attribute
+ * that attr must == TEMPLATE_TYPE
+ * TEMPLATE_TAG cannot be nested within one another
+ * TEMPLATE_TAG must have an id attribute
+ """
+ TEMPLATE_TAG = 'script'
+ TEMPLATE_TYPES = [ 'text/template' ]
-for fname in to_pack:
- fname_base = path.splitext( path.split( fname )[1] )[0]
- print "%s --> compiled/%s.js" % ( fname, fname_base )
- out = call( cmd % ( fname, fname_base ), shell=True )
+ HELPER_TAG = 'script'
+ HELPER_TYPES = [ 'text/javascript' ]
+
+ def __init__( self ):
+ HTMLParser.__init__( self )
+ self.templates = {}
+ self.curr_template_id = None
+ self.template_data = ''
+
+ self.helpers = {}
+ self.curr_helper_id = None
+ self.helper_data = ''
+
+ def is_template_tag( self, tag, attr_dict ):
+ # both tag and type attr must match
+ return ( ( tag == self.TEMPLATE_TAG )
+ and ( 'type' in attr_dict )
+ and ( attr_dict[ 'type' ] in self.TEMPLATE_TYPES ) )
+
+ def is_helper_tag( self, tag, attr_dict ):
+ # both tag and type attr must match
+ return ( ( tag == self.HELPER_TAG )
+ and ( 'type' in attr_dict )
+ and ( attr_dict[ 'type' ] in self.HELPER_TYPES ) )
+
+ def handle_starttag( self, tag, attrs ):
+ attr_dict = dict( attrs )
+ if self.is_template_tag( tag, attr_dict ):
+ log.debug( "\t template tag: %s, %s", tag, str( attr_dict ) );
+
+ # as far as I know these tags can't/shouldn't nest/overlap
+ #pre: not already inside a template/helper tag
+ assert self.curr_template_id == None, "Found nested template tag: %s" % ( self.curr_template_id )
+ assert self.curr_helper_id == None, "Found template tag inside helper: %s" % ( self.curr_helper_id )
+ #pre: must have an id
+ assert 'id' in attr_dict, "No id attribute in template: " + str( attr_dict )
+
+ self.curr_template_id = attr_dict[ 'id' ]
+
+ elif self.is_helper_tag( tag, attr_dict ):
+ log.debug( "\t helper tag: %s, %s", tag, str( attr_dict ) );
+
+ #pre: not already inside a template/helper tag
+ assert self.curr_helper_id == None, "Found nested helper tag: %s" % ( self.curr_helper_id )
+ assert self.curr_template_id == None, "Found helper tag inside template: %s" % ( self.curr_template_id )
+ #pre: must have an id
+ assert 'id' in attr_dict, "No id attribute in helper: " + str( attr_dict )
+
+ self.curr_helper_id = attr_dict[ 'id' ]
+
+ def handle_endtag( self, tag ):
+ if( ( tag == self.TEMPLATE_TAG )
+ and ( self.curr_template_id ) ):
+ log.debug( "\t ending template tag :", tag, self.curr_template_id );
+
+ # store the template data by the id
+ if self.template_data:
+ self.templates[ self.curr_template_id ] = self.template_data
+
+ #! reset for next template
+ self.curr_template_id = None
+ self.template_data = ''
+
+ elif( ( tag == self.HELPER_TAG )
+ and ( self.curr_helper_id ) ):
+ log.debug( "\t ending helper tag :", tag, self.curr_template_id );
+
+ # store the template data by the id
+ if self.helper_data:
+ self.helpers[ self.curr_helper_id ] = self.helper_data
+
+ #! reset for next template
+ self.curr_helper_id = None
+ self.helper_data = ''
+
+ def handle_data(self, data):
+ data = data.strip()
+ if data:
+ if self.curr_template_id:
+ log.debug( "\t template text :", data );
+ self.template_data += data
+
+ elif self.curr_helper_id:
+ log.debug( "\t helper js fn :", data );
+ self.helper_data += data
+
+
+# ------------------------------------------------------------------------------
+def break_multi_template( multi_template_filename ):
+ """parse the multi template, writing each template into a new handlebars tmpl and returning their names"""
+ template_filenames = []
+ parser = HTMLMultiTemplateParser()
+
+ # parse the multi template
+ print "\nBreaking multi-template file %s into individual templates and helpers:" % ( multi_template_filename )
+ with open( multi_template_filename, 'r' ) as multi_template_file:
+ # wish I could use a gen here
+ parser.feed( multi_template_file.read() )
+
+ # after breaking, write each indiv. template and save the names
+ for template_id, template_text in parser.templates.items():
+ handlebar_template_filename = template_id + '.handlebars'
+ with open( handlebar_template_filename, 'w' ) as handlebar_template_file:
+ handlebar_template_file.write( template_text )
+
+ template_filenames.append( handlebar_template_filename )
+
+ # write all helpers to a 'helper-' prefixed js file in the compilation dir
+ if parser.helpers:
+ helper_filename = 'helpers-' + path.splitext( multi_template_filename )[0] + '.js'
+ helper_filename = path.join( COMPILED_DIR, helper_filename )
+ with open( helper_filename, 'w' ) as helper_file:
+ for helper_fn_name, helper_fn in parser.helpers.items():
+ print '(helper)', helper_fn_name
+ helper_file.write( helper_fn + '\n' )
+
+ print '\n'.join( template_filenames )
+ return template_filenames
+
+
+# ------------------------------------------------------------------------------
+def compile_template( template_filename, minimize=False ):
+ """compile the given template file (optionally minimizing the js) using subprocess.
+
+ Use the basename of the template file for the outputed js.
+ """
+ template_basename = path.splitext( path.split( template_filename )[1] )[0]
+ compiled_filename = path.join( COMPILED_DIR, template_basename + COMPILED_EXT )
+
+ command_string = COMPILE_CMD_STR % ( template_filename, compiled_filename )
+ if minimize:
+ command_string += COMPILE_MINIMIZE_SWITCH
+ print command_string
+ return call( command_string, shell=True )
+
+
+# ------------------------------------------------------------------------------
+def main( options, args ):
+ """Break multi template files into single templates, compile all single templates.
+
+ If args, compile that as a list of specific handlebars and/or multi template files.
+ """
+ print "(Call this script with the '-h' option for more help)"
+
+ handlebars_templates = []
+ # If specific scripts specified on command line, just compile them,
+ if len( args ) >= 1:
+ handlebars_templates = filter( lambda( x ): x.endswith( '.handlebars' ), args )
+
+ # otherwise compile all in the current dir
+ else:
+ handlebars_templates = glob( '*.handlebars' )
+
+ # if desired, break up any passed-in or found multi template files
+ # adding the names of the new single templates to those needing compilation
+ if options.multi_ext:
+ multi_templates = []
+ if len( args ) >= 1:
+ multi_templates = filter( lambda( x ): x.endswith( options.multi_ext ), args )
+ else:
+ multi_templates = glob( '*' + options.multi_ext )
+
+ for multi_template_filename in multi_templates:
+ handlebars_templates.extend( break_multi_template( multi_template_filename ) )
+
+ # unique filenames only (Q&D)
+ handlebars_templates = list( set( handlebars_templates ) )
+
+ # compile the templates
+ print "\nCompiling templates:"
+ filenames_w_possible_errors = []
+ for handlebars_template in handlebars_templates:
+ shell_ret = compile_template( handlebars_template, options.minimize )
+ if shell_ret:
+ filenames_w_possible_errors.append( handlebars_template )
+
+ # report any possible errors
+ if filenames_w_possible_errors:
+ print "\nThere may have been errors on the following files:"
+ print ',\n'.join( filenames_w_possible_errors )
+ print "\nCall this script with the '-h' for more help"
+
+
+# ------------------------------------------------------------------------------
+if __name__ == '__main__':
+ optparser = OptionParser( usage=__doc__ )
+ optparser.add_option( '-m', '--minimize', dest='minimize', action='store_true', default=DEFAULT_MINIMIZATION,
+ help=( 'minimize compiled template scripts via handlebars '
+ + '(defaults to %s)' % DEFAULT_MINIMIZATION ) )
+ optparser.add_option( '--multi-ext', dest='multi_ext', metavar="FILE_EXTENSION", default=DEFAULT_MULTI_EXT,
+ help=( 'indicates that files ending with the given string contain multiple '
+ + 'templates and the script should break those into individual '
+ + 'handlebars templates (defaults to "%s")' ) % DEFAULT_MULTI_EXT )
+
+ ( options, args ) = optparser.parse_args()
+ sys.exit( main( options, args ) )
+
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/compiled/template-history-hdaSummary.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-history-hdaSummary.js
@@ -0,0 +1,25 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-history-hdaSummary'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, foundHelper, functionType="function", escapeExpression=this.escapeExpression;
+
+
+ foundHelper = helpers.misc_blurb;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.misc_blurb; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "<br />\nformat: <span class=\"";
+ foundHelper = helpers.data_type;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.data_type; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\">";
+ foundHelper = helpers.data_type;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.data_type; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "</span>, ',\ndatabase: ";
+ foundHelper = helpers.dbkeyHTML;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.dbkeyHTML; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1);
+ return buffer;});
+})();
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/compiled/template-history-warning-messages.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-history-warning-messages.js
@@ -0,0 +1,87 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-history-warning-messages'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+
+ var buffer = "", stack1;
+ buffer += "\n<div class=warningmessagesmall><strong>\nThis dataset has been deleted.\n ";
+ stack1 = depth0.undelete_url;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(2, program2, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n</strong></div>\n";
+ return buffer;}
+function program2(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n Click <a href=\"";
+ foundHelper = helpers.undelete_url;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.undelete_url; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\" class=\"historyItemUndelete\" id=\"historyItemUndeleter-";
+ foundHelper = helpers.id;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\"\n target=\"galaxy_history\">here</a> to undelete it\n ";
+ stack1 = depth0.purge_url;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(3, program3, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n ";
+ return buffer;}
+function program3(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n or\n <a href=\"";
+ foundHelper = helpers.purge_url;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.purge_url; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\" class=\"historyItemPurge\" id=\"historyItemPurger-";
+ foundHelper = helpers.id;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\"\n target=\"galaxy_history\">here</a> to immediately remove it from disk\n ";
+ return buffer;}
+
+function program5(depth0,data) {
+
+
+ return "\n<div class=warningmessagesmall><strong>\nThis dataset has been deleted and removed from disk.\n</strong></div>\n";}
+
+function program7(depth0,data) {
+
+ var buffer = "", stack1;
+ buffer += "\n<div class=warningmessagesmall><strong>\nThis dataset has been hidden.\n ";
+ stack1 = depth0.undelete_url;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(8, program8, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n</strong></div>\n";
+ return buffer;}
+function program8(depth0,data) {
+
+ var buffer = "", stack1, foundHelper;
+ buffer += "\n Click <a href=\"";
+ foundHelper = helpers.unhide_url;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.unhide_url; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\" class=\"historyItemUnhide\" id=\"historyItemUnhider-";
+ foundHelper = helpers.id;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + "\"\n target=\"galaxy_history\">here</a> to unhide it\n ";
+ return buffer;}
+
+ stack1 = depth0.deleted;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n\n";
+ stack1 = depth0.purged;
+ stack1 = helpers['if'].call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(5, program5, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n\n";
+ stack1 = depth0.visible;
+ stack1 = helpers.unless.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(7, program7, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ return buffer;});
+})();
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/compiled/template-warningmessagesmall.js
--- /dev/null
+++ b/static/scripts/templates/compiled/template-warningmessagesmall.js
@@ -0,0 +1,15 @@
+(function() {
+ var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
+templates['template-warningmessagesmall'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ helpers = helpers || Handlebars.helpers;
+ var buffer = "", stack1, foundHelper, functionType="function";
+
+
+ buffer += "<div class=warningmessagesmall><strong>";
+ foundHelper = helpers.warning;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.warning; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "</strong></div>";
+ return buffer;});
+})();
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/history-templates.html
--- /dev/null
+++ b/static/scripts/templates/history-templates.html
@@ -0,0 +1,38 @@
+<script type="text/template" class="template-history" id="template-history-warning-messages">
+{{#if deleted}}
+<div class=warningmessagesmall><strong>
+This dataset has been deleted.
+ {{#if undelete_url}}
+ Click <a href="{{ undelete_url }}" class="historyItemUndelete" id="historyItemUndeleter-{{ id }}"
+ target="galaxy_history">here</a> to undelete it
+ {{#if purge_url}}
+ or
+ <a href="{{ purge_url }}" class="historyItemPurge" id="historyItemPurger-{{ id }}"
+ target="galaxy_history">here</a> to immediately remove it from disk
+ {{/if}}
+ {{/if}}
+</strong></div>
+{{/if}}
+
+{{#if purged}}
+<div class=warningmessagesmall><strong>
+This dataset has been deleted and removed from disk.
+</strong></div>
+{{/if}}
+
+{{#unless visible}}
+<div class=warningmessagesmall><strong>
+This dataset has been hidden.
+ {{#if undelete_url}}
+ Click <a href="{{ unhide_url }}" class="historyItemUnhide" id="historyItemUnhider-{{ id }}"
+ target="galaxy_history">here</a> to unhide it
+ {{/if}}
+</strong></div>
+{{/unless}}
+</script>
+
+<script type="text/template" class="template-history" id="template-history-hdaSummary">
+{{ misc_blurb }}<br />
+format: <span class="{{ data_type }}">{{ data_type }}</span>, ',
+database: {{ dbkeyHTML }}
+</script>
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/template-history-hdaSummary.handlebars
--- /dev/null
+++ b/static/scripts/templates/template-history-hdaSummary.handlebars
@@ -0,0 +1,3 @@
+{{ misc_blurb }}<br />
+format: <span class="{{ data_type }}">{{ data_type }}</span>, ',
+database: {{ dbkeyHTML }}
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/template-history-warning-messages.handlebars
--- /dev/null
+++ b/static/scripts/templates/template-history-warning-messages.handlebars
@@ -0,0 +1,30 @@
+{{#if deleted}}
+<div class=warningmessagesmall><strong>
+This dataset has been deleted.
+ {{#if undelete_url}}
+ Click <a href="{{ undelete_url }}" class="historyItemUndelete" id="historyItemUndeleter-{{ id }}"
+ target="galaxy_history">here</a> to undelete it
+ {{#if purge_url}}
+ or
+ <a href="{{ purge_url }}" class="historyItemPurge" id="historyItemPurger-{{ id }}"
+ target="galaxy_history">here</a> to immediately remove it from disk
+ {{/if}}
+ {{/if}}
+</strong></div>
+{{/if}}
+
+{{#if purged}}
+<div class=warningmessagesmall><strong>
+This dataset has been deleted and removed from disk.
+</strong></div>
+{{/if}}
+
+{{#unless visible}}
+<div class=warningmessagesmall><strong>
+This dataset has been hidden.
+ {{#if undelete_url}}
+ Click <a href="{{ unhide_url }}" class="historyItemUnhide" id="historyItemUnhider-{{ id }}"
+ target="galaxy_history">here</a> to unhide it
+ {{/if}}
+</strong></div>
+{{/unless}}
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 static/scripts/templates/template-warningmessagesmall.handlebars
--- /dev/null
+++ b/static/scripts/templates/template-warningmessagesmall.handlebars
@@ -0,0 +1,1 @@
+<div class=warningmessagesmall><strong>{{{warning}}}</strong></div>
\ No newline at end of file
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 templates/base.mako
--- a/templates/base.mako
+++ b/templates/base.mako
@@ -26,7 +26,18 @@
## <!--[if lt IE 7]>
## <script type='text/javascript' src="/static/scripts/libs/IE/IE7.js"></script>
## <![endif]-->
- ${h.js( "libs/jquery/jquery", "libs/bootstrap", "galaxy.base", "libs/underscore", "libs/backbone/backbone", "libs/backbone/backbone-relational", "libs/handlebars.runtime", "mvc/ui" )}
+
+ ${h.js(
+ "libs/jquery/jquery",
+ "libs/bootstrap",
+ "galaxy.base",
+ "libs/underscore",
+ "libs/backbone/backbone",
+ "libs/backbone/backbone-relational",
+ "libs/handlebars.runtime",
+ "mvc/ui"
+ )}
+
<script type="text/javascript">
// Set up needed paths.
var galaxy_paths = new GalaxyPaths({
diff -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 templates/root/alternate_history.mako
--- a/templates/root/alternate_history.mako
+++ b/templates/root/alternate_history.mako
@@ -54,7 +54,7 @@
<%def name="get_urls_for_hda( hda, encoded_data_id, for_editing )"><%
from galaxy.datatypes.metadata import FileParameter
-
+
data_dict = {}
def add_to_data( **kwargs ):
data_dict.update( kwargs )
@@ -79,7 +79,10 @@
#TODO??: better way to do urls (move into js galaxy_paths (decorate) - _not_ dataset specific)
deleted = hda.deleted
- purged = hda.purged or hda.dataset.purged
+ purged = hda.purged
+
+ #purged = purged or hda.dataset.purged //??
+
# all of these urls are 'datasets/data_id/<action>
if not ( dataset_purged or purged ) and for_editing:
add_to_data( undelete_url=h.url_for( controller='dataset', action='undelete', dataset_id=encoded_data_id ) )
@@ -106,7 +109,21 @@
display_url = h.url_for( controller='dataset', action='display', dataset_id=encoded_data_id, preview=True, filename='' )
add_to_data( display_url=display_url )
- if app.config.get_bool( 'enable_tracks', False ) and hda.ext in app.datatypes_registry.get_available_tracks():
+ # edit attr button
+ if for_editing:
+ if not( hda.deleted or hda.purged ):
+ edit_url = h.url_for( controller='dataset', action='edit', dataset_id=encoded_data_id )
+ add_to_data( edit_url=edit_url )
+
+ # delete button
+ if for_editing and not ( dataset_purged or purged ):
+ add_to_data( delete_url=h.url_for( controller='dataset', action='delete', dataset_id=encoded_data_id ) )
+ ##TODO: loose end
+ ##show_deleted_on_refresh=show_deleted_on_refresh ) )
+ #show_deleted_on_refresh=False ) )
+
+ #print app
+ if hda.ext in app.datatypes_registry.get_available_tracks():
# do these need _localized_ dbkeys?
trackster_urls = {}
if hda.dbkey != '?':
@@ -117,11 +134,21 @@
trackster_urls[ 'hda-url' ] = data_url
trackster_urls[ 'action-url' ] = h.url_for( controller='tracks', action='browser', dataset_id=encoded_data_id )
trackster_urls[ 'new-url' ] = h.url_for( controller='tracks', action='index', dataset_id=encoded_data_id, default_dbkey=hda.dbkey )
- add_to_data( trackster_url=trackster_url )
+ add_to_data( trackster_url=trackster_urls )
if trans.user:
- add_to_data( retag_url=( h.url_for( controller='tag', action='retag', item_class=hda.__class__.__name__, item_id=encoded_data_id ) ) )
+ add_to_data( ajax_get_tag_url=( h.url_for(
+ controller='tag', action='get_tagging_elt_async',
+ item_class=hda.__class__.__name__, item_id=encoded_data_id ) ) )
+ add_to_data( retag_url=( h.url_for(
+ controller='tag', action='retag',
+ item_class=hda.__class__.__name__, item_id=encoded_data_id ) ) )
+
add_to_data( annotate_url=( h.url_for( controller='dataset', action='annotate', id=encoded_data_id ) ) )
+ add_to_data( ajax_get_annotation_url=( h.url_for(
+ controller='dataset', action='get_annotation_async', id=encoded_data_id ) ) )
+ add_to_data( ajax_set_annotation_url=( h.url_for(
+ controller='/dataset', action='annotate_async', id=encoded_data_id ) ) )
display_type_display_links = {}
#TODO: this doesn't seem to get called with the hda I'm using. What would call this? How can I spoof it?
@@ -182,8 +209,8 @@
data_dict.update( kwargs )
# trans
+ add_to_data( non_encoded_id=data.id )
encoded_data_id = trans.security.encode_id( data.id )
- #print data.name, encoded_data_id
add_to_data( id=encoded_data_id )
# localize dbkey
@@ -205,8 +232,8 @@
add_to_data( accessible=accessible )
url_dict = get_urls_for_hda( data, encoded_data_id, for_editing )
+ data_dict.update( url_dict )
#print 'url_dict:', pformat( url_dict, indent=2 )
- data_dict.update( url_dict )
#print 'data_dict:', pformat( data_dict, indent=2 ), "\n"
#print data_dict
@@ -227,7 +254,7 @@
user_is_admin = trans.user_is_admin()
user_roles = trans.get_current_user_roles()
prepped_hdas = [
- prep_hda( hda, true ) for hda in datasets ]
+ prep_hda( hda, True ) for hda in datasets ]
context_dict = {
'history' : {
@@ -257,74 +284,66 @@
<%def name="javascripts()">
${parent.javascripts()}
- ${h.templates( "tool_link", "panel_section", "tool_search" )}
- ${h.js( "galaxy.base", "json2", "autocomplete_tagging", "bootstrap", "mvc/tools", "mvc/history" )}
+
+ ${h.js(
+ "libs/json2", "libs/jquery/jstorage", "libs/jquery/jquery.autocomplete",
+ ##"libs/handlebars.full",
+ "galaxy.autocom_tagging",
+ "mvc/base-mvc", "mvc/ui"
+ )}
+
+ ${h.templates(
+ "helpers-common-templates",
+ "template-warningmessagesmall",
+
+ "template-history-warning-messages",
+ "template-history-hdaSummary"
+ )}
+
+ ## if using in-dom templates they need to go here (before the Backbone classes are defined)
+ ##NOTE: it's impossible(?) to include _ templates in this way bc they use identical delims as mako
+ ## (without altering Underscore.templateSettings)
+ ##<%include file="../../static/scripts/templates/common-templates.html" />
+ ##<%include file="../../static/scripts/templates/history-templates.html" />
+
+ ${h.js(
+ "mvc/history"
+ ##"mvc/tags", "mvc/annotations"
+ )}
<script type="text/javascript">
- ##//var localization = local( ${ create_localization_json( get_page_localized_strings() ) } );
+ GalaxyLocalization.setLocalizedString( ${ create_localization_json( get_page_localized_strings() ) } );
+ // add needed controller urls to GalaxyPaths
+ galaxy_paths.set( 'dataset_path', "${h.url_for( controller='dataset' )}" )
+
// Init. on document load.
var pageData = ${context_to_js()};
- // add needed controller urls to GalaxyPaths
- galaxy_paths.set( 'dataset_path', "${h.url_for( controller='dataset' )}" )
+ //USE_MOCK_DATA = true;
$(function(){
if( console && console.debug ){ console.debug( 'using backbone.js in history panel' ); }
- if( window.USE_MOCK_DATA ){ return; }
+ if( window.USE_MOCK_DATA ){
+ if( console && console.debug ){ console.debug( '\t using mock data' ); }
+ createMockHistoryData();
+ return;
+ }
glx_history = new History( pageData.history ).loadDatasetsAsHistoryItems( pageData.hdas );
glx_history_view = new HistoryView({ model: glx_history });
glx_history_view.render();
- //hi = glx_history.items.at( 0 );
- //hi_view = new HistoryItemView({ model: hi });
- //$( 'body' ).append( hi_view.render() );
+ hi = glx_history.items.at( 0 );
+ hi_view = new HistoryItemView({ model: hi });
+ $( 'body' ).append( hi_view.render() );
});
</script>
-
- <script type='text/javascript'>
-function tag_handling(parent_elt) {
- $(parent_elt).find("a.icon-button.tags").each( function() {
- // Use links parameters but custom URL as ajax URL.
- $(this).click( function() {
- // Get tag area, tag element.
- var history_item = $(this).parents(".historyItem");
- var tag_area = history_item.find(".tag-area");
- var tag_elt = history_item.find(".tag-elt");
-
- // Show or hide tag area; if showing tag area and it's empty, fill it.
- if ( tag_area.is( ":hidden" ) ) {
- if (!tag_elt.html()) {
- // Need to fill tag element.
- var href_parms = $(this).attr("href").split("?")[1];
- var ajax_url = "${h.url_for( controller='tag', action='get_tagging_elt_async' )}?" + href_parms;
- $.ajax({
- url: ajax_url,
- error: function() { alert( "Tagging failed" ) },
- success: function(tag_elt_html) {
- tag_elt.html(tag_elt_html);
- tag_elt.find(".tooltip").tooltip();
- tag_area.slideDown("fast");
- }
- });
- } else {
- // Tag element is filled; show.
- tag_area.slideDown("fast");
- }
- } else {
- // Hide.
- tag_area.slideUp("fast");
- }
- return false;
- });
- });
-};
- </script></%def><%def name="stylesheets()">
${parent.stylesheets()}
- <style>
+ ${h.css("base", "history", "autocomplete_tagging" )}
+ <style>"
.historyItemBody {
display: none;
}
https://bitbucket.org/galaxy/galaxy-central/changeset/e2cb958b3f75/
changeset: e2cb958b3f75
user: carlfeberhard
date: 2012-09-12 17:09:09
summary: clean debug statements from history.js
affected #: 1 file
diff -r ce64f5d64ff4a55d85bd0e9ba2c3ec712357c115 -r e2cb958b3f7596744bb08febc858fa25139fd1ac static/scripts/mvc/history.js
--- a/static/scripts/mvc/history.js
+++ b/static/scripts/mvc/history.js
@@ -1,4 +1,3 @@
-if( console ){ console.debug( 'history mvc loaded' ); }
/*
Backbone.js implementation of history panel
@@ -678,7 +677,7 @@
url: this.model.get( 'ajax_get_tag_url' ),
error: function() { alert( "Tagging failed" ) },
success: function(tag_elt_html) {
- console.debug( 'tag_elt_html:', tag_elt_html );
+ this.log( 'tag_elt_html:', tag_elt_html );
tagElt.html(tag_elt_html);
tagElt.find(".tooltip").tooltip();
tagArea.slideDown("fast");
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: natefoo: Allow for overriding app.config.track_jobs_in_database.
by Bitbucket 11 Sep '12
by Bitbucket 11 Sep '12
11 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/244b4cb100d1/
changeset: 244b4cb100d1
user: natefoo
date: 2012-09-11 20:58:13
summary: Allow for overriding app.config.track_jobs_in_database.
affected #: 2 files
diff -r 9a8614de4141be276ab4a416ef7b0def8c5703d2 -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -105,6 +105,7 @@
self.smtp_server = kwargs.get( 'smtp_server', None )
self.smtp_username = kwargs.get( 'smtp_username', None )
self.smtp_password = kwargs.get( 'smtp_password', None )
+ self.track_jobs_in_database = kwargs.get( 'track_jobs_in_database', None )
self.start_job_runners = kwargs.get( 'start_job_runners', None )
self.expose_dataset_path = string_as_bool( kwargs.get( 'expose_dataset_path', 'False' ) )
# External Service types used in sample tracking
@@ -210,9 +211,12 @@
self.job_handlers = [ x.strip() for x in kwargs.get('job_handlers', self.server_name).split(',') ]
self.default_job_handlers = [ x.strip() for x in kwargs.get('default_job_handlers', ','.join( self.job_handlers ) ).split(',') ]
# Use database for IPC unless this is a standalone server (or multiple servers doing self dispatching in memory)
- self.track_jobs_in_database = True
- if ( len( self.job_handlers ) == 1 ) and ( self.job_handlers[0] == self.server_name ) and ( self.job_manager == self.server_name ):
- self.track_jobs_in_database = False
+ if self.track_jobs_in_database is None or self.track_jobs_in_database == "None":
+ self.track_jobs_in_database = True
+ if ( len( self.job_handlers ) == 1 ) and ( self.job_handlers[0] == self.server_name ) and ( self.job_manager == self.server_name ):
+ self.track_jobs_in_database = False
+ else:
+ self.track_jobs_in_database = string_as_bool( self.track_jobs_in_database )
# Store per-tool runner configs
self.tool_handlers = self.__read_tool_job_config( global_conf_parser, 'galaxy:tool_handlers', 'name' )
self.tool_runners = self.__read_tool_job_config( global_conf_parser, 'galaxy:tool_runners', 'url' )
diff -r 9a8614de4141be276ab4a416ef7b0def8c5703d2 -r 244b4cb100d19ed7d005fabcf2390204b3630aa5 universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -586,6 +586,15 @@
# used for jobs without explicit handlers.
#default_job_handlers = main
+# In multiprocess configurations, notification between processes about new jobs
+# is done via the database. In single process configurations, this is done in
+# memory, which is a bit quicker. Galaxy tries to automatically determine
+# which method it should used based on your manager/handler configuration
+# above, but can't reliably determine if you have multiple processes for web
+# servers but only a single process as a manager/handler. In that scenario,
+# you can override the tracking method by setting the following to True:
+#track_jobs_in_database = None
+
# This enables splitting of jobs into tasks, if specified by the particular tool config.
# This is a new feature and not recommended for production servers yet.
#use_tasked_jobs = False
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: removed console.debug statement from history.mako
by Bitbucket 11 Sep '12
by Bitbucket 11 Sep '12
11 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/9a8614de4141/
changeset: 9a8614de4141
user: carlfeberhard
date: 2012-09-11 19:52:55
summary: removed console.debug statement from history.mako
affected #: 1 file
diff -r 7b1b6e81b1756234001eb1c3a32d7a9b98da92c3 -r 9a8614de4141be276ab4a416ef7b0def8c5703d2 templates/root/history.mako
--- a/templates/root/history.mako
+++ b/templates/root/history.mako
@@ -233,7 +233,6 @@
//TODO: hack (github has an issue on this - see how it's resolved)
// fix for two line bootstrap tooltips when placement: above
$( this ).find( '.tooltip' ).each( function(){
- console.debug( 'tooltip:', this );
var $this = $( this );
// documented method - that doesn't seem to work
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: natefoo: Do not report the disk usage of purged HDAs in history disk usage.
by Bitbucket 11 Sep '12
by Bitbucket 11 Sep '12
11 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/7b1b6e81b175/
changeset: 7b1b6e81b175
user: natefoo
date: 2012-09-11 19:33:20
summary: Do not report the disk usage of purged HDAs in history disk usage.
affected #: 1 file
diff -r 2b4111248390ae6340f4bec54d0cff29f48665fb -r 7b1b6e81b1756234001eb1c3a32d7a9b98da92c3 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -685,6 +685,7 @@
db_session = object_session( self )
rval = db_session.query( func.sum( db_session.query( HistoryDatasetAssociation.dataset_id, Dataset.total_size ).join( Dataset )
.filter( HistoryDatasetAssociation.table.c.history_id == self.id )
+ .filter( HistoryDatasetAssociation.purged != True )
.filter( Dataset.purged != True )
.distinct().subquery().c.total_size ) ).first()[0]
if rval is None:
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: natefoo: Distributed object store fixes for pgcleanup.py.
by Bitbucket 11 Sep '12
by Bitbucket 11 Sep '12
11 Sep '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/2b4111248390/
changeset: 2b4111248390
user: natefoo
date: 2012-09-11 18:22:03
summary: Distributed object store fixes for pgcleanup.py.
affected #: 1 file
diff -r 63cd6402badaf98f26080e71b1f0ed49db30e3ab -r 2b4111248390ae6340f4bec54d0cff29f48665fb scripts/cleanup_datasets/pgcleanup.py
--- a/scripts/cleanup_datasets/pgcleanup.py
+++ b/scripts/cleanup_datasets/pgcleanup.py
@@ -31,6 +31,12 @@
log = logging.getLogger()
+class MetadataFile(Bunch):
+ pass
+
+class Dataset(Bunch):
+ pass
+
class Cleanup(object):
def __init__(self):
self.options = None
@@ -199,12 +205,13 @@
log.info("All changes committed")
def _remove_metadata_file(self, id, object_store_id, action_name):
- metadata_file = Bunch(id=id, object_store_id=object_store_id)
+ metadata_file = MetadataFile(id=id, object_store_id=object_store_id)
try:
filename = self.object_store.get_filename(metadata_file, extra_dir='_metadata_files', extra_dir_at_root=True, alt_name="metadata_%d.dat" % id)
self._log('Removing from disk: %s' % filename, action_name)
- except ObjectNotFound:
+ except (ObjectNotFound, AttributeError), e:
+ log.error('Unable to get MetadataFile %s filename: %s' % (id, e))
return
if not self.options.dry_run:
@@ -656,8 +663,6 @@
"""
log.info('Marking purged all Datasets marked deleted that are older than the specified number of days.')
- # TODO: force retry option
-
event_id = self._create_event(inspect.stack()[0][3])
sql = """
@@ -700,15 +705,16 @@
self._log('Marked Dataset purged: %s in Object Store: %s' % (tup[0], tup[1]))
# always try to remove the "object store path" - if it's at an external_filename, that file will be untouched anyway (which is what we want)
- dataset = Bunch(id=tup[0], object_store_id=tup[1])
+ dataset = Dataset(id=tup[0], object_store_id=tup[1])
try:
filename = self.object_store.get_filename(dataset)
- except ObjectNotFound, AttributeError:
+ except (ObjectNotFound, AttributeError), e:
+ log.error('Unable to get Dataset %s filename: %s' % (tup[0], e))
continue
try:
extra_files_dir = self.object_store.get_filename(dataset, dir_only=True, extra_dir="dataset_%d_files" % tup[0])
- except ObjectNotFound, AttributeError:
+ except (ObjectNotFound, AttributeError):
extra_files_dir = None
# don't check for existence of the dataset, it should exist
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