").addClass("menu dropdown-menu").addClass("pull-"+this.model.get("pull")).attr("role","menu"),this.$el.append(this.$menu));var i=a("").addClass("dropdown-item").attr({href:t.href,target:t.target}).append(a("").addClass("fa").addClass(t.icon).css("display",t.icon?"inline-block":"none")).append(t.title).on("click",function(e){t.onclick&&(e.preventDefault(),t.onclick())});this.$menu.append(a("").append(i)),t.divider&&this.$menu.append(a("").addClass("divider"))}});return{ButtonDefault:t,ButtonLink:i,ButtonIcon:s,ButtonCheck:n,ButtonMenu:r}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(1))},function(e,t,i){var n,s;(function(i,o,a){n=[],s=function(){var e=i.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:!0,id:null,href:null,target:null,enabled:!0,visible:!0,tooltip_config:{}}}),t=i.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"},this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var e=this.template(this.model.toJSON());return e.tooltip(this.model.get("tooltip_config")),this.$el.replaceWith(e),this.setElement(e),this},events:{click:"click"},click:function(e){return o.isFunction(this.model.get("on_click"))?(this.model.get("on_click")(e),!1):!0},template:function(e){var t='title="'+e.title+'" class="icon-button';return e.is_menu_button&&(t+=" menu-button"),t+=" "+e.icon_class,e.enabled||(t+="_disabled"),t+='"',e.id&&(t+=' id="'+e.id+'"'),t+=' href="'+e.href+'"',e.target&&(t+=' target="'+e.target+'"'),e.visible||(t+=' style="display: none;"'),t=e.enabled?"":"",a(t)}}),n=i.Collection.extend({model:e}),s=i.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var e=this;return this.collection.each(function(t){var i=a("").attr("href","javascript:void(0)").attr("title",t.attributes.title).addClass("icon-button menu-button").addClass(t.attributes.icon_class).appendTo(e.$el).click(t.attributes.on_click);t.attributes.tooltip_config&&i.tooltip(t.attributes.tooltip_config);var n=t.get("options");n&&make_popupmenu(i,n)}),this}}),r=function(t,i){i||(i={});var a=new n(o.map(t,function(t){return new e(o.extend(t,i))}));return new s({collection:a})};return{IconButton:e,IconButtonView:t,IconButtonCollection:n,IconButtonMenuView:s,create_icon_buttons_menu:r}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(2),i(1))},function(e,t,i){var n,s,o;!function(a){s=[i(1)],n=a,o="function"==typeof n?n.apply(t,s):n,!(void 0!==o&&(e.exports=o))}(function(e){"use_strict";var t=e;t.fn.make_text_editable=function(e){var i="num_cols"in e?e.num_cols:30,n="num_rows"in e?e.num_rows:4,s="use_textarea"in e?e.use_textarea:!1,o="on_finish"in e?e.on_finish:null,a="help_text"in e?e.help_text:null,r=t(this);return r.addClass("editable-text").click(function(a){if(!(t(this).children(":input").length>0)){r.removeClass("editable-text");var l,c,d=function(e){r.find(":input").remove(),""!==e?r.text(e):r.html(" "),r.addClass("editable-text"),o&&o(e)},h="cur_text"in e?e.cur_text:r.text();s?(l=t("").attr({rows:n,cols:i}).text(t.trim(h)).keyup(function(e){27===e.keyCode&&d(h)}),c=t("").text("Done").click(function(){return d(l.val()),!1})):l=t("").attr({value:t.trim(h),size:i}).blur(function(){d(h)}).keyup(function(e){27===e.keyCode?t(this).trigger("blur"):13===e.keyCode&&d(t(this).val()),e.stopPropagation()}),r.text(""),r.append(l),c&&r.append(c),l.focus(),l.select(),a.stopPropagation()}}),a&&r.attr("title",a).tooltip(),r}})},function(e,t,i){var n;(function(s,o){n=function(){function e(e){e+="";for(var t=/(\d+)(\d{3})/;t.test(e);)e=e.replace(t,"$1,$2");return e}var t=function(e){return"promise"in e},i=s.Model.extend({defaults:{ajax_settings:{},interval:1e3,success_fn:function(e){return!0}},go:function(){var e=o.Deferred(),t=this,i=t.get("ajax_settings"),n=t.get("success_fn"),s=t.get("interval"),a=function(){o.ajax(i).success(function(t){n(t)?e.resolve(t):setTimeout(a,s)})};return a(),e}}),n=function(e){e||(e="#ffffff"),"string"==typeof e&&(e=[e]);for(var t=0;t>16,s=(65280&i)>>8,o=255&i,d=p(n,s,o),f=!0,t=0;t>16,l=(65280&a)>>8,c=255&a,h=p(r,l,c),u=g(n,s,o,r,l,c),Math.abs(d-h)<40||200>u){f=!1;break}m++}while(!f&&10>=m);return"#"+(16777216+i).toString(16).substr(1,6)};return{commatize:e,is_deferred:t,ServerStateDeferred:i,get_random_color:n}}.call(t,i,t,e),!(void 0!==n&&(e.exports=n))}).call(t,i(3),i(1))},function(e,t,i){var n,s;(function(o,a){n=[i(26),i(6),i(5)],s=function(e,t,i){e=e||window.BibtexParser;var n="citation",s=o.Model.extend(t.LoggableMixin).extend({_logNamespace:n,defaults:{content:""},initialize:function(){var t;try{t=e(this.attributes.content)}catch(i){return}if(t.errors.length){var n=t.errors.reduce(function(e,t){return e+"; "+t});this.log("Error parsing bibtex: "+n)}if(this._fields={},this.entry=a.first(t.entries),this.entry){var s=this.entry.Fields;for(var o in s){var r=s[o],l=o.toLowerCase();this._fields[l]=r}}},entryType:function(){return this.entry?this.entry.EntryType:void 0},fields:function(){return this._fields}}),r=o.Collection.extend(t.LoggableMixin).extend({_logNamespace:n,urlRoot:Galaxy.root+"api",partial:!0,model:s}),l=r.extend({url:function(){return this.urlRoot+"/histories/"+this.history_id+"/citations"}}),c=r.extend({url:function(){return this.urlRoot+"/tools/"+this.tool_id+"/citations"},partial:!1});return{Citation:s,HistoryCitationCollection:l,ToolCitationCollection:c}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(2))},function(e,t,i){var n,s;(function(o,a){n=[i(4),i(7),i(8),i(42)],s=function(e,t,i,n){var s=n.extend({initialize:function(s){var o=this;s.listen_to_history=!0,s.always_refresh=!1,this.modal=parent.Galaxy.modal||new i.View,n.prototype.initialize.call(this,e.merge({customize:function(e){e.buttons={execute:execute_btn=new t.Button({icon:"fa-check",tooltip:"Execute: "+e.name+" ("+e.version+")",title:"Execute",cls:"ui-button btn btn-primary",floating:"clear",onclick:function(){execute_btn.wait(),o.portlet.disable(),o.submit(e,function(){execute_btn.unwait(),o.portlet.enable()})}})},e.job_id&&e.job_remap&&(e.inputs.rerun_remap_job_id={label:"Resume dependencies from this job",name:"rerun_remap_job_id",type:"select",display:"radio",ignore:"__ignore__",value:"__ignore__",options:[["Yes",e.job_id],["No","__ignore__"]],help:"The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run."})}},s))},submit:function(t,i){var n=this,s={tool_id:t.id,tool_version:t.version,inputs:this.data.create()};if(this.trigger("reset"),!n.validate(s))return Galaxy.emit.debug("tool-form::submit()","Submission canceled. Validation failed."),void(i&&i());if(t.action!==Galaxy.root+"tool_runner/index"){var r=o("").attr({action:t.action,method:t.method,enctype:t.enctype});return a.each(s.inputs,function(e,t){r.append(o("").attr({name:t,value:e}))}),r.hide().appendTo("body").submit().remove(),void(i&&i())}Galaxy.emit.debug("tool-form::submit()","Validation complete.",s),e.request({type:"POST",url:Galaxy.root+"api/tools",data:s,success:function(e){i&&i(),n.$el.children().hide(),n.$el.append(n._templateSuccess(e)),parent.Galaxy&&parent.Galaxy.currHistoryPanel&&parent.Galaxy.currHistoryPanel.refreshContents()},error:function(e){i&&i(),Galaxy.emit.debug("tool-form::submit","Submission failed.",e);var t=!1;if(e&&e.err_data){var o=n.data.matchResponse(e.err_data);for(var a in o){n.highlight(a,o[a]),t=!0;break}}t||n.modal.show({title:"Job submission failed",body:e&&e.err_msg||n._templateError(s),buttons:{Close:function(){n.modal.hide()}}})}})},validate:function(e){var t=e.inputs,i=-1,n=null;for(var s in t){var o=t[s],a=this.data.match(s),r=this.field_list[a],l=this.input_list[a];if(a&&l&&r){if(!l.optional&&null==o)return this.highlight(a),!1;if(o&&o.batch){var c=o.values.length,d=c>0&&o.values[0]&&o.values[0].src;if(d)if(null===n)n=d;else if(n!==d)return this.highlight(a,"Please select either dataset or dataset list fields for all batch mode fields."),!1;if(-1===i)i=c;else if(i!==c)return this.highlight(a,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains "+c+" selection(s) while a previous field contains "+i+"."),!1}}else Galaxy.emit.debug("tool-form::validate()","Retrieving input objects failed.")}return!0},_templateSuccess:function(e){if(e.jobs&&e.jobs.length>0){var t=e.jobs.length,i=1==t?"1 job has":t+" jobs have",n=o("").addClass("donemessagelarge").append(o("").text(i+" been successfully added to the queue - resulting in the following datasets:"));return a.each(e.outputs,function(e){
-n.append(o("").addClass("messagerow").append(o("").text(e.hid+": "+e.name)))}),n.append(o("").append("").text("You can check the status of queued jobs and view the resulting data by refreshing the History pane. When the job has been run the status will change from 'running' to 'finished' if completed successfully or 'error' if problems were encountered.")),n}return this._templateError(e)},_templateError:function(e){return o("").addClass("errormessagelarge").append(o("").text("The server could not complete the request. Please contact the Galaxy Team if this error persists.")).append(o("").text(JSON.stringify(e,null,4)))}});return{View:s}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(1),i(2))},function(e,t,i){var n,s;(function(o,a){n=[i(4),i(9),i(7)],s=function(e,t,i){var n=o.View.extend({initialize:function(e){var n=this;this.options=e,this.name=e.name||"element",this.multiple=e.multiple||!1,this.message=new i.Message,this.portlet=new t.View({cls:"ui-portlet-section"}),this.select=new i.Select.View({optional:e.optional}),this.button=new i.ButtonIcon({icon:"fa fa-sign-in",floating:"left",tooltip:"Insert new "+this.name,onclick:function(){n.add({id:n.select.value(),name:n.select.text()})}}),this.setElement(this._template(e)),this.$(".ui-list-message").append(this.message.$el),this.$(".ui-list-portlet").append(this.portlet.$el),this.$(".ui-list-button").append(this.button.$el),this.$(".ui-list-select").append(this.select.$el)},value:function(e){if(void 0!==e){if(this.portlet.empty(),a.isArray(e))for(var t in e){var i=e[t],n=null,s=null;"string"!=a.type(i)?(n=i.id,s=i.name):n=s=i,null!=n&&this.add({id:n,name:s})}this._refresh()}var o=[];return this.$(".ui-list-id").each(function(){o.push({id:a(this).prop("id"),name:a(this).find(".ui-list-name").html()})}),0==o.length?null:o},add:function(t){var i=this;if(0===this.$('[id="'+t.id+'"]').length)if(e.isEmpty(t.id))this.message.update({message:"Please select a valid "+this.name+".",status:"danger"});else{var n=a(this._templateRow({id:t.id,name:t.name}));n.on("click",function(){n.remove(),i._refresh()}),n.on("mouseover",function(){n.addClass("portlet-highlight")}),n.on("mouseout",function(){n.removeClass("portlet-highlight")}),this.portlet.append(n),this._refresh()}else this.message.update({message:"This "+this.name+" is already in the list."})},update:function(e){this.select.update(e)},_refresh:function(){this.$(".ui-list-id").length>0?(!this.multiple&&this.button.disable(),this.$(".ui-list-portlet").show()):(this.button.enable(),this.$(".ui-list-portlet").hide()),this.options.onchange&&this.options.onchange()},_template:function(e){return'
").addClass("menu dropdown-menu").addClass("pull-"+e.model.get("pull")).attr("role","menu"),this.$el.append(this.$menu)),this.collection.each(function(t){var i=t.attributes;if(i.visible){var n=a("").addClass("dropdown-item").attr({href:i.href,target:i.target}).append(a("").addClass("fa").addClass(i.icon).css("display",i.icon?"inline-block":"none")).append(i.title).on("click",function(e){i.onclick&&(e.preventDefault(),i.onclick())});e.$menu.append(a("").append(n)),i.divider&&e.$menu.append(a("").addClass("divider"))}})},addMenu:function(t){this.collection.add(e.merge(t,{title:"",target:"",href:"",onclick:null,divider:!1,visible:!0,icon:null,cls:"button-menu btn-group"}))}});return{ButtonDefault:t,ButtonLink:i,ButtonIcon:s,ButtonCheck:n,ButtonMenu:r}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(1))},function(e,t,i){var n,s;(function(i,o,a){n=[],s=function(){var e=i.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:!0,id:null,href:null,target:null,enabled:!0,visible:!0,tooltip_config:{}}}),t=i.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"},this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var e=this.template(this.model.toJSON());return e.tooltip(this.model.get("tooltip_config")),this.$el.replaceWith(e),this.setElement(e),this},events:{click:"click"},click:function(e){return o.isFunction(this.model.get("on_click"))?(this.model.get("on_click")(e),!1):!0},template:function(e){var t='title="'+e.title+'" class="icon-button';return e.is_menu_button&&(t+=" menu-button"),t+=" "+e.icon_class,e.enabled||(t+="_disabled"),t+='"',e.id&&(t+=' id="'+e.id+'"'),t+=' href="'+e.href+'"',e.target&&(t+=' target="'+e.target+'"'),e.visible||(t+=' style="display: none;"'),t=e.enabled?"":"",a(t)}}),n=i.Collection.extend({model:e}),s=i.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var e=this;return this.collection.each(function(t){var i=a("").attr("href","javascript:void(0)").attr("title",t.attributes.title).addClass("icon-button menu-button").addClass(t.attributes.icon_class).appendTo(e.$el).click(t.attributes.on_click);t.attributes.tooltip_config&&i.tooltip(t.attributes.tooltip_config);var n=t.get("options");n&&make_popupmenu(i,n)}),this}}),r=function(t,i){i||(i={});var a=new n(o.map(t,function(t){return new e(o.extend(t,i))}));return new s({collection:a})};return{IconButton:e,IconButtonView:t,IconButtonCollection:n,IconButtonMenuView:s,create_icon_buttons_menu:r}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(2),i(1))},function(e,t,i){var n,s,o;!function(a){s=[i(1)],n=a,o="function"==typeof n?n.apply(t,s):n,!(void 0!==o&&(e.exports=o))}(function(e){"use_strict";var t=e;t.fn.make_text_editable=function(e){var i="num_cols"in e?e.num_cols:30,n="num_rows"in e?e.num_rows:4,s="use_textarea"in e?e.use_textarea:!1,o="on_finish"in e?e.on_finish:null,a="help_text"in e?e.help_text:null,r=t(this);return r.addClass("editable-text").click(function(a){if(!(t(this).children(":input").length>0)){r.removeClass("editable-text");var l,c,d=function(e){r.find(":input").remove(),""!==e?r.text(e):r.html(" "),r.addClass("editable-text"),o&&o(e)},h="cur_text"in e?e.cur_text:r.text();s?(l=t("").attr({rows:n,cols:i}).text(t.trim(h)).keyup(function(e){27===e.keyCode&&d(h)}),c=t("").text("Done").click(function(){return d(l.val()),!1})):l=t("").attr({value:t.trim(h),size:i}).blur(function(){d(h)}).keyup(function(e){27===e.keyCode?t(this).trigger("blur"):13===e.keyCode&&d(t(this).val()),e.stopPropagation()}),r.text(""),r.append(l),c&&r.append(c),l.focus(),l.select(),a.stopPropagation()}}),a&&r.attr("title",a).tooltip(),r}})},function(e,t,i){var n;(function(s,o){n=function(){function e(e){e+="";for(var t=/(\d+)(\d{3})/;t.test(e);)e=e.replace(t,"$1,$2");return e}var t=function(e){return"promise"in e},i=s.Model.extend({defaults:{ajax_settings:{},interval:1e3,success_fn:function(e){return!0}},go:function(){var e=o.Deferred(),t=this,i=t.get("ajax_settings"),n=t.get("success_fn"),s=t.get("interval"),a=function(){o.ajax(i).success(function(t){n(t)?e.resolve(t):setTimeout(a,s)})};return a(),e}}),n=function(e){e||(e="#ffffff"),"string"==typeof e&&(e=[e]);for(var t=0;t>16,s=(65280&i)>>8,o=255&i,d=p(n,s,o),f=!0,t=0;t>16,l=(65280&a)>>8,c=255&a,h=p(r,l,c),u=g(n,s,o,r,l,c),Math.abs(d-h)<40||200>u){f=!1;break}m++}while(!f&&10>=m);return"#"+(16777216+i).toString(16).substr(1,6)};return{commatize:e,is_deferred:t,ServerStateDeferred:i,get_random_color:n}}.call(t,i,t,e),!(void 0!==n&&(e.exports=n))}).call(t,i(3),i(1))},function(e,t,i){var n,s;(function(o,a){n=[i(26),i(6),i(5)],s=function(e,t,i){e=e||window.BibtexParser;var n="citation",s=o.Model.extend(t.LoggableMixin).extend({_logNamespace:n,defaults:{content:""},initialize:function(){var t;try{t=e(this.attributes.content)}catch(i){return}if(t.errors.length){var n=t.errors.reduce(function(e,t){return e+"; "+t});this.log("Error parsing bibtex: "+n)}if(this._fields={},this.entry=a.first(t.entries),this.entry){var s=this.entry.Fields;for(var o in s){var r=s[o],l=o.toLowerCase();this._fields[l]=r}}},entryType:function(){return this.entry?this.entry.EntryType:void 0},fields:function(){return this._fields}}),r=o.Collection.extend(t.LoggableMixin).extend({_logNamespace:n,urlRoot:Galaxy.root+"api",partial:!0,model:s}),l=r.extend({url:function(){return this.urlRoot+"/histories/"+this.history_id+"/citations"}}),c=r.extend({url:function(){return this.urlRoot+"/tools/"+this.tool_id+"/citations"},partial:!1});return{Citation:s,HistoryCitationCollection:l,ToolCitationCollection:c}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(2))},function(e,t,i){var n,s;(function(o,a){n=[i(4),i(7),i(8),i(42)],s=function(e,t,i,n){var s=n.extend({initialize:function(s){var o=this;s.listen_to_history=!0,s.always_refresh=!1,this.modal=parent.Galaxy.modal||new i.View,n.prototype.initialize.call(this,e.merge({customize:function(e){e.buttons={execute:execute_btn=new t.Button({icon:"fa-check",tooltip:"Execute: "+e.name+" ("+e.version+")",title:"Execute",cls:"ui-button btn btn-primary",floating:"clear",onclick:function(){execute_btn.wait(),o.portlet.disable(),o.submit(e,function(){execute_btn.unwait(),o.portlet.enable()})}})},e.job_id&&e.job_remap&&(e.inputs.rerun_remap_job_id={label:"Resume dependencies from this job",name:"rerun_remap_job_id",type:"select",display:"radio",ignore:"__ignore__",value:"__ignore__",options:[["Yes",e.job_id],["No","__ignore__"]],help:"The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run."})}},s))},submit:function(t,i){var n=this,s={tool_id:t.id,tool_version:t.version,inputs:this.data.create()};if(this.trigger("reset"),!n.validate(s))return Galaxy.emit.debug("tool-form::submit()","Submission canceled. Validation failed."),void(i&&i());if(t.action!==Galaxy.root+"tool_runner/index"){var r=o("").attr({action:t.action,method:t.method,enctype:t.enctype});return a.each(s.inputs,function(e,t){r.append(o("").attr({name:t,value:e}))}),r.hide().appendTo("body").submit().remove(),void(i&&i())}Galaxy.emit.debug("tool-form::submit()","Validation complete.",s),e.request({type:"POST",url:Galaxy.root+"api/tools",data:s,success:function(e){i&&i(),n.$el.children().hide(),n.$el.append(n._templateSuccess(e)),parent.Galaxy&&parent.Galaxy.currHistoryPanel&&parent.Galaxy.currHistoryPanel.refreshContents()},error:function(e){i&&i(),Galaxy.emit.debug("tool-form::submit","Submission failed.",e);var t=!1;if(e&&e.err_data){var o=n.data.matchResponse(e.err_data);for(var a in o){n.highlight(a,o[a]),t=!0;break}}t||n.modal.show({title:"Job submission failed",body:e&&e.err_msg||n._templateError(s),buttons:{Close:function(){n.modal.hide()}}})}})},validate:function(e){var t=e.inputs,i=-1,n=null;for(var s in t){var o=t[s],a=this.data.match(s),r=this.field_list[a],l=this.input_list[a];if(a&&l&&r){if(!l.optional&&null==o)return this.highlight(a),!1;if(o&&o.batch){var c=o.values.length,d=c>0&&o.values[0]&&o.values[0].src;if(d)if(null===n)n=d;else if(n!==d)return this.highlight(a,"Please select either dataset or dataset list fields for all batch mode fields."),!1;if(-1===i)i=c;else if(i!==c)return this.highlight(a,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains "+c+" selection(s) while a previous field contains "+i+"."),!1}}else Galaxy.emit.debug("tool-form::validate()","Retrieving input objects failed.")}return!0;
+},_templateSuccess:function(e){if(e.jobs&&e.jobs.length>0){var t=e.jobs.length,i=1==t?"1 job has":t+" jobs have",n=o("").addClass("donemessagelarge").append(o("").text(i+" been successfully added to the queue - resulting in the following datasets:"));return a.each(e.outputs,function(e){n.append(o("").addClass("messagerow").append(o("").text(e.hid+": "+e.name)))}),n.append(o("").append("").text("You can check the status of queued jobs and view the resulting data by refreshing the History pane. When the job has been run the status will change from 'running' to 'finished' if completed successfully or 'error' if problems were encountered.")),n}return this._templateError(e)},_templateError:function(e){return o("").addClass("errormessagelarge").append(o("").text("The server could not complete the request. Please contact the Galaxy Team if this error persists.")).append(o("").text(JSON.stringify(e,null,4)))}});return{View:s}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(1),i(2))},function(e,t,i){var n,s;(function(o,a){n=[i(4),i(9),i(7)],s=function(e,t,i){var n=o.View.extend({initialize:function(e){var n=this;this.options=e,this.name=e.name||"element",this.multiple=e.multiple||!1,this.message=new i.Message,this.portlet=new t.View({cls:"ui-portlet-section"}),this.select=new i.Select.View({optional:e.optional}),this.button=new i.ButtonIcon({icon:"fa fa-sign-in",floating:"left",tooltip:"Insert new "+this.name,onclick:function(){n.add({id:n.select.value(),name:n.select.text()})}}),this.setElement(this._template(e)),this.$(".ui-list-message").append(this.message.$el),this.$(".ui-list-portlet").append(this.portlet.$el),this.$(".ui-list-button").append(this.button.$el),this.$(".ui-list-select").append(this.select.$el)},value:function(e){if(void 0!==e){if(this.portlet.empty(),a.isArray(e))for(var t in e){var i=e[t],n=null,s=null;"string"!=a.type(i)?(n=i.id,s=i.name):n=s=i,null!=n&&this.add({id:n,name:s})}this._refresh()}var o=[];return this.$(".ui-list-id").each(function(){o.push({id:a(this).prop("id"),name:a(this).find(".ui-list-name").html()})}),0==o.length?null:o},add:function(t){var i=this;if(0===this.$('[id="'+t.id+'"]').length)if(e.isEmpty(t.id))this.message.update({message:"Please select a valid "+this.name+".",status:"danger"});else{var n=a(this._templateRow({id:t.id,name:t.name}));n.on("click",function(){n.remove(),i._refresh()}),n.on("mouseover",function(){n.addClass("portlet-highlight")}),n.on("mouseout",function(){n.removeClass("portlet-highlight")}),this.portlet.append(n),this._refresh()}else this.message.update({message:"This "+this.name+" is already in the list."})},update:function(e){this.select.update(e)},_refresh:function(){this.$(".ui-list-id").length>0?(!this.multiple&&this.button.disable(),this.$(".ui-list-portlet").show()):(this.button.enable(),this.$(".ui-list-portlet").hide()),this.options.onchange&&this.options.onchange()},_template:function(e){return'
',"Warning: This is a experimental feature. Most Galaxy tools will not annotate"," citations explicitly at this time. When writing up your analysis, please manually"," review your histories and find all references"," that should be cited in order to completely describe your work. Also, please remember to",' cite Galaxy.',"
"],"element");return o.extend({},t.DatasetListItemView.prototype.templates,{titleBar:e})}();var u=c.extend({className:c.prototype.className+" dataset-collection-element",_swapNewRender:function(e){c.prototype._swapNewRender.call(this,e);var t=this.model.get("state")||"ok";return this.$el.addClass("state-"+t),this.$el},toString:function(){var e=this.model?this.model+"":"(no model)";return"NestedDCDCEListItemView("+e+")"}});return{DCListItemView:c,DCEListItemView:d,DatasetDCEListItemView:h,NestedDCDCEListItemView:u}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(2),i(1),i(1))},function(e,t,i){var n,s;(function(o,a,r){n=[i(70),i(6),i(5)],s=function(e,t,i){"use strict";var n={defaults:{model_class:"DatasetCollectionElement",element_identifier:null,element_index:null,element_type:null},_mergeObject:function(e){return o.extend(e,e.object,{element_id:e.id}),delete e.object,e},constructor:function(e,t){e=this._mergeObject(e),this.idAttribute="element_id",a.Model.apply(this,arguments)},parse:function(e,t){var i=e;return i=this._mergeObject(i)}},s=a.Model.extend(t.LoggableMixin).extend(n).extend({_logNamespace:"collections"}),l=a.Collection.extend(t.LoggableMixin).extend({_logNamespace:"collections",model:s,toString:function(){return["DatasetCollectionElementCollection(",this.length,")"].join("")}}),c=e.DatasetAssociation.extend(t.mixin(n,{url:function(){return this.has("history_id")?Galaxy.root+"api/histories/"+this.get("history_id")+"/contents/"+this.get("id"):(console.warn("no endpoint for non-hdas within a collection yet"),Galaxy.root+"api/datasets")},defaults:o.extend({},e.DatasetAssociation.prototype.defaults,n.defaults),constructor:function(e,t){this.debug(" DatasetDCE.constructor:",e,t),n.constructor.call(this,e,t)},hasDetails:function(){return this.elements&&this.elements.length},toString:function(){var e=this.get("element_identifier");return["DatasetDCE(",e,")"].join("")}})),d=l.extend({model:c,toString:function(){return["DatasetDCECollection(",this.length,")"].join("")}}),h=a.Model.extend(t.LoggableMixin).extend(t.SearchableModelMixin).extend({_logNamespace:"collections",defaults:{collection_type:null,deleted:!1},collectionClass:l,initialize:function(e,t){this.debug(this+"(DatasetCollection).initialize:",e,t,this),this.elements=this._createElementsModel(),this.on("change:elements",function(){this.log("change:elements"),this.elements=this._createElementsModel()})},_createElementsModel:function(){this.debug(this+"._createElementsModel",this.collectionClass,this.get("elements"),this.elements);var e=this.get("elements")||[];return this.unset("elements",{silent:!0}),this.elements=new this.collectionClass(e),this.elements},toJSON:function(){var e=a.Model.prototype.toJSON.call(this);return this.elements&&(e.elements=this.elements.toJSON()),e},inReadyState:function(){var e=this.get("populated");return this.isDeletedOrPurged()||e},hasDetails:function(){return 0!==this.elements.length},getVisibleContents:function(e){return this.elements},parse:function(e,t){var i=a.Model.prototype.parse.call(this,e,t);return i.create_time&&(i.create_time=new Date(i.create_time)),i.update_time&&(i.update_time=new Date(i.update_time)),i},"delete":function(e){return this.get("deleted")?r.when():this.save({deleted:!0},e)},undelete:function(e){return!this.get("deleted")||this.get("purged")?r.when():this.save({deleted:!1},e)},isDeletedOrPurged:function(){return this.get("deleted")||this.get("purged")},searchAttributes:["name"],toString:function(){var e=[this.get("id"),this.get("name")||this.get("element_identifier")];return"DatasetCollection("+e.join(",")+")"}}),u=h.extend({collectionClass:d,toString:function(){return"List"+h.prototype.toString.call(this)}}),p=u.extend({toString:function(){return"Pair"+h.prototype.toString.call(this)}}),g=h.extend(t.mixin(n,{constructor:function(e,t){this.debug(" NestedDCDCE.constructor:",e,t),n.constructor.call(this,e,t)},toString:function(){var e=this.object?""+this.object:this.get("element_identifier");return["NestedDCDCE(",e,")"].join("")}})),f=l.extend({model:g,toString:function(){return["NestedDCDCECollection(",this.length,")"].join("")}}),m=p.extend(t.mixin(n,{constructor:function(e,t){this.debug(" NestedPairDCDCE.constructor:",e,t),n.constructor.call(this,e,t)},toString:function(){var e=this.object?""+this.object:this.get("element_identifier");return["NestedPairDCDCE(",e,")"].join("")}})),v=f.extend({model:m,toString:function(){return["NestedPairDCDCECollection(",this.length,")"].join("")}}),_=h.extend({collectionClass:v,toString:function(){return["ListPairedDatasetCollection(",this.get("name"),")"].join("")}}),y=u.extend(t.mixin(n,{constructor:function(e,t){this.debug(" NestedListDCDCE.constructor:",e,t),n.constructor.call(this,e,t)},toString:function(){var e=this.object?""+this.object:this.get("element_identifier");return["NestedListDCDCE(",e,")"].join("")}})),w=f.extend({model:y,toString:function(){return["NestedListDCDCECollection(",this.length,")"].join("")}}),b=h.extend({collectionClass:w,toString:function(){return["ListOfListsDatasetCollection(",this.get("name"),")"].join("")}});return{ListDatasetCollection:u,PairDatasetCollection:p,ListPairedDatasetCollection:_,ListOfListsDatasetCollection:b}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(2),i(3),i(1))},function(e,t,i){var n,s;(function(o,a,r,l){n=[i(38),i(12),i(6),i(8),i(87),i(5),i(84)],s=function(e,t,i,n,s,c){"use strict";function d(e){var t=e.toJSON(),i=f(t,{creationFn:function(t,i){return t=t.map(function(e){return{id:e.id,name:e.name,src:"dataset"===e.history_content_type?"hda":"hdca"}}),e.createHDCA(t,"list",i)}});return i}var h="collections",u=o.View.extend(i.LoggableMixin).extend({_logNamespace:h,tagName:"li",className:"collection-element",initialize:function(e){this.element=e.element||{},this.selected=e.selected||!1},render:function(){return this.$el.attr("data-element-id",this.element.id).attr("draggable",!0).html(this.template({element:this.element})),this.selected&&this.$el.addClass("selected"),this},template:a.template(['',"<%- element.name %>","",'"].join("")),select:function(e){this.$el.toggleClass("selected",e),this.trigger("select",{source:this,selected:this.$el.hasClass("selected")})},discard:function(){var e=this,t=this.$el.parent().width();this.$el.animate({"margin-right":t},"fast",function(){e.trigger("discard",{source:e}),e.destroy()})},destroy:function(){this.off(),this.$el.remove()},events:{click:"_click","click .name":"_clickName","click .discard":"_clickDiscard",dragstart:"_dragstart",dragend:"_dragend",dragover:"_sendToParent",drop:"_sendToParent"},_click:function(e){e.stopPropagation(),this.select(e)},_clickName:function(e){e.stopPropagation(),e.preventDefault();var t=([c("Enter a new name for the element"),":\n(",c("Note that changing the name here will not rename the dataset"),")"].join(""),prompt(c("Enter a new name for the element")+":",this.element.name));t&&(this.element.name=t,this.render())},_clickDiscard:function(e){e.stopPropagation(),this.discard()},_dragstart:function(e){e.originalEvent&&(e=e.originalEvent),e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",JSON.stringify(this.element)),this.$el.addClass("dragging"),this.$el.parent().trigger("collection-element.dragstart",[this])},_dragend:function(e){this.$el.removeClass("dragging"),this.$el.parent().trigger("collection-element.dragend",[this])},_sendToParent:function(e){this.$el.parent().trigger(e)},toString:function(){return"DatasetCollectionElementView()"}}),p=o.View.extend(i.LoggableMixin).extend({_logNamespace:h,elementViewClass:u,collectionClass:e.HistoryListDatasetCollection,className:"list-collection-creator collection-creator flex-row-container",minElements:1,defaultAttributes:{creationFn:function(){throw new TypeError("no creation fn for creator")},oncreate:function(){},oncancel:function(){},autoscrollDist:24,highlightClr:"rgba( 64, 255, 255, 1.0 )"},initialize:function(e){this.metric("ListCollectionCreator.initialize",e);var t=this;a.each(this.defaultAttributes,function(i,n){i=e[n]||i,t[n]=i}),t.initialElements=e.elements||[],this._instanceSetUp(),this._elementsSetUp(),this._setUpBehaviors()},_instanceSetUp:function(){this.selectedIds={},this.$dragging=null,this.blocking=!1},_elementsSetUp:function(){this.invalidElements=[],this.workingElements=[],this.elementViews=[],this.workingElements=this.initialElements.slice(0),this._ensureElementIds(),this._validateElements(),this._mangleDuplicateNames(),this._sortElements()},_ensureElementIds:function(){return this.workingElements.forEach(function(e){e.hasOwnProperty("id")||(e.id=a.uniqueId())}),this.workingElements},_validateElements:function(){var e=this;return e.invalidElements=[],this.workingElements=this.workingElements.filter(function(t){var i=e._isElementInvalid(t);return i&&e.invalidElements.push({element:t,text:i}),!i}),this.workingElements},_isElementInvalid:function(e){return"dataset"!==e.history_content_type?c("is not a dataset"):e.state!==t.OK?c(a.contains(t.NOT_READY_STATES,e.state)?"hasn't finished running yet":"has errored, is paused, or is not accessible"):e.deleted||e.purged?c("has been deleted or purged"):null},_mangleDuplicateNames:function(){var e=900,t=1,i={};this.workingElements.forEach(function(n){for(var s=n.name;i.hasOwnProperty(s);)if(s=n.name+" ("+t+")",t+=1,t>=e)throw new Error("Safety hit in while loop - thats impressive");n.name=s,i[n.name]=!0})},_sortElements:function(e){},render:function(e,t){return this.workingElements.length .clear-selected").show():this.$(".collection-elements-controls > .clear-selected").hide()},_renderList:function(e,t){var i=this,n=l(""),s=i.$list();a.each(this.elementViews,function(e){e.destroy(),i.removeElementView(e)}),i.workingElements.forEach(function(e){var t=i._createElementView(e);n.append(t.$el)}),i._renderClearSelected(),s.empty().append(n.children()),a.invoke(i.elementViews,"render"),s.height()>s.css("max-height")?s.css("border-width","1px 0px 1px 0px"):s.css("border-width","0px")},_createElementView:function(e){var t=new this.elementViewClass({element:e,selected:a.has(this.selectedIds,e.id)});return this.elementViews.push(t),this._listenToElementView(t),t},_listenToElementView:function(e){var t=this;t.listenTo(e,{select:function(e){var i=e.source.element;e.selected?t.selectedIds[i.id]=!0:delete t.selectedIds[i.id],t.trigger("elements:select",e)},discard:function(e){t.trigger("elements:discard",e)}})},addElementView:function(e){},removeElementView:function(e){delete this.selectedIds[e.element.id],this._renderClearSelected(),this.elementViews=a.without(this.elementViews,e),this.stopListening(e)},_renderNoElementsLeft:function(){this._disableNameAndCreate(!0),this.$(".collection-elements").append(this.templates.noElementsLeft())},_elementToJSON:function(e){return e},createList:function(e){if(!this.workingElements.length){var t=c("No valid elements for final list")+". ";return t+=''+c("Cancel")+" ",t+=c("or"),t+=' '+c("start over")+".",void this._showAlert(t)}var i=this,n=this.workingElements.map(function(e){return i._elementToJSON(e)});return i.blocking=!0,i.creationFn(n,e).always(function(){i.blocking=!1}).fail(function(e,t,n){i.trigger("error",{xhr:e,status:t,message:c("An error occurred while creating this collection")})}).done(function(e,t,n){i.trigger("collection:created",e,t,n),i.metric("collection:created",e),"function"==typeof i.oncreate&&i.oncreate.call(this,e,t,n)})},_setUpBehaviors:function(){return this.on("error",this._errorHandler),this.once("rendered",function(){this.trigger("rendered:initial",this)}),this.on("elements:select",function(e){this._renderClearSelected()}),this.on("elements:discard",function(e){var t=e.source.element;this.removeElementView(e.source),this.workingElements=a.without(this.workingElements,t),this.workingElements.length||this._renderNoElementsLeft()}),this},_errorHandler:function(e){this.error(e);var t=this;if(content=e.message||c("An error occurred"),e.xhr){var i=e.xhr,n=e.message;0===i.readyState&&0===i.status?content+=": "+c("Galaxy could not be reached and may be updating.")+c(" Try again in a few minutes."):i.responseJSON?content+=":
",c(["Collections of datasets are permanent, ordered lists of datasets that can be passed to tools and ","workflows in order to have analyses done on each member of the entire group. This interface allows ","you to create a collection and re-order the final collection."].join("")),"
","
","
",c(["Rename elements in the list by clicking on ",'the existing name.'].join("")),"
","
",c(["Discard elements from the final created list by clicking on the ",'"Discard" button.'].join("")),"
","
",c(["Reorder the list by clicking and dragging elements. Select multiple elements by clicking on ",'them and you can then move those selected by dragging the ',"entire group. Deselect them by clicking them again or by clicking the ",'the "Clear selected" link.'].join("")),"
","
",c(['Click the "Start over" link to begin again as if you had just opened ',"the interface."].join("")),"
","
",c(['Click the "Cancel" button to exit the interface.'].join("")),"
","
","
",c(['Once your collection is complete, enter a name and ','click "Create list".'].join("")),"
"].join("")),invalidElements:a.template([c("The following selections could not be included due to problems:"),"
','',"<% if( _.size( problems ) ){ %>",c("The following selections could not be included due to problems"),":","
<% _.each( problems, function( problem ){ %>","
<%- problem.element.name %>: <%- problem.text %>
","<% }); %>
","<% } else if( _.size( elements ) < 1 ){ %>",c("No datasets were selected"),".","<% } %>"," ",c("At least one element is needed for the collection"),". ",c("You may need to "),'',c("cancel")," ",c("and reselect new elements"),".","","
","
",'"].join(""))},toString:function(){return"ListCollectionCreator"}}),g=function(e,t,i){var s,o=l.Deferred(),r=Galaxy.modal||new n.View;return t=a.defaults(t||{},{elements:e,oncancel:function(){r.hide(),o.reject("cancelled")},oncreate:function(e,t){r.hide(),o.resolve(t)}}),s=new i(t),r.show({title:t.title||c("Create a collection"),body:s.$el,width:"80%",height:"100%",closing_events:!0}),s.render(),window._collectionCreator=s,o},f=function(e,t){return t=t||{},t.title=c("Create a collection from a list of datasets"),g(e,t,p)};return{DatasetCollectionElementView:u,ListCollectionCreator:p,collectionCreatorModal:g,listCollectionCreatorModal:f,createListCollection:d}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(2),i(1),i(1))},function(e,t,i){var n,s;(function(o,a,r,l){n=[i(41),i(12),i(22),i(6),i(5)],s=function(e,t,i,n,s){"use strict";var c="dataset",d=e.ListItemView,h=d.extend({_logNamespace:c,className:d.prototype.className+" dataset",id:function(){return["dataset",this.model.get("id")].join("-")},initialize:function(e){e.logger&&(this.logger=this.model.logger=e.logger),
this.log(this+".initialize:",e),d.prototype.initialize.call(this,e),this.linkTarget=e.linkTarget||"_blank"},_setUpListeners:function(){d.prototype._setUpListeners.call(this);var e=this;return e.listenTo(e.model,{change:function(t,i){e.model.changedAttributes().state&&e.model.inReadyState()&&e.expanded&&!e.model.hasDetails()?e.model.fetch({silent:!0}).done(function(){e.render()}):e.render()}})},_fetchModelDetails:function(){var e=this;return e.model.inReadyState()&&!e.model.hasDetails()?e.model.fetch({silent:!0}):o.when()},remove:function(e,t){var i=this;e=e||this.fxSpeed,this.$el.fadeOut(e,function(){a.View.prototype.remove.call(i),t&&t.call(i)})},_swapNewRender:function(e){return d.prototype._swapNewRender.call(this,e),this.model.has("state")&&this.$el.addClass("state-"+this.model.get("state")),this.$el},_renderPrimaryActions:function(){return[this._renderDisplayButton()]},_renderDisplayButton:function(){var e=this.model.get("state");if(e===t.NOT_VIEWABLE||e===t.DISCARDED||!this.model.get("accessible"))return null;var n={target:this.linkTarget,classes:"display-btn"};if(this.model.get("purged"))n.disabled=!0,n.title=s("Cannot display datasets removed from disk");else if(e===t.UPLOAD)n.disabled=!0,n.title=s("This dataset must finish uploading before it can be viewed");else if(e===t.NEW)n.disabled=!0,n.title=s("This dataset is not yet viewable");else{n.title=s("View data"),n.href=this.model.urls.display;var o=this;n.onclick=function(e){Galaxy.frame&&Galaxy.frame.active&&(Galaxy.frame.addDataset(o.model.get("id")),e.preventDefault())}}return n.faIcon="fa-eye",i(n)},_renderDetails:function(){if(this.model.get("state")===t.NOT_VIEWABLE)return r(this.templates.noAccess(this.model.toJSON(),this));var e=d.prototype._renderDetails.call(this);return e.find(".actions .left").empty().append(this._renderSecondaryActions()),e.find(".summary").html(this._renderSummary()).prepend(this._renderDetailMessages()),e.find(".display-applications").html(this._renderDisplayApplications()),this._setUpBehaviors(e),e},_renderSummary:function(){var e=this.model.toJSON(),t=this.templates.summaries[e.state];return(t=t||this.templates.summaries.unknown)(e,this)},_renderDetailMessages:function(){var e=this,t=r(''),i=e.model.toJSON();return l.each(e.templates.detailMessages,function(n){t.append(r(n(i,e)))}),t},_renderDisplayApplications:function(){return this.model.isDeletedOrPurged()?"":[this.templates.displayApplications(this.model.get("display_apps"),this),this.templates.displayApplications(this.model.get("display_types"),this)].join("")},_renderSecondaryActions:function(){switch(this.debug("_renderSecondaryActions"),this.model.get("state")){case t.NOT_VIEWABLE:return[];case t.OK:case t.FAILED_METADATA:case t.ERROR:return[this._renderDownloadButton(),this._renderShowParamsButton()]}return[this._renderShowParamsButton()]},_renderShowParamsButton:function(){return i({title:s("View details"),classes:"params-btn",href:this.model.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle",onclick:function(e){Galaxy.frame&&Galaxy.frame.active&&(Galaxy.frame.add({title:"Dataset details",url:this.href}),e.preventDefault(),e.stopPropagation())}})},_renderDownloadButton:function(){return this.model.get("purged")||!this.model.hasData()?null:l.isEmpty(this.model.get("meta_files"))?r(['','',""].join("")):this._renderMetaFileDownloadButton()},_renderMetaFileDownloadButton:function(){var e=this.model.urls;return r(['
'}})}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(1))},function(e,t,n){var s,o;(function(a,r){s=[n(4),n(20)],o=function(e,t){var n=t.BaseIcons.extend({initialize:function(e){e.type=e.display||"checkbox",e.multiple="checkbox"==e.type,t.BaseIcons.prototype.initialize.call(this,e)},_setValue:function(e){if(t.BaseIcons.prototype._setValue.call(this,e),void 0!==e&&null!==e&&this.header_index){var i=this,n=a.isArray(e)?e:[e];r.each(n,function(e){var t=i.header_index[e];r.each(t,function(e){i._setState(e,!0)})})}},_setState:function(e,t){var i=this.$(".button-"+e),n=this.$(".subgroup-"+e);i.data("is_expanded",t),t?(n.show(),i.removeClass("fa-plus-square").addClass("fa-minus-square")):(n.hide(),i.removeClass("fa-minus-square").addClass("fa-plus-square"))},_templateOptions:function(){function t(e,t){var i=e.find(".button-"+t);i.on("click",function(){s._setState(t,!i.data("is_expanded"))})}function n(o,r,l){l=l||[];for(i in r){var c=r[i],d=c.options&&c.options.length>0,h=l.slice(0);s.header_index[c.value]=h.slice(0);var u=a("");if(d){var p=e.uid(),g=a("").addClass("button-"+p).addClass("ui-drilldown-button fa fa-plus-square"),f=a("").addClass("subgroup-"+p).addClass("ui-drilldown-subgroup");u.append(a("").append(g).append(s._templateOption({label:c.name,value:c.value}))),h.push(p),n(f,c.options,h),u.append(f),t(u,p)}else u.append(s._templateOption({label:c.name,value:c.value}));o.append(u)}}var s=this;this.header_index={};var o=a("");return n(o,this.model.get("data")),o},_template:function(){return a("").addClass("ui-options-list drilldown-container").attr("id",this.model.id)}});return{View:n}}.apply(t,s),!(void 0!==o&&(e.exports=o))}).call(t,n(1),n(2))},function(e,t,i){var n,s;(function(o,a,r){n=[i(4),i(7),i(21)],s=function(e,t,i){var n={DISABLED:"disabled",ENABLED:"enabled",LINKED:"linked"},s={data:[{src:"hda",icon:"fa-file-o",tooltip:"Single dataset",multiple:!1,batch:n.DISABLED},{src:"hda",icon:"fa-files-o",tooltip:"Multiple datasets",multiple:!0,batch:n.LINKED},{src:"hdca",icon:"fa-folder-o",tooltip:"Dataset collection",multiple:!1,batch:n.LINKED}],data_multiple:[{src:"hda",icon:"fa-files-o",tooltip:"Multiple datasets",multiple:!0,batch:n.DISABLED},{src:"hdca",icon:"fa-folder-o",tooltip:"Dataset collection",multiple:!1,batch:n.DISABLED}],data_collection:[{src:"hdca",icon:"fa-folder-o",tooltip:"Dataset collection",multiple:!1,batch:n.DISABLED}],workflow_data:[{src:"hda",icon:"fa-file-o",tooltip:"Single dataset",multiple:!1,batch:n.DISABLED}],workflow_data_multiple:[{src:"hda",icon:"fa-files-o",tooltip:"Multiple datasets",multiple:!0,batch:n.DISABLED}],workflow_data_collection:[{src:"hdca",icon:"fa-folder-o",tooltip:"Dataset collection",multiple:!1,batch:n.DISABLED}],module_data:[{src:"hda",
icon:"fa-file-o",tooltip:"Single dataset",multiple:!1,batch:n.DISABLED},{src:"hda",icon:"fa-files-o",tooltip:"Multiple datasets",multiple:!0,batch:n.ENABLED}],module_data_collection:[{src:"hdca",icon:"fa-folder-o",tooltip:"Dataset collection",multiple:!1,batch:n.DISABLED},{src:"hdca",icon:"fa-folder",tooltip:"Multiple collections",multiple:!0,batch:n.ENABLED}]},l=o.View.extend({initialize:function(e){var i=this;this.model=e&&e.model||new o.Model({src_labels:{hda:"dataset",hdca:"dataset collection"},pagelimit:100}).set(e),this.setElement(a("").addClass("ui-select-content")),this.button_product=new t.RadioButton.View({value:"false",data:[{icon:"fa fa-chain",value:"false",tooltip:"Linked inputs will be run in matched order with other datasets e.g. use this for matching forward and reverse reads."},{icon:"fa fa-chain-broken",value:"true",tooltip:"Unlinked dataset inputs will be run against *all* other inputs."}]});var n=a("").addClass("ui-form-info").append(a("").addClass("fa fa-sitemap")).append(a("").html("This is a batch mode input field. Separate jobs will be triggered for each dataset selection."));this.$batch={linked:n.clone(),enabled:n.clone().append(a("").append(a("").addClass("ui-form-title").html("Batch options:")).append(this.button_product.$el)).append(a("").css("clear","both"))},this.history={},this.listenTo(this.model,"change:data",this._changeData,this),this.listenTo(this.model,"change:wait",this._changeWait,this),this.listenTo(this.model,"change:current",this._changeCurrent,this),this.listenTo(this.model,"change:value",this._changeValue,this),this.listenTo(this.model,"change:type change:optional change:multiple change:extensions",this._changeType,this),this.render(),this.on("change",function(){e.onchange&&e.onchange(i.value())})},render:function(){this._changeType(),this._changeValue(),this._changeWait()},wait:function(){this.model.set("wait",!0)},unwait:function(){this.model.set("wait",!1)},update:function(e){this.model.set("data",e)},value:function(e){void 0!==e&&this.model.set("value",e);var t=this.model.get("current");if(this.config[t]){var i=this.fields[t].value();if(null!==i&&(i=a.isArray(i)?i:[i],i.length>0)){var n=this._batch({values:[]});for(var s in i){var o=this.history[i[s]+"_"+this.config[t].src];if(!o)return Galaxy.emit.debug("ui-select-content::value()","Requested details not found for '"+i[s]+"'."),null;n.values.push(o)}return n.values.sort(function(e,t){return e.hid-t.hid}),n}}else Galaxy.emit.debug("ui-select-content::value()","Invalid value/source '"+e+"'.");return null},_changeCurrent:function(){var e=this;r.each(this.fields,function(t,i){e.model.get("current")==i?(t.$el.show(),r.each(e.$batch,function(t,n){t[e.config[i].batch==n?"show":"hide"]()}),e.button_type.value(i)):t.$el.hide()})},_changeType:function(){var n=this,o=(this.model.get("flavor")?this.model.get("flavor")+"_":"")+String(this.model.get("type"))+(this.model.get("multiple")?"_multiple":"");s[o]?this.config=s[o]:(this.config=s.data,Galaxy.emit.debug("ui-select-content::_changeType()","Invalid configuration/type id '"+o+"'."));var a=n.model.get("data"),l=e.textify(this.model.get("extensions")),c=this.model.get("src_labels");this.fields=[],this.button_data=[],r.each(this.config,function(e,t){n.button_data.push({value:t,icon:e.icon,tooltip:e.tooltip}),n.fields.push(new i.View({optional:n.model.get("optional"),multiple:e.multiple,searchable:!e.multiple||a&&a[e.src]&&a[e.src].length>n.model.get("pagelimit"),selectall:!1,error_text:"No "+(l?l+" ":"")+(c[e.src]||"content")+" available.",onchange:function(){n.trigger("change")}}))}),this.button_type=new t.RadioButton.View({value:this.model.get("current"),data:this.button_data,onchange:function(e){n.model.set("current",e),n.trigger("change")}}),this.$el.empty();var d=0;this.fields.length>1&&(this.$el.append(this.button_type.$el),d=Math.max(0,35*this.fields.length)+"px"),r.each(this.fields,function(e){n.$el.append(e.$el.css({"margin-left":d}))}),r.each(this.$batch,function(e,t){n.$el.append(e.css({"margin-left":d}))}),this.model.set("current",0),this._changeCurrent(),this._changeData()},_changeWait:function(){var e=this;r.each(this.fields,function(t){t[e.model.get("wait")?"wait":"unwait"]()})},_changeData:function(){var e=this.model.get("data"),t=this,i={};r.each(e,function(e,n){i[n]=[],r.each(e,function(e){i[n].push({hid:e.hid,keep:e.keep,label:e.hid+": "+e.name,value:e.id}),t.history[e.id+"_"+n]=e})}),r.each(this.config,function(e,n){i[e.src]&&t.fields[n].add(i[e.src],function(e,t){return t.hid-e.hid})})},_changeValue:function(){var e=this.model.get("value");if(e&&e.values&&e.values.length>0){var t=[];r.each(e.values,function(e){t.push(e.id)});for(var i=e.values[0].src,n=e.values.length>1,s=0;s
"]);return a.extend(a.clone(l.prototype.templates),{controls:e})}(),{ListPanel:l,ModelListPanel:c}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(2),i(1))},function(e,t,i){var n,s;(function(o,a){n=[i(6),i(5)],s=function(e,t){var i=o.View.extend(e.LoggableMixin).extend(e.HiddenUntilActivatedViewMixin).extend({tagName:"div",className:"tags-display",initialize:function(e){this.listenTo(this.model,"change:tags",function(){this.render()}),this.hiddenUntilActivated(e.$activator,e)},render:function(){var e=this;return this.$el.html(this._template()),this.$input().select2({placeholder:"Add tags",width:"100%",tags:function(){return e._getTagsUsed()}}),this._setUpBehaviors(),this},_template:function(){return['",''].join("")},tagsToCSV:function(){var e=this.model.get("tags");return!a.isArray(e)||a.isEmpty(e)?"":e.map(function(e){return a.escape(e)}).sort().join(",")},$input:function(){return this.$el.find("input.tags-input")},_getTagsUsed:function(){return Galaxy.user.get("tags_used")},_setUpBehaviors:function(){var e=this;this.$input().on("change",function(t){e.model.save({tags:t.val},{silent:!0}),t.added&&e._addNewTagToTagsUsed(t.added.text+"")})},_addNewTagToTagsUsed:function(e){var t=Galaxy.user.get("tags_used");a.contains(t,e)||(t.push(e),t.sort(),Galaxy.user.set("tags_used",t))},remove:function(){this.$input.off(),this.stopListening(this.model),o.View.prototype.remove.call(this)},toString:function(){return["TagsEditor(",this.model+"",")"].join("")}});return{TagsEditor:i}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(2))},function(e,t,i){var n,s;(function(o,a){n=[i(5)],s=function(e){"use strict";function t(t,i,n){return Galaxy.modal.show({title:i,body:t,closing_events:!0,buttons:{Ok:function(){Galaxy.modal.hide()}}}),Galaxy.modal.$el.addClass("error-modal"),n&&(Galaxy.modal.$(".error-details").add(Galaxy.modal.$('button:contains("Details")')).remove(),o("").addClass("error-details").hide().appendTo(Galaxy.modal.$(".modal-content")).append([o("").text(h),o("").text(JSON.stringify(n,null," "))]),o('").appendTo(Galaxy.modal.$(".buttons")).click(function(){Galaxy.modal.$(".error-details").toggle()})),Galaxy.modal}function i(i,n,s){if(i){if(i=e(i),n=e(n)||e("Error:"),window.Galaxy&&Galaxy.modal)return t(i,n,s);alert(n+"\n\n"+i),console.log("error details:",JSON.stringify(s))}}function n(){return i(e("You appear to be offline. Please check your connection and try again."),e("Offline?"))}function s(){return i(e("Galaxy is currently unreachable. Please try again in a few minutes.")+" "+c,e("Cannot connect to Galaxy"))}function r(t,n,s,o,a){o=o||d,o+=" "+c,a=a||e("An error occurred");var r=l(t,n,s);return i(o,a,r)}function l(e,t,i){return{raven:a.result(window.Raven,"lastEventId"),userAgent:navigator.userAgent,onLine:navigator.onLine,version:a.result(Galaxy.config,"version_major"),xhr:a.omit(t,a.functions(t)),options:a.omit(i,"xhr"),url:a.result(Galaxy.lastAjax,"url"),data:a.result(Galaxy.lastAjax,"data"),model:a.result(e,"toJSON",e+""),user:a.omit(a.result(Galaxy.user,"toJSON"),"email")}}var c=e("Please contact a Galaxy administrator if the problem persists."),d=e("An error occurred while updating information with the server."),h=e("The following information can assist the developers in finding the source of the error:");return{errorModal:i,offlineErrorModal:n,badGatewayErrorModal:s,ajaxErrorModal:r}}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(1),i(2))},function(e,t,i){var n,s;(function(i,o,a,r){n=[],s=function(){var e=i.View.extend({initialize:function(e,t){this.$button=e,this.$button.length||(this.$button=o("")),this.options=t||[],this.$button.data("popupmenu",this);var i=this;this.$button.click(function(e){return o(".popmenu-wrapper").remove(),i._renderAndShow(e),!1})},_renderAndShow:function(e){this.render(),this.$el.appendTo("body").css(this._getShownPosition(e)).show(),this._setUpCloseBehavior()},render:function(){if(this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options)),this.options.length){var e=this;this.$el.find("li").each(function(t,i){var n=e.options[t];n.func&&o(this).children("a.popupmenu-option").click(function(t){n.func.call(e,t,n),t.preventDefault()})})}return this},template:function(e,t){return['
This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at '+this.options.ftp_upload_site+' using your Galaxy credentials (email address and password).
'}})}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(1))},function(e,t,i){var n,s;(function(o,a,r){n=[i(4)],s=function(e){return o.View.extend({options:{class_check:"fa-check-square-o",class_uncheck:"fa-square-o",parameters:[{id:"space_to_tab",title:"Convert spaces to tabs"},{id:"to_posix_lines",title:"Use POSIX standard"}]},initialize:function(e){this.model=e.model,this.setElement(a("").addClass("upload-settings")),this.$el.append(a("").addClass("upload-settings-cover")),this.$el.append(a("
');\n\t }\n\t }\n\t\n\t // Render first chunk.\n\t var self = this,\n\t first_chunk = this.model.get('first_data_chunk');\n\t if (first_chunk) {\n\t // First chunk is bootstrapped, so render now.\n\t this._renderChunk(first_chunk);\n\t }\n\t else {\n\t // No bootstrapping, so get first chunk and then render.\n\t $.when(self.model.get_next_chunk()).then(function(result) {\n\t self._renderChunk(result);\n\t });\n\t }\n\t\n\t // -- Show new chunks during scrolling. --\n\t\n\t // Set up chunk loading when scrolling using the scrolling element.\n\t this.scroll_elt.scroll(function(){\n\t self.attempt_to_fetch();\n\t });\n\t },\n\t\n\t /**\n\t * Returns true if user has scrolled to the bottom of the view.\n\t */\n\t scrolled_to_bottom: function() {\n\t return false;\n\t },\n\t\n\t // -- Helper functions. --\n\t\n\t _renderCell: function(cell_contents, index, colspan) {\n\t var $cell = $('
').text(cell_contents);\n\t var column_types = this.model.get_metadata('column_types');\n\t if (colspan !== undefined) {\n\t $cell.attr('colspan', colspan).addClass('stringalign');\n\t } else if (column_types) {\n\t if (index < column_types.length) {\n\t if (column_types[index] === 'str' || column_types[index] === 'list') {\n\t /* Left align all str columns, right align the rest */\n\t $cell.addClass('stringalign');\n\t }\n\t }\n\t }\n\t return $cell;\n\t },\n\t\n\t _renderRow: function(line) {\n\t // Check length of cells to ensure this is a complete row.\n\t var cells = line.split('\\t'),\n\t row = $('
'),\n\t num_columns = this.model.get_metadata('columns');\n\t\n\t if (this.row_count % 2 !== 0) {\n\t row.addClass('dark_row');\n\t }\n\t\n\t if (cells.length === num_columns) {\n\t _.each(cells, function(cell_contents, index) {\n\t row.append(this._renderCell(cell_contents, index));\n\t }, this);\n\t }\n\t else if (cells.length > num_columns) {\n\t // SAM file or like format with optional metadata included.\n\t _.each(cells.slice(0, num_columns - 1), function(cell_contents, index) {\n\t row.append(this._renderCell(cell_contents, index));\n\t }, this);\n\t row.append(this._renderCell(cells.slice(num_columns - 1).join('\\t'), num_columns - 1));\n\t }\n\t else if (cells.length === 1){\n\t // Comment line, just return the one cell.\n\t row.append(this._renderCell(line, 0, num_columns));\n\t }\n\t else {\n\t // cells.length is greater than one, but less than num_columns. Render cells and pad tds.\n\t // Possibly a SAM file or like format with optional metadata missing.\n\t // Could also be a tabular file with a line with missing columns.\n\t _.each(cells, function(cell_contents, index) {\n\t row.append(this._renderCell(cell_contents, index));\n\t }, this);\n\t _.each(_.range(num_columns - cells.length), function(){\n\t row.append($('
'));\n\t });\n\t }\n\t\n\t this.row_count++;\n\t return row;\n\t },\n\t\n\t _renderChunk: function(chunk) {\n\t var data_table = this.$el.find('table');\n\t _.each(chunk.ck_data.split('\\n'), function(line, index) {\n\t if (line !== ''){\n\t data_table.append(this._renderRow(line));\n\t }\n\t }, this);\n\t }\n\t});\n\t\n\t/**\n\t * Tabular view that is placed at the top level of page. Scrolling occurs\n\t * view top-level elements outside of view.\n\t */\n\tvar TopLevelTabularDatasetChunkedView = TabularDatasetChunkedView.extend({\n\t\n\t initialize: function(options) {\n\t TabularDatasetChunkedView.prototype.initialize.call(this, options);\n\t\n\t // Scrolling happens in top-level elements.\n\t scroll_elt = _.find(this.$el.parents(), function(p) {\n\t return $(p).css('overflow') === 'auto';\n\t });\n\t\n\t // If no scrolling element found, use window.\n\t if (!scroll_elt) { scroll_elt = window; }\n\t\n\t // Wrap scrolling element for easy access.\n\t this.scroll_elt = $(scroll_elt);\n\t },\n\t\n\t /**\n\t * Returns true if user has scrolled to the bottom of the view.\n\t */\n\t scrolled_to_bottom: function() {\n\t return (this.$el.height() - this.scroll_elt.scrollTop() - this.scroll_elt.height() <= 0);\n\t }\n\t\n\t});\n\t\n\t/**\n\t * Tabular view tnat is embedded in a page. Scrolling occurs in view's el.\n\t */\n\tvar EmbeddedTabularDatasetChunkedView = TabularDatasetChunkedView.extend({\n\t\n\t initialize: function(options) {\n\t TabularDatasetChunkedView.prototype.initialize.call(this, options);\n\t\n\t // Because view is embedded, set up div to do scrolling.\n\t this.scroll_elt = this.$el.css({\n\t position: 'relative',\n\t overflow: 'scroll',\n\t height: options.height || '500px'\n\t });\n\t },\n\t\n\t /**\n\t * Returns true if user has scrolled to the bottom of the view.\n\t */\n\t scrolled_to_bottom: function() {\n\t return this.$el.scrollTop() + this.$el.innerHeight() >= this.el.scrollHeight;\n\t }\n\t\n\t});\n\t\n\t// button for trackster visualization\n\tvar TabularButtonTracksterView = Backbone.View.extend({\n\t\n\t // gene region columns\n\t col: {\n\t chrom : null,\n\t start : null,\n\t end : null\n\t },\n\t\n\t // url for trackster\n\t url_viz : null,\n\t\n\t // dataset id\n\t dataset_id : null,\n\t\n\t // database key\n\t genome_build: null,\n\t\n\t // data type\n\t file_ext : null,\n\t\n\t // backbone initialize\n\t initialize: function (options) {\n\t // check if environment is available\n\t var Galaxy = parent.Galaxy;\n\t\n\t // link galaxy modal or create one\n\t if (Galaxy && Galaxy.modal) {\n\t this.modal = Galaxy.modal;\n\t }\n\t\n\t // link galaxy frames\n\t if (Galaxy && Galaxy.frame) {\n\t this.frame = Galaxy.frame;\n\t }\n\t\n\t // check\n\t if (!this.modal || !this.frame) {\n\t return;\n\t }\n\t\n\t // model/metadata\n\t var model = options.model;\n\t var metadata = model.get('metadata');\n\t\n\t // check for datatype\n\t if (!model.get('file_ext')) {\n\t return;\n\t }\n\t\n\t // get data type\n\t this.file_ext = model.get('file_ext');\n\t\n\t // check for bed-file format\n\t if (this.file_ext == 'bed')\n\t {\n\t // verify that metadata exists\n\t if (metadata.get('chromCol') && metadata.get('startCol') && metadata.get('endCol'))\n\t {\n\t // read in columns\n\t this.col.chrom = metadata.get('chromCol') - 1;\n\t this.col.start = metadata.get('startCol') - 1;\n\t this.col.end = metadata.get('endCol') - 1;\n\t } else {\n\t console.log('TabularButtonTrackster : Bed-file metadata incomplete.');\n\t return;\n\t }\n\t }\n\t\n\t // check for vcf-file format\n\t if (this.file_ext == 'vcf')\n\t {\n\t // search array\n\t function search (str, array) {\n\t for (var j = 0; j < array.length; j++)\n\t if (array[j].match(str)) return j;\n\t return -1;\n\t };\n\t\n\t // load\n\t this.col.chrom = search('Chrom', metadata.get('column_names'));\n\t this.col.start = search('Pos', metadata.get('column_names'));\n\t this.col.end = null;\n\t\n\t // verify that metadata exists\n\t if (this.col.chrom == -1 || this.col.start == -1) {\n\t console.log('TabularButtonTrackster : VCF-file metadata incomplete.');\n\t return;\n\t }\n\t }\n\t\n\t // check\n\t if(this.col.chrom === undefined) {\n\t return;\n\t }\n\t\n\t // get dataset id\n\t if (model.id) {\n\t this.dataset_id = model.id;\n\t } else {\n\t console.log('TabularButtonTrackster : Dataset identification is missing.');\n\t return;\n\t }\n\t\n\t // get url\n\t if (model.get('url_viz')) {\n\t this.url_viz = model.get('url_viz');\n\t } else {\n\t console.log('TabularButtonTrackster : Url for visualization controller is missing.');\n\t return;\n\t }\n\t\n\t // get genome_build / database key\n\t if (model.get('genome_build')) {\n\t this.genome_build = model.get('genome_build');\n\t }\n\t\n\t // create the icon\n\t var btn_viz = new mod_icon_btn.IconButtonView({\n\t model : new mod_icon_btn.IconButton({\n\t title : 'Visualize',\n\t icon_class : 'chart_curve',\n\t id : 'btn_viz'\n\t })\n\t });\n\t\n\t // set element\n\t this.setElement(options.$el);\n\t\n\t // add to element\n\t this.$el.append(btn_viz.render().$el);\n\t\n\t // hide the button\n\t this.hide();\n\t },\n\t\n\t // backbone events\n\t events:\n\t {\n\t 'mouseover tr' : 'show',\n\t 'mouseleave' : 'hide'\n\t },\n\t\n\t // show button\n\t show: function (e) {\n\t // is numeric\n\t function is_numeric(n) {\n\t return !isNaN(parseFloat(n)) && isFinite(n);\n\t };\n\t\n\t // check\n\t if(this.col.chrom === null)\n\t return;\n\t\n\t // get selected data line\n\t var row = $(e.target).parent();\n\t\n\t // verify that location has been found\n\t var chrom = row.children().eq(this.col.chrom).html();\n\t var start = row.children().eq(this.col.start).html();\n\t\n\t // end is optional\n\t var end = this.col.end ? row.children().eq(this.col.end).html() : start;\n\t\n\t // double check location\n\t if (!chrom.match(\"^#\") && chrom !== \"\" && is_numeric(start)) {\n\t\n\t // get target gene region\n\t var btn_viz_pars = {\n\t dataset_id : this.dataset_id,\n\t gene_region : chrom + \":\" + start + \"-\" + end\n\t };\n\t\n\t // get button position\n\t var offset = row.offset();\n\t var left = offset.left - 10;\n\t var top = offset.top - $(window).scrollTop() + 3;\n\t\n\t // update css\n\t $('#btn_viz').css({'position': 'fixed', 'top': top + 'px', 'left': left + 'px'});\n\t $('#btn_viz').off('click');\n\t $('#btn_viz').click(this.create_trackster_action(this.url_viz, btn_viz_pars, this.genome_build));\n\t\n\t // show the button\n\t $('#btn_viz').show();\n\t } else {\n\t // hide the button\n\t $('#btn_viz').hide();\n\t }\n\t },\n\t\n\t // hide button\n\t hide: function () {\n\t this.$el.find('#btn_viz').hide();\n\t },\n\t\n\t // create action\n\t create_trackster_action : function (vis_url, dataset_params, dbkey) {\n\t // link this\n\t var self = this;\n\t\n\t // create function\n\t return function() {\n\t var listTracksParams = {};\n\t if (dbkey) {\n\t listTracksParams[ 'f-dbkey' ] = dbkey;\n\t }\n\t $.ajax({\n\t url: vis_url + '/list_tracks?' + $.param( listTracksParams ),\n\t dataType: 'html',\n\t error: function() {\n\t // show error message\n\t self.modal.show({\n\t title : 'Something went wrong!',\n\t body : 'Unfortunately we could not add this dataset to the track browser. Please try again or contact us.',\n\t buttons : {\n\t 'Cancel': function(){\n\t self.modal.hide();\n\t }\n\t }\n\t });\n\t },\n\t success: function(table_html) {\n\t self.modal.show({\n\t title : 'View Data in a New or Saved Visualization',\n\t buttons :{\n\t 'Cancel': function(){\n\t self.modal.hide();\n\t },\n\t 'View in saved visualization': function(){\n\t // show modal with saved visualizations\n\t self.modal.show(\n\t {\n\t title : 'Add Data to Saved Visualization',\n\t body : table_html,\n\t buttons : {\n\t 'Cancel': function(){\n\t self.modal.hide();\n\t },\n\t 'Add to visualization': function(){\n\t // hide\n\t self.modal.hide();\n\t\n\t // search selected fields\n\t self.modal.$el.find('input[name=id]:checked').each(function(){\n\t // get visualization id\n\t var vis_id = $(this).val();\n\t dataset_params.id = vis_id;\n\t\n\t // add widget\n\t self.frame.add({\n\t title : 'Trackster',\n\t type : 'url',\n\t content : vis_url + '/trackster?' + $.param(dataset_params)\n\t });\n\t });\n\t }\n\t }\n\t });\n\t },\n\t 'View in new visualization': function(){\n\t // hide\n\t self.modal.hide();\n\t\n\t // add widget\n\t self.frame.add({\n\t title : 'Trackster',\n\t type : 'url',\n\t content : vis_url + '/trackster?' + $.param(dataset_params)\n\t });\n\t }\n\t }\n\t });\n\t }\n\t });\n\t return false;\n\t };\n\t }\n\t});\n\t\n\t// -- Utility functions. --\n\t\n\t/**\n\t * Create a model, attach it to a view, render view, and attach it to a parent element.\n\t */\n\tvar createModelAndView = function(model, view, model_config, parent_elt) {\n\t // Create model, view.\n\t var a_view = new view({\n\t model: new model(model_config)\n\t });\n\t\n\t // Render view and add to parent element.\n\t a_view.render();\n\t if (parent_elt) {\n\t parent_elt.append(a_view.$el);\n\t }\n\t\n\t return a_view;\n\t};\n\t\n\t/**\n\t * Create a tabular dataset chunked view (and requisite tabular dataset model)\n\t * and appends to parent_elt.\n\t */\n\tvar createTabularDatasetChunkedView = function(options) {\n\t // If no model, create and set model from dataset config.\n\t if (!options.model) {\n\t options.model = new TabularDataset(options.dataset_config);\n\t }\n\t\n\t var parent_elt = options.parent_elt;\n\t var embedded = options.embedded;\n\t\n\t // Clean up options so that only needed options are passed to view.\n\t delete options.embedded;\n\t delete options.parent_elt;\n\t delete options.dataset_config;\n\t\n\t // Create and set up view.\n\t var view = (embedded ? new EmbeddedTabularDatasetChunkedView(options) :\n\t new TopLevelTabularDatasetChunkedView(options));\n\t view.render();\n\t\n\t if (parent_elt) {\n\t parent_elt.append(view.$el);\n\t // If we're sticking this in another element, once it's appended check\n\t // to make sure we've filled enough space.\n\t // Without this, the scroll elements don't work.\n\t view.expand_to_container();\n\t }\n\t\n\t return view;\n\t};\n\t\n\treturn {\n\t Dataset: Dataset,\n\t TabularDataset: TabularDataset,\n\t DatasetCollection: DatasetCollection,\n\t TabularDatasetChunkedView: TabularDatasetChunkedView,\n\t createTabularDatasetChunkedView: createTabularDatasetChunkedView\n\t};\n\t\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(2), __webpack_require__(1)))\n\n/***/ },\n/* 12 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function(){\n\t\n\t'use strict';\n\t//==============================================================================\n\t/** Map of possible HDA/collection/job states to their string equivalents.\n\t * A port of galaxy.model.Dataset.states.\n\t */\n\tvar STATES = {\n\t // NOT ready states\n\t /** is uploading and not ready */\n\t UPLOAD : 'upload',\n\t /** the job that will produce the dataset queued in the runner */\n\t QUEUED : 'queued',\n\t /** the job that will produce the dataset is running */\n\t RUNNING : 'running',\n\t /** metadata for the dataset is being discovered/set */\n\t SETTING_METADATA : 'setting_metadata',\n\t\n\t // ready states\n\t /** was created without a tool */\n\t NEW : 'new',\n\t /** has no data */\n\t EMPTY : 'empty',\n\t /** has successfully completed running */\n\t OK : 'ok',\n\t\n\t /** the job that will produce the dataset paused */\n\t PAUSED : 'paused',\n\t /** metadata discovery/setting failed or errored (but otherwise ok) */\n\t FAILED_METADATA : 'failed_metadata',\n\t//TODO: not in trans.app.model.Dataset.states - is in database\n\t /** not accessible to the current user (i.e. due to permissions) */\n\t NOT_VIEWABLE : 'noPermission',\n\t /** deleted while uploading */\n\t DISCARDED : 'discarded',\n\t /** the tool producing this dataset failed */\n\t ERROR : 'error'\n\t};\n\t\n\tSTATES.READY_STATES = [\n\t STATES.OK,\n\t STATES.EMPTY,\n\t STATES.PAUSED,\n\t STATES.FAILED_METADATA,\n\t STATES.NOT_VIEWABLE,\n\t STATES.DISCARDED,\n\t STATES.ERROR\n\t];\n\t\n\tSTATES.NOT_READY_STATES = [\n\t STATES.UPLOAD,\n\t STATES.QUEUED,\n\t STATES.RUNNING,\n\t STATES.SETTING_METADATA,\n\t STATES.NEW\n\t];\n\t\n\t\n\t//==============================================================================\n\t return STATES;\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\n\n/***/ },\n/* 13 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $) {/** This module contains all button views. */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4) ], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils ) {\n\t /** This renders the default button which is used e.g. at the bottom of the upload modal. */\n\t var ButtonDefault = Backbone.View.extend({\n\t initialize: function( options ) {\n\t this.model = options && options.model || new Backbone.Model({\n\t id : Utils.uid(),\n\t title : '',\n\t floating : 'right',\n\t icon : '',\n\t cls : 'btn btn-default',\n\t wait : false,\n\t wait_text : 'Sending...',\n\t wait_cls : 'btn btn-info',\n\t disabled : false,\n\t percentage : -1\n\t }).set( options );\n\t this.setElement( $( '' ).attr( 'type', 'button' )\n\t .append( this.$icon = $( '' ) )\n\t .append( this.$title = $( '' ) )\n\t .append( this.$progress = $( '' ).append( this.$progress_bar = $( '' ) ) ) );\n\t this.listenTo( this.model, 'change', this.render, this );\n\t this.render();\n\t },\n\t\n\t render: function() {\n\t var self = this;\n\t var options = this.model.attributes;\n\t this.$el.removeClass()\n\t .addClass( 'ui-button-default' )\n\t .addClass( options.disabled && 'disabled' )\n\t .attr( 'id', options.id )\n\t .attr( 'disabled', options.disabled )\n\t .css( 'float', options.floating )\n\t .off( 'click' ).on( 'click' , function() {\n\t $( '.tooltip' ).hide();\n\t options.onclick && !self.disabled && options.onclick();\n\t })\n\t .tooltip( { title: options.tooltip, placement: 'bottom' } );\n\t this.$progress.addClass( 'progress' ).css( 'display', options.percentage !== -1 ? 'block' : 'none' );\n\t this.$progress_bar.addClass( 'progress-bar' ).css( { width : options.percentage + '%' } );\n\t this.$icon.removeClass().addClass( 'icon fa' );\n\t this.$title.removeClass().addClass( 'title' );\n\t if ( options.wait ) {\n\t this.$el.addClass( options.wait_cls ).prop( 'disabled', true );\n\t this.$icon.addClass( 'fa-spinner fa-spin ui-margin-right' );\n\t this.$title.html( options.wait_text );\n\t } else {\n\t this.$el.addClass( options.cls );\n\t this.$icon.addClass( options.icon );\n\t this.$title.html( options.title );\n\t options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n\t }\n\t },\n\t\n\t /** Show button */\n\t show: function() {\n\t this.$el.show();\n\t },\n\t\n\t /** Hide button */\n\t hide: function() {\n\t this.$el.hide();\n\t },\n\t\n\t /** Disable button */\n\t disable: function() {\n\t this.model.set( 'disabled', true );\n\t },\n\t\n\t /** Enable button */\n\t enable: function() {\n\t this.model.set( 'disabled', false );\n\t },\n\t\n\t /** Show spinner to indicate that the button is not ready to be clicked */\n\t wait: function() {\n\t this.model.set( 'wait', true );\n\t },\n\t\n\t /** Hide spinner to indicate that the button is ready to be clicked */\n\t unwait: function() {\n\t this.model.set( 'wait', false );\n\t },\n\t\n\t /** Change icon */\n\t setIcon: function( icon ) {\n\t this.model.set( 'icon', icon );\n\t }\n\t });\n\t\n\t /** This button allows the right-click/open-in-new-tab feature, its used e.g. for panel buttons. */\n\t var ButtonLink = ButtonDefault.extend({\n\t initialize: function( options ) {\n\t this.model = options && options.model || new Backbone.Model({\n\t id : Utils.uid(),\n\t title : '',\n\t icon : '',\n\t cls : ''\n\t }).set( options );\n\t this.setElement( $( '' ).append( this.$icon = $( '' ) ) );\n\t this.listenTo( this.model, 'change', this.render, this );\n\t this.render();\n\t },\n\t\n\t render: function() {\n\t var options = this.model.attributes;\n\t this.$el.removeClass()\n\t .addClass( options.cls )\n\t .attr( { id : options.id,\n\t href : options.href || 'javascript:void(0)',\n\t title : options.title,\n\t target : options.target || '_top',\n\t disabled : options.disabled } )\n\t .off( 'click' ).on( 'click' , function() {\n\t options.onclick && !options.disabled && options.onclick();\n\t });\n\t this.$icon.removeClass().addClass( options.icon );\n\t }\n\t });\n\t\n\t /** The check button is used in the tool form and allows to distinguish between multiple states e.g. all, partially and nothing selected. */\n\t var ButtonCheck = Backbone.View.extend({\n\t initialize: function( options ) {\n\t this.model = options && options.model || new Backbone.Model({\n\t id : Utils.uid(),\n\t title : 'Select/Unselect all',\n\t icons : [ 'fa-square-o', 'fa-minus-square-o', 'fa-check-square-o' ],\n\t value : 0,\n\t onchange : function(){}\n\t }).set( options );\n\t this.setElement( $( '' ).append( this.$icon = $( '' ) )\n\t .append( this.$title = $( '' ) ) );\n\t this.listenTo( this.model, 'change', this.render, this );\n\t this.render();\n\t },\n\t\n\t render: function( options ) {\n\t var self = this;\n\t var options = this.model.attributes;\n\t this.$el.addClass( 'ui-button-check' )\n\t .off( 'click' ).on('click', function() {\n\t self.model.set( 'value', ( self.model.get( 'value' ) === 0 && 2 ) || 0 );\n\t options.onclick && options.onclick();\n\t });\n\t this.$title.html( options.title );\n\t this.$icon.removeClass()\n\t .addClass( 'icon fa ui-margin-right' )\n\t .addClass( options.icons[ options.value ] );\n\t },\n\t\n\t /* Sets a new value and/or returns the value.\n\t * @param{Integer} new_val - Set a new value 0=unchecked, 1=partial and 2=checked.\n\t * OR:\n\t * @param{Integer} new_val - Number of selected options.\n\t * @param{Integer} total - Total number of available options.\n\t */\n\t value: function ( new_val, total ) {\n\t if ( new_val !== undefined ) {\n\t if ( total && new_val !== 0 ) {\n\t new_val = ( new_val !== total ) && 1 || 2;\n\t }\n\t this.model.set( 'value', new_val );\n\t this.model.get( 'onchange' )( this.model.get( 'value' ) );\n\t }\n\t return this.model.get( 'value' );\n\t }\n\t });\n\t\n\t /** This renders a differently styled, more compact button version. */\n\t var ButtonIcon = ButtonDefault.extend({\n\t initialize: function( options ) {\n\t this.model = options && options.model || new Backbone.Model({\n\t id : Utils.uid(),\n\t title : '',\n\t floating : 'right',\n\t icon : '',\n\t cls : 'ui-button-icon',\n\t disabled : false\n\t }).set( options );\n\t this.setElement( $( '' ).append( this.$button = $( '' ).append( this.$icon = $( '' ) )\n\t .append( this.$title = $( '' ) ) ) );\n\t this.listenTo( this.model, 'change', this.render, this );\n\t this.render();\n\t },\n\t\n\t render : function( options ) {\n\t var self = this;\n\t var options = this.model.attributes;\n\t this.$el.removeClass()\n\t .addClass( options.cls )\n\t .addClass( options.disabled && 'disabled' )\n\t .attr( 'disabled', options.disabled )\n\t .attr( 'id', options.id )\n\t .css( 'float', options.floating )\n\t .off( 'click' ).on( 'click', function() {\n\t $( '.tooltip' ).hide();\n\t !options.disabled && options.onclick && options.onclick();\n\t });\n\t this.$button.addClass( 'button' ).tooltip( { title: options.tooltip, placement: 'bottom' } );\n\t this.$icon.removeClass().addClass( 'icon fa' ).addClass( options.icon );\n\t this.$title.addClass( 'title' ).html( options.title );\n\t options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n\t }\n\t });\n\t\n\t /** This class creates a button with dropdown menu. */\n\t var ButtonMenu = ButtonDefault.extend({\n\t $menu: null,\n\t initialize: function ( options ) {\n\t this.model = options && options.model || new Backbone.Model({\n\t id : '',\n\t title : '',\n\t floating : 'right',\n\t pull : 'right',\n\t icon : null,\n\t onclick : null,\n\t cls : 'ui-button-icon ui-button-menu',\n\t tooltip : '',\n\t target : '',\n\t href : '',\n\t onunload : null,\n\t visible : true,\n\t tag : ''\n\t }).set( options );\n\t this.setElement( $( '' ).append( this.$root = $( '' ).append( this.$icon = $( '' ) )\n\t .append( this.$title = $( '' ) ) ) );\n\t this.listenTo( this.model, 'change', this.render, this );\n\t this.render();\n\t },\n\t\n\t render: function() {\n\t var self = this;\n\t var options = this.model.attributes;\n\t this.$el.removeClass()\n\t .addClass( 'dropdown' )\n\t .addClass( options.cls )\n\t .attr( 'id', options.id )\n\t .css( { float : options.floating,\n\t display : options.visible ? 'block' : 'none' } );\n\t this.$root.addClass( 'root button dropdown-toggle' )\n\t .attr( 'data-toggle', 'dropdown' )\n\t .tooltip( { title: options.tooltip, placement: 'bottom' } )\n\t .off( 'click' ).on( 'click', function( e ) {\n\t $( '.tooltip' ).hide();\n\t e.preventDefault();\n\t options.onclick && options.onclick();\n\t } );\n\t this.$icon.removeClass().addClass( 'icon fa' ).addClass( options.icon );\n\t this.$title.removeClass().addClass( 'title' ).html( options.title );\n\t options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n\t },\n\t\n\t /** Add a new menu item */\n\t addMenu: function ( options ) {\n\t var options = Utils.merge( options, {\n\t title : '',\n\t target : '',\n\t href : '',\n\t onclick : null,\n\t divider : false,\n\t icon : null,\n\t cls : 'button-menu btn-group'\n\t });\n\t if ( !this.$menu ) {\n\t this.$menu = $( '
' ).addClass( 'menu dropdown-menu' )\n\t .addClass( 'pull-' + this.model.get( 'pull' ) )\n\t .attr( 'role', 'menu' );\n\t this.$el.append( this.$menu );\n\t }\n\t var $link = $( '' ).addClass( 'dropdown-item' )\n\t .attr( { href : options.href,\n\t target : options.target } )\n\t .append( $( '' ).addClass( 'fa' )\n\t .addClass( options.icon )\n\t .css( 'display', options.icon ? 'inline-block' : 'none' ) )\n\t .append( options.title )\n\t .on( 'click', function( e ) {\n\t if ( options.onclick ) {\n\t e.preventDefault();\n\t options.onclick();\n\t }\n\t } );\n\t this.$menu.append( $( '' ).append( $link ) );\n\t options.divider && this.$menu.append( $( '' ).addClass( 'divider' ) );\n\t }\n\t });\n\t\n\t return {\n\t ButtonDefault : ButtonDefault,\n\t ButtonLink : ButtonLink,\n\t ButtonIcon : ButtonIcon,\n\t ButtonCheck : ButtonCheck,\n\t ButtonMenu : ButtonMenu\n\t }\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(1)))\n\n/***/ },\n/* 14 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _, $) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t //jquery\n\t //backbone\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function(){\n\t//=============================================================================\n\t/**\n\t * backbone model for icon buttons\n\t */\n\tvar IconButton = Backbone.Model.extend({\n\t defaults: {\n\t title : \"\",\n\t icon_class : \"\",\n\t on_click : null,\n\t menu_options : null,\n\t is_menu_button : true,\n\t id : null,\n\t href : null,\n\t target : null,\n\t enabled : true,\n\t visible : true,\n\t tooltip_config : {}\n\t }\n\t});\n\t\n\t/**\n\t * backbone view for icon buttons\n\t */\n\tvar IconButtonView = Backbone.View.extend({\n\t\n\t initialize : function(){\n\t // better rendering this way\n\t this.model.attributes.tooltip_config = { placement : 'bottom' };\n\t this.model.bind( 'change', this.render, this );\n\t },\n\t\n\t render : function( ){\n\t // hide tooltip\n\t this.$el.tooltip( 'hide' );\n\t\n\t var new_elem = this.template( this.model.toJSON() );\n\t // configure tooltip\n\t new_elem.tooltip( this.model.get( 'tooltip_config' ));\n\t this.$el.replaceWith( new_elem );\n\t this.setElement( new_elem );\n\t return this;\n\t },\n\t\n\t events : {\n\t 'click' : 'click'\n\t },\n\t\n\t click : function( event ){\n\t // if on_click pass to that function\n\t if( _.isFunction( this.model.get( 'on_click' ) ) ){\n\t this.model.get( 'on_click' )( event );\n\t return false;\n\t }\n\t // otherwise, bubble up ( to href or whatever )\n\t return true;\n\t },\n\t\n\t // generate html element\n\t template: function( options ){\n\t var buffer = 'title=\"' + options.title + '\" class=\"icon-button';\n\t\n\t if( options.is_menu_button ){\n\t buffer += ' menu-button';\n\t }\n\t\n\t buffer += ' ' + options.icon_class;\n\t\n\t if( !options.enabled ){\n\t buffer += '_disabled';\n\t }\n\t\n\t // close class tag\n\t buffer += '\"';\n\t\n\t if( options.id ){\n\t buffer += ' id=\"' + options.id + '\"';\n\t }\n\t\n\t buffer += ' href=\"' + options.href + '\"';\n\t // add target for href\n\t if( options.target ){\n\t buffer += ' target=\"' + options.target + '\"';\n\t }\n\t // set visibility\n\t if( !options.visible ){\n\t buffer += ' style=\"display: none;\"';\n\t }\n\t\n\t // enabled/disabled\n\t if ( options.enabled ){\n\t buffer = '';\n\t } else {\n\t buffer = '';\n\t }\n\t\n\t // return element\n\t return $( buffer );\n\t }\n\t} );\n\t\n\t// define collection\n\tvar IconButtonCollection = Backbone.Collection.extend({\n\t model: IconButton\n\t});\n\t\n\t/**\n\t * menu with multiple icon buttons\n\t * views are not needed nor used for individual buttons\n\t */\n\tvar IconButtonMenuView = Backbone.View.extend({\n\t\n\t tagName: 'div',\n\t\n\t initialize: function(){\n\t this.render();\n\t },\n\t\n\t render: function(){\n\t // initialize icon buttons\n\t var self = this;\n\t this.collection.each(function(button){\n\t // create and add icon button to menu\n\t var elt = $('')\n\t .attr('href', 'javascript:void(0)')\n\t .attr('title', button.attributes.title)\n\t .addClass('icon-button menu-button')\n\t .addClass(button.attributes.icon_class)\n\t .appendTo(self.$el)\n\t .click(button.attributes.on_click);\n\t\n\t // configure tooltip\n\t if (button.attributes.tooltip_config){\n\t elt.tooltip(button.attributes.tooltip_config);\n\t }\n\t\n\t // add popup menu to icon\n\t var menu_options = button.get('options');\n\t if (menu_options){\n\t make_popupmenu(elt, menu_options);\n\t }\n\t });\n\t\n\t // return\n\t return this;\n\t }\n\t});\n\t\n\t/**\n\t * Returns an IconButtonMenuView for the provided configuration.\n\t * Configuration is a list of dictionaries where each dictionary\n\t * defines an icon button. Each dictionary must have the following\n\t * elements: icon_class, title, and on_click.\n\t */\n\tvar create_icon_buttons_menu = function(config, global_config)\n\t{\n\t // initialize global configuration\n\t if (!global_config) global_config = {};\n\t\n\t // create and initialize menu\n\t var buttons = new IconButtonCollection(\n\t _.map(config, function(button_config){\n\t return new IconButton(_.extend(button_config, global_config));\n\t })\n\t );\n\t\n\t // return menu\n\t return new IconButtonMenuView( {collection: buttons} );\n\t};\n\t\n\t\n\t//=============================================================================\n\t return {\n\t IconButton : IconButton,\n\t IconButtonView : IconButtonView,\n\t IconButtonCollection : IconButtonCollection,\n\t IconButtonMenuView : IconButtonMenuView,\n\t create_icon_buttons_menu: create_icon_buttons_menu\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(2), __webpack_require__(1)))\n\n/***/ },\n/* 15 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js\n\t// Uses AMD or browser globals to create a jQuery plugin.\n\t(function (factory) {\n\t if (true) {\n\t //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will\n\t // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still\n\t // relying on jquery being loaded globally* in order to preserve plugins.\n\t !(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(1) ], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t } else {\n\t // Browser globals\n\t factory(jQuery);\n\t }\n\t\n\t}(function ( jQuery ) {\n\t'use_strict';\n\t\n\tvar $ = jQuery;\n\t\n\t// ============================================================================\n\t/**\n\t * Make an element with text editable: (a) when user clicks on text, a textbox/area\n\t * is provided for editing; (b) when enter key pressed, element's text is set and on_finish\n\t * is called.\n\t */\n\t$.fn.make_text_editable = function(config_dict) {\n\t // Get config options.\n\t var num_cols = (\"num_cols\" in config_dict ? config_dict.num_cols : 30),\n\t num_rows = (\"num_rows\" in config_dict ? config_dict.num_rows : 4),\n\t use_textarea = (\"use_textarea\" in config_dict ? config_dict.use_textarea : false),\n\t on_finish = (\"on_finish\" in config_dict ? config_dict.on_finish : null),\n\t help_text = (\"help_text\" in config_dict ? config_dict.help_text : null);\n\t\n\t // Add element behavior.\n\t var container = $(this);\n\t container.addClass(\"editable-text\").click(function(e) {\n\t // If there's already an input element, editing is active, so do nothing.\n\t if ($(this).children(\":input\").length > 0) {\n\t return;\n\t }\n\t\n\t container.removeClass(\"editable-text\");\n\t\n\t // Handler for setting element text.\n\t var set_text = function(new_text) {\n\t container.find(\":input\").remove();\n\t\n\t if (new_text !== \"\") {\n\t container.text(new_text);\n\t } else {\n\t // No text; need a line so that there is a click target.\n\t container.html(\" \");\n\t }\n\t container.addClass(\"editable-text\");\n\t\n\t if (on_finish) {\n\t on_finish(new_text);\n\t }\n\t };\n\t\n\t // Create input element(s) for editing.\n\t var cur_text = (\"cur_text\" in config_dict ? config_dict.cur_text : container.text() ),\n\t input_elt, button_elt;\n\t\n\t if (use_textarea) {\n\t input_elt = $(\"\")\n\t .attr({ rows: num_rows, cols: num_cols }).text($.trim(cur_text))\n\t .keyup(function(e) {\n\t if (e.keyCode === 27) {\n\t // Escape key.\n\t set_text(cur_text);\n\t }\n\t });\n\t button_elt = $(\"\").text(\"Done\").click(function() {\n\t set_text(input_elt.val());\n\t // Return false so that click does not propogate to container.\n\t return false;\n\t });\n\t }\n\t else {\n\t input_elt = $(\"\").attr({ value: $.trim(cur_text), size: num_cols })\n\t .blur(function() {\n\t set_text(cur_text);\n\t }).keyup(function(e) {\n\t if (e.keyCode === 27) {\n\t // Escape key.\n\t $(this).trigger(\"blur\");\n\t } else if (e.keyCode === 13) {\n\t // Enter key.\n\t set_text($(this).val());\n\t }\n\t\n\t // Do not propogate event to avoid unwanted side effects.\n\t e.stopPropagation();\n\t });\n\t }\n\t\n\t // Replace text with input object(s) and focus & select.\n\t container.text(\"\");\n\t container.append(input_elt);\n\t if (button_elt) {\n\t container.append(button_elt);\n\t }\n\t input_elt.focus();\n\t input_elt.select();\n\t\n\t // Do not propogate to elements below b/c that blurs input and prevents it from being used.\n\t e.stopPropagation();\n\t });\n\t\n\t // Add help text if there some.\n\t if (help_text) {\n\t container.attr(\"title\", help_text).tooltip();\n\t }\n\t\n\t return container;\n\t};\n\t\n\t// ============================================================================\n\t}));\n\n\n/***/ },\n/* 16 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $) {!(__WEBPACK_AMD_DEFINE_RESULT__ = function(){\n\t\n\t/**\n\t * Stringifies a number adding commas for digit grouping as per North America.\n\t */\n\tfunction commatize( number ) {\n\t number += ''; // Convert to string\n\t var rgx = /(\\d+)(\\d{3})/;\n\t while (rgx.test(number)) {\n\t number = number.replace(rgx, '$1' + ',' + '$2');\n\t }\n\t return number;\n\t}\n\t\n\t/**\n\t * Helper to determine if object is jQuery deferred.\n\t */\n\tvar is_deferred = function ( d ) {\n\t return ('promise' in d);\n\t};\n\t\n\t/**\n\t * Implementation of a server-state based deferred. Server is repeatedly polled, and when\n\t * condition is met, deferred is resolved.\n\t */\n\tvar ServerStateDeferred = Backbone.Model.extend({\n\t defaults: {\n\t ajax_settings: {},\n\t interval: 1000,\n\t success_fn: function(result) { return true; }\n\t },\n\t\n\t /**\n\t * Returns a deferred that resolves when success function returns true.\n\t */\n\t go: function() {\n\t var deferred = $.Deferred(),\n\t self = this,\n\t ajax_settings = self.get('ajax_settings'),\n\t success_fn = self.get('success_fn'),\n\t interval = self.get('interval'),\n\t _go = function() {\n\t $.ajax(ajax_settings).success(function(result) {\n\t if (success_fn(result)) {\n\t // Result is good, so resolve.\n\t deferred.resolve(result);\n\t }\n\t else {\n\t // Result not good, try again.\n\t setTimeout(_go, interval);\n\t }\n\t });\n\t };\n\t _go();\n\t return deferred;\n\t }\n\t});\n\t\n\t/**\n\t * Returns a random color in hexadecimal format that is sufficiently different from a single color\n\t * or set of colors.\n\t * @param colors a color or list of colors in the format '#RRGGBB'\n\t */\n\tvar get_random_color = function(colors) {\n\t // Default for colors is white.\n\t if (!colors) { colors = \"#ffffff\"; }\n\t\n\t // If needed, create list of colors.\n\t if ( typeof(colors) === \"string\" ) {\n\t colors = [ colors ];\n\t }\n\t\n\t // Convert colors to numbers.\n\t for (var i = 0; i < colors.length; i++) {\n\t colors[i] = parseInt( colors[i].slice(1), 16 );\n\t }\n\t\n\t // -- Perceived brightness and difference formulas are from\n\t // -- http://www.w3.org/WAI/ER/WD-AERT/#color-contrast\n\t\n\t // Compute perceived color brightness (based on RGB-YIQ transformation):\n\t var brightness = function(r, g, b) {\n\t return ( (r * 299) + (g * 587) + (b * 114) ) / 1000;\n\t };\n\t\n\t // Compute color difference:\n\t var difference = function(r1, g1, b1, r2, g2, b2) {\n\t return ( Math.max(r1, r2) - Math.min(r1, r2) ) +\n\t ( Math.max(g1, g2) - Math.min(g1, g2) ) +\n\t ( Math.max(b1, b2) - Math.min(b1, b2) );\n\t };\n\t\n\t // Create new random color.\n\t var new_color, nr, ng, nb,\n\t other_color, or, og, ob,\n\t n_brightness, o_brightness,\n\t diff, ok = false,\n\t num_tries = 0;\n\t do {\n\t // New color is never white b/c random in [0,1)\n\t new_color = Math.round( Math.random() * 0xffffff );\n\t nr = ( new_color & 0xff0000 ) >> 16;\n\t ng = ( new_color & 0x00ff00 ) >> 8;\n\t nb = new_color & 0x0000ff;\n\t n_brightness = brightness(nr, ng, nb);\n\t ok = true;\n\t for (i = 0; i < colors.length; i++) {\n\t other_color = colors[i];\n\t or = ( other_color & 0xff0000 ) >> 16;\n\t og = ( other_color & 0x00ff00 ) >> 8;\n\t ob = other_color & 0x0000ff;\n\t o_brightness = brightness(or, og, ob);\n\t diff = difference(nr, ng, nb, or, og, ob);\n\t // These thresholds may need to be adjusted. Brightness difference range is 125;\n\t // color difference range is 500.\n\t if ( ( Math.abs(n_brightness - o_brightness) < 40 ) ||\n\t ( diff < 200 ) ) {\n\t ok = false;\n\t break;\n\t }\n\t }\n\t\n\t num_tries++\n\t; } while (!ok && num_tries <= 10 );\n\t\n\t // Add 0x1000000 to left pad number with 0s.\n\t return '#' + ( 0x1000000 + new_color ).toString(16).substr(1,6);\n\t};\n\t\n\treturn {\n\t commatize: commatize,\n\t is_deferred: is_deferred,\n\t ServerStateDeferred : ServerStateDeferred,\n\t get_random_color : get_random_color\n\t};\n\t\n\t}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(1)))\n\n/***/ },\n/* 17 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(26),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( parseBibtex, baseMVC, _l ){\n\t/* global Backbone */\n\t// we use amd here to require, but bibtex uses a global or commonjs pattern.\n\t// webpack will load via commonjs and plain requirejs will load as global. Check both\n\tparseBibtex = parseBibtex || window.BibtexParser;\n\t\n\tvar logNamespace = 'citation';\n\t//==============================================================================\n\t/** @class model for tool citations.\n\t * @name Citation\n\t * @augments Backbone.Model\n\t */\n\tvar Citation = Backbone.Model.extend( baseMVC.LoggableMixin ).extend({\n\t _logNamespace : logNamespace,\n\t\n\t defaults : {\n\t content: ''\n\t },\n\t\n\t initialize: function() {\n\t var parsed;\n\t try {\n\t // TODO: to model.parse/.validate\n\t parsed = parseBibtex( this.attributes.content );\n\t } catch( err ){\n\t return;\n\t }\n\t // bibtex returns successfully parsed in .entries and any parsing errors in .errors\n\t if( parsed.errors.length ){\n\t // the gen. form of these errors seems to be [ line, col, char, error message ]\n\t var errors = parsed.errors.reduce( function( all, current ){ return all + '; ' + current; });\n\t // throw new Error( 'Error parsing bibtex: ' + errors );\n\t this.log( 'Error parsing bibtex: ' + errors );\n\t }\n\t\n\t this._fields = {};\n\t this.entry = _.first( parsed.entries );\n\t if( this.entry ){\n\t var rawFields = this.entry.Fields;\n\t for( var key in rawFields ){\n\t var value = rawFields[ key ];\n\t var lowerKey = key.toLowerCase();\n\t this._fields[ lowerKey ] = value;\n\t }\n\t }\n\t },\n\t entryType: function() {\n\t return this.entry? this.entry.EntryType : undefined;\n\t },\n\t fields: function() {\n\t return this._fields;\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone collection of citations.\n\t */\n\tvar BaseCitationCollection = Backbone.Collection.extend( baseMVC.LoggableMixin ).extend( {\n\t _logNamespace : logNamespace,\n\t\n\t /** root api url */\n\t urlRoot : Galaxy.root + 'api',\n\t partial : true, // Assume some tools in history/workflow may not be properly annotated yet.\n\t model : Citation,\n\t} );\n\t\n\tvar HistoryCitationCollection = BaseCitationCollection.extend( {\n\t /** complete api url */\n\t url : function() {\n\t return this.urlRoot + '/histories/' + this.history_id + '/citations';\n\t }\n\t} );\n\t\n\tvar ToolCitationCollection = BaseCitationCollection.extend( {\n\t /** complete api url */\n\t url : function() {\n\t return this.urlRoot + '/tools/' + this.tool_id + '/citations';\n\t },\n\t partial : false, // If a tool has citations, assume they are complete.\n\t} );\n\t\n\t\n\t//==============================================================================\n\t\n\treturn {\n\t Citation : Citation,\n\t HistoryCitationCollection : HistoryCitationCollection,\n\t ToolCitationCollection: ToolCitationCollection\n\t};\n\t\n\t\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(2)))\n\n/***/ },\n/* 18 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function($, _) {/**\n\t This is the regular tool form.\n\t*/\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(7), __webpack_require__(8), __webpack_require__(42) ], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, Ui, Modal, ToolFormBase ) {\n\t var View = ToolFormBase.extend({\n\t initialize: function( options ) {\n\t var self = this;\n\t options.listen_to_history = true;\n\t options.always_refresh = false;\n\t this.modal = parent.Galaxy.modal || new Modal.View();\n\t ToolFormBase.prototype.initialize.call( this, Utils.merge({\n\t customize: function( options ) {\n\t // build execute button\n\t options.buttons = {\n\t execute: execute_btn = new Ui.Button({\n\t icon : 'fa-check',\n\t tooltip : 'Execute: ' + options.name + ' (' + options.version + ')',\n\t title : 'Execute',\n\t cls : 'ui-button btn btn-primary',\n\t floating : 'clear',\n\t onclick : function() {\n\t execute_btn.wait();\n\t self.portlet.disable();\n\t self.submit( options, function() {\n\t execute_btn.unwait();\n\t self.portlet.enable();\n\t } );\n\t }\n\t })\n\t };\n\t // remap feature\n\t if ( options.job_id && options.job_remap ) {\n\t options.inputs[ 'rerun_remap_job_id' ] = {\n\t label : 'Resume dependencies from this job',\n\t name : 'rerun_remap_job_id',\n\t type : 'select',\n\t display : 'radio',\n\t ignore : '__ignore__',\n\t value : '__ignore__',\n\t options : [ [ 'Yes', options.job_id ], [ 'No', '__ignore__' ] ],\n\t help : 'The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run.'\n\t }\n\t }\n\t }\n\t }, options ) );\n\t },\n\t\n\t /** Submit a regular job.\n\t * @param{dict} options - Specifies tool id and version\n\t * @param{function} callback - Called when request has completed\n\t */\n\t submit: function( options, callback ) {\n\t var self = this;\n\t var job_def = {\n\t tool_id : options.id,\n\t tool_version : options.version,\n\t inputs : this.data.create()\n\t }\n\t this.trigger( 'reset' );\n\t if ( !self.validate( job_def ) ) {\n\t Galaxy.emit.debug( 'tool-form::submit()', 'Submission canceled. Validation failed.' );\n\t callback && callback();\n\t return;\n\t }\n\t if ( options.action !== Galaxy.root + 'tool_runner/index' ) {\n\t var $f = $( '' ).attr( { action: options.action, method: options.method, enctype: options.enctype } );\n\t _.each( job_def.inputs, function( value, key ) { $f.append( $( '' ).attr( { 'name': key, 'value': value } ) ) } );\n\t $f.hide().appendTo( 'body' ).submit().remove();\n\t callback && callback();\n\t return;\n\t }\n\t Galaxy.emit.debug( 'tool-form::submit()', 'Validation complete.', job_def );\n\t Utils.request({\n\t type : 'POST',\n\t url : Galaxy.root + 'api/tools',\n\t data : job_def,\n\t success : function( response ) {\n\t callback && callback();\n\t self.$el.children().hide();\n\t self.$el.append( self._templateSuccess( response ) );\n\t parent.Galaxy && parent.Galaxy.currHistoryPanel && parent.Galaxy.currHistoryPanel.refreshContents();\n\t },\n\t error : function( response ) {\n\t callback && callback();\n\t Galaxy.emit.debug( 'tool-form::submit', 'Submission failed.', response );\n\t var input_found = false;\n\t if ( response && response.err_data ) {\n\t var error_messages = self.data.matchResponse( response.err_data );\n\t for (var input_id in error_messages) {\n\t self.highlight( input_id, error_messages[ input_id ]);\n\t input_found = true;\n\t break;\n\t }\n\t }\n\t if ( !input_found ) {\n\t self.modal.show({\n\t title : 'Job submission failed',\n\t body : ( response && response.err_msg ) || self._templateError( job_def ),\n\t buttons : {\n\t 'Close' : function() {\n\t self.modal.hide();\n\t }\n\t }\n\t });\n\t }\n\t }\n\t });\n\t },\n\t\n\t /** Validate job dictionary.\n\t * @param{dict} job_def - Job execution dictionary\n\t */\n\t validate: function( job_def ) {\n\t var job_inputs = job_def.inputs;\n\t var batch_n = -1;\n\t var batch_src = null;\n\t for ( var job_input_id in job_inputs ) {\n\t var input_value = job_inputs[ job_input_id ];\n\t var input_id = this.data.match( job_input_id );\n\t var input_field = this.field_list[ input_id ];\n\t var input_def = this.input_list[ input_id ];\n\t if ( !input_id || !input_def || !input_field ) {\n\t Galaxy.emit.debug('tool-form::validate()', 'Retrieving input objects failed.');\n\t continue;\n\t }\n\t if ( !input_def.optional && input_value == null ) {\n\t this.highlight( input_id );\n\t return false;\n\t }\n\t if ( input_value && input_value.batch ) {\n\t var n = input_value.values.length;\n\t var src = n > 0 && input_value.values[ 0 ] && input_value.values[ 0 ].src;\n\t if ( src ) {\n\t if ( batch_src === null ) {\n\t batch_src = src;\n\t } else if ( batch_src !== src ) {\n\t this.highlight( input_id, 'Please select either dataset or dataset list fields for all batch mode fields.' );\n\t return false;\n\t }\n\t }\n\t if ( batch_n === -1 ) {\n\t batch_n = n;\n\t } else if ( batch_n !== n ) {\n\t this.highlight( input_id, 'Please make sure that you select the same number of inputs for all batch mode fields. This field contains ' + n + ' selection(s) while a previous field contains ' + batch_n + '.' );\n\t return false;\n\t }\n\t }\n\t }\n\t return true;\n\t },\n\t\n\t _templateSuccess: function( response ) {\n\t if ( response.jobs && response.jobs.length > 0 ) {\n\t var njobs = response.jobs.length;\n\t var njobs_text = njobs == 1 ? '1 job has' : njobs + ' jobs have';\n\t var $message = $( '' ).addClass( 'donemessagelarge' )\n\t .append( $( '' ).text( njobs_text + ' been successfully added to the queue - resulting in the following datasets:' ) );\n\t _.each( response.outputs, function( output ) {\n\t $message.append( $( '' ).addClass( 'messagerow' ).append( $( '' ).text( output.hid + ': ' + output.name ) ) );\n\t });\n\t $message.append( $( '' ).append( '' ).text( 'You can check the status of queued jobs and view the resulting data by refreshing the History pane. When the job has been run the status will change from \\'running\\' to \\'finished\\' if completed successfully or \\'error\\' if problems were encountered.' ) );\n\t return $message;\n\t } else {\n\t return this._templateError( response );\n\t }\n\t },\n\t\n\t _templateError: function( response ) {\n\t return $( '' ).addClass( 'errormessagelarge' )\n\t .append( $( '' ).text( 'The server could not complete the request. Please contact the Galaxy Team if this error persists.' ) )\n\t .append( $( '' ).text( JSON.stringify( response, null, 4 ) ) );\n\t }\n\t });\n\t\n\t return {\n\t View: View\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(2)))\n\n/***/ },\n/* 19 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $) {// dependencies\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(4), __webpack_require__(9), __webpack_require__(7)], __WEBPACK_AMD_DEFINE_RESULT__ = function(Utils, Portlet, Ui) {\n\t\n\t// ui list element\n\tvar View = Backbone.View.extend({\n\t // create portlet to keep track of selected list elements\n\t initialize : function(options) {\n\t // link this\n\t var self = this;\n\t\n\t // initialize options\n\t this.options = options;\n\t this.name = options.name || 'element';\n\t this.multiple = options.multiple || false;\n\t\n\t // create message handler\n\t this.message = new Ui.Message();\n\t\n\t // create portlet\n\t this.portlet = new Portlet.View({ cls: 'ui-portlet-section' });\n\t\n\t // create select field containing the options which can be inserted into the list\n\t this.select = new Ui.Select.View({ optional : options.optional });\n\t\n\t // create insert new list element button\n\t this.button = new Ui.ButtonIcon({\n\t icon : 'fa fa-sign-in',\n\t floating : 'left',\n\t tooltip : 'Insert new ' + this.name,\n\t onclick : function() {\n\t self.add({\n\t id : self.select.value(),\n\t name : self.select.text()\n\t });\n\t }\n\t });\n\t\n\t // build main element\n\t this.setElement(this._template(options));\n\t this.$('.ui-list-message').append(this.message.$el);\n\t this.$('.ui-list-portlet').append(this.portlet.$el);\n\t this.$('.ui-list-button').append(this.button.$el);\n\t this.$('.ui-list-select').append(this.select.$el);\n\t },\n\t\n\t /** Return/Set currently selected list elements */\n\t value: function(val) {\n\t // set new value\n\t if (val !== undefined) {\n\t this.portlet.empty();\n\t if ($.isArray(val)) {\n\t for (var i in val) {\n\t var v = val[i];\n\t var v_id = null;\n\t var v_name = null;\n\t if ($.type(v) != 'string') {\n\t v_id = v.id;\n\t v_name = v.name;\n\t } else {\n\t v_id = v_name = v;\n\t }\n\t if (v_id != null) {\n\t this.add({\n\t id : v_id,\n\t name : v_name\n\t });\n\t }\n\t }\n\t }\n\t this._refresh();\n\t }\n\t // get current value\n\t var lst = [];\n\t this.$('.ui-list-id').each(function() {\n\t lst.push({\n\t id : $(this).prop('id'),\n\t name : $(this).find('.ui-list-name').html()\n\t });\n\t });\n\t if (lst.length == 0) {\n\t return null;\n\t }\n\t return lst;\n\t },\n\t\n\t /** Add row */\n\t add: function(options) {\n\t var self = this;\n\t if (this.$('[id=\"' + options.id + '\"]').length === 0) {\n\t if (!Utils.isEmpty(options.id)) {\n\t var $el = $(this._templateRow({\n\t id : options.id,\n\t name : options.name\n\t }));\n\t $el.on('click', function() {\n\t $el.remove();\n\t self._refresh();\n\t });\n\t $el.on('mouseover', function() {\n\t $el.addClass('portlet-highlight');\n\t });\n\t $el.on('mouseout', function() {\n\t $el.removeClass('portlet-highlight');\n\t });\n\t this.portlet.append($el);\n\t this._refresh();\n\t } else {\n\t this.message.update({ message: 'Please select a valid ' + this.name + '.', status: 'danger' });\n\t }\n\t } else {\n\t this.message.update({ message: 'This ' + this.name + ' is already in the list.' });\n\t }\n\t },\n\t\n\t /** Update available options */\n\t update: function(options) {\n\t this.select.update(options);\n\t },\n\t\n\t /** Refresh view */\n\t _refresh: function() {\n\t if (this.$('.ui-list-id').length > 0) {\n\t !this.multiple && this.button.disable();\n\t this.$('.ui-list-portlet').show();\n\t } else {\n\t this.button.enable();\n\t this.$('.ui-list-portlet').hide();\n\t }\n\t this.options.onchange && this.options.onchange();\n\t },\n\t\n\t /** Main Template */\n\t _template: function(options) {\n\t return '
',\n\t 'Warning: This is a experimental feature. Most Galaxy tools will not annotate',\n\t ' citations explicitly at this time. When writing up your analysis, please manually',\n\t ' review your histories and find all references',\n\t ' that should be cited in order to completely describe your work. Also, please remember to',\n\t ' cite Galaxy.',\n\t '
'\n\t ], 'collection' );\n\t\n\t // use element identifier\n\t var subtitleTemplate = BASE_MVC.wrapTemplate([\n\t '
',\n\t '<% var countText = collection.element_count? ( collection.element_count + \" \" ) : \"\"; %>',\n\t '<% if( collection.collection_type === \"list\" ){ %>',\n\t _l( 'a list of <%- countText %>datasets' ),\n\t '<% } else if( collection.collection_type === \"paired\" ){ %>',\n\t _l( 'a pair of datasets' ),\n\t '<% } else if( collection.collection_type === \"list:paired\" ){ %>',\n\t _l( 'a list of <%- countText %>dataset pairs' ),\n\t '<% } else if( collection.collection_type === \"list:list\" ){ %>',\n\t _l( 'a list of <%- countText %>dataset lists' ),\n\t '<% } %>',\n\t '
'\n\t ], 'collection' );\n\t\n\t return _.extend( {}, FoldoutListItemView.prototype.templates, {\n\t warnings : warnings,\n\t titleBar : titleBarTemplate,\n\t subtitle : subtitleTemplate\n\t });\n\t}());\n\t\n\t\n\t//==============================================================================\n\t/** @class Read only view for DatasetCollectionElement.\n\t */\n\tvar DCEListItemView = ListItemView.extend(\n\t/** @lends DCEListItemView.prototype */{\n\t\n\t /** add the DCE class to the list item */\n\t className : ListItemView.prototype.className + \" dataset-collection-element\",\n\t\n\t /** set up */\n\t initialize : function( attributes ){\n\t if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; }\n\t this.log( 'DCEListItemView.initialize:', attributes );\n\t ListItemView.prototype.initialize.call( this, attributes );\n\t },\n\t\n\t // ......................................................................... misc\n\t /** String representation */\n\t toString : function(){\n\t var modelString = ( this.model )?( this.model + '' ):( '(no model)' );\n\t return 'DCEListItemView(' + modelString + ')';\n\t }\n\t});\n\t\n\t// ............................................................................ TEMPLATES\n\t/** underscore templates */\n\tDCEListItemView.prototype.templates = (function(){\n\t\n\t // use the element identifier here - since that will persist and the user will need it\n\t var titleBarTemplate = BASE_MVC.wrapTemplate([\n\t '
',\n\t '
',\n\t '<%- element.element_identifier %>',\n\t '
',\n\t '',\n\t '
'\n\t ], 'element' );\n\t\n\t return _.extend( {}, ListItemView.prototype.templates, {\n\t titleBar : titleBarTemplate\n\t });\n\t}());\n\t\n\t\n\t//==============================================================================\n\t/** @class Read only view for a DatasetCollectionElement that is also an DatasetAssociation\n\t * (a dataset contained in a dataset collection).\n\t */\n\tvar DatasetDCEListItemView = DATASET_LI.DatasetListItemView.extend(\n\t/** @lends DatasetDCEListItemView.prototype */{\n\t\n\t className : DATASET_LI.DatasetListItemView.prototype.className + \" dataset-collection-element\",\n\t\n\t /** set up */\n\t initialize : function( attributes ){\n\t if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; }\n\t this.log( 'DatasetDCEListItemView.initialize:', attributes );\n\t DATASET_LI.DatasetListItemView.prototype.initialize.call( this, attributes );\n\t },\n\t\n\t /** In this override, only get details if in the ready state.\n\t * Note: fetch with no 'change' event triggering to prevent automatic rendering.\n\t */\n\t _fetchModelDetails : function(){\n\t var view = this;\n\t if( view.model.inReadyState() && !view.model.hasDetails() ){\n\t return view.model.fetch({ silent: true });\n\t }\n\t return jQuery.when();\n\t },\n\t\n\t // ......................................................................... misc\n\t /** String representation */\n\t toString : function(){\n\t var modelString = ( this.model )?( this.model + '' ):( '(no model)' );\n\t return 'DatasetDCEListItemView(' + modelString + ')';\n\t }\n\t});\n\t\n\t// ............................................................................ TEMPLATES\n\t/** underscore templates */\n\tDatasetDCEListItemView.prototype.templates = (function(){\n\t\n\t // use the element identifier here and not the dataset name\n\t //TODO:?? can we steal the DCE titlebar?\n\t var titleBarTemplate = BASE_MVC.wrapTemplate([\n\t '
',\n\t '',\n\t '
',\n\t '<%- element.element_identifier %>',\n\t '
',\n\t '
'\n\t ], 'element' );\n\t\n\t return _.extend( {}, DATASET_LI.DatasetListItemView.prototype.templates, {\n\t titleBar : titleBarTemplate\n\t });\n\t}());\n\t\n\t\n\t//==============================================================================\n\t/** @class Read only view for a DatasetCollectionElement that is also a DatasetCollection\n\t * (a nested DC).\n\t */\n\tvar NestedDCDCEListItemView = DCListItemView.extend(\n\t/** @lends NestedDCDCEListItemView.prototype */{\n\t\n\t className : DCListItemView.prototype.className + \" dataset-collection-element\",\n\t\n\t /** In this override, add the state as a class for use with state-based CSS */\n\t _swapNewRender : function( $newRender ){\n\t DCListItemView.prototype._swapNewRender.call( this, $newRender );\n\t var state = this.model.get( 'state' ) || 'ok';\n\t this.$el.addClass( 'state-' + state );\n\t return this.$el;\n\t },\n\t\n\t // ......................................................................... misc\n\t /** String representation */\n\t toString : function(){\n\t var modelString = ( this.model )?( this.model + '' ):( '(no model)' );\n\t return 'NestedDCDCEListItemView(' + modelString + ')';\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t return {\n\t DCListItemView : DCListItemView,\n\t DCEListItemView : DCEListItemView,\n\t DatasetDCEListItemView : DatasetDCEListItemView,\n\t NestedDCDCEListItemView : NestedDCDCEListItemView\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2), __webpack_require__(1), __webpack_require__(1)))\n\n/***/ },\n/* 29 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(_, Backbone, jQuery) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(70),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( DATASET_MODEL, BASE_MVC, _l ){\n\t'use strict';\n\t\n\t//==============================================================================\n\t/*\n\tNotes:\n\t\n\tTerminology:\n\t DatasetCollection/DC : a container of datasets or nested DatasetCollections\n\t Element/DatasetCollectionElement/DCE : an item contained in a DatasetCollection\n\t HistoryDatasetCollectionAssociation/HDCA: a DatasetCollection contained in a history\n\t\n\t\n\tThis all seems too complex unfortunately:\n\t\n\t- Terminology collision between DatasetCollections (DCs) and Backbone Collections.\n\t- In the DatasetCollections API JSON, DC Elements use a 'Has A' stucture to *contain*\n\t either a dataset or a nested DC. This would make the hierarchy much taller. I've\n\t decided to merge the contained JSON with the DC element json - making the 'has a'\n\t relation into an 'is a' relation. This seems simpler to me and allowed a lot of\n\t DRY in both models and views, but may make tracking or tracing within these models\n\t more difficult (since DatasetCollectionElements are now *also* DatasetAssociations\n\t or DatasetCollections (nested)). This also violates the rule of thumb about\n\t favoring aggregation over inheritance.\n\t- Currently, there are three DatasetCollection subclasses: List, Pair, and ListPaired.\n\t These each should a) be usable on their own, b) be usable in the context of\n\t nesting within a collection model (at least in the case of ListPaired), and\n\t c) be usable within the context of other container models (like History or\n\t LibraryFolder, etc.). I've tried to separate/extract classes in order to\n\t handle those three situations, but it's proven difficult to do in a simple,\n\t readable manner.\n\t- Ideally, histories and libraries would inherit from the same server models as\n\t dataset collections do since they are (in essence) dataset collections themselves -\n\t making the whole nested structure simpler. This would be a large, error-prone\n\t refactoring and migration.\n\t\n\tMany of the classes and heirarchy are meant as extension points so, while the\n\trelations and flow may be difficult to understand initially, they'll allow us to\n\thandle the growth or flux dataset collection in the future (w/o actually implementing\n\tany YAGNI).\n\t\n\t*/\n\t//_________________________________________________________________________________________________ ELEMENTS\n\t/** @class mixin for Dataset collection elements.\n\t * When collection elements are passed from the API, the underlying element is\n\t * in a sub-object 'object' (IOW, a DCE representing an HDA will have HDA json in element.object).\n\t * This mixin uses the constructor and parse methods to merge that JSON with the DCE attribtues\n\t * effectively changing a DCE from a container to a subclass (has a --> is a).\n\t */\n\tvar DatasetCollectionElementMixin = {\n\t\n\t /** default attributes used by elements in a dataset collection */\n\t defaults : {\n\t model_class : 'DatasetCollectionElement',\n\t element_identifier : null,\n\t element_index : null,\n\t element_type : null\n\t },\n\t\n\t /** merge the attributes of the sub-object 'object' into this model */\n\t _mergeObject : function( attributes ){\n\t // if we don't preserve and correct ids here, the element id becomes the object id\n\t // and collision in backbone's _byId will occur and only\n\t _.extend( attributes, attributes.object, { element_id: attributes.id });\n\t delete attributes.object;\n\t return attributes;\n\t },\n\t\n\t /** override to merge this.object into this */\n\t constructor : function( attributes, options ){\n\t // console.debug( '\\t DatasetCollectionElement.constructor:', attributes, options );\n\t attributes = this._mergeObject( attributes );\n\t this.idAttribute = 'element_id';\n\t Backbone.Model.apply( this, arguments );\n\t },\n\t\n\t /** when the model is fetched, merge this.object into this */\n\t parse : function( response, options ){\n\t var attributes = response;\n\t attributes = this._mergeObject( attributes );\n\t return attributes;\n\t }\n\t};\n\t\n\t/** @class Concrete class of Generic DatasetCollectionElement */\n\tvar DatasetCollectionElement = Backbone.Model\n\t .extend( BASE_MVC.LoggableMixin )\n\t .extend( DatasetCollectionElementMixin )\n\t .extend({ _logNamespace : 'collections' });\n\t\n\t\n\t//==============================================================================\n\t/** @class Base/Abstract Backbone collection for Generic DCEs. */\n\tvar DCECollection = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).extend(\n\t/** @lends DCECollection.prototype */{\n\t _logNamespace : 'collections',\n\t\n\t model: DatasetCollectionElement,\n\t\n\t /** String representation. */\n\t toString : function(){\n\t return ([ 'DatasetCollectionElementCollection(', this.length, ')' ].join( '' ));\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone model for a dataset collection element that is a dataset (HDA).\n\t */\n\tvar DatasetDCE = DATASET_MODEL.DatasetAssociation.extend( BASE_MVC.mixin( DatasetCollectionElementMixin,\n\t/** @lends DatasetDCE.prototype */{\n\t\n\t /** url fn */\n\t url : function(){\n\t // won't always be an hda\n\t if( !this.has( 'history_id' ) ){\n\t console.warn( 'no endpoint for non-hdas within a collection yet' );\n\t // (a little silly since this api endpoint *also* points at hdas)\n\t return Galaxy.root + 'api/datasets';\n\t }\n\t return Galaxy.root + 'api/histories/' + this.get( 'history_id' ) + '/contents/' + this.get( 'id' );\n\t },\n\t\n\t defaults : _.extend( {},\n\t DATASET_MODEL.DatasetAssociation.prototype.defaults,\n\t DatasetCollectionElementMixin.defaults\n\t ),\n\t\n\t // because all objects have constructors (as this hashmap would even if this next line wasn't present)\n\t // the constructor in hcontentMixin won't be attached by BASE_MVC.mixin to this model\n\t // - re-apply manually for now\n\t /** call the mixin constructor */\n\t constructor : function( attributes, options ){\n\t this.debug( '\\t DatasetDCE.constructor:', attributes, options );\n\t //DATASET_MODEL.DatasetAssociation.prototype.constructor.call( this, attributes, options );\n\t DatasetCollectionElementMixin.constructor.call( this, attributes, options );\n\t },\n\t\n\t /** Does this model already contain detailed data (as opposed to just summary level data)? */\n\t hasDetails : function(){\n\t return this.elements && this.elements.length;\n\t },\n\t\n\t /** String representation. */\n\t toString : function(){\n\t var objStr = this.get( 'element_identifier' );\n\t return ([ 'DatasetDCE(', objStr, ')' ].join( '' ));\n\t }\n\t}));\n\t\n\t\n\t//==============================================================================\n\t/** @class DCECollection of DatasetDCE's (a list of datasets, a pair of datasets).\n\t */\n\tvar DatasetDCECollection = DCECollection.extend(\n\t/** @lends DatasetDCECollection.prototype */{\n\t model: DatasetDCE,\n\t\n\t /** String representation. */\n\t toString : function(){\n\t return ([ 'DatasetDCECollection(', this.length, ')' ].join( '' ));\n\t }\n\t});\n\t\n\t\n\t//_________________________________________________________________________________________________ COLLECTIONS\n\t/** @class Backbone model for Dataset Collections.\n\t * The DC API returns an array of JSON objects under the attribute elements.\n\t * This model:\n\t * - removes that array/attribute ('elements') from the model,\n\t * - creates a bbone collection (of the class defined in the 'collectionClass' attribute),\n\t * - passes that json onto the bbone collection\n\t * - caches the bbone collection in this.elements\n\t */\n\tvar DatasetCollection = Backbone.Model\n\t .extend( BASE_MVC.LoggableMixin )\n\t .extend( BASE_MVC.SearchableModelMixin )\n\t .extend(/** @lends DatasetCollection.prototype */{\n\t _logNamespace : 'collections',\n\t\n\t /** default attributes for a model */\n\t defaults : {\n\t /* 'list', 'paired', or 'list:paired' */\n\t collection_type : null,\n\t //??\n\t deleted : false\n\t },\n\t\n\t /** Which class to use for elements */\n\t collectionClass : DCECollection,\n\t\n\t /** set up: create elements instance var and (on changes to elements) update them */\n\t initialize : function( model, options ){\n\t this.debug( this + '(DatasetCollection).initialize:', model, options, this );\n\t this.elements = this._createElementsModel();\n\t this.on( 'change:elements', function(){\n\t this.log( 'change:elements' );\n\t //TODO: prob. better to update the collection instead of re-creating it\n\t this.elements = this._createElementsModel();\n\t });\n\t },\n\t\n\t /** move elements model attribute to full collection */\n\t _createElementsModel : function(){\n\t this.debug( this + '._createElementsModel', this.collectionClass, this.get( 'elements' ), this.elements );\n\t //TODO: same patterns as DatasetCollectionElement _createObjectModel - refactor to BASE_MVC.hasSubModel?\n\t var elements = this.get( 'elements' ) || [];\n\t this.unset( 'elements', { silent: true });\n\t this.elements = new this.collectionClass( elements );\n\t //this.debug( 'collectionClass:', this.collectionClass + '', this.elements );\n\t return this.elements;\n\t },\n\t\n\t // ........................................................................ common queries\n\t /** pass the elements back within the model json when this is serialized */\n\t toJSON : function(){\n\t var json = Backbone.Model.prototype.toJSON.call( this );\n\t if( this.elements ){\n\t json.elements = this.elements.toJSON();\n\t }\n\t return json;\n\t },\n\t\n\t /** Is this collection in a 'ready' state no processing (for the collection) is left\n\t * to do on the server.\n\t */\n\t inReadyState : function(){\n\t var populated = this.get( 'populated' );\n\t return ( this.isDeletedOrPurged() || populated );\n\t },\n\t\n\t //TODO:?? the following are the same interface as DatasetAssociation - can we combine?\n\t /** Does the DC contain any elements yet? Is a fetch() required? */\n\t hasDetails : function(){\n\t return this.elements.length !== 0;\n\t },\n\t\n\t /** Given the filters, what models in this.elements would be returned? */\n\t getVisibleContents : function( filters ){\n\t // filters unused for now\n\t return this.elements;\n\t },\n\t\n\t // ........................................................................ ajax\n\t /** override to use actual Dates objects for create/update times */\n\t parse : function( response, options ){\n\t var parsed = Backbone.Model.prototype.parse.call( this, response, options );\n\t if( parsed.create_time ){\n\t parsed.create_time = new Date( parsed.create_time );\n\t }\n\t if( parsed.update_time ){\n\t parsed.update_time = new Date( parsed.update_time );\n\t }\n\t return parsed;\n\t },\n\t\n\t /** save this dataset, _Mark_ing it as deleted (just a flag) */\n\t 'delete' : function( options ){\n\t if( this.get( 'deleted' ) ){ return jQuery.when(); }\n\t return this.save( { deleted: true }, options );\n\t },\n\t /** save this dataset, _Mark_ing it as undeleted */\n\t undelete : function( options ){\n\t if( !this.get( 'deleted' ) || this.get( 'purged' ) ){ return jQuery.when(); }\n\t return this.save( { deleted: false }, options );\n\t },\n\t\n\t /** Is this collection deleted or purged? */\n\t isDeletedOrPurged : function(){\n\t return ( this.get( 'deleted' ) || this.get( 'purged' ) );\n\t },\n\t\n\t // ........................................................................ searchable\n\t /** searchable attributes for collections */\n\t searchAttributes : [\n\t 'name'\n\t ],\n\t\n\t // ........................................................................ misc\n\t /** String representation */\n\t toString : function(){\n\t var idAndName = [ this.get( 'id' ), this.get( 'name' ) || this.get( 'element_identifier' ) ];\n\t return 'DatasetCollection(' + ( idAndName.join(',') ) + ')';\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t/** Model for a DatasetCollection containing datasets (non-nested).\n\t */\n\tvar ListDatasetCollection = DatasetCollection.extend(\n\t/** @lends ListDatasetCollection.prototype */{\n\t\n\t /** override since we know the collection will only contain datasets */\n\t collectionClass : DatasetDCECollection,\n\t\n\t /** String representation. */\n\t toString : function(){ return 'List' + DatasetCollection.prototype.toString.call( this ); }\n\t});\n\t\n\t\n\t//==============================================================================\n\t/** Model for a DatasetCollection containing fwd/rev datasets (a list of 2).\n\t */\n\tvar PairDatasetCollection = ListDatasetCollection.extend(\n\t/** @lends PairDatasetCollection.prototype */{\n\t\n\t /** String representation. */\n\t toString : function(){ return 'Pair' + DatasetCollection.prototype.toString.call( this ); }\n\t});\n\t\n\t\n\t//_________________________________________________________________________________________________ NESTED COLLECTIONS\n\t// this is where things get weird, man. Weird.\n\t//TODO: it might be possible to compact all the following...I think.\n\t//==============================================================================\n\t/** @class Backbone model for a Generic DatasetCollectionElement that is also a DatasetCollection\n\t * (a nested collection). Currently only list:paired.\n\t */\n\tvar NestedDCDCE = DatasetCollection.extend( BASE_MVC.mixin( DatasetCollectionElementMixin,\n\t/** @lends NestedDCDCE.prototype */{\n\t\n\t // because all objects have constructors (as this hashmap would even if this next line wasn't present)\n\t // the constructor in hcontentMixin won't be attached by BASE_MVC.mixin to this model\n\t // - re-apply manually it now\n\t /** call the mixin constructor */\n\t constructor : function( attributes, options ){\n\t this.debug( '\\t NestedDCDCE.constructor:', attributes, options );\n\t DatasetCollectionElementMixin.constructor.call( this, attributes, options );\n\t },\n\t\n\t /** String representation. */\n\t toString : function(){\n\t var objStr = ( this.object )?( '' + this.object ):( this.get( 'element_identifier' ) );\n\t return ([ 'NestedDCDCE(', objStr, ')' ].join( '' ));\n\t }\n\t}));\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone collection containing Generic NestedDCDCE's (nested dataset collections).\n\t */\n\tvar NestedDCDCECollection = DCECollection.extend(\n\t/** @lends NestedDCDCECollection.prototype */{\n\t\n\t /** This is a collection of nested collections */\n\t model: NestedDCDCE,\n\t\n\t /** String representation. */\n\t toString : function(){\n\t return ([ 'NestedDCDCECollection(', this.length, ')' ].join( '' ));\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone model for a paired dataset collection within a list:paired dataset collection.\n\t */\n\tvar NestedPairDCDCE = PairDatasetCollection.extend( BASE_MVC.mixin( DatasetCollectionElementMixin,\n\t/** @lends NestedPairDCDCE.prototype */{\n\t//TODO:?? possibly rename to NestedDatasetCollection?\n\t\n\t // because all objects have constructors (as this hashmap would even if this next line wasn't present)\n\t // the constructor in hcontentMixin won't be attached by BASE_MVC.mixin to this model\n\t // - re-apply manually it now\n\t /** This is both a collection and a collection element - call the constructor */\n\t constructor : function( attributes, options ){\n\t this.debug( '\\t NestedPairDCDCE.constructor:', attributes, options );\n\t //DatasetCollection.constructor.call( this, attributes, options );\n\t DatasetCollectionElementMixin.constructor.call( this, attributes, options );\n\t },\n\t\n\t /** String representation. */\n\t toString : function(){\n\t var objStr = ( this.object )?( '' + this.object ):( this.get( 'element_identifier' ) );\n\t return ([ 'NestedPairDCDCE(', objStr, ')' ].join( '' ));\n\t }\n\t}));\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone collection for a backbone collection containing paired dataset collections.\n\t */\n\tvar NestedPairDCDCECollection = NestedDCDCECollection.extend(\n\t/** @lends PairDCDCECollection.prototype */{\n\t\n\t /** We know this collection is composed of only nested pair collections */\n\t model: NestedPairDCDCE,\n\t\n\t /** String representation. */\n\t toString : function(){\n\t return ([ 'NestedPairDCDCECollection(', this.length, ')' ].join( '' ));\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone Model for a DatasetCollection (list) that contains DatasetCollections (pairs).\n\t */\n\tvar ListPairedDatasetCollection = DatasetCollection.extend(\n\t/** @lends ListPairedDatasetCollection.prototype */{\n\t\n\t /** list:paired is the only collection that itself contains collections */\n\t collectionClass : NestedPairDCDCECollection,\n\t\n\t /** String representation. */\n\t toString : function(){\n\t return ([ 'ListPairedDatasetCollection(', this.get( 'name' ), ')' ].join( '' ));\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone model for a list dataset collection within a list:list dataset collection. */\n\tvar NestedListDCDCE = ListDatasetCollection.extend( BASE_MVC.mixin( DatasetCollectionElementMixin,\n\t/** @lends NestedListDCDCE.prototype */{\n\t\n\t /** This is both a collection and a collection element - call the constructor */\n\t constructor : function( attributes, options ){\n\t this.debug( '\\t NestedListDCDCE.constructor:', attributes, options );\n\t DatasetCollectionElementMixin.constructor.call( this, attributes, options );\n\t },\n\t\n\t /** String representation. */\n\t toString : function(){\n\t var objStr = ( this.object )?( '' + this.object ):( this.get( 'element_identifier' ) );\n\t return ([ 'NestedListDCDCE(', objStr, ')' ].join( '' ));\n\t }\n\t}));\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone collection containing list dataset collections. */\n\tvar NestedListDCDCECollection = NestedDCDCECollection.extend({\n\t\n\t /** We know this collection is composed of only nested pair collections */\n\t model: NestedListDCDCE,\n\t\n\t /** String representation. */\n\t toString : function(){\n\t return ([ 'NestedListDCDCECollection(', this.length, ')' ].join( '' ));\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone Model for a DatasetCollection (list) that contains other lists. */\n\tvar ListOfListsDatasetCollection = DatasetCollection.extend({\n\t\n\t /** list:paired is the only collection that itself contains collections */\n\t collectionClass : NestedListDCDCECollection,\n\t\n\t /** String representation. */\n\t toString : function(){\n\t return ([ 'ListOfListsDatasetCollection(', this.get( 'name' ), ')' ].join( '' ));\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t return {\n\t ListDatasetCollection : ListDatasetCollection,\n\t PairDatasetCollection : PairDatasetCollection,\n\t ListPairedDatasetCollection : ListPairedDatasetCollection,\n\t ListOfListsDatasetCollection: ListOfListsDatasetCollection\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2), __webpack_require__(3), __webpack_require__(1)))\n\n/***/ },\n/* 30 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _, $, jQuery) {\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(38),\n\t __webpack_require__(12),\n\t __webpack_require__(6),\n\t __webpack_require__(8),\n\t __webpack_require__(87),\n\t __webpack_require__(5),\n\t __webpack_require__(84)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( HDCA, STATES, BASE_MVC, UI_MODAL, naturalSort, _l ){\n\t\n\t'use strict';\n\t\n\tvar logNamespace = 'collections';\n\t/*==============================================================================\n\tTODO:\n\t use proper Element model and not just json\n\t straighten out createFn, collection.createHDCA\n\t possibly stop using modals for this\n\t It would be neat to do a drag and drop\n\t\n\t==============================================================================*/\n\t/** A view for both DatasetDCEs and NestedDCDCEs\n\t * (things that implement collection-model:DatasetCollectionElementMixin)\n\t */\n\tvar DatasetCollectionElementView = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({\n\t _logNamespace : logNamespace,\n\t\n\t//TODO: use proper class (DatasetDCE or NestedDCDCE (or the union of both))\n\t tagName : 'li',\n\t className : 'collection-element',\n\t\n\t initialize : function( attributes ){\n\t this.element = attributes.element || {};\n\t this.selected = attributes.selected || false;\n\t },\n\t\n\t render : function(){\n\t this.$el\n\t .attr( 'data-element-id', this.element.id )\n\t .attr( 'draggable', true )\n\t .html( this.template({ element: this.element }) );\n\t if( this.selected ){\n\t this.$el.addClass( 'selected' );\n\t }\n\t return this;\n\t },\n\t\n\t //TODO: lots of unused space in the element - possibly load details and display them horiz.\n\t template : _.template([\n\t '',\n\t '<%- element.name %>',\n\t '',\n\t '',\n\t ].join('')),\n\t\n\t /** select this element and pub */\n\t select : function( toggle ){\n\t this.$el.toggleClass( 'selected', toggle );\n\t this.trigger( 'select', {\n\t source : this,\n\t selected : this.$el.hasClass( 'selected' )\n\t });\n\t },\n\t\n\t /** animate the removal of this element and pub */\n\t discard : function(){\n\t var view = this,\n\t parentWidth = this.$el.parent().width();\n\t this.$el.animate({ 'margin-right' : parentWidth }, 'fast', function(){\n\t view.trigger( 'discard', {\n\t source : view\n\t });\n\t view.destroy();\n\t });\n\t },\n\t\n\t /** remove the DOM and any listeners */\n\t destroy : function(){\n\t this.off();\n\t this.$el.remove();\n\t },\n\t\n\t events : {\n\t 'click' : '_click',\n\t 'click .name' : '_clickName',\n\t 'click .discard': '_clickDiscard',\n\t\n\t 'dragstart' : '_dragstart',\n\t 'dragend' : '_dragend',\n\t 'dragover' : '_sendToParent',\n\t 'drop' : '_sendToParent'\n\t },\n\t\n\t /** select when the li is clicked */\n\t _click : function( ev ){\n\t ev.stopPropagation();\n\t this.select( ev );\n\t },\n\t\n\t /** rename a pair when the name is clicked */\n\t _clickName : function( ev ){\n\t ev.stopPropagation();\n\t ev.preventDefault();\n\t var promptString = [ _l( 'Enter a new name for the element' ), ':\\n(',\n\t _l( 'Note that changing the name here will not rename the dataset' ), ')' ].join( '' ),\n\t response = prompt( _l( 'Enter a new name for the element' ) + ':', this.element.name );\n\t if( response ){\n\t this.element.name = response;\n\t this.render();\n\t }\n\t //TODO: cancelling with ESC leads to closure of the creator...\n\t },\n\t\n\t /** discard when the discard button is clicked */\n\t _clickDiscard : function( ev ){\n\t ev.stopPropagation();\n\t this.discard();\n\t },\n\t\n\t /** dragging pairs for re-ordering */\n\t _dragstart : function( ev ){\n\t if( ev.originalEvent ){ ev = ev.originalEvent; }\n\t ev.dataTransfer.effectAllowed = 'move';\n\t ev.dataTransfer.setData( 'text/plain', JSON.stringify( this.element ) );\n\t\n\t this.$el.addClass( 'dragging' );\n\t this.$el.parent().trigger( 'collection-element.dragstart', [ this ] );\n\t },\n\t\n\t /** dragging for re-ordering */\n\t _dragend : function( ev ){\n\t this.$el.removeClass( 'dragging' );\n\t this.$el.parent().trigger( 'collection-element.dragend', [ this ] );\n\t },\n\t\n\t /** manually bubble up an event to the parent/container */\n\t _sendToParent : function( ev ){\n\t this.$el.parent().trigger( ev );\n\t },\n\t\n\t /** string rep */\n\t toString : function(){\n\t return 'DatasetCollectionElementView()';\n\t }\n\t});\n\t\n\t\n\t// ============================================================================\n\t/** An interface for building collections.\n\t */\n\tvar ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({\n\t _logNamespace : logNamespace,\n\t\n\t /** the class used to display individual elements */\n\t elementViewClass : DatasetCollectionElementView,\n\t /** the class this creator will create and save */\n\t collectionClass : HDCA.HistoryListDatasetCollection,\n\t className : 'list-collection-creator collection-creator flex-row-container',\n\t\n\t /** minimum number of valid elements to start with in order to build a collection of this type */\n\t minElements : 1,\n\t\n\t defaultAttributes : {\n\t//TODO: remove - use new collectionClass().save()\n\t /** takes elements and creates the proper collection - returns a promise */\n\t creationFn : function(){ throw new TypeError( 'no creation fn for creator' ); },\n\t /** fn to call when the collection is created (scoped to this) */\n\t oncreate : function(){},\n\t /** fn to call when the cancel button is clicked (scoped to this) - if falsy, no btn is displayed */\n\t oncancel : function(){},\n\t /** distance from list edge to begin autoscrolling list */\n\t autoscrollDist : 24,\n\t /** Color passed to hoverhighlight */\n\t highlightClr : 'rgba( 64, 255, 255, 1.0 )'\n\t },\n\t\n\t /** set up initial options, instance vars, behaviors */\n\t initialize : function( attributes ){\n\t this.metric( 'ListCollectionCreator.initialize', attributes );\n\t var creator = this;\n\t _.each( this.defaultAttributes, function( value, key ){\n\t value = attributes[ key ] || value;\n\t creator[ key ] = value;\n\t });\n\t\n\t /** unordered, original list - cache to allow reversal */\n\t creator.initialElements = attributes.elements || [];\n\t\n\t this._instanceSetUp();\n\t this._elementsSetUp();\n\t this._setUpBehaviors();\n\t },\n\t\n\t /** set up instance vars */\n\t _instanceSetUp : function(){\n\t /** Ids of elements that have been selected by the user - to preserve over renders */\n\t this.selectedIds = {};\n\t /** DOM elements currently being dragged */\n\t this.$dragging = null;\n\t /** Used for blocking UI events during ajax/operations (don't post twice) */\n\t this.blocking = false;\n\t },\n\t\n\t // ------------------------------------------------------------------------ process raw list\n\t /** set up main data */\n\t _elementsSetUp : function(){\n\t //this.debug( '-- _dataSetUp' );\n\t /** a list of invalid elements and the reasons they aren't valid */\n\t this.invalidElements = [];\n\t//TODO: handle fundamental problem of syncing DOM, views, and list here\n\t /** data for list in progress */\n\t this.workingElements = [];\n\t /** views for workingElements */\n\t this.elementViews = [];\n\t\n\t // copy initial list, sort, add ids if needed\n\t this.workingElements = this.initialElements.slice( 0 );\n\t this._ensureElementIds();\n\t this._validateElements();\n\t this._mangleDuplicateNames();\n\t this._sortElements();\n\t },\n\t\n\t /** add ids to dataset objs in initial list if none */\n\t _ensureElementIds : function(){\n\t this.workingElements.forEach( function( element ){\n\t if( !element.hasOwnProperty( 'id' ) ){\n\t element.id = _.uniqueId();\n\t }\n\t });\n\t return this.workingElements;\n\t },\n\t\n\t /** separate working list into valid and invalid elements for this collection */\n\t _validateElements : function(){\n\t var creator = this,\n\t existingNames = {};\n\t creator.invalidElements = [];\n\t\n\t this.workingElements = this.workingElements.filter( function( element ){\n\t var problem = creator._isElementInvalid( element );\n\t if( problem ){\n\t creator.invalidElements.push({\n\t element : element,\n\t text : problem\n\t });\n\t }\n\t return !problem;\n\t });\n\t return this.workingElements;\n\t },\n\t\n\t /** describe what is wrong with a particular element if anything */\n\t _isElementInvalid : function( element ){\n\t if( element.history_content_type !== 'dataset' ){\n\t return _l( \"is not a dataset\" );\n\t }\n\t if( element.state !== STATES.OK ){\n\t if( _.contains( STATES.NOT_READY_STATES, element.state ) ){\n\t return _l( \"hasn't finished running yet\" );\n\t }\n\t return _l( \"has errored, is paused, or is not accessible\" );\n\t }\n\t if( element.deleted || element.purged ){\n\t return _l( \"has been deleted or purged\" );\n\t }\n\t return null;\n\t },\n\t\n\t /** mangle duplicate names using a mac-like '(counter)' addition to any duplicates */\n\t _mangleDuplicateNames : function(){\n\t var SAFETY = 900,\n\t counter = 1,\n\t existingNames = {};\n\t this.workingElements.forEach( function( element ){\n\t var currName = element.name;\n\t while( existingNames.hasOwnProperty( currName ) ){\n\t currName = element.name + ' (' + counter + ')';\n\t counter += 1;\n\t if( counter >= SAFETY ){\n\t throw new Error( 'Safety hit in while loop - thats impressive' );\n\t }\n\t }\n\t element.name = currName;\n\t existingNames[ element.name ] = true;\n\t });\n\t },\n\t\n\t /** sort a list of elements */\n\t _sortElements : function( list ){\n\t // // currently only natural sort by name\n\t // this.workingElements.sort( function( a, b ){ return naturalSort( a.name, b.name ); });\n\t // return this.workingElements;\n\t },\n\t\n\t // ------------------------------------------------------------------------ rendering\n\t // templates : ListCollectionCreator.templates,\n\t /** render the entire interface */\n\t render : function( speed, callback ){\n\t //this.debug( '-- _render' );\n\t if( this.workingElements.length < this.minElements ){\n\t return this._renderInvalid( speed, callback );\n\t }\n\t\n\t this.$el.empty().html( this.templates.main() );\n\t this._renderHeader( speed );\n\t this._renderMiddle( speed );\n\t this._renderFooter( speed );\n\t this._addPluginComponents();\n\t this.$( '.collection-name' ).focus();\n\t this.trigger( 'rendered', this );\n\t return this;\n\t },\n\t\n\t\n\t /** render a simplified interface aimed at telling the user why they can't move forward */\n\t _renderInvalid : function( speed, callback ){\n\t //this.debug( '-- _render' );\n\t this.$el.empty().html( this.templates.invalidInitial({\n\t problems: this.invalidElements,\n\t elements: this.workingElements,\n\t }));\n\t if( typeof this.oncancel === 'function' ){\n\t this.$( '.cancel-create.btn' ).show();\n\t }\n\t this.trigger( 'rendered', this );\n\t return this;\n\t },\n\t\n\t /** render the header section */\n\t _renderHeader : function( speed, callback ){\n\t var $header = this.$( '.header' ).empty().html( this.templates.header() )\n\t .find( '.help-content' ).prepend( $( this.templates.helpContent() ) );\n\t //TODO: should only show once despite calling _renderHeader again\n\t if( this.invalidElements.length ){\n\t this._invalidElementsAlert();\n\t }\n\t return $header;\n\t },\n\t\n\t /** render the middle including the elements */\n\t _renderMiddle : function( speed, callback ){\n\t var $middle = this.$( '.middle' ).empty().html( this.templates.middle() );\n\t this._renderList( speed );\n\t return $middle;\n\t },\n\t\n\t /** render the footer, completion controls, and cancel controls */\n\t _renderFooter : function( speed, callback ){\n\t var $footer = this.$( '.footer' ).empty().html( this.templates.footer() );\n\t if( typeof this.oncancel === 'function' ){\n\t this.$( '.cancel-create.btn' ).show();\n\t }\n\t return $footer;\n\t },\n\t\n\t /** add any jQuery/bootstrap/custom plugins to elements rendered */\n\t _addPluginComponents : function(){\n\t this.$( '.help-content i' ).hoverhighlight( '.collection-creator', this.highlightClr );\n\t },\n\t\n\t /** build and show an alert describing any elements that could not be included due to problems */\n\t _invalidElementsAlert : function(){\n\t this._showAlert( this.templates.invalidElements({ problems: this.invalidElements }), 'alert-warning' );\n\t },\n\t\n\t /** add (or clear if clear is truthy) a validation warning to the DOM element described in what */\n\t _validationWarning : function( what, clear ){\n\t var VALIDATION_CLASS = 'validation-warning';\n\t if( what === 'name' ){\n\t what = this.$( '.collection-name' ).add( this.$( '.collection-name-prompt' ) );\n\t this.$( '.collection-name' ).focus().select();\n\t }\n\t if( clear ){\n\t what = what || this.$( '.' + VALIDATION_CLASS );\n\t what.removeClass( VALIDATION_CLASS );\n\t } else {\n\t what.addClass( VALIDATION_CLASS );\n\t }\n\t },\n\t\n\t _disableNameAndCreate : function( disable ){\n\t disable = !_.isUndefined( disable )? disable : true;\n\t if( disable ){\n\t this.$( '.collection-name' ).prop( 'disabled', true );\n\t this.$( '.create-collection' ).toggleClass( 'disabled', true );\n\t // } else {\n\t // this.$( '.collection-name' ).prop( 'disabled', false );\n\t // this.$( '.create-collection' ).removeClass( 'disable' );\n\t }\n\t },\n\t\n\t // ------------------------------------------------------------------------ rendering elements\n\t /** conv. to the main list display DOM */\n\t $list : function(){\n\t return this.$( '.collection-elements' );\n\t },\n\t\n\t /** show or hide the clear selected control based on the num of selected elements */\n\t _renderClearSelected : function(){\n\t if( _.size( this.selectedIds ) ){\n\t this.$( '.collection-elements-controls > .clear-selected' ).show();\n\t } else {\n\t this.$( '.collection-elements-controls > .clear-selected' ).hide();\n\t }\n\t },\n\t\n\t /** render the elements in order (or a warning if no elements found) */\n\t _renderList : function( speed, callback ){\n\t //this.debug( '-- _renderList' );\n\t var creator = this,\n\t $tmp = jQuery( '' ),\n\t $list = creator.$list();\n\t\n\t _.each( this.elementViews, function( view ){\n\t view.destroy();\n\t creator.removeElementView( view );\n\t });\n\t\n\t // if( !this.workingElements.length ){\n\t // this._renderNoValidElements();\n\t // return;\n\t // }\n\t\n\t creator.workingElements.forEach( function( element ){\n\t var elementView = creator._createElementView( element );\n\t $tmp.append( elementView.$el );\n\t });\n\t\n\t creator._renderClearSelected();\n\t $list.empty().append( $tmp.children() );\n\t _.invoke( creator.elementViews, 'render' );\n\t\n\t if( $list.height() > $list.css( 'max-height' ) ){\n\t $list.css( 'border-width', '1px 0px 1px 0px' );\n\t } else {\n\t $list.css( 'border-width', '0px' );\n\t }\n\t },\n\t\n\t /** create an element view, cache in elementViews, set up listeners, and return */\n\t _createElementView : function( element ){\n\t var elementView = new this.elementViewClass({\n\t//TODO: use non-generic class or not all\n\t // model : COLLECTION.DatasetDCE( element )\n\t element : element,\n\t selected: _.has( this.selectedIds, element.id )\n\t });\n\t this.elementViews.push( elementView );\n\t this._listenToElementView( elementView );\n\t return elementView;\n\t },\n\t\n\t /** listen to any element events */\n\t _listenToElementView : function( view ){\n\t var creator = this;\n\t creator.listenTo( view, {\n\t select : function( data ){\n\t var element = data.source.element;\n\t if( data.selected ){\n\t creator.selectedIds[ element.id ] = true;\n\t } else {\n\t delete creator.selectedIds[ element.id ];\n\t }\n\t creator.trigger( 'elements:select', data );\n\t },\n\t discard : function( data ){\n\t creator.trigger( 'elements:discard', data );\n\t }\n\t });\n\t },\n\t\n\t /** add a new element view based on the json in element */\n\t addElementView : function( element ){\n\t//TODO: workingElements is sorted, add element in appropo index\n\t // add element, sort elements, find element index\n\t // var view = this._createElementView( element );\n\t // return view;\n\t },\n\t\n\t /** stop listening to view and remove from caches */\n\t removeElementView : function( view ){\n\t delete this.selectedIds[ view.element.id ];\n\t this._renderClearSelected();\n\t\n\t this.elementViews = _.without( this.elementViews, view );\n\t this.stopListening( view );\n\t },\n\t\n\t /** render a message in the list that no elements remain to create a collection */\n\t _renderNoElementsLeft : function(){\n\t this._disableNameAndCreate( true );\n\t this.$( '.collection-elements' ).append( this.templates.noElementsLeft() );\n\t },\n\t\n\t // /** render a message in the list that no valid elements were found to create a collection */\n\t // _renderNoValidElements : function(){\n\t // this._disableNameAndCreate( true );\n\t // this.$( '.collection-elements' ).append( this.templates.noValidElements() );\n\t // },\n\t\n\t // ------------------------------------------------------------------------ API\n\t /** convert element into JSON compatible with the collections API */\n\t _elementToJSON : function( element ){\n\t // return element.toJSON();\n\t return element;\n\t },\n\t\n\t /** create the collection via the API\n\t * @returns {jQuery.xhr Object} the jquery ajax request\n\t */\n\t createList : function( name ){\n\t if( !this.workingElements.length ){\n\t var message = _l( 'No valid elements for final list' ) + '. ';\n\t message += '' + _l( 'Cancel' ) + ' ';\n\t message += _l( 'or' );\n\t message += ' ' + _l( 'start over' ) + '.';\n\t this._showAlert( message );\n\t return;\n\t }\n\t\n\t var creator = this,\n\t elements = this.workingElements.map( function( element ){\n\t return creator._elementToJSON( element );\n\t });\n\t\n\t creator.blocking = true;\n\t return creator.creationFn( elements, name )\n\t .always( function(){\n\t creator.blocking = false;\n\t })\n\t .fail( function( xhr, status, message ){\n\t creator.trigger( 'error', {\n\t xhr : xhr,\n\t status : status,\n\t message : _l( 'An error occurred while creating this collection' )\n\t });\n\t })\n\t .done( function( response, message, xhr ){\n\t creator.trigger( 'collection:created', response, message, xhr );\n\t creator.metric( 'collection:created', response );\n\t if( typeof creator.oncreate === 'function' ){\n\t creator.oncreate.call( this, response, message, xhr );\n\t }\n\t });\n\t },\n\t\n\t // ------------------------------------------------------------------------ events\n\t /** set up event handlers on self */\n\t _setUpBehaviors : function(){\n\t this.on( 'error', this._errorHandler );\n\t\n\t this.once( 'rendered', function(){\n\t this.trigger( 'rendered:initial', this );\n\t });\n\t\n\t this.on( 'elements:select', function( data ){\n\t this._renderClearSelected();\n\t });\n\t\n\t this.on( 'elements:discard', function( data ){\n\t var element = data.source.element;\n\t this.removeElementView( data.source );\n\t\n\t this.workingElements = _.without( this.workingElements, element );\n\t if( !this.workingElements.length ){\n\t this._renderNoElementsLeft();\n\t }\n\t });\n\t\n\t //this.on( 'all', function(){\n\t // this.info( arguments );\n\t //});\n\t return this;\n\t },\n\t\n\t /** handle errors with feedback and details to the user (if available) */\n\t _errorHandler : function( data ){\n\t this.error( data );\n\t\n\t var creator = this;\n\t content = data.message || _l( 'An error occurred' );\n\t if( data.xhr ){\n\t var xhr = data.xhr,\n\t message = data.message;\n\t if( xhr.readyState === 0 && xhr.status === 0 ){\n\t content += ': ' + _l( 'Galaxy could not be reached and may be updating.' ) +\n\t _l( ' Try again in a few minutes.' );\n\t } else if( xhr.responseJSON ){\n\t content += ':
' + JSON.stringify( xhr.responseJSON ) + '
';\n\t } else {\n\t content += ': ' + message;\n\t }\n\t }\n\t creator._showAlert( content, 'alert-danger' );\n\t },\n\t\n\t events : {\n\t // header\n\t 'click .more-help' : '_clickMoreHelp',\n\t 'click .less-help' : '_clickLessHelp',\n\t 'click .main-help' : '_toggleHelp',\n\t 'click .header .alert button' : '_hideAlert',\n\t\n\t 'click .reset' : 'reset',\n\t 'click .clear-selected' : 'clearSelectedElements',\n\t\n\t // elements - selection\n\t 'click .collection-elements' : 'clearSelectedElements',\n\t\n\t // elements - drop target\n\t // 'dragenter .collection-elements': '_dragenterElements',\n\t // 'dragleave .collection-elements': '_dragleaveElements',\n\t 'dragover .collection-elements' : '_dragoverElements',\n\t 'drop .collection-elements' : '_dropElements',\n\t\n\t // these bubble up from the elements as custom events\n\t 'collection-element.dragstart .collection-elements' : '_elementDragstart',\n\t 'collection-element.dragend .collection-elements' : '_elementDragend',\n\t\n\t // footer\n\t 'change .collection-name' : '_changeName',\n\t 'keydown .collection-name' : '_nameCheckForEnter',\n\t 'click .cancel-create' : function( ev ){\n\t if( typeof this.oncancel === 'function' ){\n\t this.oncancel.call( this );\n\t }\n\t },\n\t 'click .create-collection' : '_clickCreate'//,\n\t },\n\t\n\t // ........................................................................ header\n\t /** expand help */\n\t _clickMoreHelp : function( ev ){\n\t ev.stopPropagation();\n\t this.$( '.main-help' ).addClass( 'expanded' );\n\t this.$( '.more-help' ).hide();\n\t },\n\t /** collapse help */\n\t _clickLessHelp : function( ev ){\n\t ev.stopPropagation();\n\t this.$( '.main-help' ).removeClass( 'expanded' );\n\t this.$( '.more-help' ).show();\n\t },\n\t /** toggle help */\n\t _toggleHelp : function( ev ){\n\t ev.stopPropagation();\n\t this.$( '.main-help' ).toggleClass( 'expanded' );\n\t this.$( '.more-help' ).toggle();\n\t },\n\t\n\t /** show an alert on the top of the interface containing message (alertClass is bootstrap's alert-*) */\n\t _showAlert : function( message, alertClass ){\n\t alertClass = alertClass || 'alert-danger';\n\t this.$( '.main-help' ).hide();\n\t this.$( '.header .alert' )\n\t .attr( 'class', 'alert alert-dismissable' ).addClass( alertClass ).show()\n\t .find( '.alert-message' ).html( message );\n\t },\n\t /** hide the alerts at the top */\n\t _hideAlert : function( message ){\n\t this.$( '.main-help' ).show();\n\t this.$( '.header .alert' ).hide();\n\t },\n\t\n\t // ........................................................................ elements\n\t /** reset all data to the initial state */\n\t reset : function(){\n\t this._instanceSetUp();\n\t this._elementsSetUp();\n\t this.render();\n\t },\n\t\n\t /** deselect all elements */\n\t clearSelectedElements : function( ev ){\n\t this.$( '.collection-elements .collection-element' ).removeClass( 'selected' );\n\t this.$( '.collection-elements-controls > .clear-selected' ).hide();\n\t },\n\t\n\t //_dragenterElements : function( ev ){\n\t // //this.debug( '_dragenterElements:', ev );\n\t //},\n\t//TODO: if selected are dragged out of the list area - remove the placeholder - cuz it won't work anyway\n\t // _dragleaveElements : function( ev ){\n\t // //this.debug( '_dragleaveElements:', ev );\n\t // },\n\t\n\t /** track the mouse drag over the list adding a placeholder to show where the drop would occur */\n\t _dragoverElements : function( ev ){\n\t //this.debug( '_dragoverElements:', ev );\n\t ev.preventDefault();\n\t\n\t var $list = this.$list();\n\t this._checkForAutoscroll( $list, ev.originalEvent.clientY );\n\t var $nearest = this._getNearestElement( ev.originalEvent.clientY );\n\t\n\t //TODO: no need to re-create - move instead\n\t this.$( '.element-drop-placeholder' ).remove();\n\t var $placeholder = $( '' );\n\t if( !$nearest.length ){\n\t $list.append( $placeholder );\n\t } else {\n\t $nearest.before( $placeholder );\n\t }\n\t },\n\t\n\t /** If the mouse is near enough to the list's top or bottom, scroll the list */\n\t _checkForAutoscroll : function( $element, y ){\n\t var AUTOSCROLL_SPEED = 2,\n\t offset = $element.offset(),\n\t scrollTop = $element.scrollTop(),\n\t upperDist = y - offset.top,\n\t lowerDist = ( offset.top + $element.outerHeight() ) - y;\n\t if( upperDist >= 0 && upperDist < this.autoscrollDist ){\n\t $element.scrollTop( scrollTop - AUTOSCROLL_SPEED );\n\t } else if( lowerDist >= 0 && lowerDist < this.autoscrollDist ){\n\t $element.scrollTop( scrollTop + AUTOSCROLL_SPEED );\n\t }\n\t },\n\t\n\t /** get the nearest element based on the mouse's Y coordinate.\n\t * If the y is at the end of the list, return an empty jQuery object.\n\t */\n\t _getNearestElement : function( y ){\n\t var WIGGLE = 4,\n\t lis = this.$( '.collection-elements li.collection-element' ).toArray();\n\t for( var i=0; i y && top - halfHeight < y ){\n\t return $li;\n\t }\n\t }\n\t return $();\n\t },\n\t\n\t /** drop (dragged/selected elements) onto the list, re-ordering the internal list */\n\t _dropElements : function( ev ){\n\t if( ev.originalEvent ){ ev = ev.originalEvent; }\n\t // both required for firefox\n\t ev.preventDefault();\n\t ev.dataTransfer.dropEffect = 'move';\n\t\n\t // insert before the nearest element or after the last.\n\t var $nearest = this._getNearestElement( ev.clientY );\n\t if( $nearest.length ){\n\t this.$dragging.insertBefore( $nearest );\n\t } else {\n\t // no nearest before - insert after last element\n\t this.$dragging.insertAfter( this.$( '.collection-elements .collection-element' ).last() );\n\t }\n\t // resync the creator's list based on the new DOM order\n\t this._syncOrderToDom();\n\t return false;\n\t },\n\t\n\t /** resync the creator's list of elements based on the DOM order */\n\t _syncOrderToDom : function(){\n\t var creator = this,\n\t newElements = [];\n\t //TODO: doesn't seem wise to use the dom to store these - can't we sync another way?\n\t this.$( '.collection-elements .collection-element' ).each( function(){\n\t var id = $( this ).attr( 'data-element-id' ),\n\t element = _.findWhere( creator.workingElements, { id: id });\n\t if( element ){\n\t newElements.push( element );\n\t } else {\n\t console.error( 'missing element: ', id );\n\t }\n\t });\n\t this.workingElements = newElements;\n\t this._renderList();\n\t },\n\t\n\t /** drag communication with element sub-views: dragstart */\n\t _elementDragstart : function( ev, element ){\n\t // auto select the element causing the event and move all selected\n\t element.select( true );\n\t this.$dragging = this.$( '.collection-elements .collection-element.selected' );\n\t },\n\t\n\t /** drag communication with element sub-views: dragend - remove the placeholder */\n\t _elementDragend : function( ev, element ){\n\t $( '.element-drop-placeholder' ).remove();\n\t this.$dragging = null;\n\t },\n\t\n\t // ........................................................................ footer\n\t /** handle a collection name change */\n\t _changeName : function( ev ){\n\t this._validationWarning( 'name', !!this._getName() );\n\t },\n\t\n\t /** check for enter key press when in the collection name and submit */\n\t _nameCheckForEnter : function( ev ){\n\t if( ev.keyCode === 13 && !this.blocking ){\n\t this._clickCreate();\n\t }\n\t },\n\t\n\t /** get the current collection name */\n\t _getName : function(){\n\t return _.escape( this.$( '.collection-name' ).val() );\n\t },\n\t\n\t /** attempt to create the current collection */\n\t _clickCreate : function( ev ){\n\t var name = this._getName();\n\t if( !name ){\n\t this._validationWarning( 'name' );\n\t } else if( !this.blocking ){\n\t this.createList( name );\n\t }\n\t },\n\t\n\t // ------------------------------------------------------------------------ templates\n\t //TODO: move to require text plugin and load these as text\n\t //TODO: underscore currently unnecc. bc no vars are used\n\t //TODO: better way of localizing text-nodes in long strings\n\t /** underscore template fns attached to class */\n\t templates : {\n\t /** the skeleton */\n\t main : _.template([\n\t '',\n\t '',\n\t ''\n\t ].join('')),\n\t\n\t /** the header (not including help text) */\n\t header : _.template([\n\t '
'\n\t ].join('')),\n\t\n\t /** help content */\n\t helpContent : _.template([\n\t '
', _l([\n\t 'Collections of datasets are permanent, ordered lists of datasets that can be passed to tools and ',\n\t 'workflows in order to have analyses done on each member of the entire group. This interface allows ',\n\t 'you to create a collection and re-order the final collection.'\n\t ].join( '' )), '
',\n\t '
',\n\t '
', _l([\n\t 'Rename elements in the list by clicking on ',\n\t 'the existing name.'\n\t ].join( '' )), '
',\n\t '
', _l([\n\t 'Discard elements from the final created list by clicking on the ',\n\t '\"Discard\" button.'\n\t ].join( '' )), '
',\n\t '
', _l([\n\t 'Reorder the list by clicking and dragging elements. Select multiple elements by clicking on ',\n\t 'them and you can then move those selected by dragging the ',\n\t 'entire group. Deselect them by clicking them again or by clicking the ',\n\t 'the \"Clear selected\" link.'\n\t ].join( '' )), '
',\n\t '
', _l([\n\t 'Click the \"Start over\" link to begin again as if you had just opened ',\n\t 'the interface.'\n\t ].join( '' )), '
',\n\t '
', _l([\n\t 'Click the \"Cancel\" button to exit the interface.'\n\t ].join( '' )), '
',\n\t '
',\n\t '
', _l([\n\t 'Once your collection is complete, enter a name and ',\n\t 'click \"Create list\".'\n\t ].join( '' )), '
'\n\t ].join('')),\n\t\n\t /** shown in list when all elements are discarded */\n\t invalidElements : _.template([\n\t _l( 'The following selections could not be included due to problems:' ),\n\t '
<% _.each( problems, function( problem ){ %>',\n\t '
<%- problem.element.name %>: <%- problem.text %>
',\n\t '<% }); %>
'\n\t ].join('')),\n\t\n\t /** shown in list when all elements are discarded */\n\t noElementsLeft : _.template([\n\t '
',\n\t _l( 'No elements left! ' ),\n\t _l( 'Would you like to ' ), '', _l( 'start over' ), '?',\n\t '
'\n\t ].join('')),\n\t\n\t /** a simplified page communicating what went wrong and why the user needs to reselect something else */\n\t invalidInitial : _.template([\n\t '
',\n\t '
',\n\t '',\n\t '<% if( _.size( problems ) ){ %>',\n\t _l( 'The following selections could not be included due to problems' ), ':',\n\t '
<% _.each( problems, function( problem ){ %>',\n\t '
<%- problem.element.name %>: <%- problem.text %>
',\n\t '<% }); %>
',\n\t '<% } else if( _.size( elements ) < 1 ){ %>',\n\t _l( 'No datasets were selected' ), '.',\n\t '<% } %>',\n\t ' ',\n\t _l( 'At least one element is needed for the collection' ), '. ',\n\t _l( 'You may need to ' ),\n\t '', _l( 'cancel' ), ' ',\n\t _l( 'and reselect new elements' ), '.',\n\t '',\n\t '
',\n\t '
',\n\t ''\n\t ].join('')),\n\t },\n\t\n\t // ------------------------------------------------------------------------ misc\n\t /** string rep */\n\t toString : function(){ return 'ListCollectionCreator'; }\n\t});\n\t\n\t\n\t\n\t//=============================================================================\n\t/** Create a modal and load its body with the given CreatorClass creator type\n\t * @returns {Deferred} resolved when creator has built a collection.\n\t */\n\tvar collectionCreatorModal = function _collectionCreatorModal( elements, options, CreatorClass ){\n\t\n\t var deferred = jQuery.Deferred(),\n\t modal = Galaxy.modal || ( new UI_MODAL.View() ),\n\t creator;\n\t\n\t options = _.defaults( options || {}, {\n\t elements : elements,\n\t oncancel : function(){\n\t modal.hide();\n\t deferred.reject( 'cancelled' );\n\t },\n\t oncreate : function( creator, response ){\n\t modal.hide();\n\t deferred.resolve( response );\n\t }\n\t });\n\t\n\t creator = new CreatorClass( options );\n\t modal.show({\n\t title : options.title || _l( 'Create a collection' ),\n\t body : creator.$el,\n\t width : '80%',\n\t height : '100%',\n\t closing_events: true\n\t });\n\t creator.render();\n\t window._collectionCreator = creator;\n\t\n\t //TODO: remove modal header\n\t return deferred;\n\t};\n\t\n\t/** List collection flavor of collectionCreatorModal. */\n\tvar listCollectionCreatorModal = function _listCollectionCreatorModal( elements, options ){\n\t options = options || {};\n\t options.title = _l( 'Create a collection from a list of datasets' );\n\t return collectionCreatorModal( elements, options, ListCollectionCreator );\n\t};\n\t\n\t\n\t//==============================================================================\n\t/** Use a modal to create a list collection, then add it to the given history contents.\n\t * @returns {Deferred} resolved when the collection is added to the history.\n\t */\n\tfunction createListCollection( contents ){\n\t var elements = contents.toJSON(),\n\t promise = listCollectionCreatorModal( elements, {\n\t creationFn : function( elements, name ){\n\t elements = elements.map( function( element ){\n\t return {\n\t id : element.id,\n\t name : element.name,\n\t //TODO: this allows for list:list even if the filter above does not - reconcile\n\t src : ( element.history_content_type === 'dataset'? 'hda' : 'hdca' )\n\t };\n\t });\n\t return contents.createHDCA( elements, 'list', name );\n\t }\n\t });\n\t return promise;\n\t}\n\t\n\t//==============================================================================\n\t return {\n\t DatasetCollectionElementView: DatasetCollectionElementView,\n\t ListCollectionCreator : ListCollectionCreator,\n\t\n\t collectionCreatorModal : collectionCreatorModal,\n\t listCollectionCreatorModal : listCollectionCreatorModal,\n\t createListCollection : createListCollection\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(2), __webpack_require__(1), __webpack_require__(1)))\n\n/***/ },\n/* 31 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(jQuery, Backbone, $, _) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(41),\n\t __webpack_require__(12),\n\t __webpack_require__(22),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( LIST_ITEM, STATES, faIconButton, BASE_MVC, _l ){\n\t'use strict';\n\t\n\tvar logNamespace = 'dataset';\n\t/*==============================================================================\n\tTODO:\n\t straighten out state rendering and templates used\n\t inaccessible/STATES.NOT_VIEWABLE is a special case\n\t simplify button rendering\n\t\n\t==============================================================================*/\n\tvar _super = LIST_ITEM.ListItemView;\n\t/** @class Read only list view for either LDDAs, HDAs, or HDADCEs.\n\t * Roughly, any DatasetInstance (and not a raw Dataset).\n\t */\n\tvar DatasetListItemView = _super.extend(\n\t/** @lends DatasetListItemView.prototype */{\n\t _logNamespace : logNamespace,\n\t\n\t className : _super.prototype.className + \" dataset\",\n\t //TODO:?? doesn't exactly match an hda's type_id\n\t id : function(){\n\t return [ 'dataset', this.model.get( 'id' ) ].join( '-' );\n\t },\n\t\n\t /** Set up: instance vars, options, and event handlers */\n\t initialize : function( attributes ){\n\t if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; }\n\t this.log( this + '.initialize:', attributes );\n\t _super.prototype.initialize.call( this, attributes );\n\t\n\t /** where should pages from links be displayed? (default to new tab/window) */\n\t this.linkTarget = attributes.linkTarget || '_blank';\n\t },\n\t\n\t /** event listeners */\n\t _setUpListeners : function(){\n\t _super.prototype._setUpListeners.call( this );\n\t var self = this;\n\t\n\t // re-rendering on any model changes\n\t return self.listenTo( self.model, {\n\t 'change': function( model, options ){\n\t // if the model moved into the ready state and is expanded without details, fetch those details now\n\t if( self.model.changedAttributes().state\n\t && self.model.inReadyState()\n\t && self.expanded\n\t && !self.model.hasDetails() ){\n\t // normally, will render automatically (due to fetch -> change),\n\t // but! setting_metadata sometimes doesn't cause any other changes besides state\n\t // so, not rendering causes it to seem frozen in setting_metadata state\n\t self.model.fetch({ silent : true })\n\t .done( function(){ self.render(); });\n\t\n\t } else {\n\t self.render();\n\t }\n\t }\n\t });\n\t },\n\t\n\t // ......................................................................... expandable\n\t /** In this override, only get details if in the ready state, get rerunnable if in other states.\n\t * Note: fetch with no 'change' event triggering to prevent automatic rendering.\n\t */\n\t _fetchModelDetails : function(){\n\t var view = this;\n\t if( view.model.inReadyState() && !view.model.hasDetails() ){\n\t return view.model.fetch({ silent: true });\n\t }\n\t return jQuery.when();\n\t },\n\t\n\t // ......................................................................... removal\n\t /** Remove this view's html from the DOM and remove all event listeners.\n\t * @param {Number or String} speed jq effect speed\n\t * @param {Function} callback an optional function called when removal is done (scoped to this view)\n\t */\n\t remove : function( speed, callback ){\n\t var view = this;\n\t speed = speed || this.fxSpeed;\n\t this.$el.fadeOut( speed, function(){\n\t Backbone.View.prototype.remove.call( view );\n\t if( callback ){ callback.call( view ); }\n\t });\n\t },\n\t\n\t // ......................................................................... rendering\n\t /* TODO:\n\t dataset states are the issue primarily making dataset rendering complex\n\t each state should have it's own way of displaying/set of details\n\t often with different actions that can be applied\n\t throw in deleted/purged/visible and things get complicated easily\n\t I've considered (a couple of times) - creating a view for each state\n\t - but recreating the view during an update...seems wrong\n\t */\n\t /** In this override, add the dataset state as a class for use with state-based CSS */\n\t _swapNewRender : function( $newRender ){\n\t _super.prototype._swapNewRender.call( this, $newRender );\n\t if( this.model.has( 'state' ) ){\n\t this.$el.addClass( 'state-' + this.model.get( 'state' ) );\n\t }\n\t return this.$el;\n\t },\n\t\n\t // ................................................................................ titlebar\n\t /** In this override, add the dataset display button. */\n\t _renderPrimaryActions : function(){\n\t // render just the display for read-only\n\t return [ this._renderDisplayButton() ];\n\t },\n\t\n\t /** Render icon-button to display dataset data */\n\t _renderDisplayButton : function(){\n\t // don't show display if not viewable or not accessible\n\t var state = this.model.get( 'state' );\n\t if( ( state === STATES.NOT_VIEWABLE )\n\t || ( state === STATES.DISCARDED )\n\t || ( !this.model.get( 'accessible' ) ) ){\n\t return null;\n\t }\n\t\n\t var displayBtnData = {\n\t target : this.linkTarget,\n\t classes : 'display-btn'\n\t };\n\t\n\t // show a disabled display if the data's been purged\n\t if( this.model.get( 'purged' ) ){\n\t displayBtnData.disabled = true;\n\t displayBtnData.title = _l( 'Cannot display datasets removed from disk' );\n\t\n\t // disable if still uploading\n\t } else if( state === STATES.UPLOAD ){\n\t displayBtnData.disabled = true;\n\t displayBtnData.title = _l( 'This dataset must finish uploading before it can be viewed' );\n\t\n\t // disable if still new\n\t } else if( state === STATES.NEW ){\n\t displayBtnData.disabled = true;\n\t displayBtnData.title = _l( 'This dataset is not yet viewable' );\n\t\n\t } else {\n\t displayBtnData.title = _l( 'View data' );\n\t\n\t // default link for dataset\n\t displayBtnData.href = this.model.urls.display;\n\t\n\t // add frame manager option onclick event\n\t var self = this;\n\t displayBtnData.onclick = function( ev ){\n\t if (Galaxy.frame && Galaxy.frame.active) {\n\t // Add dataset to frames.\n\t Galaxy.frame.addDataset(self.model.get('id'));\n\t ev.preventDefault();\n\t }\n\t };\n\t }\n\t displayBtnData.faIcon = 'fa-eye';\n\t return faIconButton( displayBtnData );\n\t },\n\t\n\t // ......................................................................... rendering details\n\t /** Render the enclosing div of the hda body and, if expanded, the html in the body\n\t * @returns {jQuery} rendered DOM\n\t */\n\t _renderDetails : function(){\n\t //TODO: generalize to be allow different details for each state\n\t\n\t // no access - render nothing but a message\n\t if( this.model.get( 'state' ) === STATES.NOT_VIEWABLE ){\n\t return $( this.templates.noAccess( this.model.toJSON(), this ) );\n\t }\n\t\n\t var $details = _super.prototype._renderDetails.call( this );\n\t $details.find( '.actions .left' ).empty().append( this._renderSecondaryActions() );\n\t $details.find( '.summary' ).html( this._renderSummary() )\n\t .prepend( this._renderDetailMessages() );\n\t $details.find( '.display-applications' ).html( this._renderDisplayApplications() );\n\t\n\t this._setUpBehaviors( $details );\n\t return $details;\n\t },\n\t\n\t /** Defer to the appropo summary rendering fn based on state */\n\t _renderSummary : function(){\n\t var json = this.model.toJSON(),\n\t summaryRenderFn = this.templates.summaries[ json.state ];\n\t summaryRenderFn = summaryRenderFn || this.templates.summaries.unknown;\n\t return summaryRenderFn( json, this );\n\t },\n\t\n\t /** Render messages to be displayed only when the details are shown */\n\t _renderDetailMessages : function(){\n\t var view = this,\n\t $warnings = $( '' ),\n\t json = view.model.toJSON();\n\t //TODO:! unordered (map)\n\t _.each( view.templates.detailMessages, function( templateFn ){\n\t $warnings.append( $( templateFn( json, view ) ) );\n\t });\n\t return $warnings;\n\t },\n\t\n\t /** Render the external display application links */\n\t _renderDisplayApplications : function(){\n\t if( this.model.isDeletedOrPurged() ){ return ''; }\n\t // render both old and new display apps using the same template\n\t return [\n\t this.templates.displayApplications( this.model.get( 'display_apps' ), this ),\n\t this.templates.displayApplications( this.model.get( 'display_types' ), this )\n\t ].join( '' );\n\t },\n\t\n\t // ......................................................................... secondary/details actions\n\t /** A series of links/buttons for less commonly used actions: re-run, info, etc. */\n\t _renderSecondaryActions : function(){\n\t this.debug( '_renderSecondaryActions' );\n\t switch( this.model.get( 'state' ) ){\n\t case STATES.NOT_VIEWABLE:\n\t return [];\n\t case STATES.OK:\n\t case STATES.FAILED_METADATA:\n\t case STATES.ERROR:\n\t return [ this._renderDownloadButton(), this._renderShowParamsButton() ];\n\t }\n\t return [ this._renderShowParamsButton() ];\n\t },\n\t\n\t /** Render icon-button to show the input and output (stdout/err) for the job that created this.\n\t * @returns {jQuery} rendered DOM\n\t */\n\t _renderShowParamsButton : function(){\n\t // gen. safe to show in all cases\n\t return faIconButton({\n\t title : _l( 'View details' ),\n\t classes : 'params-btn',\n\t href : this.model.urls.show_params,\n\t target : this.linkTarget,\n\t faIcon : 'fa-info-circle',\n\t onclick : function( ev ) {\n\t if ( Galaxy.frame && Galaxy.frame.active ) {\n\t Galaxy.frame.add( { title: 'Dataset details', url: this.href } );\n\t ev.preventDefault();\n\t ev.stopPropagation();\n\t }\n\t }\n\t });\n\t },\n\t\n\t /** Render icon-button/popupmenu to download the data (and/or the associated meta files (bai, etc.)) for this.\n\t * @returns {jQuery} rendered DOM\n\t */\n\t _renderDownloadButton : function(){\n\t // don't show anything if the data's been purged\n\t if( this.model.get( 'purged' ) || !this.model.hasData() ){ return null; }\n\t\n\t // return either: a popupmenu with links to download assoc. meta files (if there are meta files)\n\t // or a single download icon-button (if there are no meta files)\n\t if( !_.isEmpty( this.model.get( 'meta_files' ) ) ){\n\t return this._renderMetaFileDownloadButton();\n\t }\n\t\n\t return $([\n\t '',\n\t '',\n\t ''\n\t ].join( '' ));\n\t },\n\t\n\t /** Render the download button which opens a dropdown with links to download assoc. meta files (indeces, etc.) */\n\t _renderMetaFileDownloadButton : function(){\n\t var urls = this.model.urls;\n\t return $([\n\t '
'\n\t ], 'dataset' );\n\t\n\t // messages to be displayed only within the details section ('below the fold')\n\t var detailMessageTemplates = {\n\t resubmitted : BASE_MVC.wrapTemplate([\n\t // deleted not purged\n\t '<% if( model.resubmitted ){ %>',\n\t '
',\n\t _l( 'The job creating this dataset has been resubmitted' ),\n\t '
',\n\t '<% } %>'\n\t ])\n\t };\n\t\n\t // this is applied to both old and new style display apps\n\t var displayApplicationsTemplate = BASE_MVC.wrapTemplate([\n\t '<% _.each( apps, function( app ){ %>',\n\t '
',\n\t // space for title bar buttons - gen. floated to the right\n\t '',\n\t '',\n\t\n\t // expandable area for more details\n\t '',\n\t '
'\n\t ]);\n\t\n\t var warnings = {};\n\t\n\t var titleBarTemplate = BASE_MVC.wrapTemplate([\n\t // adding a tabindex here allows focusing the title bar and the use of keydown to expand the dataset display\n\t '
',\n\t //TODO: prob. belongs in dataset-list-item\n\t '',\n\t '
',\n\t '<%- element.name %>',\n\t '
',\n\t '',\n\t '
'\n\t ], 'element' );\n\t\n\t var subtitleTemplate = BASE_MVC.wrapTemplate([\n\t // override this\n\t ''\n\t ]);\n\t\n\t var detailsTemplate = BASE_MVC.wrapTemplate([\n\t // override this\n\t ''\n\t ]);\n\t\n\t return {\n\t el : elTemplato,\n\t warnings : warnings,\n\t titleBar : titleBarTemplate,\n\t subtitle : subtitleTemplate,\n\t details : detailsTemplate\n\t };\n\t}());\n\t\n\t\n\t//==============================================================================\n\t/** A view that is displayed in some larger list/grid/collection.\n\t * *AND* can display some sub-list of it's own when expanded (e.g. dataset collections).\n\t * This list will 'foldout' when the item is expanded depending on this.foldoutStyle:\n\t * If 'foldout': will expand vertically to show the nested list\n\t * If 'drilldown': will overlay the parent list\n\t *\n\t * Inherits from ListItemView.\n\t *\n\t * _renderDetails does the work of creating this.details: a sub-view that shows the nested list\n\t */\n\tvar FoldoutListItemView = ListItemView.extend({\n\t\n\t /** If 'foldout': show the sub-panel inside the expanded item\n\t * If 'drilldown': only fire events and handle by pub-sub\n\t * (allow the panel containing this item to attach it, hide itself, etc.)\n\t */\n\t foldoutStyle : 'foldout',\n\t /** Panel view class to instantiate for the sub-panel */\n\t foldoutPanelClass : null,\n\t\n\t /** override to:\n\t * add attributes foldoutStyle and foldoutPanelClass for config poly\n\t * disrespect attributes.expanded if drilldown\n\t */\n\t initialize : function( attributes ){\n\t if( this.foldoutStyle === 'drilldown' ){ this.expanded = false; }\n\t this.foldoutStyle = attributes.foldoutStyle || this.foldoutStyle;\n\t this.foldoutPanelClass = attributes.foldoutPanelClass || this.foldoutPanelClass;\n\t\n\t ListItemView.prototype.initialize.call( this, attributes );\n\t this.foldout = this._createFoldoutPanel();\n\t },\n\t\n\t /** in this override, attach the foldout panel when rendering details */\n\t _renderDetails : function(){\n\t if( this.foldoutStyle === 'drilldown' ){ return $(); }\n\t var $newDetails = ListItemView.prototype._renderDetails.call( this );\n\t return this._attachFoldout( this.foldout, $newDetails );\n\t },\n\t\n\t /** In this override, handle collection expansion. */\n\t _createFoldoutPanel : function(){\n\t var model = this.model;\n\t var FoldoutClass = this._getFoldoutPanelClass( model ),\n\t options = this._getFoldoutPanelOptions( model ),\n\t foldout = new FoldoutClass( _.extend( options, {\n\t model : model\n\t }));\n\t return foldout;\n\t },\n\t\n\t /** Stub to return proper foldout panel class */\n\t _getFoldoutPanelClass : function(){\n\t // override\n\t return this.foldoutPanelClass;\n\t },\n\t\n\t /** Stub to return proper foldout panel options */\n\t _getFoldoutPanelOptions : function(){\n\t return {\n\t // propagate foldout style down\n\t foldoutStyle : this.foldoutStyle,\n\t fxSpeed : this.fxSpeed\n\t };\n\t },\n\t\n\t /** Render the foldout panel inside the view, hiding controls */\n\t _attachFoldout : function( foldout, $whereTo ){\n\t $whereTo = $whereTo || this.$( '> .details' );\n\t this.foldout = foldout.render( 0 );\n\t foldout.$( '> .controls' ).hide();\n\t return $whereTo.append( foldout.$el );\n\t },\n\t\n\t /** In this override, branch on foldoutStyle to show expanded */\n\t expand : function(){\n\t var view = this;\n\t return view._fetchModelDetails()\n\t .always(function(){\n\t if( view.foldoutStyle === 'foldout' ){\n\t view._expand();\n\t } else if( view.foldoutStyle === 'drilldown' ){\n\t view._expandByDrilldown();\n\t }\n\t });\n\t },\n\t\n\t /** For drilldown, set up close handler and fire expanded:drilldown\n\t * containing views can listen to this and handle other things\n\t * (like hiding themselves) by listening for expanded/collapsed:drilldown\n\t */\n\t _expandByDrilldown : function(){\n\t var view = this;\n\t // attachment and rendering done by listener\n\t view.listenTo( view.foldout, 'close', function(){\n\t view.trigger( 'collapsed:drilldown', view, view.foldout );\n\t });\n\t view.trigger( 'expanded:drilldown', view, view.foldout );\n\t }\n\t\n\t});\n\t\n\t// ............................................................................ TEMPLATES\n\t/** underscore templates */\n\tFoldoutListItemView.prototype.templates = (function(){\n\t\n\t var detailsTemplate = BASE_MVC.wrapTemplate([\n\t '
',\n\t // override with more info (that goes above the panel)\n\t '
'\n\t ], 'collection' );\n\t\n\t return _.extend( {}, ListItemView.prototype.templates, {\n\t details : detailsTemplate\n\t });\n\t}());\n\t\n\t\n\t//==============================================================================\n\t return {\n\t ExpandableView : ExpandableView,\n\t ListItemView : ListItemView,\n\t FoldoutListItemView : FoldoutListItemView\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(1), __webpack_require__(2), __webpack_require__(1)))\n\n/***/ },\n/* 42 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function($, _) {/**\n\t This is the base class of the tool form plugin. This class is e.g. inherited by the regular and the workflow tool form.\n\t*/\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(4), __webpack_require__(54), __webpack_require__(7), __webpack_require__(37),\n\t __webpack_require__(17), __webpack_require__(27)], __WEBPACK_AMD_DEFINE_RESULT__ = function(Utils, Deferred, Ui, FormBase, CitationModel, CitationView) {\n\t return FormBase.extend({\n\t initialize: function(options) {\n\t var self = this;\n\t FormBase.prototype.initialize.call(this, options);\n\t this.deferred = new Deferred();\n\t if (options.inputs) {\n\t this._buildForm(options);\n\t } else {\n\t this.deferred.execute(function(process) {\n\t self._buildModel(process, options, true);\n\t });\n\t }\n\t // Listen to history panel\n\t if ( options.listen_to_history && parent.Galaxy && parent.Galaxy.currHistoryPanel ) {\n\t this.listenTo( parent.Galaxy.currHistoryPanel.collection, 'change', function() {\n\t this.refresh();\n\t });\n\t }\n\t },\n\t\n\t /** Listen to history panel changes and update the tool form */\n\t refresh: function() {\n\t var self = this;\n\t self.deferred.reset();\n\t this.deferred.execute( function (process){\n\t self._updateModel( process)\n\t });\n\t },\n\t\n\t /** Wait for deferred build processes before removal */\n\t remove: function() {\n\t var self = this;\n\t this.$el.hide();\n\t this.deferred.execute(function(){\n\t FormBase.prototype.remove.call(self);\n\t Galaxy.emit.debug('tool-form-base::remove()', 'Destroy view.');\n\t });\n\t },\n\t\n\t /** Build form */\n\t _buildForm: function(options) {\n\t var self = this;\n\t this.options = Utils.merge(options, this.options);\n\t this.options = Utils.merge({\n\t icon : options.icon,\n\t title : '' + options.name + ' ' + options.description + ' (Galaxy Version ' + options.version + ')',\n\t operations : !this.options.hide_operations && this._operations(),\n\t onchange : function() {\n\t self.refresh();\n\t }\n\t }, this.options);\n\t this.options.customize && this.options.customize( this.options );\n\t this.render();\n\t if ( !this.options.collapsible ) {\n\t this.$el.append( $( '' ).addClass( 'ui-margin-top-large' ).append( this._footer() ) );\n\t }\n\t },\n\t\n\t /** Builds a new model through api call and recreates the entire form\n\t */\n\t _buildModel: function(process, options, hide_message) {\n\t var self = this;\n\t this.options.id = options.id;\n\t this.options.version = options.version;\n\t\n\t // build request url\n\t var build_url = '';\n\t var build_data = {};\n\t if ( options.job_id ) {\n\t build_url = Galaxy.root + 'api/jobs/' + options.job_id + '/build_for_rerun';\n\t } else {\n\t build_url = Galaxy.root + 'api/tools/' + options.id + '/build';\n\t if ( Galaxy.params && Galaxy.params.tool_id == options.id ) {\n\t build_data = $.extend( {}, Galaxy.params );\n\t options.version && ( build_data[ 'tool_version' ] = options.version );\n\t }\n\t }\n\t\n\t // get initial model\n\t Utils.get({\n\t url : build_url,\n\t data : build_data,\n\t success : function(new_model) {\n\t new_model = new_model.tool_model || new_model;\n\t if( !new_model.display ) {\n\t window.location = Galaxy.root;\n\t return;\n\t }\n\t self._buildForm(new_model);\n\t !hide_message && self.message.update({\n\t status : 'success',\n\t message : 'Now you are using \\'' + self.options.name + '\\' version ' + self.options.version + ', id \\'' + self.options.id + '\\'.',\n\t persistent : false\n\t });\n\t Galaxy.emit.debug('tool-form-base::initialize()', 'Initial tool model ready.', new_model);\n\t process.resolve();\n\t },\n\t error : function(response, xhr) {\n\t var error_message = ( response && response.err_msg ) || 'Uncaught error.';\n\t if ( xhr.status == 401 ) {\n\t window.location = Galaxy.root + 'user/login?' + $.param({ redirect : Galaxy.root + '?tool_id=' + self.options.id });\n\t } else if ( self.$el.is(':empty') ) {\n\t self.$el.prepend((new Ui.Message({\n\t message : error_message,\n\t status : 'danger',\n\t persistent : true,\n\t large : true\n\t })).$el);\n\t } else {\n\t Galaxy.modal && Galaxy.modal.show({\n\t title : 'Tool request failed',\n\t body : error_message,\n\t buttons : {\n\t 'Close' : function() {\n\t Galaxy.modal.hide();\n\t }\n\t }\n\t });\n\t }\n\t Galaxy.emit.debug('tool-form::initialize()', 'Initial tool model request failed.', response);\n\t process.reject();\n\t }\n\t });\n\t },\n\t\n\t /** Request a new model for an already created tool form and updates the form inputs\n\t */\n\t _updateModel: function(process) {\n\t // link this\n\t var self = this;\n\t var model_url = this.options.update_url || Galaxy.root + 'api/tools/' + this.options.id + '/build';\n\t var current_state = {\n\t tool_id : this.options.id,\n\t tool_version : this.options.version,\n\t inputs : $.extend(true, {}, self.data.create())\n\t }\n\t this.wait(true);\n\t\n\t // log tool state\n\t Galaxy.emit.debug('tool-form-base::_updateModel()', 'Sending current state.', current_state);\n\t\n\t // post job\n\t Utils.request({\n\t type : 'POST',\n\t url : model_url,\n\t data : current_state,\n\t success : function(new_model) {\n\t self.update(new_model['tool_model'] || new_model);\n\t self.options.update && self.options.update(new_model);\n\t self.wait(false);\n\t Galaxy.emit.debug('tool-form-base::_updateModel()', 'Received new model.', new_model);\n\t process.resolve();\n\t },\n\t error : function(response) {\n\t Galaxy.emit.debug('tool-form-base::_updateModel()', 'Refresh request failed.', response);\n\t process.reject();\n\t }\n\t });\n\t },\n\t\n\t /** Create tool operation menu\n\t */\n\t _operations: function() {\n\t var self = this;\n\t var options = this.options;\n\t\n\t // button for version selection\n\t var versions_button = new Ui.ButtonMenu({\n\t icon : 'fa-cubes',\n\t title : (!options.narrow && 'Versions') || null,\n\t tooltip : 'Select another tool version'\n\t });\n\t if (!options.sustain_version && options.versions && options.versions.length > 1) {\n\t for (var i in options.versions) {\n\t var version = options.versions[i];\n\t if (version != options.version) {\n\t versions_button.addMenu({\n\t title : 'Switch to ' + version,\n\t version : version,\n\t icon : 'fa-cube',\n\t onclick : function() {\n\t // here we update the tool version (some tools encode the version also in the id)\n\t var id = options.id.replace(options.version, this.version);\n\t var version = this.version;\n\t // queue model request\n\t self.deferred.reset();\n\t self.deferred.execute(function(process) {\n\t self._buildModel(process, {id: id, version: version})\n\t });\n\t }\n\t });\n\t }\n\t }\n\t } else {\n\t versions_button.$el.hide();\n\t }\n\t\n\t // button for options e.g. search, help\n\t var menu_button = new Ui.ButtonMenu({\n\t icon : 'fa-caret-down',\n\t title : (!options.narrow && 'Options') || null,\n\t tooltip : 'View available options'\n\t });\n\t if(options.biostar_url) {\n\t menu_button.addMenu({\n\t icon : 'fa-question-circle',\n\t title : 'Question?',\n\t tooltip : 'Ask a question about this tool (Biostar)',\n\t onclick : function() {\n\t window.open(options.biostar_url + '/p/new/post/');\n\t }\n\t });\n\t menu_button.addMenu({\n\t icon : 'fa-search',\n\t title : 'Search',\n\t tooltip : 'Search help for this tool (Biostar)',\n\t onclick : function() {\n\t window.open(options.biostar_url + '/local/search/page/?q=' + options.name);\n\t }\n\t });\n\t };\n\t menu_button.addMenu({\n\t icon : 'fa-share',\n\t title : 'Share',\n\t tooltip : 'Share this tool',\n\t onclick : function() {\n\t prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + Galaxy.root + 'root?tool_id=' + options.id);\n\t }\n\t });\n\t\n\t // add admin operations\n\t if (Galaxy.user && Galaxy.user.get('is_admin')) {\n\t menu_button.addMenu({\n\t icon : 'fa-download',\n\t title : 'Download',\n\t tooltip : 'Download this tool',\n\t onclick : function() {\n\t window.location.href = Galaxy.root + 'api/tools/' + options.id + '/download';\n\t }\n\t });\n\t }\n\t\n\t // button for version selection\n\t if (options.requirements && options.requirements.length > 0) {\n\t menu_button.addMenu({\n\t icon : 'fa-info-circle',\n\t title : 'Requirements',\n\t tooltip : 'Display tool requirements',\n\t onclick : function() {\n\t if (!this.visible || self.portlet.collapsed ) {\n\t this.visible = true;\n\t self.portlet.expand();\n\t self.message.update({\n\t persistent : true,\n\t message : self._templateRequirements(options),\n\t status : 'info'\n\t });\n\t } else {\n\t this.visible = false;\n\t self.message.update({\n\t message : ''\n\t });\n\t }\n\t }\n\t });\n\t }\n\t\n\t // add toolshed url\n\t if (options.sharable_url) {\n\t menu_button.addMenu({\n\t icon : 'fa-external-link',\n\t title : 'See in Tool Shed',\n\t tooltip : 'Access the repository',\n\t onclick : function() {\n\t window.open(options.sharable_url);\n\t }\n\t });\n\t }\n\t\n\t return {\n\t menu : menu_button,\n\t versions : versions_button\n\t }\n\t },\n\t\n\t /** Create footer\n\t */\n\t _footer: function() {\n\t var options = this.options;\n\t var $el = $( '' ).append( this._templateHelp( options ) );\n\t if ( options.citations ) {\n\t var $citations = $( '' );\n\t var citations = new CitationModel.ToolCitationCollection();\n\t citations.tool_id = options.id;\n\t var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations });\n\t citation_list_view.render();\n\t citations.fetch();\n\t $el.append( $citations );\n\t }\n\t return $el;\n\t },\n\t\n\t /** Templates\n\t */\n\t _templateHelp: function( options ) {\n\t var $tmpl = $( '' ).addClass( 'ui-form-help' ).append( options.help );\n\t $tmpl.find( 'a' ).attr( 'target', '_blank' );\n\t return $tmpl;\n\t },\n\t\n\t _templateRequirements: function( options ) {\n\t var nreq = options.requirements.length;\n\t if ( nreq > 0 ) {\n\t var requirements_message = 'This tool requires ';\n\t _.each( options.requirements, function( req, i ) {\n\t requirements_message += req.name + ( req.version ? ' (Version ' + req.version + ')' : '' ) + ( i < nreq - 2 ? ', ' : ( i == nreq - 2 ? ' and ' : '' ) );\n\t });\n\t var requirements_link = $( '' ).attr( 'target', '_blank' ).attr( 'href', 'https://wiki.galaxyproject.org/Tools/Requirements' ).text( 'here' );\n\t return $( '' ).append( requirements_message + '. Click ' ).append( requirements_link ).append( ' for more information.' );\n\t }\n\t return 'No requirements found.';\n\t }\n\t });\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(2)))\n\n/***/ },\n/* 43 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $) {/**\n\t * Model, view, and controller objects for Galaxy tools and tool panel.\n\t */\n\t\n\t !(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(2),\n\t __webpack_require__(16),\n\t __webpack_require__(11),\n\t __webpack_require__(18)\n\t\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function(_, util, data, ToolForm) {\n\t 'use strict';\n\t\n\t/**\n\t * Mixin for tracking model visibility.\n\t */\n\tvar VisibilityMixin = {\n\t hidden: false,\n\t\n\t show: function() {\n\t this.set(\"hidden\", false);\n\t },\n\t\n\t hide: function() {\n\t this.set(\"hidden\", true);\n\t },\n\t\n\t toggle: function() {\n\t this.set(\"hidden\", !this.get(\"hidden\"));\n\t },\n\t\n\t is_visible: function() {\n\t return !this.attributes.hidden;\n\t }\n\t\n\t};\n\t\n\t/**\n\t * A tool parameter.\n\t */\n\tvar ToolParameter = Backbone.Model.extend({\n\t defaults: {\n\t name: null,\n\t label: null,\n\t type: null,\n\t value: null,\n\t html: null,\n\t num_samples: 5\n\t },\n\t\n\t initialize: function(options) {\n\t this.attributes.html = unescape(this.attributes.html);\n\t },\n\t\n\t copy: function() {\n\t return new ToolParameter(this.toJSON());\n\t },\n\t\n\t set_value: function(value) {\n\t this.set('value', value || '');\n\t }\n\t});\n\t\n\tvar ToolParameterCollection = Backbone.Collection.extend({\n\t model: ToolParameter\n\t});\n\t\n\t/**\n\t * A data tool parameter.\n\t */\n\tvar DataToolParameter = ToolParameter.extend({});\n\t\n\t/**\n\t * An integer tool parameter.\n\t */\n\tvar IntegerToolParameter = ToolParameter.extend({\n\t set_value: function(value) {\n\t this.set('value', parseInt(value, 10));\n\t },\n\t\n\t /**\n\t * Returns samples from a tool input.\n\t */\n\t get_samples: function() {\n\t return d3.scale.linear()\n\t .domain([this.get('min'), this.get('max')])\n\t .ticks(this.get('num_samples'));\n\t }\n\t});\n\t\n\tvar FloatToolParameter = IntegerToolParameter.extend({\n\t set_value: function(value) {\n\t this.set('value', parseFloat(value));\n\t }\n\t});\n\t\n\t/**\n\t * A select tool parameter.\n\t */\n\tvar SelectToolParameter = ToolParameter.extend({\n\t /**\n\t * Returns tool options.\n\t */\n\t get_samples: function() {\n\t return _.map(this.get('options'), function(option) {\n\t return option[0];\n\t });\n\t }\n\t});\n\t\n\t// Set up dictionary of parameter types.\n\tToolParameter.subModelTypes = {\n\t 'integer': IntegerToolParameter,\n\t 'float': FloatToolParameter,\n\t 'data': DataToolParameter,\n\t 'select': SelectToolParameter\n\t};\n\t\n\t/**\n\t * A Galaxy tool.\n\t */\n\tvar Tool = Backbone.Model.extend({\n\t // Default attributes.\n\t defaults: {\n\t id: null,\n\t name: null,\n\t description: null,\n\t target: null,\n\t inputs: [],\n\t outputs: []\n\t },\n\t\n\t urlRoot: Galaxy.root + 'api/tools',\n\t\n\t initialize: function(options) {\n\t\n\t // Set parameters.\n\t this.set('inputs', new ToolParameterCollection(_.map(options.inputs, function(p) {\n\t var p_class = ToolParameter.subModelTypes[p.type] || ToolParameter;\n\t return new p_class(p);\n\t })));\n\t },\n\t\n\t /**\n\t *\n\t */\n\t toJSON: function() {\n\t var rval = Backbone.Model.prototype.toJSON.call(this);\n\t\n\t // Convert inputs to JSON manually.\n\t rval.inputs = this.get('inputs').map(function(i) { return i.toJSON(); });\n\t return rval;\n\t },\n\t\n\t /**\n\t * Removes inputs of a particular type; this is useful because not all inputs can be handled by\n\t * client and server yet.\n\t */\n\t remove_inputs: function(types) {\n\t var tool = this,\n\t incompatible_inputs = tool.get('inputs').filter( function(input) {\n\t return ( types.indexOf( input.get('type') ) !== -1);\n\t });\n\t tool.get('inputs').remove(incompatible_inputs);\n\t },\n\t\n\t /**\n\t * Returns object copy, optionally including only inputs that can be sampled.\n\t */\n\t copy: function(only_samplable_inputs) {\n\t var copy = new Tool(this.toJSON());\n\t\n\t // Return only samplable inputs if flag is set.\n\t if (only_samplable_inputs) {\n\t var valid_inputs = new Backbone.Collection();\n\t copy.get('inputs').each(function(input) {\n\t if (input.get_samples()) {\n\t valid_inputs.push(input);\n\t }\n\t });\n\t copy.set('inputs', valid_inputs);\n\t }\n\t\n\t return copy;\n\t },\n\t\n\t apply_search_results: function(results) {\n\t ( _.indexOf(results, this.attributes.id) !== -1 ? this.show() : this.hide() );\n\t return this.is_visible();\n\t },\n\t\n\t /**\n\t * Set a tool input's value.\n\t */\n\t set_input_value: function(name, value) {\n\t this.get('inputs').find(function(input) {\n\t return input.get('name') === name;\n\t }).set('value', value);\n\t },\n\t\n\t /**\n\t * Set many input values at once.\n\t */\n\t set_input_values: function(inputs_dict) {\n\t var self = this;\n\t _.each(_.keys(inputs_dict), function(input_name) {\n\t self.set_input_value(input_name, inputs_dict[input_name]);\n\t });\n\t },\n\t\n\t /**\n\t * Run tool; returns a Deferred that resolves to the tool's output(s).\n\t */\n\t run: function() {\n\t return this._run();\n\t },\n\t\n\t /**\n\t * Rerun tool using regions and a target dataset.\n\t */\n\t rerun: function(target_dataset, regions) {\n\t return this._run({\n\t action: 'rerun',\n\t target_dataset_id: target_dataset.id,\n\t regions: regions\n\t });\n\t },\n\t\n\t /**\n\t * Returns input dict for tool's inputs.\n\t */\n\t get_inputs_dict: function() {\n\t var input_dict = {};\n\t this.get('inputs').each(function(input) {\n\t input_dict[input.get('name')] = input.get('value');\n\t });\n\t return input_dict;\n\t },\n\t\n\t /**\n\t * Run tool; returns a Deferred that resolves to the tool's output(s).\n\t * NOTE: this method is a helper method and should not be called directly.\n\t */\n\t _run: function(additional_params) {\n\t // Create payload.\n\t var payload = _.extend({\n\t tool_id: this.id,\n\t inputs: this.get_inputs_dict()\n\t }, additional_params);\n\t\n\t // Because job may require indexing datasets, use server-side\n\t // deferred to ensure that job is run. Also use deferred that\n\t // resolves to outputs from tool.\n\t var run_deferred = $.Deferred(),\n\t ss_deferred = new util.ServerStateDeferred({\n\t ajax_settings: {\n\t url: this.urlRoot,\n\t data: JSON.stringify(payload),\n\t dataType: \"json\",\n\t contentType: 'application/json',\n\t type: \"POST\"\n\t },\n\t interval: 2000,\n\t success_fn: function(response) {\n\t return response !== \"pending\";\n\t }\n\t });\n\t\n\t // Run job and resolve run_deferred to tool outputs.\n\t $.when(ss_deferred.go()).then(function(result) {\n\t run_deferred.resolve(new data.DatasetCollection(result));\n\t });\n\t return run_deferred;\n\t }\n\t});\n\t_.extend(Tool.prototype, VisibilityMixin);\n\t\n\t/**\n\t * Tool view.\n\t */\n\tvar ToolView = Backbone.View.extend({\n\t\n\t});\n\t\n\t/**\n\t * Wrap collection of tools for fast access/manipulation.\n\t */\n\tvar ToolCollection = Backbone.Collection.extend({\n\t model: Tool\n\t});\n\t\n\t/**\n\t * Label or section header in tool panel.\n\t */\n\tvar ToolSectionLabel = Backbone.Model.extend(VisibilityMixin);\n\t\n\t/**\n\t * Section of tool panel with elements (labels and tools).\n\t */\n\tvar ToolSection = Backbone.Model.extend({\n\t defaults: {\n\t elems: [],\n\t open: false\n\t },\n\t\n\t clear_search_results: function() {\n\t _.each(this.attributes.elems, function(elt) {\n\t elt.show();\n\t });\n\t\n\t this.show();\n\t this.set(\"open\", false);\n\t },\n\t\n\t apply_search_results: function(results) {\n\t var all_hidden = true,\n\t cur_label;\n\t _.each(this.attributes.elems, function(elt) {\n\t if (elt instanceof ToolSectionLabel) {\n\t cur_label = elt;\n\t cur_label.hide();\n\t }\n\t else if (elt instanceof Tool) {\n\t if (elt.apply_search_results(results)) {\n\t all_hidden = false;\n\t if (cur_label) {\n\t cur_label.show();\n\t }\n\t }\n\t }\n\t });\n\t\n\t if (all_hidden) {\n\t this.hide();\n\t }\n\t else {\n\t this.show();\n\t this.set(\"open\", true);\n\t }\n\t }\n\t});\n\t_.extend(ToolSection.prototype, VisibilityMixin);\n\t\n\t/**\n\t * Tool search that updates results when query is changed. Result value of null\n\t * indicates that query was not run; if not null, results are from search using\n\t * query.\n\t */\n\tvar ToolSearch = Backbone.Model.extend({\n\t defaults: {\n\t search_hint_string: \"search tools\",\n\t min_chars_for_search: 3,\n\t clear_btn_url: \"\",\n\t search_url: \"\",\n\t visible: true,\n\t query: \"\",\n\t results: null,\n\t // ESC (27) will clear the input field and tool search filters\n\t clear_key: 27\n\t },\n\t\n\t urlRoot: Galaxy.root + 'api/tools',\n\t\n\t initialize: function() {\n\t this.on(\"change:query\", this.do_search);\n\t },\n\t\n\t /**\n\t * Do the search and update the results.\n\t */\n\t do_search: function() {\n\t var query = this.attributes.query;\n\t\n\t // If query is too short, do not search.\n\t if (query.length < this.attributes.min_chars_for_search) {\n\t this.set(\"results\", null);\n\t return;\n\t }\n\t\n\t // Do search via AJAX.\n\t var q = query;\n\t // Stop previous ajax-request\n\t if (this.timer) {\n\t clearTimeout(this.timer);\n\t }\n\t // Start a new ajax-request in X ms\n\t $(\"#search-clear-btn\").hide();\n\t $(\"#search-spinner\").show();\n\t var self = this;\n\t this.timer = setTimeout(function () {\n\t // log the search to analytics if present\n\t if ( typeof ga !== 'undefined' ) {\n\t ga( 'send', 'pageview', Galaxy.root + '?q=' + q );\n\t }\n\t $.get( self.urlRoot, { q: q }, function (data) {\n\t self.set(\"results\", data);\n\t $(\"#search-spinner\").hide();\n\t $(\"#search-clear-btn\").show();\n\t }, \"json\" );\n\t }, 400 );\n\t },\n\t\n\t clear_search: function() {\n\t this.set(\"query\", \"\");\n\t this.set(\"results\", null);\n\t }\n\t\n\t});\n\t_.extend(ToolSearch.prototype, VisibilityMixin);\n\t\n\t/**\n\t * Tool Panel.\n\t */\n\tvar ToolPanel = Backbone.Model.extend({\n\t\n\t initialize: function(options) {\n\t this.attributes.tool_search = options.tool_search;\n\t this.attributes.tool_search.on(\"change:results\", this.apply_search_results, this);\n\t this.attributes.tools = options.tools;\n\t this.attributes.layout = new Backbone.Collection( this.parse(options.layout) );\n\t },\n\t\n\t /**\n\t * Parse tool panel dictionary and return collection of tool panel elements.\n\t */\n\t parse: function(response) {\n\t // Recursive function to parse tool panel elements.\n\t var self = this,\n\t // Helper to recursively parse tool panel.\n\t parse_elt = function(elt_dict) {\n\t var type = elt_dict.model_class;\n\t // There are many types of tools; for now, anything that ends in 'Tool'\n\t // is treated as a generic tool.\n\t if ( type.indexOf('Tool') === type.length - 4 ) {\n\t return self.attributes.tools.get(elt_dict.id);\n\t }\n\t else if (type === 'ToolSection') {\n\t // Parse elements.\n\t var elems = _.map(elt_dict.elems, parse_elt);\n\t elt_dict.elems = elems;\n\t return new ToolSection(elt_dict);\n\t }\n\t else if (type === 'ToolSectionLabel') {\n\t return new ToolSectionLabel(elt_dict);\n\t }\n\t };\n\t\n\t return _.map(response, parse_elt);\n\t },\n\t\n\t clear_search_results: function() {\n\t this.get('layout').each(function(panel_elt) {\n\t if (panel_elt instanceof ToolSection) {\n\t panel_elt.clear_search_results();\n\t }\n\t else {\n\t // Label or tool, so just show.\n\t panel_elt.show();\n\t }\n\t });\n\t },\n\t\n\t apply_search_results: function() {\n\t var results = this.get('tool_search').get('results');\n\t if (results === null) {\n\t this.clear_search_results();\n\t return;\n\t }\n\t\n\t var cur_label = null;\n\t this.get('layout').each(function(panel_elt) {\n\t if (panel_elt instanceof ToolSectionLabel) {\n\t cur_label = panel_elt;\n\t cur_label.hide();\n\t }\n\t else if (panel_elt instanceof Tool) {\n\t if (panel_elt.apply_search_results(results)) {\n\t if (cur_label) {\n\t cur_label.show();\n\t }\n\t }\n\t }\n\t else {\n\t // Starting new section, so clear current label.\n\t cur_label = null;\n\t panel_elt.apply_search_results(results);\n\t }\n\t });\n\t }\n\t});\n\t\n\t/**\n\t * View classes for Galaxy tools and tool panel.\n\t *\n\t * Views use the templates defined below for rendering. Views update as needed\n\t * based on (a) model/collection events and (b) user interactions; in this sense,\n\t * they are controllers are well and the HTML is the real view in the MVC architecture.\n\t */\n\t\n\t/**\n\t * Base view that handles visibility based on model's hidden attribute.\n\t */\n\tvar BaseView = Backbone.View.extend({\n\t initialize: function() {\n\t this.model.on(\"change:hidden\", this.update_visible, this);\n\t this.update_visible();\n\t },\n\t update_visible: function() {\n\t ( this.model.attributes.hidden ? this.$el.hide() : this.$el.show() );\n\t }\n\t});\n\t\n\t/**\n\t * Link to a tool.\n\t */\n\tvar ToolLinkView = BaseView.extend({\n\t tagName: 'div',\n\t\n\t render: function() {\n\t // create element\n\t var $link = $('');\n\t $link.append(templates.tool_link(this.model.toJSON()));\n\t\n\t var formStyle = this.model.get( 'form_style', null );\n\t // open upload dialog for upload tool\n\t if (this.model.id === 'upload1') {\n\t $link.find('a').on('click', function(e) {\n\t e.preventDefault();\n\t Galaxy.upload.show();\n\t });\n\t }\n\t else if ( formStyle === 'regular' ) { // regular tools\n\t var self = this;\n\t $link.find('a').on('click', function(e) {\n\t e.preventDefault();\n\t var form = new ToolForm.View( { id : self.model.id, version : self.model.get('version') } );\n\t form.deferred.execute(function() {\n\t Galaxy.app.display( form );\n\t });\n\t });\n\t }\n\t\n\t // add element\n\t this.$el.append($link);\n\t return this;\n\t }\n\t});\n\t\n\t/**\n\t * Panel label/section header.\n\t */\n\tvar ToolSectionLabelView = BaseView.extend({\n\t tagName: 'div',\n\t className: 'toolPanelLabel',\n\t\n\t render: function() {\n\t this.$el.append( $(\"\").text(this.model.attributes.text) );\n\t return this;\n\t }\n\t});\n\t\n\t/**\n\t * Panel section.\n\t */\n\tvar ToolSectionView = BaseView.extend({\n\t tagName: 'div',\n\t className: 'toolSectionWrapper',\n\t\n\t initialize: function() {\n\t BaseView.prototype.initialize.call(this);\n\t this.model.on(\"change:open\", this.update_open, this);\n\t },\n\t\n\t render: function() {\n\t // Build using template.\n\t this.$el.append( templates.panel_section(this.model.toJSON()) );\n\t\n\t // Add tools to section.\n\t var section_body = this.$el.find(\".toolSectionBody\");\n\t _.each(this.model.attributes.elems, function(elt) {\n\t if (elt instanceof Tool) {\n\t var tool_view = new ToolLinkView({model: elt, className: \"toolTitle\"});\n\t tool_view.render();\n\t section_body.append(tool_view.$el);\n\t }\n\t else if (elt instanceof ToolSectionLabel) {\n\t var label_view = new ToolSectionLabelView({model: elt});\n\t label_view.render();\n\t section_body.append(label_view.$el);\n\t }\n\t else {\n\t // TODO: handle nested section bodies?\n\t }\n\t });\n\t return this;\n\t },\n\t\n\t events: {\n\t 'click .toolSectionTitle > a': 'toggle'\n\t },\n\t\n\t /**\n\t * Toggle visibility of tool section.\n\t */\n\t toggle: function() {\n\t this.model.set(\"open\", !this.model.attributes.open);\n\t },\n\t\n\t /**\n\t * Update whether section is open or close.\n\t */\n\t update_open: function() {\n\t (this.model.attributes.open ?\n\t this.$el.children(\".toolSectionBody\").slideDown(\"fast\") :\n\t this.$el.children(\".toolSectionBody\").slideUp(\"fast\")\n\t );\n\t }\n\t});\n\t\n\tvar ToolSearchView = Backbone.View.extend({\n\t tagName: 'div',\n\t id: 'tool-search',\n\t className: 'bar',\n\t\n\t events: {\n\t 'click': 'focus_and_select',\n\t 'keyup :input': 'query_changed',\n\t 'click #search-clear-btn': 'clear'\n\t },\n\t\n\t render: function() {\n\t this.$el.append( templates.tool_search(this.model.toJSON()) );\n\t if (!this.model.is_visible()) {\n\t this.$el.hide();\n\t }\n\t this.$el.find('[title]').tooltip();\n\t return this;\n\t },\n\t\n\t focus_and_select: function() {\n\t this.$el.find(\":input\").focus().select();\n\t },\n\t\n\t clear: function() {\n\t this.model.clear_search();\n\t this.$el.find(\":input\").val('');\n\t this.focus_and_select();\n\t return false;\n\t },\n\t\n\t query_changed: function( evData ) {\n\t // check for the 'clear key' (ESC) first\n\t if( ( this.model.attributes.clear_key ) &&\n\t ( this.model.attributes.clear_key === evData.which ) ){\n\t this.clear();\n\t return false;\n\t }\n\t this.model.set(\"query\", this.$el.find(\":input\").val());\n\t }\n\t});\n\t\n\t/**\n\t * Tool panel view. Events triggered include:\n\t * tool_link_click(click event, tool_model)\n\t */\n\tvar ToolPanelView = Backbone.View.extend({\n\t tagName: 'div',\n\t className: 'toolMenu',\n\t\n\t /**\n\t * Set up view.\n\t */\n\t initialize: function() {\n\t this.model.get('tool_search').on(\"change:results\", this.handle_search_results, this);\n\t },\n\t\n\t render: function() {\n\t var self = this;\n\t\n\t // Render search.\n\t var search_view = new ToolSearchView( { model: this.model.get('tool_search') } );\n\t search_view.render();\n\t self.$el.append(search_view.$el);\n\t\n\t // Render panel.\n\t this.model.get('layout').each(function(panel_elt) {\n\t if (panel_elt instanceof ToolSection) {\n\t var section_title_view = new ToolSectionView({model: panel_elt});\n\t section_title_view.render();\n\t self.$el.append(section_title_view.$el);\n\t }\n\t else if (panel_elt instanceof Tool) {\n\t var tool_view = new ToolLinkView({model: panel_elt, className: \"toolTitleNoSection\"});\n\t tool_view.render();\n\t self.$el.append(tool_view.$el);\n\t }\n\t else if (panel_elt instanceof ToolSectionLabel) {\n\t var label_view = new ToolSectionLabelView({model: panel_elt});\n\t label_view.render();\n\t self.$el.append(label_view.$el);\n\t }\n\t });\n\t\n\t // Setup tool link click eventing.\n\t self.$el.find(\"a.tool-link\").click(function(e) {\n\t // Tool id is always the first class.\n\t var\n\t tool_id = $(this).attr('class').split(/\\s+/)[0],\n\t tool = self.model.get('tools').get(tool_id);\n\t\n\t self.trigger(\"tool_link_click\", e, tool);\n\t });\n\t\n\t return this;\n\t },\n\t\n\t handle_search_results: function() {\n\t var results = this.model.get('tool_search').get('results');\n\t if (results && results.length === 0) {\n\t $(\"#search-no-results\").show();\n\t }\n\t else {\n\t $(\"#search-no-results\").hide();\n\t }\n\t }\n\t});\n\t\n\t/**\n\t * View for working with a tool: setting parameters and inputs and executing the tool.\n\t */\n\tvar ToolFormView = Backbone.View.extend({\n\t className: 'toolForm',\n\t\n\t render: function() {\n\t this.$el.children().remove();\n\t this.$el.append( templates.tool_form(this.model.toJSON()) );\n\t }\n\t});\n\t\n\t/**\n\t * Integrated tool menu + tool execution.\n\t */\n\tvar IntegratedToolMenuAndView = Backbone.View.extend({\n\t className: 'toolMenuAndView',\n\t\n\t initialize: function() {\n\t this.tool_panel_view = new ToolPanelView({collection: this.collection});\n\t this.tool_form_view = new ToolFormView();\n\t },\n\t\n\t render: function() {\n\t // Render and append tool panel.\n\t this.tool_panel_view.render();\n\t this.tool_panel_view.$el.css(\"float\", \"left\");\n\t this.$el.append(this.tool_panel_view.$el);\n\t\n\t // Append tool form view.\n\t this.tool_form_view.$el.hide();\n\t this.$el.append(this.tool_form_view.$el);\n\t\n\t // On tool link click, show tool.\n\t var self = this;\n\t this.tool_panel_view.on(\"tool_link_click\", function(e, tool) {\n\t // Prevents click from activating link:\n\t e.preventDefault();\n\t // Show tool that was clicked on:\n\t self.show_tool(tool);\n\t });\n\t },\n\t\n\t /**\n\t * Fetch and display tool.\n\t */\n\t show_tool: function(tool) {\n\t var self = this;\n\t tool.fetch().done( function() {\n\t self.tool_form_view.model = tool;\n\t self.tool_form_view.render();\n\t self.tool_form_view.$el.show();\n\t $('#left').width(\"650px\");\n\t });\n\t }\n\t});\n\t\n\t// TODO: move into relevant views\n\tvar templates = {\n\t // the search bar at the top of the tool panel\n\t tool_search : _.template([\n\t '\" autocomplete=\"off\" type=\"text\" />',\n\t '',\n\t //TODO: replace with icon\n\t '',\n\t ].join('')),\n\t\n\t // the category level container in the tool panel (e.g. 'Get Data', 'Text Manipulation')\n\t panel_section : _.template([\n\t '
'\n\t ].join('')),\n\t\n\t // a single tool's link in the tool panel; will load the tool form in the center panel\n\t tool_link : _.template([\n\t '',\n\t '<% _.each( labels, function( label ){ %>',\n\t '\">',\n\t '<%- label %>',\n\t '',\n\t '<% }); %>',\n\t '',\n\t ' tool-link\" href=\"<%= link %>\" target=\"<%- target %>\" minsizehint=\"<%- min_width %>\">',\n\t '<%- name %>',\n\t '',\n\t ' <%- description %>'\n\t ].join('')),\n\t\n\t // the tool form for entering tool parameters, viewing help and executing the tool\n\t // loaded when a tool link is clicked in the tool panel\n\t tool_form : _.template([\n\t '
',\n\t '<% } %>'\n\t ], 'dataset' ),\n\t\n\t deleted : BASE_MVC.wrapTemplate([\n\t // in this override, provide links to undelete or purge the dataset\n\t '<% if( dataset.deleted && !dataset.purged ){ %>',\n\t // deleted not purged\n\t '
',\n\t '<% } %>'\n\t ], 'visualizations' );\n\t\n\t return _.extend( {}, _super.prototype.templates, {\n\t warnings : warnings,\n\t visualizations : visualizationsTemplate\n\t });\n\t}());\n\t\n\t\n\t//==============================================================================\n\t return {\n\t DatasetListItemEdit : DatasetListItemEdit\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2), __webpack_require__(1)))\n\n/***/ },\n/* 70 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _, jQuery) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(12),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( STATES, BASE_MVC, _l ){\n\t'use strict';\n\t\n\tvar logNamespace = 'dataset';\n\t//==============================================================================\n\tvar searchableMixin = BASE_MVC.SearchableModelMixin;\n\t/** @class base model for any DatasetAssociation (HDAs, LDDAs, DatasetCollectionDAs).\n\t * No knowledge of what type (HDA/LDDA/DCDA) should be needed here.\n\t * The DA's are made searchable (by attribute) by mixing in SearchableModelMixin.\n\t */\n\tvar DatasetAssociation = Backbone.Model\n\t .extend( BASE_MVC.LoggableMixin )\n\t .extend( BASE_MVC.mixin( searchableMixin, /** @lends DatasetAssociation.prototype */{\n\t _logNamespace : logNamespace,\n\t\n\t /** default attributes for a model */\n\t defaults : {\n\t state : STATES.NEW,\n\t deleted : false,\n\t purged : false,\n\t name : '(unnamed dataset)',\n\t accessible : true,\n\t // sniffed datatype (sam, tabular, bed, etc.)\n\t data_type : '',\n\t file_ext : '',\n\t file_size : 0,\n\t\n\t // array of associated file types (eg. [ 'bam_index', ... ])\n\t meta_files : [],\n\t\n\t misc_blurb : '',\n\t misc_info : '',\n\t\n\t tags : []\n\t // do NOT default on annotation, as this default is valid and will be passed on 'save'\n\t // which is incorrect behavior when the model is only partially fetched (annos are not passed in summary data)\n\t //annotation : ''\n\t },\n\t\n\t /** instance vars and listeners */\n\t initialize : function( attributes, options ){\n\t this.debug( this + '(Dataset).initialize', attributes, options );\n\t\n\t //!! this state is not in trans.app.model.Dataset.states - set it here -\n\t if( !this.get( 'accessible' ) ){\n\t this.set( 'state', STATES.NOT_VIEWABLE );\n\t }\n\t\n\t /** Datasets rely/use some web controllers - have the model generate those URLs on startup */\n\t this.urls = this._generateUrls();\n\t\n\t this._setUpListeners();\n\t },\n\t\n\t /** returns misc. web urls for rendering things like re-run, display, etc. */\n\t _generateUrls : function(){\n\t var id = this.get( 'id' );\n\t if( !id ){ return {}; }\n\t var urls = {\n\t 'purge' : 'datasets/' + id + '/purge_async',\n\t 'display' : 'datasets/' + id + '/display/?preview=True',\n\t 'edit' : 'datasets/' + id + '/edit',\n\t 'download' : 'datasets/' + id + '/display?to_ext=' + this.get( 'file_ext' ),\n\t 'report_error' : 'dataset/errors?id=' + id,\n\t 'rerun' : 'tool_runner/rerun?id=' + id,\n\t 'show_params' : 'datasets/' + id + '/show_params',\n\t 'visualization' : 'visualization',\n\t 'meta_download' : 'dataset/get_metadata_file?hda_id=' + id + '&metadata_name='\n\t };\n\t _.each( urls, function( value, key ){\n\t urls[ key ] = Galaxy.root + value;\n\t });\n\t this.urls = urls;\n\t return urls;\n\t },\n\t\n\t /** set up any event listeners\n\t * event: state:ready fired when this DA moves into/is already in a ready state\n\t */\n\t _setUpListeners : function(){\n\t // if the state has changed and the new state is a ready state, fire an event\n\t this.on( 'change:state', function( currModel, newState ){\n\t this.log( this + ' has changed state:', currModel, newState );\n\t if( this.inReadyState() ){\n\t this.trigger( 'state:ready', currModel, newState, this.previous( 'state' ) );\n\t }\n\t });\n\t // the download url (currently) relies on having a correct file extension\n\t this.on( 'change:id change:file_ext', function( currModel ){\n\t this._generateUrls();\n\t });\n\t },\n\t\n\t // ........................................................................ common queries\n\t /** override to add urls */\n\t toJSON : function(){\n\t var json = Backbone.Model.prototype.toJSON.call( this );\n\t //console.warn( 'returning json?' );\n\t //return json;\n\t return _.extend( json, {\n\t urls : this.urls\n\t });\n\t },\n\t\n\t /** Is this dataset deleted or purged? */\n\t isDeletedOrPurged : function(){\n\t return ( this.get( 'deleted' ) || this.get( 'purged' ) );\n\t },\n\t\n\t /** Is this dataset in a 'ready' state; where 'Ready' states are states where no\n\t * processing (for the ds) is left to do on the server.\n\t */\n\t inReadyState : function(){\n\t var ready = _.contains( STATES.READY_STATES, this.get( 'state' ) );\n\t return ( this.isDeletedOrPurged() || ready );\n\t },\n\t\n\t /** Does this model already contain detailed data (as opposed to just summary level data)? */\n\t hasDetails : function(){\n\t // if it's inaccessible assume it has everything it needs\n\t if( !this.get( 'accessible' ) ){ return true; }\n\t return this.has( 'annotation' );\n\t },\n\t\n\t /** Convenience function to match dataset.has_data. */\n\t hasData : function(){\n\t return ( this.get( 'file_size' ) > 0 );\n\t },\n\t\n\t // ........................................................................ ajax\n\t fetch : function( options ){\n\t var dataset = this;\n\t return Backbone.Model.prototype.fetch.call( this, options )\n\t .always( function(){\n\t dataset._generateUrls();\n\t });\n\t },\n\t\n\t /** override to use actual Dates objects for create/update times */\n\t parse : function( response, options ){\n\t var parsed = Backbone.Model.prototype.parse.call( this, response, options );\n\t if( parsed.create_time ){\n\t parsed.create_time = new Date( parsed.create_time );\n\t }\n\t if( parsed.update_time ){\n\t parsed.update_time = new Date( parsed.update_time );\n\t }\n\t return parsed;\n\t },\n\t\n\t /** override to wait by default */\n\t save : function( attrs, options ){\n\t options = options || {};\n\t options.wait = _.isUndefined( options.wait ) ? true : options.wait;\n\t return Backbone.Model.prototype.save.call( this, attrs, options );\n\t },\n\t\n\t //NOTE: subclasses of DA's will need to implement url and urlRoot in order to have these work properly\n\t /** save this dataset, _Mark_ing it as deleted (just a flag) */\n\t 'delete' : function( options ){\n\t if( this.get( 'deleted' ) ){ return jQuery.when(); }\n\t return this.save( { deleted: true }, options );\n\t },\n\t /** save this dataset, _Mark_ing it as undeleted */\n\t undelete : function( options ){\n\t if( !this.get( 'deleted' ) || this.get( 'purged' ) ){ return jQuery.when(); }\n\t return this.save( { deleted: false }, options );\n\t },\n\t\n\t /** remove the file behind this dataset from the filesystem (if permitted) */\n\t purge : function _purge( options ){\n\t //TODO: use, override model.destroy, HDA.delete({ purge: true })\n\t if( this.get( 'purged' ) ){ return jQuery.when(); }\n\t options = options || {};\n\t options.url = this.urls.purge;\n\t\n\t //TODO: ideally this would be a DELETE call to the api\n\t // using purge async for now\n\t var hda = this,\n\t xhr = jQuery.ajax( options );\n\t xhr.done( function( message, status, responseObj ){\n\t hda.set({ deleted: true, purged: true });\n\t });\n\t xhr.fail( function( xhr, status, message ){\n\t // Exception messages are hidden within error page including: '...not allowed in this Galaxy instance.'\n\t // unbury and re-add to xhr\n\t var error = _l( \"Unable to purge dataset\" );\n\t var messageBuriedInUnfortunatelyFormattedError = ( 'Removal of datasets by users '\n\t + 'is not allowed in this Galaxy instance' );\n\t if( xhr.responseJSON && xhr.responseJSON.error ){\n\t error = xhr.responseJSON.error;\n\t } else if( xhr.responseText.indexOf( messageBuriedInUnfortunatelyFormattedError ) !== -1 ){\n\t error = messageBuriedInUnfortunatelyFormattedError;\n\t }\n\t xhr.responseText = error;\n\t hda.trigger( 'error', hda, xhr, options, _l( error ), { error: error } );\n\t });\n\t return xhr;\n\t },\n\t\n\t // ........................................................................ searching\n\t /** what attributes of an HDA will be used in a text search */\n\t searchAttributes : [\n\t 'name', 'file_ext', 'genome_build', 'misc_blurb', 'misc_info', 'annotation', 'tags'\n\t ],\n\t\n\t /** our attr keys don't often match the labels we display to the user - so, when using\n\t * attribute specifiers ('name=\"bler\"') in a term, allow passing in aliases for the\n\t * following attr keys.\n\t */\n\t searchAliases : {\n\t title : 'name',\n\t format : 'file_ext',\n\t database : 'genome_build',\n\t blurb : 'misc_blurb',\n\t description : 'misc_blurb',\n\t info : 'misc_info',\n\t tag : 'tags'\n\t },\n\t\n\t // ........................................................................ misc\n\t /** String representation */\n\t toString : function(){\n\t var nameAndId = this.get( 'id' ) || '';\n\t if( this.get( 'name' ) ){\n\t nameAndId = '\"' + this.get( 'name' ) + '\",' + nameAndId;\n\t }\n\t return 'Dataset(' + nameAndId + ')';\n\t }\n\t}));\n\t\n\t\n\t//==============================================================================\n\t/** @class Backbone collection for dataset associations.\n\t */\n\tvar DatasetAssociationCollection = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).extend(\n\t/** @lends HistoryContents.prototype */{\n\t _logNamespace : logNamespace,\n\t\n\t model : DatasetAssociation,\n\t\n\t /** root api url */\n\t urlRoot : Galaxy.root + 'api/datasets',\n\t\n\t /** url fn */\n\t url : function(){\n\t return this.urlRoot;\n\t },\n\t\n\t // ........................................................................ common queries\n\t /** Get the ids of every item in this collection\n\t * @returns array of encoded ids\n\t */\n\t ids : function(){\n\t return this.map( function( item ){ return item.get('id'); });\n\t },\n\t\n\t /** Get contents that are not ready\n\t * @returns array of content models\n\t */\n\t notReady : function(){\n\t return this.filter( function( content ){\n\t return !content.inReadyState();\n\t });\n\t },\n\t\n\t /** return true if any datasets don't have details */\n\t haveDetails : function(){\n\t return this.all( function( dataset ){ return dataset.hasDetails(); });\n\t },\n\t\n\t // ........................................................................ ajax\n\t /** using a queue, perform ajaxFn on each of the models in this collection */\n\t ajaxQueue : function( ajaxFn, options ){\n\t var deferred = jQuery.Deferred(),\n\t startingLength = this.length,\n\t responses = [];\n\t\n\t if( !startingLength ){\n\t deferred.resolve([]);\n\t return deferred;\n\t }\n\t\n\t // use reverse order (stylistic choice)\n\t var ajaxFns = this.chain().reverse().map( function( dataset, i ){\n\t return function(){\n\t var xhr = ajaxFn.call( dataset, options );\n\t // if successful, notify using the deferred to allow tracking progress\n\t xhr.done( function( response ){\n\t deferred.notify({ curr: i, total: startingLength, response: response, model: dataset });\n\t });\n\t // (regardless of previous error or success) if not last ajax call, shift and call the next\n\t // if last fn, resolve deferred\n\t xhr.always( function( response ){\n\t responses.push( response );\n\t if( ajaxFns.length ){\n\t ajaxFns.shift()();\n\t } else {\n\t deferred.resolve( responses );\n\t }\n\t });\n\t };\n\t }).value();\n\t // start the queue\n\t ajaxFns.shift()();\n\t\n\t return deferred;\n\t },\n\t\n\t // ........................................................................ sorting/filtering\n\t /** return a new collection of datasets whose attributes contain the substring matchesWhat */\n\t matches : function( matchesWhat ){\n\t return this.filter( function( dataset ){\n\t return dataset.matches( matchesWhat );\n\t });\n\t },\n\t\n\t /** String representation. */\n\t toString : function(){\n\t return ([ 'DatasetAssociationCollection(', this.length, ')' ].join( '' ));\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\t return {\n\t DatasetAssociation : DatasetAssociation,\n\t DatasetAssociationCollection : DatasetAssociationCollection\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(2), __webpack_require__(1)))\n\n/***/ },\n/* 71 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(_) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(31),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( DATASET_LI, BASE_MVC, _l ){\n\t\n\t'use strict';\n\t\n\t//==============================================================================\n\tvar _super = DATASET_LI.DatasetListItemView;\n\t/** @class Read only view for HistoryDatasetAssociation.\n\t * Since there are no controls on the HDAView to hide the dataset,\n\t * the primary thing this class does (currently) is override templates\n\t * to render the HID.\n\t */\n\tvar HDAListItemView = _super.extend(\n\t/** @lends HDAListItemView.prototype */{\n\t\n\t className : _super.prototype.className + \" history-content\",\n\t\n\t initialize : function( attributes, options ){\n\t _super.prototype.initialize.call( this, attributes, options );\n\t },\n\t\n\t // ......................................................................... misc\n\t /** String representation */\n\t toString : function(){\n\t var modelString = ( this.model )?( this.model + '' ):( '(no model)' );\n\t return 'HDAListItemView(' + modelString + ')';\n\t }\n\t});\n\t\n\t// ............................................................................ TEMPLATES\n\t/** underscore templates */\n\tHDAListItemView.prototype.templates = (function(){\n\t\n\t var titleBarTemplate = BASE_MVC.wrapTemplate([\n\t // adding the hid display to the title\n\t '
',\n\t '',\n\t '
',\n\t //TODO: remove whitespace and use margin-right\n\t '<%- dataset.hid %> ',\n\t '<%- dataset.name %>',\n\t '
',\n\t '
'\n\t ], 'dataset' );\n\t\n\t var warnings = _.extend( {}, _super.prototype.templates.warnings, {\n\t hidden : BASE_MVC.wrapTemplate([\n\t // add a warning when hidden\n\t '<% if( !dataset.visible ){ %>',\n\t '
',\n\t _l( 'This dataset has been hidden' ),\n\t '
',\n\t '<% } %>'\n\t ], 'dataset' )\n\t });\n\t\n\t return _.extend( {}, _super.prototype.templates, {\n\t titleBar : titleBarTemplate,\n\t warnings : warnings\n\t });\n\t}());\n\t\n\t\n\t\n\t//==============================================================================\n\t return {\n\t HDAListItemView : HDAListItemView\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)))\n\n/***/ },\n/* 72 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(_) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(70),\n\t __webpack_require__(74),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( DATASET, HISTORY_CONTENT, BASE_MVC, _l ){\n\t'use strict';\n\t\n\t//==============================================================================\n\tvar _super = DATASET.DatasetAssociation,\n\t hcontentMixin = HISTORY_CONTENT.HistoryContentMixin;\n\t/** @class (HDA) model for a Galaxy dataset contained in and related to a history.\n\t */\n\tvar HistoryDatasetAssociation = _super.extend( BASE_MVC.mixin( hcontentMixin,\n\t/** @lends HistoryDatasetAssociation.prototype */{\n\t\n\t /** default attributes for a model */\n\t defaults : _.extend( {}, _super.prototype.defaults, hcontentMixin.defaults, {\n\t history_content_type: 'dataset',\n\t model_class : 'HistoryDatasetAssociation'\n\t }),\n\t}));\n\t\n\t//==============================================================================\n\t return {\n\t HistoryDatasetAssociation : HistoryDatasetAssociation\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)))\n\n/***/ },\n/* 73 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(_) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(12),\n\t __webpack_require__(28),\n\t __webpack_require__(68),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( STATES, DC_LI, DC_VIEW, BASE_MVC, _l ){\n\t\n\t'use strict';\n\t\n\t//==============================================================================\n\tvar _super = DC_LI.DCListItemView;\n\t/** @class Read only view for HistoryDatasetCollectionAssociation (a dataset collection inside a history).\n\t */\n\tvar HDCAListItemView = _super.extend(\n\t/** @lends HDCAListItemView.prototype */{\n\t\n\t className : _super.prototype.className + \" history-content\",\n\t\n\t /** event listeners */\n\t _setUpListeners : function(){\n\t _super.prototype._setUpListeners.call( this );\n\t\n\t this.listenTo( this.model, {\n\t 'change:populated change:visible' : function( model, options ){ this.render(); },\n\t });\n\t },\n\t\n\t /** Override to provide the proper collections panels as the foldout */\n\t _getFoldoutPanelClass : function(){\n\t switch( this.model.get( 'collection_type' ) ){\n\t case 'list':\n\t return DC_VIEW.ListCollectionView;\n\t case 'paired':\n\t return DC_VIEW.PairCollectionView;\n\t case 'list:paired':\n\t return DC_VIEW.ListOfPairsCollectionView;\n\t case 'list:list':\n\t return DC_VIEW.ListOfListsCollectionView;\n\t }\n\t throw new TypeError( 'Uknown collection_type: ' + this.model.get( 'collection_type' ) );\n\t },\n\t\n\t /** In this override, add the state as a class for use with state-based CSS */\n\t _swapNewRender : function( $newRender ){\n\t _super.prototype._swapNewRender.call( this, $newRender );\n\t //TODO: model currently has no state\n\t var state = !this.model.get( 'populated' ) ? STATES.RUNNING : STATES.OK;\n\t //if( this.model.has( 'state' ) ){\n\t this.$el.addClass( 'state-' + state );\n\t //}\n\t return this.$el;\n\t },\n\t\n\t // ......................................................................... misc\n\t /** String representation */\n\t toString : function(){\n\t var modelString = ( this.model )?( this.model + '' ):( '(no model)' );\n\t return 'HDCAListItemView(' + modelString + ')';\n\t }\n\t});\n\t\n\t/** underscore templates */\n\tHDCAListItemView.prototype.templates = (function(){\n\t\n\t var warnings = _.extend( {}, _super.prototype.templates.warnings, {\n\t hidden : BASE_MVC.wrapTemplate([\n\t // add a warning when hidden\n\t '<% if( !collection.visible ){ %>',\n\t '
',\n\t _l( 'This collection has been hidden' ),\n\t '
',\n\t '<% } %>'\n\t ], 'collection' )\n\t });\n\t\n\t// could steal this from hda-base (or use mixed content)\n\t var titleBarTemplate = BASE_MVC.wrapTemplate([\n\t // adding the hid display to the title\n\t '
',\n\t '',\n\t '
',\n\t //TODO: remove whitespace and use margin-right\n\t '<%- collection.hid %> ',\n\t '<%- collection.name %>',\n\t '
',\n\t '',\n\t '
'\n\t ], 'collection' );\n\t\n\t return _.extend( {}, _super.prototype.templates, {\n\t warnings : warnings,\n\t titleBar : titleBarTemplate\n\t });\n\t}());\n\t\n\t\n\t//==============================================================================\n\t return {\n\t HDCAListItemView : HDCAListItemView\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)))\n\n/***/ },\n/* 74 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(jQuery) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(12),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( STATES, BASE_MVC, _l ){\n\t'use strict';\n\t\n\t//==============================================================================\n\t/** @class Mixin for HistoryContents content (HDAs, HDCAs).\n\t */\n\tvar HistoryContentMixin = {\n\t\n\t /** default attributes for a model */\n\t defaults : {\n\t /** parent (containing) history */\n\t history_id : null,\n\t /** some content_type (HistoryContents can contain mixed model classes) */\n\t history_content_type: null,\n\t /** indicating when/what order the content was generated in the context of the history */\n\t hid : null,\n\t /** whether the user wants the content shown (visible) */\n\t visible : true\n\t },\n\t\n\t // ........................................................................ mixed content element\n\t // In order to be part of a MIXED bbone collection, we can't rely on the id\n\t // (which may collide btwn models of different classes)\n\t // Instead, use type_id which prefixes the history_content_type so the bbone collection can differentiate\n\t idAttribute : 'type_id',\n\t\n\t // ........................................................................ common queries\n\t /** the more common alias of visible */\n\t hidden : function(){\n\t return !this.get( 'visible' );\n\t },\n\t\n\t//TODO: remove\n\t /** based on includeDeleted, includeHidden (gen. from the container control),\n\t * would this ds show in the list of ds's?\n\t * @param {Boolean} includeDeleted are we showing deleted hdas?\n\t * @param {Boolean} includeHidden are we showing hidden hdas?\n\t */\n\t isVisible : function( includeDeleted, includeHidden ){\n\t var isVisible = true;\n\t if( ( !includeDeleted )\n\t && ( this.get( 'deleted' ) || this.get( 'purged' ) ) ){\n\t isVisible = false;\n\t }\n\t if( ( !includeHidden )\n\t && ( !this.get( 'visible' ) ) ){\n\t isVisible = false;\n\t }\n\t return isVisible;\n\t },\n\t\n\t // ........................................................................ ajax\n\t //TODO?: these are probably better done on the leaf classes\n\t /** history content goes through the 'api/histories' API */\n\t urlRoot: Galaxy.root + 'api/histories/',\n\t\n\t /** full url spec. for this content */\n\t url : function(){\n\t var url = this.urlRoot + this.get( 'history_id' ) + '/contents/'\n\t + this.get('history_content_type') + 's/' + this.get( 'id' );\n\t return url;\n\t },\n\t\n\t /** save this content as not visible */\n\t hide : function( options ){\n\t if( !this.get( 'visible' ) ){ return jQuery.when(); }\n\t return this.save( { visible: false }, options );\n\t },\n\t /** save this content as visible */\n\t unhide : function( options ){\n\t if( this.get( 'visible' ) ){ return jQuery.when(); }\n\t return this.save( { visible: true }, options );\n\t },\n\t\n\t // ........................................................................ misc\n\t toString : function(){\n\t return ([ this.get( 'type_id' ), this.get( 'hid' ), this.get( 'name' ) ].join(':'));\n\t }\n\t};\n\t\n\t\n\t//==============================================================================\n\t return {\n\t HistoryContentMixin : HistoryContentMixin\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))\n\n/***/ },\n/* 75 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, jQuery, _, $) {\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(39),\n\t __webpack_require__(40),\n\t __webpack_require__(67),\n\t __webpack_require__(4),\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( HISTORY_CONTENTS, HISTORY_PREFS, CONTROLLED_FETCH_COLLECTION, UTILS, BASE_MVC, _l ){\n\t'use strict';\n\t\n\t//==============================================================================\n\t/** @class Model for a Galaxy history resource - both a record of user\n\t * tool use and a collection of the datasets those tools produced.\n\t * @name History\n\t * @augments Backbone.Model\n\t */\n\tvar History = Backbone.Model\n\t .extend( BASE_MVC.LoggableMixin )\n\t .extend( BASE_MVC.mixin( BASE_MVC.SearchableModelMixin, /** @lends History.prototype */{\n\t _logNamespace : 'history',\n\t\n\t /** ms between fetches when checking running jobs/datasets for updates */\n\t UPDATE_DELAY : 4000,\n\t\n\t // values from api (may need more)\n\t defaults : {\n\t model_class : 'History',\n\t id : null,\n\t name : 'Unnamed History',\n\t state : 'new',\n\t\n\t deleted : false,\n\t contents_active : {},\n\t contents_states : {},\n\t },\n\t\n\t urlRoot: Galaxy.root + 'api/histories',\n\t\n\t contentsClass : HISTORY_CONTENTS.HistoryContents,\n\t\n\t /** What model fields to search with */\n\t searchAttributes : [\n\t 'name', 'annotation', 'tags'\n\t ],\n\t\n\t /** Adding title and singular tag */\n\t searchAliases : {\n\t title : 'name',\n\t tag : 'tags'\n\t },\n\t\n\t // ........................................................................ set up/tear down\n\t /** Set up the model\n\t * @param {Object} historyJSON model data for this History\n\t * @param {Object} options any extra settings including logger\n\t */\n\t initialize : function( historyJSON, options ){\n\t options = options || {};\n\t this.logger = options.logger || null;\n\t this.log( this + \".initialize:\", historyJSON, options );\n\t\n\t /** HistoryContents collection of the HDAs contained in this history. */\n\t this.contents = new this.contentsClass( [], {\n\t history : this,\n\t historyId : this.get( 'id' ),\n\t order : options.order,\n\t });\n\t\n\t this._setUpListeners();\n\t this._setUpCollectionListeners();\n\t\n\t /** cached timeout id for the dataset updater */\n\t this.updateTimeoutId = null;\n\t },\n\t\n\t /** set up any event listeners for this history including those to the contained HDAs\n\t * events: error:contents if an error occurred with the contents collection\n\t */\n\t _setUpListeners : function(){\n\t // if the model's id changes ('current' or null -> an actual id), update the contents history_id\n\t return this.on({\n\t 'error' : function( model, xhr, options, msg, details ){\n\t this.clearUpdateTimeout();\n\t },\n\t 'change:id' : function( model, newId ){\n\t if( this.contents ){\n\t this.contents.historyId = newId;\n\t }\n\t },\n\t });\n\t },\n\t\n\t /** event handlers for the contents submodels */\n\t _setUpCollectionListeners : function(){\n\t if( !this.contents ){ return this; }\n\t // bubble up errors\n\t return this.listenTo( this.contents, {\n\t 'error' : function(){\n\t this.trigger.apply( this, jQuery.makeArray( arguments ) );\n\t },\n\t });\n\t },\n\t\n\t // ........................................................................ derived attributes\n\t /** */\n\t contentsShown : function(){\n\t var contentsActive = this.get( 'contents_active' );\n\t var shown = contentsActive.active || 0;\n\t shown += this.contents.includeDeleted? contentsActive.deleted : 0;\n\t shown += this.contents.includeHidden? contentsActive.hidden : 0;\n\t return shown;\n\t },\n\t\n\t /** convert size in bytes to a more human readable version */\n\t nice_size : function(){\n\t var size = this.get( 'size' );\n\t return size? UTILS.bytesToString( size, true, 2 ) : _l( '(empty)' );\n\t },\n\t\n\t /** override to add nice_size */\n\t toJSON : function(){\n\t return _.extend( Backbone.Model.prototype.toJSON.call( this ), {\n\t nice_size : this.nice_size()\n\t });\n\t },\n\t\n\t /** override to allow getting nice_size */\n\t get : function( key ){\n\t if( key === 'nice_size' ){\n\t return this.nice_size();\n\t }\n\t return Backbone.Model.prototype.get.apply( this, arguments );\n\t },\n\t\n\t // ........................................................................ common queries\n\t /** T/F is this history owned by the current user (Galaxy.user)\n\t * Note: that this will return false for an anon user even if the history is theirs.\n\t */\n\t ownedByCurrUser : function(){\n\t // no currUser\n\t if( !Galaxy || !Galaxy.user ){\n\t return false;\n\t }\n\t // user is anon or history isn't owned\n\t if( Galaxy.user.isAnonymous() || Galaxy.user.id !== this.get( 'user_id' ) ){\n\t return false;\n\t }\n\t return true;\n\t },\n\t\n\t /** Return the number of running jobs assoc with this history (note: unknown === 0) */\n\t numOfUnfinishedJobs : function(){\n\t var unfinishedJobIds = this.get( 'non_ready_jobs' );\n\t return unfinishedJobIds? unfinishedJobIds.length : 0;\n\t },\n\t\n\t /** Return the number of running hda/hdcas in this history (note: unknown === 0) */\n\t numOfUnfinishedShownContents : function(){\n\t return this.contents.runningAndActive().length || 0;\n\t },\n\t\n\t // ........................................................................ updates\n\t _fetchContentRelatedAttributes : function(){\n\t var contentRelatedAttrs = [ 'size', 'non_ready_jobs', 'contents_active', 'hid_counter' ];\n\t return this.fetch({ data : $.param({ keys : contentRelatedAttrs.join( ',' ) }) });\n\t },\n\t\n\t /** check for any changes since the last time we updated (or fetch all if ) */\n\t refresh : function( options ){\n\t // console.log( this + '.refresh' );\n\t options = options || {};\n\t var self = this;\n\t\n\t // note if there was no previous update time, all summary contents will be fetched\n\t var lastUpdateTime = self.lastUpdateTime;\n\t // if we don't flip this, then a fully-fetched list will not be re-checked via fetch\n\t this.contents.allFetched = false;\n\t var fetchFn = self.contents.currentPage !== 0\n\t ? function(){ return self.contents.fetchPage( 0 ); }\n\t : function(){ return self.contents.fetchUpdated( lastUpdateTime ); };\n\t // note: if there was no previous update time, all summary contents will be fetched\n\t return fetchFn()\n\t .done( function( response, status, xhr ){\n\t var serverResponseDatetime;\n\t try {\n\t serverResponseDatetime = new Date( xhr.getResponseHeader( 'Date' ) );\n\t } catch( err ){}\n\t self.lastUpdateTime = serverResponseDatetime || new Date();\n\t self.checkForUpdates( options );\n\t });\n\t },\n\t\n\t /** continuously fetch updated contents every UPDATE_DELAY ms if this history's datasets or jobs are unfinished */\n\t checkForUpdates : function( options ){\n\t // console.log( this + '.checkForUpdates' );\n\t options = options || {};\n\t var delay = this.UPDATE_DELAY;\n\t var self = this;\n\t if( !self.id ){ return; }\n\t\n\t function _delayThenUpdate(){\n\t // prevent buildup of updater timeouts by clearing previous if any, then set new and cache id\n\t self.clearUpdateTimeout();\n\t self.updateTimeoutId = setTimeout( function(){\n\t self.refresh( options );\n\t }, delay );\n\t }\n\t\n\t // if there are still datasets in the non-ready state, recurse into this function with the new time\n\t var nonReadyContentCount = this.numOfUnfinishedShownContents();\n\t // console.log( 'nonReadyContentCount:', nonReadyContentCount );\n\t if( nonReadyContentCount > 0 ){\n\t _delayThenUpdate();\n\t\n\t } else {\n\t // no datasets are running, but currently runnning jobs may still produce new datasets\n\t // see if the history has any running jobs and continue to update if so\n\t // (also update the size for the user in either case)\n\t self._fetchContentRelatedAttributes()\n\t .done( function( historyData ){\n\t // console.log( 'non_ready_jobs:', historyData.non_ready_jobs );\n\t if( self.numOfUnfinishedJobs() > 0 ){\n\t _delayThenUpdate();\n\t\n\t } else {\n\t // otherwise, let listeners know that all updates have stopped\n\t self.trigger( 'ready' );\n\t }\n\t });\n\t }\n\t },\n\t\n\t /** clear the timeout and the cached timeout id */\n\t clearUpdateTimeout : function(){\n\t if( this.updateTimeoutId ){\n\t clearTimeout( this.updateTimeoutId );\n\t this.updateTimeoutId = null;\n\t }\n\t },\n\t\n\t // ........................................................................ ajax\n\t /** override to use actual Dates objects for create/update times */\n\t parse : function( response, options ){\n\t var parsed = Backbone.Model.prototype.parse.call( this, response, options );\n\t if( parsed.create_time ){\n\t parsed.create_time = new Date( parsed.create_time );\n\t }\n\t if( parsed.update_time ){\n\t parsed.update_time = new Date( parsed.update_time );\n\t }\n\t return parsed;\n\t },\n\t\n\t /** fetch this histories data (using options) then it's contents (using contentsOptions) */\n\t fetchWithContents : function( options, contentsOptions ){\n\t options = options || {};\n\t var self = this;\n\t\n\t // console.log( this + '.fetchWithContents' );\n\t // TODO: push down to a base class\n\t options.view = 'dev-detailed';\n\t\n\t // fetch history then use history data to fetch (paginated) contents\n\t return this.fetch( options ).then( function getContents( history ){\n\t self.contents.history = self;\n\t self.contents.setHistoryId( history.id );\n\t return self.fetchContents( contentsOptions );\n\t });\n\t },\n\t\n\t /** fetch this histories contents, adjusting options based on the stored history preferences */\n\t fetchContents : function( options ){\n\t options = options || {};\n\t var self = this;\n\t\n\t // we're updating, reset the update time\n\t self.lastUpdateTime = new Date();\n\t return self.contents.fetchCurrentPage( options );\n\t },\n\t\n\t /** save this history, _Mark_ing it as deleted (just a flag) */\n\t _delete : function( options ){\n\t if( this.get( 'deleted' ) ){ return jQuery.when(); }\n\t return this.save( { deleted: true }, options );\n\t },\n\t /** purge this history, _Mark_ing it as purged and removing all dataset data from the server */\n\t purge : function( options ){\n\t if( this.get( 'purged' ) ){ return jQuery.when(); }\n\t return this.save( { deleted: true, purged: true }, options );\n\t },\n\t /** save this history, _Mark_ing it as undeleted */\n\t undelete : function( options ){\n\t if( !this.get( 'deleted' ) ){ return jQuery.when(); }\n\t return this.save( { deleted: false }, options );\n\t },\n\t\n\t /** Make a copy of this history on the server\n\t * @param {Boolean} current if true, set the copy as the new current history (default: true)\n\t * @param {String} name name of new history (default: none - server sets to: Copy of )\n\t * @fires copied passed this history and the response JSON from the copy\n\t * @returns {xhr}\n\t */\n\t copy : function( current, name, allDatasets ){\n\t current = ( current !== undefined )?( current ):( true );\n\t if( !this.id ){\n\t throw new Error( 'You must set the history ID before copying it.' );\n\t }\n\t\n\t var postData = { history_id : this.id };\n\t if( current ){\n\t postData.current = true;\n\t }\n\t if( name ){\n\t postData.name = name;\n\t }\n\t if( !allDatasets ){\n\t postData.all_datasets = false;\n\t }\n\t postData.view = 'dev-detailed';\n\t\n\t var history = this;\n\t var copy = jQuery.post( this.urlRoot, postData );\n\t // if current - queue to setAsCurrent before firing 'copied'\n\t if( current ){\n\t return copy.then( function( response ){\n\t var newHistory = new History( response );\n\t return newHistory.setAsCurrent()\n\t .done( function(){\n\t history.trigger( 'copied', history, response );\n\t });\n\t });\n\t }\n\t return copy.done( function( response ){\n\t history.trigger( 'copied', history, response );\n\t });\n\t },\n\t\n\t setAsCurrent : function(){\n\t var history = this,\n\t xhr = jQuery.getJSON( Galaxy.root + 'history/set_as_current?id=' + this.id );\n\t\n\t xhr.done( function(){\n\t history.trigger( 'set-as-current', history );\n\t });\n\t return xhr;\n\t },\n\t\n\t // ........................................................................ misc\n\t toString : function(){\n\t return 'History(' + this.get( 'id' ) + ',' + this.get( 'name' ) + ')';\n\t }\n\t}));\n\t\n\t\n\t//==============================================================================\n\tvar _collectionSuper = CONTROLLED_FETCH_COLLECTION.InfinitelyScrollingCollection;\n\t/** @class A collection of histories (per user)\n\t * that maintains the current history as the first in the collection.\n\t * New or copied histories become the current history.\n\t */\n\tvar HistoryCollection = _collectionSuper.extend( BASE_MVC.LoggableMixin ).extend({\n\t _logNamespace : 'history',\n\t\n\t model : History,\n\t /** @type {String} initial order used by collection */\n\t order : 'update_time',\n\t /** @type {Number} limit used for the first fetch (or a reset) */\n\t limitOnFirstFetch : 10,\n\t /** @type {Number} limit used for each subsequent fetch */\n\t limitPerFetch : 10,\n\t\n\t initialize : function( models, options ){\n\t options = options || {};\n\t this.log( 'HistoryCollection.initialize', models, options );\n\t _collectionSuper.prototype.initialize.call( this, models, options );\n\t\n\t /** @type {boolean} should deleted histories be included */\n\t this.includeDeleted = options.includeDeleted || false;\n\t\n\t /** @type {String} encoded id of the history that's current */\n\t this.currentHistoryId = options.currentHistoryId;\n\t\n\t this.setUpListeners();\n\t // note: models are sent to reset *after* this fn ends; up to this point\n\t // the collection *is empty*\n\t },\n\t\n\t urlRoot : Galaxy.root + 'api/histories',\n\t url : function(){ return this.urlRoot; },\n\t\n\t /** set up reflexive event handlers */\n\t setUpListeners : function setUpListeners(){\n\t return this.on({\n\t // when a history is deleted, remove it from the collection (if optionally set to do so)\n\t 'change:deleted' : function( history ){\n\t // TODO: this becomes complicated when more filters are used\n\t this.debug( 'change:deleted', this.includeDeleted, history.get( 'deleted' ) );\n\t if( !this.includeDeleted && history.get( 'deleted' ) ){\n\t this.remove( history );\n\t }\n\t },\n\t // listen for a history copy, setting it to current\n\t 'copied' : function( original, newData ){\n\t this.setCurrent( new History( newData, [] ) );\n\t },\n\t // when a history is made current, track the id in the collection\n\t 'set-as-current' : function( history ){\n\t var oldCurrentId = this.currentHistoryId;\n\t this.trigger( 'no-longer-current', oldCurrentId );\n\t this.currentHistoryId = history.id;\n\t }\n\t });\n\t },\n\t\n\t /** override to change view */\n\t _buildFetchData : function( options ){\n\t return _.extend( _collectionSuper.prototype._buildFetchData.call( this, options ), {\n\t view : 'dev-detailed'\n\t });\n\t },\n\t\n\t /** override to filter out deleted and purged */\n\t _buildFetchFilters : function( options ){\n\t var superFilters = _collectionSuper.prototype._buildFetchFilters.call( this, options ) || {};\n\t var filters = {};\n\t if( !this.includeDeleted ){\n\t filters.deleted = false;\n\t filters.purged = false;\n\t } else {\n\t // force API to return both deleted and non\n\t //TODO: when the API is updated, remove this\n\t filters.deleted = null;\n\t }\n\t return _.defaults( superFilters, filters );\n\t },\n\t\n\t /** override to fetch current as well (as it may be outside the first 10, etc.) */\n\t fetchFirst : function( options ){\n\t var self = this;\n\t // TODO: batch?\n\t var xhr = $.when();\n\t if( this.currentHistoryId ){\n\t xhr = _collectionSuper.prototype.fetchFirst.call( self, {\n\t silent: true,\n\t limit : 1,\n\t filters: {\n\t // without these a deleted current history will return [] here and block the other xhr\n\t 'purged' : '',\n\t 'deleted' : '',\n\t 'encoded_id-in' : this.currentHistoryId,\n\t }\n\t });\n\t }\n\t return xhr.then( function(){\n\t options = options || {};\n\t options.offset = 0;\n\t return self.fetchMore( options );\n\t });\n\t },\n\t\n\t /** @type {Object} map of collection available sorting orders containing comparator fns */\n\t comparators : _.extend( _.clone( _collectionSuper.prototype.comparators ), {\n\t 'name' : BASE_MVC.buildComparator( 'name', { ascending: true }),\n\t 'name-dsc' : BASE_MVC.buildComparator( 'name', { ascending: false }),\n\t 'size' : BASE_MVC.buildComparator( 'size', { ascending: false }),\n\t 'size-asc' : BASE_MVC.buildComparator( 'size', { ascending: true }),\n\t }),\n\t\n\t /** override to always have the current history first */\n\t sort : function( options ){\n\t options = options || {};\n\t var silent = options.silent;\n\t var currentHistory = this.remove( this.get( this.currentHistoryId ) );\n\t _collectionSuper.prototype.sort.call( this, _.defaults({ silent: true }, options ) );\n\t this.unshift( currentHistory, { silent: true });\n\t if( !silent ){\n\t this.trigger( 'sort', this, options );\n\t }\n\t return this;\n\t },\n\t\n\t /** create a new history and by default set it to be the current history */\n\t create : function create( data, hdas, historyOptions, xhrOptions ){\n\t //TODO: .create is actually a collection function that's overridden here\n\t var collection = this,\n\t xhr = jQuery.getJSON( Galaxy.root + 'history/create_new_current' );\n\t return xhr.done( function( newData ){\n\t collection.setCurrent( new History( newData, [], historyOptions || {} ) );\n\t });\n\t },\n\t\n\t /** set the current history to the given history, placing it first in the collection.\n\t * Pass standard bbone options for use in unshift.\n\t * @triggers new-current passed history and this collection\n\t */\n\t setCurrent : function( history, options ){\n\t options = options || {};\n\t // new histories go in the front\n\t this.unshift( history, options );\n\t this.currentHistoryId = history.get( 'id' );\n\t if( !options.silent ){\n\t this.trigger( 'new-current', history, this );\n\t }\n\t return this;\n\t },\n\t\n\t toString: function toString(){\n\t return 'HistoryCollection(' + this.length + ',current:' + this.currentHistoryId + ')';\n\t }\n\t});\n\t\n\t\n\t//==============================================================================\n\treturn {\n\t History : History,\n\t HistoryCollection : HistoryCollection\n\t};}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(1), __webpack_require__(2), __webpack_require__(1)))\n\n/***/ },\n/* 76 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _, $) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(41),\n\t __webpack_require__(123),\n\t __webpack_require__(6),\n\t __webpack_require__(5),\n\t __webpack_require__(85)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( LIST_ITEM, LoadingIndicator, BASE_MVC, _l ){\n\t\n\t'use strict';\n\t\n\tvar logNamespace = 'list';\n\t/* ============================================================================\n\tTODO:\n\t\n\t============================================================================ */\n\t/** @class View for a list/collection of models and the sub-views of those models.\n\t * Sub-views must (at least have the interface if not) inherit from ListItemView.\n\t * (For a list panel that also includes some 'container' model (History->HistoryContents)\n\t * use ModelWithListPanel)\n\t *\n\t * Allows for:\n\t * searching collection/sub-views\n\t * selecting/multi-selecting sub-views\n\t *\n\t * Currently used:\n\t * for dataset/dataset-choice\n\t * as superclass of ModelListPanel\n\t */\n\tvar ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend(/** @lends ListPanel.prototype */{\n\t _logNamespace : logNamespace,\n\t\n\t /** class to use for constructing the sub-views */\n\t viewClass : LIST_ITEM.ListItemView,\n\t /** class to used for constructing collection of sub-view models */\n\t collectionClass : Backbone.Collection,\n\t\n\t tagName : 'div',\n\t className : 'list-panel',\n\t\n\t /** (in ms) that jquery effects will use */\n\t fxSpeed : 'fast',\n\t\n\t /** string to display when the collection has no contents */\n\t emptyMsg : _l( 'This list is empty' ),\n\t /** displayed when no items match the search terms */\n\t noneFoundMsg : _l( 'No matching items found' ),\n\t /** string used for search placeholder */\n\t searchPlaceholder : _l( 'search' ),\n\t\n\t // ......................................................................... SET UP\n\t /** Set up the view, set up storage, bind listeners to HistoryContents events\n\t * @param {Object} attributes optional settings for the list\n\t */\n\t initialize : function( attributes, options ){\n\t attributes = attributes || {};\n\t // set the logger if requested\n\t if( attributes.logger ){\n\t this.logger = attributes.logger;\n\t }\n\t this.log( this + '.initialize:', attributes );\n\t\n\t // ---- instance vars\n\t /** how quickly should jquery fx run? */\n\t this.fxSpeed = _.has( attributes, 'fxSpeed' )?( attributes.fxSpeed ):( this.fxSpeed );\n\t\n\t /** filters for displaying subviews */\n\t this.filters = [];\n\t /** current search terms */\n\t this.searchFor = attributes.searchFor || '';\n\t\n\t /** loading indicator */\n\t // this.indicator = new LoadingIndicator( this.$el );\n\t\n\t /** currently showing selectors on items? */\n\t this.selecting = ( attributes.selecting !== undefined )? attributes.selecting : true;\n\t //this.selecting = false;\n\t\n\t /** cached selected item.model.ids to persist btwn renders */\n\t this.selected = attributes.selected || [];\n\t /** the last selected item.model.id */\n\t this.lastSelected = null;\n\t\n\t /** are sub-views draggable */\n\t this.dragItems = attributes.dragItems || false;\n\t\n\t /** list item view class (when passed models) */\n\t this.viewClass = attributes.viewClass || this.viewClass;\n\t\n\t /** list item views */\n\t this.views = [];\n\t /** list item models */\n\t this.collection = attributes.collection || this._createDefaultCollection();\n\t\n\t /** filter fns run over collection items to see if they should show in the list */\n\t this.filters = attributes.filters || [];\n\t\n\t /** override $scrollContainer fn via attributes - fn should return jq for elem to call scrollTo on */\n\t this.$scrollContainer = attributes.$scrollContainer || this.$scrollContainer;\n\t\n\t /** @type {String} generic title */\n\t this.title = attributes.title || '';\n\t /** @type {String} generic subtitle */\n\t this.subtitle = attributes.subtitle || '';\n\t\n\t this._setUpListeners();\n\t },\n\t\n\t // ------------------------------------------------------------------------ listeners\n\t /** create any event listeners for the list */\n\t _setUpListeners : function(){\n\t this.off();\n\t\n\t //TODO: move errorHandler down into list-view from history-view or\n\t // pass to global error handler (Galaxy)\n\t this.on({\n\t error: function( model, xhr, options, msg, details ){\n\t //this.errorHandler( model, xhr, options, msg, details );\n\t console.error( model, xhr, options, msg, details );\n\t },\n\t // show hide the loading indicator\n\t loading: function(){\n\t this._showLoadingIndicator( 'loading...', 40 );\n\t },\n\t 'loading-done': function(){\n\t this._hideLoadingIndicator( 40 );\n\t },\n\t });\n\t\n\t // throw the first render up as a diff namespace using once (for outside consumption)\n\t this.once( 'rendered', function(){\n\t this.trigger( 'rendered:initial', this );\n\t });\n\t\n\t this._setUpCollectionListeners();\n\t this._setUpViewListeners();\n\t return this;\n\t },\n\t\n\t /** create and return a collection for when none is initially passed */\n\t _createDefaultCollection : function(){\n\t // override\n\t return new this.collectionClass([]);\n\t },\n\t\n\t /** listening for collection events */\n\t _setUpCollectionListeners : function(){\n\t this.log( this + '._setUpCollectionListeners', this.collection );\n\t this.stopListening( this.collection );\n\t\n\t // bubble up error events\n\t this.listenTo( this.collection, {\n\t error : function( model, xhr, options, msg, details ){\n\t this.trigger( 'error', model, xhr, options, msg, details );\n\t },\n\t update : function( collection, options ){\n\t var changes = options.changes;\n\t // console.info( collection + ', update:', changes, '\\noptions:', options );\n\t // more than one: render everything\n\t if( options.renderAll || ( changes.added.length + changes.removed.length > 1 ) ){\n\t return this.renderItems();\n\t }\n\t // otherwise, let the single add/remove handlers do it\n\t if( changes.added.length === 1 ){\n\t return this.addItemView( _.first( changes.added ), collection, options );\n\t }\n\t if( changes.removed.length === 1 ){\n\t return this.removeItemView( _.first( changes.removed ), collection, options );\n\t }\n\t }\n\t });\n\t return this;\n\t },\n\t\n\t /** listening for sub-view events that bubble up with the 'view:' prefix */\n\t _setUpViewListeners : function(){\n\t this.log( this + '._setUpViewListeners' );\n\t\n\t // shift to select a range\n\t this.on({\n\t 'view:selected': function( view, ev ){\n\t if( ev && ev.shiftKey && this.lastSelected ){\n\t var lastSelectedView = this.viewFromModelId( this.lastSelected );\n\t if( lastSelectedView ){\n\t this.selectRange( view, lastSelectedView );\n\t }\n\t } else if( ev && ev.altKey && !this.selecting ){\n\t this.showSelectors();\n\t }\n\t this.selected.push( view.model.id );\n\t this.lastSelected = view.model.id;\n\t },\n\t\n\t 'view:de-selected': function( view, ev ){\n\t this.selected = _.without( this.selected, view.model.id );\n\t }\n\t });\n\t },\n\t\n\t // ------------------------------------------------------------------------ rendering\n\t /** Render this content, set up ui.\n\t * @param {Number or String} speed the speed of the render\n\t */\n\t render : function( speed ){\n\t this.log( this + '.render', speed );\n\t var $newRender = this._buildNewRender();\n\t this._setUpBehaviors( $newRender );\n\t this._queueNewRender( $newRender, speed );\n\t return this;\n\t },\n\t\n\t /** Build a temp div containing the new children for the view's $el. */\n\t _buildNewRender : function(){\n\t this.debug( this + '(ListPanel)._buildNewRender' );\n\t var $newRender = $( this.templates.el( {}, this ) );\n\t this._renderControls( $newRender );\n\t this._renderTitle( $newRender );\n\t this._renderSubtitle( $newRender );\n\t this._renderSearch( $newRender );\n\t this.renderItems( $newRender );\n\t return $newRender;\n\t },\n\t\n\t /** Build a temp div containing the new children for the view's $el. */\n\t _renderControls : function( $newRender ){\n\t this.debug( this + '(ListPanel)._renderControls' );\n\t var $controls = $( this.templates.controls( {}, this ) );\n\t $newRender.find( '.controls' ).replaceWith( $controls );\n\t return $controls;\n\t },\n\t\n\t /** return a jQuery object containing the title DOM */\n\t _renderTitle : function( $where ){\n\t //$where = $where || this.$el;\n\t //$where.find( '.title' ).replaceWith( ... )\n\t },\n\t\n\t /** return a jQuery object containing the subtitle DOM (if any) */\n\t _renderSubtitle : function( $where ){\n\t //$where = $where || this.$el;\n\t //$where.find( '.title' ).replaceWith( ... )\n\t },\n\t\n\t /** Fade out the old el, swap in the new contents, then fade in.\n\t * @param {Number or String} speed jq speed to use for rendering effects\n\t * @fires rendered when rendered\n\t */\n\t _queueNewRender : function( $newRender, speed ) {\n\t speed = ( speed === undefined )?( this.fxSpeed ):( speed );\n\t var panel = this;\n\t panel.log( '_queueNewRender:', $newRender, speed );\n\t\n\t $( panel ).queue( 'fx', [\n\t function( next ){\n\t panel.$el.fadeOut( speed, next );\n\t },\n\t function( next ){\n\t panel._swapNewRender( $newRender );\n\t next();\n\t },\n\t function( next ){\n\t panel.$el.fadeIn( speed, next );\n\t },\n\t function( next ){\n\t panel.trigger( 'rendered', panel );\n\t next();\n\t }\n\t ]);\n\t },\n\t\n\t /** empty out the current el, move the $newRender's children in */\n\t _swapNewRender : function( $newRender ){\n\t this.$el.empty().attr( 'class', this.className ).append( $newRender.children() );\n\t if( this.selecting ){ this.showSelectors( 0 ); }\n\t return this;\n\t },\n\t\n\t /** Set up any behaviors, handlers (ep. plugins) that need to be called when the entire view has been built but\n\t * not attached to the page yet.\n\t */\n\t _setUpBehaviors : function( $where ){\n\t $where = $where || this.$el;\n\t this.$controls( $where ).find('[title]').tooltip();\n\t // set up the pupup for actions available when multi selecting\n\t this._renderMultiselectActionMenu( $where );\n\t return this;\n\t },\n\t\n\t /** render a menu containing the actions available to sets of selected items */\n\t _renderMultiselectActionMenu : function( $where ){\n\t $where = $where || this.$el;\n\t var $menu = $where.find( '.list-action-menu' ),\n\t actions = this.multiselectActions();\n\t if( !actions.length ){\n\t return $menu.empty();\n\t }\n\t\n\t var $newMenu = $([\n\t '
',\n\t '',\n\t '
', '
',\n\t '
'\n\t ].join(''));\n\t var $actions = actions.map( function( action ){\n\t var html = [ '
'\n\t ]);\n\t\n\t return {\n\t el : elTemplate,\n\t controls : controlsTemplate\n\t };\n\t}());\n\t\n\t\n\t//=============================================================================\n\t/** View for a model that has a sub-collection (e.g. History, DatasetCollection)\n\t * Allows:\n\t * the model to be reset\n\t * auto assign panel.collection to panel.model[ panel.modelCollectionKey ]\n\t *\n\t */\n\tvar ModelListPanel = ListPanel.extend({\n\t\n\t /** key of attribute in model to assign to this.collection */\n\t modelCollectionKey : 'contents',\n\t\n\t initialize : function( attributes ){\n\t ListPanel.prototype.initialize.call( this, attributes );\n\t this.selecting = ( attributes.selecting !== undefined )? attributes.selecting : false;\n\t\n\t this.setModel( this.model, attributes );\n\t },\n\t\n\t /** release/free/shutdown old models and set up panel for new models\n\t * @fires new-model with the panel as parameter\n\t */\n\t setModel : function( model, attributes ){\n\t attributes = attributes || {};\n\t this.debug( this + '.setModel:', model, attributes );\n\t\n\t this.freeModel();\n\t this.freeViews();\n\t\n\t if( model ){\n\t var oldModelId = this.model? this.model.get( 'id' ): null;\n\t\n\t // set up the new model with user, logger, storage, events\n\t this.model = model;\n\t if( this.logger ){\n\t this.model.logger = this.logger;\n\t }\n\t this._setUpModelListeners();\n\t\n\t //TODO: relation btwn model, collection becoming tangled here\n\t // free the collection, and assign the new collection to either\n\t // the model[ modelCollectionKey ], attributes.collection, or an empty vanilla collection\n\t this.stopListening( this.collection );\n\t this.collection = this.model[ this.modelCollectionKey ]\n\t || attributes.collection\n\t || this._createDefaultCollection();\n\t this._setUpCollectionListeners();\n\t\n\t if( oldModelId && model.get( 'id' ) !== oldModelId ){\n\t this.trigger( 'new-model', this );\n\t }\n\t }\n\t return this;\n\t },\n\t\n\t /** free the current model and all listeners for it, free any views for the model */\n\t freeModel : function(){\n\t // stop/release the previous model, and clear cache to sub-views\n\t if( this.model ){\n\t this.stopListening( this.model );\n\t //TODO: see base-mvc\n\t //this.model.free();\n\t //this.model = null;\n\t }\n\t return this;\n\t },\n\t\n\t // ------------------------------------------------------------------------ listening\n\t /** listening for model events */\n\t _setUpModelListeners : function(){\n\t // override\n\t this.log( this + '._setUpModelListeners', this.model );\n\t // bounce model errors up to the panel\n\t this.listenTo( this.model, 'error', function(){\n\t var args = Array.prototype.slice.call( arguments, 0 );\n\t //args.unshift( 'model:error' );\n\t args.unshift( 'error' );\n\t this.trigger.apply( this, args );\n\t }, this );\n\t\n\t // debugging\n\t if( this.logger ){\n\t this.listenTo( this.model, 'all', function( event ){\n\t this.info( this + '(model)', event, arguments );\n\t });\n\t }\n\t return this;\n\t },\n\t\n\t /** Build a temp div containing the new children for the view's $el.\n\t */\n\t _renderControls : function( $newRender ){\n\t this.debug( this + '(ModelListPanel)._renderControls' );\n\t var json = this.model? this.model.toJSON() : {},\n\t $controls = $( this.templates.controls( json, this ) );\n\t $newRender.find( '.controls' ).replaceWith( $controls );\n\t return $controls;\n\t },\n\t\n\t // ------------------------------------------------------------------------ misc\n\t /** Return a string rep of the panel */\n\t toString : function(){\n\t return 'ModelListPanel(' + this.model + ')';\n\t }\n\t});\n\t\n\t// ............................................................................ TEMPLATES\n\t/** underscore templates */\n\tModelListPanel.prototype.templates = (function(){\n\t\n\t var controlsTemplate = BASE_MVC.wrapTemplate([\n\t '
',\n\t '
',\n\t //TODO: this is really the only difference - consider factoring titlebar out\n\t '
<%- model.name %>
',\n\t '
',\n\t '
<%- view.subtitle %>
',\n\t '',\n\t '',\n\t\n\t '
',\n\t '',\n\t '
',\n\t\n\t '
',\n\t '
',\n\t '',\n\t '',\n\t '
',\n\t '
',\n\t '
',\n\t '
',\n\t '
'\n\t ]);\n\t\n\t return _.extend( _.clone( ListPanel.prototype.templates ), {\n\t controls : controlsTemplate\n\t });\n\t}());\n\t\n\t\n\t//=============================================================================\n\t return {\n\t ListPanel : ListPanel,\n\t ModelListPanel : ModelListPanel\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(2), __webpack_require__(1)))\n\n/***/ },\n/* 77 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(6),\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( baseMVC, _l ){\n\t// =============================================================================\n\t/** A view on any model that has a 'tags' attribute (a list of tag strings)\n\t * Incorporates the select2 jQuery plugin for tags display/editing:\n\t * http://ivaynberg.github.io/select2/\n\t */\n\tvar TagsEditor = Backbone.View\n\t .extend( baseMVC.LoggableMixin )\n\t .extend( baseMVC.HiddenUntilActivatedViewMixin ).extend({\n\t\n\t tagName : 'div',\n\t className : 'tags-display',\n\t\n\t /** Set up listeners, parse options */\n\t initialize : function( options ){\n\t //console.debug( this, options );\n\t // only listen to the model only for changes to tags - re-render\n\t this.listenTo( this.model, 'change:tags', function(){\n\t this.render();\n\t });\n\t this.hiddenUntilActivated( options.$activator, options );\n\t },\n\t\n\t /** Build the DOM elements, call select to on the created input, and set up behaviors */\n\t render : function(){\n\t var view = this;\n\t this.$el.html( this._template() );\n\t\n\t this.$input().select2({\n\t placeholder : 'Add tags',\n\t width : '100%',\n\t tags : function(){\n\t // initialize possible tags in the dropdown based on all the tags the user has used so far\n\t return view._getTagsUsed();\n\t }\n\t });\n\t\n\t this._setUpBehaviors();\n\t return this;\n\t },\n\t\n\t /** @returns {String} the html text used to build the view's DOM */\n\t _template : function(){\n\t return [\n\t //TODO: make prompt optional\n\t '',\n\t // set up initial tags by adding as CSV to input vals (necc. to init select2)\n\t ''\n\t ].join( '' );\n\t },\n\t\n\t /** @returns {String} the sorted, comma-separated tags from the model */\n\t tagsToCSV : function(){\n\t var tagsArray = this.model.get( 'tags' );\n\t if( !_.isArray( tagsArray ) || _.isEmpty( tagsArray ) ){\n\t return '';\n\t }\n\t return tagsArray.map( function( tag ){\n\t return _.escape( tag );\n\t }).sort().join( ',' );\n\t },\n\t\n\t /** @returns {jQuery} the input for this view */\n\t $input : function(){\n\t return this.$el.find( 'input.tags-input' );\n\t },\n\t\n\t /** @returns {String[]} all tags used by the current user */\n\t _getTagsUsed : function(){\n\t//TODO: global\n\t return Galaxy.user.get( 'tags_used' );\n\t },\n\t\n\t /** set up any event listeners on the view's DOM (mostly handled by select2) */\n\t _setUpBehaviors : function(){\n\t var view = this;\n\t this.$input().on( 'change', function( event ){\n\t // save the model's tags in either remove or added event\n\t view.model.save({ tags: event.val }, { silent: true });\n\t // if it's new, add the tag to the users tags\n\t if( event.added ){\n\t //??: solve weird behavior in FF on test.galaxyproject.org where\n\t // event.added.text is string object: 'String{ 0=\"o\", 1=\"n\", 2=\"e\" }'\n\t view._addNewTagToTagsUsed( event.added.text + '' );\n\t }\n\t });\n\t },\n\t\n\t /** add a new tag (if not already there) to the list of all tags used by the user\n\t * @param {String} newTag the tag to add to the list of used\n\t */\n\t _addNewTagToTagsUsed : function( newTag ){\n\t//TODO: global\n\t var tagsUsed = Galaxy.user.get( 'tags_used' );\n\t if( !_.contains( tagsUsed, newTag ) ){\n\t tagsUsed.push( newTag );\n\t tagsUsed.sort();\n\t Galaxy.user.set( 'tags_used', tagsUsed );\n\t }\n\t },\n\t\n\t /** shut down event listeners and remove this view's DOM */\n\t remove : function(){\n\t this.$input.off();\n\t this.stopListening( this.model );\n\t Backbone.View.prototype.remove.call( this );\n\t },\n\t\n\t /** string rep */\n\t toString : function(){ return [ 'TagsEditor(', this.model + '', ')' ].join(''); }\n\t});\n\t\n\t// =============================================================================\n\treturn {\n\t TagsEditor : TagsEditor\n\t};\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(2)))\n\n/***/ },\n/* 78 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function($, _) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t __webpack_require__(5)\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function( _l ){\n\t'use strict';\n\t\n\t//TODO: toastr is another possibility - I didn't see where I might add details, tho\n\t\n\t/* ============================================================================\n\tError modals meant to replace the o-so-easy alerts.\n\t\n\tThese are currently styled as errormessages but use the Galaxy.modal\n\tinfrastructure to be shown/closed. They're capable of showing details in a\n\ttogglable dropdown and the details are formatted in a pre.\n\t\n\tExample:\n\t errorModal( 'Heres a message', 'A Title', { some_details: 'here' });\n\t errorModal( 'Heres a message' ); // no details, title is 'Error'\n\t\n\tThere are three specialized forms:\n\t offlineErrorModal a canned response for when there's no connection\n\t badGatewayErrorModal canned response for when Galaxy is restarting\n\t ajaxErrorModal plugable into any Backbone class as an\n\t error event handler by accepting the error args: model, xhr, options\n\t\n\tExamples:\n\t if( navigator.offLine ){ offlineErrorModal(); }\n\t if( xhr.status === 502 ){ badGatewayErrorModal(); }\n\t this.listenTo( this.model, 'error', ajaxErrorModal );\n\t\n\t============================================================================ */\n\t\n\tvar CONTACT_MSG = _l( 'Please contact a Galaxy administrator if the problem persists.' );\n\tvar DEFAULT_AJAX_ERR_MSG = _l( 'An error occurred while updating information with the server.' );\n\tvar DETAILS_MSG = _l( 'The following information can assist the developers in finding the source of the error:' );\n\t\n\t/** private helper that builds the modal and handles adding details */\n\tfunction _errorModal( message, title, details ){\n\t // create and return the modal, adding details button only if needed\n\t Galaxy.modal.show({\n\t title : title,\n\t body : message,\n\t closing_events : true,\n\t buttons : { Ok: function(){ Galaxy.modal.hide(); } },\n\t });\n\t Galaxy.modal.$el.addClass( 'error-modal' );\n\t\n\t if( details ){\n\t Galaxy.modal.$( '.error-details' ).add( Galaxy.modal.$( 'button:contains(\"Details\")' ) ).remove();\n\t $( '' ).addClass( 'error-details' )\n\t .hide().appendTo( Galaxy.modal.$( '.modal-content' ) )\n\t .append([\n\t $( '' ).text( DETAILS_MSG ),\n\t $( '' ).text( JSON.stringify( details, null, ' ' ) )\n\t ]);\n\t\n\t $( '' )\n\t .appendTo( Galaxy.modal.$( '.buttons' ) )\n\t .click( function(){ Galaxy.modal.$( '.error-details' ).toggle(); });\n\t }\n\t return Galaxy.modal;\n\t}\n\t\n\t/** Display a modal showing an error message but fallback to alert if there's no modal */\n\tfunction errorModal( message, title, details ){\n\t if( !message ){ return; }\n\t\n\t message = _l( message );\n\t title = _l( title ) || _l( 'Error:' );\n\t if( window.Galaxy && Galaxy.modal ){\n\t return _errorModal( message, title, details );\n\t }\n\t\n\t alert( title + '\\n\\n' + message );\n\t console.log( 'error details:', JSON.stringify( details ) );\n\t}\n\t\n\t\n\t// ----------------------------------------------------------------------------\n\t/** display a modal when the user may be offline */\n\tfunction offlineErrorModal(){\n\t return errorModal(\n\t _l( 'You appear to be offline. Please check your connection and try again.' ),\n\t _l( 'Offline?' )\n\t );\n\t}\n\t\n\t\n\t// ----------------------------------------------------------------------------\n\t/** 502 messages that should be displayed when galaxy is restarting */\n\tfunction badGatewayErrorModal(){\n\t return errorModal(\n\t _l( 'Galaxy is currently unreachable. Please try again in a few minutes.' ) + ' ' + CONTACT_MSG,\n\t _l( 'Cannot connect to Galaxy' )\n\t );\n\t}\n\t\n\t\n\t// ----------------------------------------------------------------------------\n\t/** display a modal (with details) about a failed Backbone ajax operation */\n\tfunction ajaxErrorModal( model, xhr, options, message, title ){\n\t message = message || DEFAULT_AJAX_ERR_MSG;\n\t message += ' ' + CONTACT_MSG;\n\t title = title || _l( 'An error occurred' );\n\t var details = _ajaxDetails( model, xhr, options );\n\t return errorModal( message, title, details );\n\t}\n\t\n\t/** build details which may help debugging the ajax call */\n\tfunction _ajaxDetails( model, xhr, options ){\n\t return {\n\t//TODO: still can't manage Raven id\n\t raven : _.result( window.Raven, 'lastEventId' ),\n\t userAgent : navigator.userAgent,\n\t onLine : navigator.onLine,\n\t version : _.result( Galaxy.config, 'version_major' ),\n\t xhr : _.omit( xhr, _.functions( xhr ) ),\n\t options : _.omit( options, 'xhr' ),\n\t // add ajax data from Galaxy object cache\n\t url : _.result( Galaxy.lastAjax, 'url' ),\n\t data : _.result( Galaxy.lastAjax, 'data' ),\n\t // backbone stuff (auto-redacting email for user)\n\t model : _.result( model, 'toJSON' , model + '' ),\n\t user : _.omit( _.result( Galaxy.user, 'toJSON' ), 'email' ),\n\t };\n\t}\n\t\n\t\n\t//=============================================================================\n\t return {\n\t errorModal : errorModal,\n\t offlineErrorModal : offlineErrorModal,\n\t badGatewayErrorModal: badGatewayErrorModal,\n\t ajaxErrorModal : ajaxErrorModal\n\t };\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(2)))\n\n/***/ },\n/* 79 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $, _, jQuery) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [\n\t //jquery\n\t //backbone\n\t], __WEBPACK_AMD_DEFINE_RESULT__ = function(){\n\t// =============================================================================\n\t/**\n\t * view for a popup menu\n\t */\n\tvar PopupMenu = Backbone.View.extend({\n\t//TODO: maybe better as singleton off the Galaxy obj\n\t /** Cache the desired button element and options, set up the button click handler\n\t * NOTE: attaches this view as HTML/jQ data on the button for later use.\n\t */\n\t initialize: function( $button, options ){\n\t // default settings\n\t this.$button = $button;\n\t if( !this.$button.length ){\n\t this.$button = $( '' );\n\t }\n\t this.options = options || [];\n\t this.$button.data( 'popupmenu', this );\n\t\n\t // set up button click -> open menu behavior\n\t var menu = this;\n\t this.$button.click( function( event ){\n\t // if there's already a menu open, remove it\n\t $( '.popmenu-wrapper' ).remove();\n\t menu._renderAndShow( event );\n\t return false;\n\t });\n\t },\n\t\n\t // render the menu, append to the page body at the click position, and set up the 'click-away' handlers, show\n\t _renderAndShow: function( clickEvent ){\n\t this.render();\n\t this.$el.appendTo( 'body' ).css( this._getShownPosition( clickEvent )).show();\n\t this._setUpCloseBehavior();\n\t },\n\t\n\t // render the menu\n\t // this menu doesn't attach itself to the DOM ( see _renderAndShow )\n\t render: function(){\n\t // render the menu body absolute and hidden, fill with template\n\t this.$el.addClass( 'popmenu-wrapper' ).hide()\n\t .css({ position : 'absolute' })\n\t .html( this.template( this.$button.attr( 'id' ), this.options ));\n\t\n\t // set up behavior on each link/anchor elem\n\t if( this.options.length ){\n\t var menu = this;\n\t //precondition: there should be one option per li\n\t this.$el.find( 'li' ).each( function( i, li ){\n\t var option = menu.options[i];\n\t\n\t // if the option has 'func', call that function when the anchor is clicked\n\t if( option.func ){\n\t $( this ).children( 'a.popupmenu-option' ).click( function( event ){\n\t option.func.call( menu, event, option );\n\t // We must preventDefault otherwise clicking \"cancel\"\n\t // on a purge or something still navigates and causes\n\t // the action.\n\t event.preventDefault();\n\t // bubble up so that an option click will call the close behavior\n\t });\n\t }\n\t });\n\t }\n\t return this;\n\t },\n\t\n\t template : function( id, options ){\n\t return [\n\t '
'\n\t ].join( '' );\n\t }).join( '' );\n\t },\n\t\n\t // get the absolute position/offset for the menu\n\t _getShownPosition : function( clickEvent ){\n\t\n\t // display menu horiz. centered on click...\n\t var menuWidth = this.$el.width();\n\t var x = clickEvent.pageX - menuWidth / 2 ;\n\t\n\t // adjust to handle horiz. scroll and window dimensions ( draw entirely on visible screen area )\n\t x = Math.min( x, $( document ).scrollLeft() + $( window ).width() - menuWidth - 5 );\n\t x = Math.max( x, $( document ).scrollLeft() + 5 );\n\t return {\n\t top: clickEvent.pageY,\n\t left: x\n\t };\n\t },\n\t\n\t // bind an event handler to all available frames so that when anything is clicked\n\t // the menu is removed from the DOM and the event handler unbinds itself\n\t _setUpCloseBehavior: function(){\n\t var menu = this;\n\t//TODO: alternately: focus hack, blocking overlay, jquery.blockui\n\t\n\t // function to close popup and unbind itself\n\t function closePopup( event ){\n\t $( document ).off( 'click.close_popup' );\n\t if( window && window.parent !== window ){\n\t try {\n\t $( window.parent.document ).off( \"click.close_popup\" );\n\t } catch( err ){}\n\t } else {\n\t try {\n\t $( 'iframe#galaxy_main' ).contents().off( \"click.close_popup\" );\n\t } catch( err ){}\n\t }\n\t menu.remove();\n\t }\n\t\n\t $( 'html' ).one( \"click.close_popup\", closePopup );\n\t if( window && window.parent !== window ){\n\t try {\n\t $( window.parent.document ).find( 'html' ).one( \"click.close_popup\", closePopup );\n\t } catch( err ){}\n\t } else {\n\t try {\n\t $( 'iframe#galaxy_main' ).contents().one( \"click.close_popup\", closePopup );\n\t } catch( err ){}\n\t }\n\t },\n\t\n\t // add a menu option/item at the given index\n\t addItem: function( item, index ){\n\t // append to end if no index\n\t index = ( index >= 0 ) ? index : this.options.length;\n\t this.options.splice( index, 0, item );\n\t return this;\n\t },\n\t\n\t // remove a menu option/item at the given index\n\t removeItem: function( index ){\n\t if( index >=0 ){\n\t this.options.splice( index, 1 );\n\t }\n\t return this;\n\t },\n\t\n\t // search for a menu option by its html\n\t findIndexByHtml: function( html ){\n\t for( var i = 0; i < this.options.length; i++ ){\n\t if( _.has( this.options[i], 'html' ) && ( this.options[i].html === html )){\n\t return i;\n\t }\n\t }\n\t return null;\n\t },\n\t\n\t // search for a menu option by its html\n\t findItemByHtml: function( html ){\n\t return this.options[( this.findIndexByHtml( html ))];\n\t },\n\t\n\t // string representation\n\t toString: function(){\n\t return 'PopupMenu';\n\t }\n\t});\n\t/** shortcut to new for when you don't need to preserve the ref */\n\tPopupMenu.create = function _create( $button, options ){\n\t return new PopupMenu( $button, options );\n\t};\n\t\n\t// -----------------------------------------------------------------------------\n\t// the following class functions are bridges from the original make_popupmenu and make_popup_menus\n\t// to the newer backbone.js PopupMenu\n\t\n\t/** Create a PopupMenu from simple map initial_options activated by clicking button_element.\n\t * Converts initial_options to object array used by PopupMenu.\n\t * @param {jQuery|DOMElement} button_element element which, when clicked, activates menu\n\t * @param {Object} initial_options map of key -> values, where\n\t * key is option text, value is fn to call when option is clicked\n\t * @returns {PopupMenu} the PopupMenu created\n\t */\n\tPopupMenu.make_popupmenu = function( button_element, initial_options ){\n\t var convertedOptions = [];\n\t _.each( initial_options, function( optionVal, optionKey ){\n\t var newOption = { html: optionKey };\n\t\n\t // keys with null values indicate: header\n\t if( optionVal === null ){ // !optionVal? (null only?)\n\t newOption.header = true;\n\t\n\t // keys with function values indicate: a menu option\n\t } else if( jQuery.type( optionVal ) === 'function' ){\n\t newOption.func = optionVal;\n\t }\n\t //TODO:?? any other special optionVals?\n\t // there was no divider option originally\n\t convertedOptions.push( newOption );\n\t });\n\t return new PopupMenu( $( button_element ), convertedOptions );\n\t};\n\t\n\t/** Find all anchors in $parent (using selector) and covert anchors into a PopupMenu options map.\n\t * @param {jQuery} $parent the element that contains the links to convert to options\n\t * @param {String} selector jq selector string to find links\n\t * @returns {Object[]} the options array to initialize a PopupMenu\n\t */\n\t//TODO: lose parent and selector, pass in array of links, use map to return options\n\tPopupMenu.convertLinksToOptions = function( $parent, selector ){\n\t $parent = $( $parent );\n\t selector = selector || 'a';\n\t var options = [];\n\t $parent.find( selector ).each( function( elem, i ){\n\t var option = {}, $link = $( elem );\n\t\n\t // convert link text to the option text (html) and the href into the option func\n\t option.html = $link.text();\n\t if( $link.attr( 'href' ) ){\n\t var linkHref = $link.attr( 'href' ),\n\t linkTarget = $link.attr( 'target' ),\n\t confirmText = $link.attr( 'confirm' );\n\t\n\t option.func = function(){\n\t // if there's a \"confirm\" attribute, throw up a confirmation dialog, and\n\t // if the user cancels - do nothing\n\t if( ( confirmText ) && ( !confirm( confirmText ) ) ){ return; }\n\t\n\t // if there's no confirm attribute, or the user accepted the confirm dialog:\n\t switch( linkTarget ){\n\t // relocate the center panel\n\t case '_parent':\n\t window.parent.location = linkHref;\n\t break;\n\t\n\t // relocate the entire window\n\t case '_top':\n\t window.top.location = linkHref;\n\t break;\n\t\n\t // relocate this panel\n\t default:\n\t window.location = linkHref;\n\t }\n\t };\n\t }\n\t options.push( option );\n\t });\n\t return options;\n\t};\n\t\n\t/** Create a single popupmenu from existing DOM button and anchor elements\n\t * @param {jQuery} $buttonElement the element that when clicked will open the menu\n\t * @param {jQuery} $menuElement the element that contains the anchors to convert into a menu\n\t * @param {String} menuElementLinkSelector jq selector string used to find anchors to be made into menu options\n\t * @returns {PopupMenu} the PopupMenu (Backbone View) that can render, control the menu\n\t */\n\tPopupMenu.fromExistingDom = function( $buttonElement, $menuElement, menuElementLinkSelector ){\n\t $buttonElement = $( $buttonElement );\n\t $menuElement = $( $menuElement );\n\t var options = PopupMenu.convertLinksToOptions( $menuElement, menuElementLinkSelector );\n\t // we're done with the menu (having converted it to an options map)\n\t $menuElement.remove();\n\t return new PopupMenu( $buttonElement, options );\n\t};\n\t\n\t/** Create all popupmenus within a document or a more specific element\n\t * @param {DOMElement} parent the DOM element in which to search for popupmenus to build (defaults to document)\n\t * @param {String} menuSelector jq selector string to find popupmenu menu elements (defaults to \"div[popupmenu]\")\n\t * @param {Function} buttonSelectorBuildFn the function to build the jq button selector.\n\t * Will be passed $menuElement, parent.\n\t * (Defaults to return '#' + $menuElement.attr( 'popupmenu' ); )\n\t * @returns {PopupMenu[]} array of popupmenus created\n\t */\n\tPopupMenu.make_popup_menus = function( parent, menuSelector, buttonSelectorBuildFn ){\n\t parent = parent || document;\n\t // orig. Glx popupmenu menus have a (non-std) attribute 'popupmenu'\n\t // which contains the id of the button that activates the menu\n\t menuSelector = menuSelector || 'div[popupmenu]';\n\t // default to (orig. Glx) matching button to menu by using the popupmenu attr of the menu as the id of the button\n\t buttonSelectorBuildFn = buttonSelectorBuildFn || function( $menuElement, parent ){\n\t return '#' + $menuElement.attr( 'popupmenu' );\n\t };\n\t\n\t // aggregate and return all PopupMenus\n\t var popupMenusCreated = [];\n\t $( parent ).find( menuSelector ).each( function(){\n\t var $menuElement = $( this ),\n\t $buttonElement = $( parent ).find( buttonSelectorBuildFn( $menuElement, parent ) );\n\t popupMenusCreated.push( PopupMenu.fromDom( $buttonElement, $menuElement ) );\n\t $buttonElement.addClass( 'popup' );\n\t });\n\t return popupMenusCreated;\n\t};\n\t\n\t\n\t// =============================================================================\n\t return PopupMenu;\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(1), __webpack_require__(2), __webpack_require__(1)))\n\n/***/ },\n/* 80 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $) {/** This renders the content of the ftp popup **/\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4) ], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils ) {\n\t return Backbone.View.extend({\n\t initialize: function( options ) {\n\t var self = this;\n\t this.options = Utils.merge( options, {\n\t class_add : 'upload-icon-button fa fa-square-o',\n\t class_remove : 'upload-icon-button fa fa-check-square-o',\n\t class_partial : 'upload-icon-button fa fa-minus-square-o',\n\t collection : null,\n\t onchange : function() {},\n\t onadd : function() {},\n\t onremove : function() {}\n\t } );\n\t this.collection = this.options.collection;\n\t this.setElement( this._template() );\n\t this.rows = [];\n\t Utils.get({\n\t url : Galaxy.root + 'api/remote_files',\n\t success : function( ftp_files ) { self._fill( ftp_files ) },\n\t error : function() { self._fill(); }\n\t });\n\t },\n\t\n\t /** Fill table with ftp entries */\n\t _fill: function( ftp_files ) {\n\t if ( ftp_files && ftp_files.length > 0 ) {\n\t this.$( '.upload-ftp-content' ).html( $( this._templateTable() ) );\n\t var size = 0;\n\t for ( index in ftp_files ) {\n\t this.rows.push( this._add( ftp_files[ index ] ) );\n\t size += ftp_files[ index ].size;\n\t }\n\t this.$( '.upload-ftp-number' ).html( ftp_files.length + ' files' );\n\t this.$( '.upload-ftp-disk' ).html( Utils.bytesToString ( size, true ) );\n\t if ( this.collection ) {\n\t var self = this;\n\t this.$( '._has_collection' ).show();\n\t this.$select_all = this.$( '.upload-selectall' ).addClass( this.options.class_add );\n\t this.$select_all.on( 'click', function() {\n\t var add = self.$select_all.hasClass( self.options.class_add );\n\t for ( index in ftp_files ) {\n\t var ftp_file = ftp_files[ index ];\n\t var model_index = self._find( ftp_file );\n\t if( !model_index && add || model_index && !add ) {\n\t self.rows[ index ].trigger( 'click' );\n\t }\n\t }\n\t });\n\t this._refresh();\n\t }\n\t } else {\n\t this.$( '.upload-ftp-content' ).html( $( this._templateInfo() ) );\n\t }\n\t this.$( '.upload-ftp-wait' ).hide();\n\t },\n\t\n\t /** Add file to table */\n\t _add: function( ftp_file ) {\n\t var self = this;\n\t var $it = $( this._templateRow( ftp_file ) );\n\t var $icon = $it.find( '.icon' );\n\t this.$( 'tbody' ).append( $it );\n\t if ( this.collection ) {\n\t $icon.addClass( this._find( ftp_file ) ? this.options.class_remove : this.options.class_add );\n\t $it.on('click', function() {\n\t var model_index = self._find( ftp_file );\n\t $icon.removeClass();\n\t if ( !model_index ) {\n\t self.options.onadd( ftp_file );\n\t $icon.addClass( self.options.class_remove );\n\t } else {\n\t self.options.onremove( model_index );\n\t $icon.addClass( self.options.class_add );\n\t }\n\t self._refresh();\n\t });\n\t } else {\n\t $it.on('click', function() { self.options.onchange( ftp_file ) } );\n\t }\n\t return $it;\n\t },\n\t\n\t /** Refresh select all button state */\n\t _refresh: function() {\n\t var filtered = this.collection.where( { file_mode: 'ftp', enabled: true } );\n\t this.$select_all.removeClass();\n\t if ( filtered.length == 0 ) {\n\t this.$select_all.addClass( this.options.class_add );\n\t } else {\n\t this.$select_all.addClass( filtered.length == this.rows.length ? this.options.class_remove : this.options.class_partial );\n\t }\n\t },\n\t\n\t /** Get model index */\n\t _find: function( ftp_file ) {\n\t var item = this.collection.findWhere({\n\t file_path : ftp_file.path,\n\t file_mode : 'ftp',\n\t enabled : true\n\t });\n\t return item && item.get('id');\n\t },\n\t\n\t /** Template of row */\n\t _templateRow: function( options ) {\n\t return '
';\n\t },\n\t\n\t /** Template of info message */\n\t _templateInfo: function() {\n\t return '
' +\n\t 'Your FTP directory does not contain any files.' +\n\t '
';\n\t },\n\t\n\t /** Template of main view */\n\t _template: function() {\n\t return '
' +\n\t '' +\n\t '
This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at ' + this.options.ftp_upload_site + ' using your Galaxy credentials (email address and password).
' +\n\t '
' +\n\t '
';\n\t }\n\t });\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(1)))\n\n/***/ },\n/* 81 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $, _) {/** This renders the content of the settings popup, allowing users to specify flags i.e. for space-to-tab conversion **/\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4) ], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils ) {\n\t return Backbone.View.extend({\n\t options: {\n\t class_check : 'fa-check-square-o',\n\t class_uncheck : 'fa-square-o',\n\t parameters : [{\n\t id : 'space_to_tab',\n\t title : 'Convert spaces to tabs',\n\t },{\n\t id : 'to_posix_lines',\n\t title : 'Use POSIX standard'\n\t }]\n\t },\n\t\n\t initialize: function( options ) {\n\t var self = this;\n\t this.model = options.model;\n\t this.setElement( $( '' ).addClass( 'upload-settings' ) );\n\t this.$el.append( $( '' ).addClass( 'upload-settings-cover' ) );\n\t this.$el.append( $( '
' ).append( parameter.title ) ) )\n\t });\n\t this.$cover[ this.model.get( 'enabled' ) && 'hide' || 'show' ]();\n\t }\n\t });\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(1), __webpack_require__(2)))\n\n/***/ },\n/* 82 */,\n/* 83 */,\n/* 84 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(jQuery, $) {(function (factory) {\n\t if (true) {\n\t !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t } else {\n\t // Browser globals\n\t factory(jQuery);\n\t }\n\t\n\t}(function () {\n\t//=============================================================================\n\t\n\t jQuery.fn.extend({\n\t hoverhighlight : function $hoverhighlight( scope, color ){\n\t scope = scope || 'body';\n\t if( !this.length ){ return this; }\n\t\n\t $( this ).each( function(){\n\t var $this = $( this ),\n\t targetSelector = $this.data( 'target' );\n\t\n\t if( targetSelector ){\n\t $this.mouseover( function( ev ){\n\t $( targetSelector, scope ).css({\n\t background: color\n\t });\n\t })\n\t .mouseout( function( ev ){\n\t $( targetSelector ).css({\n\t background: ''\n\t });\n\t });\n\t }\n\t });\n\t return this;\n\t }\n\t });\n\t}));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(1)))\n\n/***/ },\n/* 85 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function($, jQuery) {// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js\n\t// Uses AMD or browser globals to create a jQuery plugin.\n\t(function (factory) {\n\t if (true) {\n\t //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will\n\t // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still\n\t // relying on jquery being loaded globally* in order to preserve plugins.\n\t !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t } else {\n\t // Browser globals\n\t factory(jQuery);\n\t }\n\t\n\t}(function () {\n\t var _l = window._l || function( s ){ return s; };\n\t\n\t //TODO: consolidate with tool menu functionality, use there\n\t\n\t /** searchInput: (jQuery plugin)\n\t * Creates a search input, a clear button, and loading indicator\n\t * within the selected node.\n\t *\n\t * When the user either presses return or enters some minimal number\n\t * of characters, a callback is called. Pressing ESC when the input\n\t * is focused will clear the input and call a separate callback.\n\t */\n\t function searchInput( parentNode, options ){\n\t var KEYCODE_ESC = 27,\n\t KEYCODE_RETURN = 13,\n\t $parentNode = $( parentNode ),\n\t firstSearch = true,\n\t defaults = {\n\t initialVal : '',\n\t name : 'search',\n\t placeholder : 'search',\n\t classes : '',\n\t onclear : function(){},\n\t onfirstsearch : null,\n\t onsearch : function( inputVal ){},\n\t minSearchLen : 0,\n\t escWillClear : true,\n\t oninit : function(){}\n\t };\n\t\n\t // .................................................................... input rendering and events\n\t // visually clear the search, trigger an event, and call the callback\n\t function clearSearchInput( event ){\n\t var $input = $( this ).parent().children( 'input' );\n\t $input.val( '' ).trigger( 'searchInput.clear' ).blur();\n\t options.onclear();\n\t }\n\t\n\t // search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)\n\t function search( event, searchTerms ){\n\t if( !searchTerms ){\n\t return clearSearchInput();\n\t }\n\t $( this ).trigger( 'search.search', searchTerms );\n\t if( typeof options.onfirstsearch === 'function' && firstSearch ){\n\t firstSearch = false;\n\t options.onfirstsearch( searchTerms );\n\t } else {\n\t options.onsearch( searchTerms );\n\t }\n\t }\n\t\n\t // .................................................................... input rendering and events\n\t function inputTemplate(){\n\t // class search-query is bootstrap 2.3 style that now lives in base.less\n\t return [ '' ].join( '' );\n\t }\n\t\n\t // the search input that responds to keyboard events and displays the search value\n\t function $input(){\n\t return $( inputTemplate() )\n\t // select all text on a focus\n\t .focus( function( event ){\n\t $( this ).select();\n\t })\n\t // attach behaviors to esc, return if desired, search on some min len string\n\t .keyup( function( event ){\n\t event.preventDefault();\n\t event.stopPropagation();\n\t\n\t // esc key will clear if desired\n\t if( event.which === KEYCODE_ESC && options.escWillClear ){\n\t clearSearchInput.call( this, event );\n\t\n\t } else {\n\t var searchTerms = $( this ).val();\n\t // return key or the search string len > minSearchLen (if not 0) triggers search\n\t if( ( event.which === KEYCODE_RETURN )\n\t || ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){\n\t search.call( this, event, searchTerms );\n\t }\n\t }\n\t })\n\t .val( options.initialVal );\n\t }\n\t\n\t // .................................................................... clear button rendering and events\n\t // a button for clearing the search bar, placed on the right hand side\n\t function $clearBtn(){\n\t return $([ '' ].join('') )\n\t .tooltip({ placement: 'bottom' })\n\t .click( function( event ){\n\t clearSearchInput.call( this, event );\n\t });\n\t }\n\t\n\t // .................................................................... loadingIndicator rendering\n\t // a button for clearing the search bar, placed on the right hand side\n\t function $loadingIndicator(){\n\t return $([ '' ].join('') )\n\t .hide().tooltip({ placement: 'bottom' });\n\t }\n\t\n\t // .................................................................... commands\n\t // visually swap the load, clear buttons\n\t function toggleLoadingIndicator(){\n\t $parentNode.find( '.search-loading' ).toggle();\n\t $parentNode.find( '.search-clear' ).toggle();\n\t }\n\t\n\t // .................................................................... init\n\t // string command (not constructor)\n\t if( jQuery.type( options ) === 'string' ){\n\t if( options === 'toggle-loading' ){\n\t toggleLoadingIndicator();\n\t }\n\t return $parentNode;\n\t }\n\t\n\t // initial render\n\t if( jQuery.type( options ) === 'object' ){\n\t options = jQuery.extend( true, {}, defaults, options );\n\t }\n\t //NOTE: prepended\n\t return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);\n\t }\n\t\n\t // as jq plugin\n\t jQuery.fn.extend({\n\t searchInput : function $searchInput( options ){\n\t return this.each( function(){\n\t return searchInput( this, options );\n\t });\n\t }\n\t });\n\t}));\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(1)))\n\n/***/ },\n/* 86 */,\n/* 87 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function(){\n\t// Alphanumeric/natural sort fn\n\tfunction naturalSort(a, b) {\n\t // setup temp-scope variables for comparison evauluation\n\t var re = /(-?[0-9\\.]+)/g,\n\t x = a.toString().toLowerCase() || '',\n\t y = b.toString().toLowerCase() || '',\n\t nC = String.fromCharCode(0),\n\t xN = x.replace( re, nC + '$1' + nC ).split(nC),\n\t yN = y.replace( re, nC + '$1' + nC ).split(nC),\n\t xD = (new Date(x)).getTime(),\n\t yD = xD ? (new Date(y)).getTime() : null;\n\t // natural sorting of dates\n\t if ( yD ) {\n\t if ( xD < yD ) { return -1; }\n\t else if ( xD > yD ) { return 1; }\n\t }\n\t // natural sorting through split numeric strings and default strings\n\t var oFxNcL, oFyNcL;\n\t for ( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {\n\t oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];\n\t oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];\n\t if (oFxNcL < oFyNcL) { return -1; }\n\t else if (oFxNcL > oFyNcL) { return 1; }\n\t }\n\t return 0;\n\t}\n\t\n\treturn naturalSort;\n\t}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))\n\n\n/***/ },\n/* 88 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(jQuery, _) {/*\n\t galaxy upload plugins - requires FormData and XMLHttpRequest\n\t*/\n\t;(function($){\n\t // add event properties\n\t jQuery.event.props.push(\"dataTransfer\");\n\t\n\t /**\n\t Posts file data to the API\n\t */\n\t $.uploadpost = function (config) {\n\t // parse options\n\t var cnf = $.extend({}, {\n\t data : {},\n\t success : function() {},\n\t error : function() {},\n\t progress : function() {},\n\t url : null,\n\t maxfilesize : 2048,\n\t error_filesize : 'File exceeds 2GB. Please use a FTP client.',\n\t error_default : 'Please make sure the file is available.',\n\t error_server : 'Upload request failed.',\n\t error_login : 'Uploads require you to log in.'\n\t }, config);\n\t\n\t // link data\n\t var data = cnf.data;\n\t\n\t // check errors\n\t if (data.error_message) {\n\t cnf.error(data.error_message);\n\t return;\n\t }\n\t\n\t // construct form data\n\t var form = new FormData();\n\t for (var key in data.payload) {\n\t form.append(key, data.payload[key]);\n\t }\n\t\n\t // add files to submission\n\t var sizes = 0;\n\t for (var key in data.files) {\n\t var d = data.files[key];\n\t form.append(d.name, d.file, d.file.name);\n\t sizes += d.file.size;\n\t }\n\t\n\t // check file size, unless it's an ftp file\n\t if (sizes > 1048576 * cnf.maxfilesize) {\n\t cnf.error(cnf.error_filesize);\n\t return;\n\t }\n\t\n\t // prepare request\n\t xhr = new XMLHttpRequest();\n\t xhr.open('POST', cnf.url, true);\n\t xhr.setRequestHeader('Accept', 'application/json');\n\t xhr.setRequestHeader('Cache-Control', 'no-cache');\n\t xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\t\n\t // captures state changes\n\t xhr.onreadystatechange = function() {\n\t // check for request completed, server connection closed\n\t if (xhr.readyState == xhr.DONE) {\n\t // parse response\n\t var response = null;\n\t if (xhr.responseText) {\n\t try {\n\t response = jQuery.parseJSON(xhr.responseText);\n\t } catch (e) {\n\t response = xhr.responseText;\n\t }\n\t }\n\t // pass any error to the error option\n\t if (xhr.status < 200 || xhr.status > 299) {\n\t var text = xhr.statusText;\n\t if (xhr.status == 403) {\n\t text = cnf.error_login;\n\t } else if (xhr.status == 0) {\n\t text = cnf.error_server;\n\t } else if (!text) {\n\t text = cnf.error_default;\n\t }\n\t cnf.error(text + ' (' + xhr.status + ')');\n\t } else {\n\t cnf.success(response);\n\t }\n\t }\n\t }\n\t\n\t // prepare upload progress\n\t xhr.upload.addEventListener('progress', function(e) {\n\t if (e.lengthComputable) {\n\t cnf.progress(Math.round((e.loaded * 100) / e.total));\n\t }\n\t }, false);\n\t\n\t // send request\n\t Galaxy.emit.debug('uploadbox::uploadpost()', 'Posting following data.', cnf);\n\t xhr.send(form);\n\t }\n\t\n\t /**\n\t Handles the upload events drag/drop etc.\n\t */\n\t $.fn.uploadinput = function(options) {\n\t // initialize\n\t var el = this;\n\t var opts = $.extend({}, {\n\t ondragover : function() {},\n\t ondragleave : function() {},\n\t onchange : function() {},\n\t multiple : false\n\t }, options);\n\t\n\t // append hidden upload field\n\t var $input = $('');\n\t el.append($input.change(function (e) {\n\t opts.onchange(e.target.files);\n\t $(this).val('');\n\t }));\n\t\n\t // drag/drop events\n\t el.on('drop', function (e) {\n\t opts.ondragleave(e);\n\t if(e.dataTransfer) {\n\t opts.onchange(e.dataTransfer.files);\n\t e.preventDefault();\n\t }\n\t });\n\t el.on('dragover', function (e) {\n\t e.preventDefault();\n\t opts.ondragover(e);\n\t });\n\t el.on('dragleave', function (e) {\n\t e.stopPropagation();\n\t opts.ondragleave(e);\n\t });\n\t\n\t // exports\n\t return {\n\t dialog: function () {\n\t $input.trigger('click');\n\t }\n\t }\n\t }\n\t\n\t /**\n\t Handles the upload queue and events such as drag/drop etc.\n\t */\n\t $.fn.uploadbox = function(options) {\n\t // parse options\n\t var opts = $.extend({}, {\n\t dragover : function() {},\n\t dragleave : function() {},\n\t announce : function(d) {},\n\t initialize : function(d) {},\n\t progress : function(d, m) {},\n\t success : function(d, m) {},\n\t error : function(d, m) { alert(m); },\n\t complete : function() {}\n\t }, options);\n\t\n\t // file queue\n\t var queue = {};\n\t\n\t // queue index/length counter\n\t var queue_index = 0;\n\t var queue_length = 0;\n\t\n\t // indicates if queue is currently running\n\t var queue_running = false;\n\t var queue_stop = false;\n\t\n\t // element\n\t var uploadinput = $(this).uploadinput({\n\t multiple : true,\n\t onchange : function(files) { add(files); },\n\t ondragover : options.ondragover,\n\t ondragleave : options.ondragleave\n\t });\n\t\n\t // add new files to upload queue\n\t function add(files) {\n\t if (files && files.length && !queue_running) {\n\t var current_index = queue_index;\n\t _.each(files, function(file, key) {\n\t if (file.mode !== 'new' && _.filter(queue, function(f) {\n\t return f.name === file.name && f.size === file.size;\n\t }).length) {\n\t file.duplicate = true;\n\t }\n\t });\n\t _.each(files, function(file) {\n\t if (!file.duplicate) {\n\t var index = String(queue_index++);\n\t queue[index] = file;\n\t opts.announce(index, queue[index]);\n\t queue_length++;\n\t }\n\t });\n\t return current_index;\n\t }\n\t }\n\t\n\t // remove file from queue\n\t function remove(index) {\n\t if (queue[index]) {\n\t delete queue[index];\n\t queue_length--;\n\t }\n\t }\n\t\n\t // process an upload, recursive\n\t function process() {\n\t // validate\n\t if (queue_length == 0 || queue_stop) {\n\t queue_stop = false;\n\t queue_running = false;\n\t opts.complete();\n\t return;\n\t } else {\n\t queue_running = true;\n\t }\n\t\n\t // get an identifier from the queue\n\t var index = -1;\n\t for (var key in queue) {\n\t index = key;\n\t break;\n\t }\n\t\n\t // get current file from queue\n\t var file = queue[index];\n\t\n\t // remove from queue\n\t remove(index)\n\t\n\t // create and submit data\n\t $.uploadpost({\n\t url : opts.url,\n\t data : opts.initialize(index),\n\t success : function(message) { opts.success(index, message); process();},\n\t error : function(message) { opts.error(index, message); process();},\n\t progress : function(percentage) { opts.progress(index, percentage); }\n\t });\n\t }\n\t\n\t /*\n\t public interface\n\t */\n\t\n\t // open file browser for selection\n\t function select() {\n\t uploadinput.dialog();\n\t }\n\t\n\t // remove all entries from queue\n\t function reset(index) {\n\t for (index in queue) {\n\t remove(index);\n\t }\n\t }\n\t\n\t // initiate upload process\n\t function start() {\n\t if (!queue_running) {\n\t queue_running = true;\n\t process();\n\t }\n\t }\n\t\n\t // stop upload process\n\t function stop() {\n\t queue_stop = true;\n\t }\n\t\n\t // set options\n\t function configure(options) {\n\t opts = $.extend({}, opts, options);\n\t return opts;\n\t }\n\t\n\t // verify browser compatibility\n\t function compatible() {\n\t return window.File && window.FormData && window.XMLHttpRequest && window.FileList;\n\t }\n\t\n\t // export functions\n\t return {\n\t 'select' : select,\n\t 'add' : add,\n\t 'remove' : remove,\n\t 'start' : start,\n\t 'stop' : stop,\n\t 'reset' : reset,\n\t 'configure' : configure,\n\t 'compatible' : compatible\n\t };\n\t }\n\t})(jQuery);\n\t\n\t\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(2)))\n\n/***/ },\n/* 89 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(_) {var RightPanel = __webpack_require__( 10 ).RightPanel,\n\t Ui = __webpack_require__( 7 ),\n\t historyOptionsMenu = __webpack_require__( 112 );\n\t CurrentHistoryView = __webpack_require__( 109 ).CurrentHistoryView,\n\t _l = __webpack_require__( 5 );\n\t\n\t/** the right hand panel in the analysis page that shows the current history */\n\tvar HistoryPanel = RightPanel.extend({\n\t\n\t title : _l( 'History' ),\n\t\n\t initialize : function( options ){\n\t RightPanel.prototype.initialize.call( this, options );\n\t this.options = _.pick( options, 'userIsAnonymous', 'allow_user_dataset_purge', 'galaxyRoot' );\n\t\n\t // view of the current history\n\t this.historyView = new CurrentHistoryView({\n\t className : CurrentHistoryView.prototype.className + ' middle',\n\t purgeAllowed : options.allow_user_dataset_purge,\n\t linkTarget : 'galaxy_main'\n\t });\n\t },\n\t\n\t /** override to change footer selector */\n\t $toggleButton : function(){\n\t return this.$( '.footer > .panel-collapse' );\n\t },\n\t\n\t render : function(){\n\t RightPanel.prototype.render.call( this );\n\t this.optionsMenu = historyOptionsMenu( this.$( '#history-options-button' ), {\n\t anonymous : this.options.userIsAnonymous,\n\t purgeAllowed : this.options.allow_user_dataset_purge,\n\t root : this.options.galaxyRoot\n\t });\n\t this.$( '> .header .buttons [title]' ).tooltip({ placement: 'bottom' });\n\t this.historyView.setElement( this.$( '.history-panel' ) );\n\t this.$el.attr( 'class', 'history-right-panel' );\n\t },\n\t\n\t /** override to add buttons */\n\t _templateHeader: function( data ){\n\t var historyUrl = this.options.galaxyRoot + 'history';\n\t var multiUrl = this.options.galaxyRoot + 'history/view_multiple';\n\t return [\n\t '
',\n\t '
',\n\t // this button re-fetches the history and contents and re-renders the history panel\n\t '',\n\t // opens a drop down menu with history related functions (like view all, delete, share, etc.)\n\t '',\n\t !this.options.userIsAnonymous?\n\t [ '' ].join('') : '',\n\t '
',\n\t '
', _.escape( this.title ), '
',\n\t '
',\n\t ].join('');\n\t },\n\t\n\t /** add history view div */\n\t _templateBody : function( data ){\n\t return [\n\t '',\n\t ].join('');\n\t },\n\t\n\t /** override to use simplified selector */\n\t _templateFooter: function( data ){\n\t return [\n\t '