From 417d1d251e7e7c735a8d4aefd53edcec941db1f4 Mon Sep 17 00:00:00 2001
From: Dannon Baker
- If you discover a repository that causes problems after installation, contact Galaxy support, + If you discover a repository that causes problems after installation, contact Galaxy support, sending all necessary information, and appropriate action will be taken.
From ed1d19a967989416caebd3588fe6b8b6460764e2 Mon Sep 17 00:00:00 2001
From: Dannon Baker ' +
'You are using ' + options.nice_total_disk_usage + ' of disk space in this Galaxy instance. ' +
( Galaxy.config.enable_quotas ? 'Your disk quota is: ' + options.quota + '. ' : '' ) +
- 'Is your usage more than expected? See the documentation for tips on how to find all of the data in your account.' +
+ 'Is your usage more than expected? See the documentation for tips on how to find all of the data in your account.' +
' "+this.formattedReference()+" ",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(['Once your collection is complete, enter a name and ','click "Create list".'].join(""))," "+this.formattedReference()+" ",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(['Once your collection is complete, enter a name and ','click "Create list".'].join(""))," ",s(["Collections of paired datasets are ordered lists of dataset pairs (often forward and reverse reads). ","These collections 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, choose which datasets are paired, ","and re-order the final collection."].join(""))," ",s(['Unpaired datasets are shown in the unpaired section ',"(hover over the underlined words to highlight below). ",'Paired datasets are shown in the paired section.'," ",s([" ",s(["To unpair individual dataset pairs, click the ",'unpair buttons ( ). ','Click the "Unpair all" link to unpair all pairs.'].join(""))," ",s(['You can include or remove the file extensions (e.g. ".fastq") from your pair names by toggling the ','"Remove file extensions from pair names?" control.'].join(""))," ",s(['Once your collection is complete, enter a name and ','click "Create list". ',"(Note: you do not have to pair all unpaired datasets to finish.)"].join(""))," ",n(["Pair collections are permanent collections containing two datasets: one forward and one reverse. ","Often these are forward and reverse reads. The pair collections can be passed to tools and ","workflows in order to have analyses done on both datasets. This interface allows ","you to create a pair, name it, and swap which is forward and which reverse."].join(""))," ",n(['Once your collection is complete, enter a name and ','click "Create list".'].join(""))," '+this.progressive+"... You are using '+e.nice_total_disk_usage+" of disk space in this Galaxy instance. "+(Galaxy.config.enable_quotas?"Your disk quota is: "+e.quota+". ":"")+'Is your usage more than expected? See the documentation for tips on how to find all of the data in your account. You are using '+e.nice_total_disk_usage+" of disk space in this Galaxy instance. "+(Galaxy.config.enable_quotas?"Your disk quota is: "+e.quota+". ":"")+'Is your usage more than expected? See the documentation for tips on how to find all of the data in your account. \" + this.formattedReference() + \" ', _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( '' )), ' ', _l([\n\t 'Once your collection is complete, enter a name and ',\n\t 'click \"Create list\".'\n\t ].join( '' )), ' ', _l([\n\t 'Collections of paired datasets are ordered lists of dataset pairs (often forward and reverse reads). ',\n\t 'These collections can be passed to tools and workflows in order to have analyses done on each member of ',\n\t 'the entire group. This interface allows you to create a collection, choose which datasets are paired, ',\n\t 'and re-order the final collection.'\n\t ].join( '' )), ' ', _l([\n\t 'Unpaired datasets are shown in the unpaired section ',\n\t '(hover over the underlined words to highlight below). ',\n\t 'Paired datasets are shown in the paired section.',\n\t ' ', _l([\n\t ' ', _l([\n\t 'To unpair individual dataset pairs, click the ',\n\t 'unpair buttons ( ). ',\n\t 'Click the \"Unpair all\" link to unpair all pairs.'\n\t ].join( '' )), ' ', _l([\n\t 'You can include or remove the file extensions (e.g. \".fastq\") from your pair names by toggling the ',\n\t '\"Remove file extensions from pair names?\" control.'\n\t ].join( '' )), ' ', _l([\n\t 'Once your collection is complete, enter a name and ',\n\t 'click \"Create list\". ',\n\t '(Note: you do not have to pair all unpaired datasets to finish.)'\n\t ].join( '' )), ' ', _l([\n\t 'Pair collections are permanent collections containing two datasets: one forward and one reverse. ',\n\t 'Often these are forward and reverse reads. The pair collections can be passed to tools and ',\n\t 'workflows in order to have analyses done on both datasets. This interface allows ',\n\t 'you to create a pair, name it, and swap which is forward and which reverse.'\n\t ].join( '' )), ' ', _l([\n\t 'Once your collection is complete, enter a name and ',\n\t 'click \"Create list\".'\n\t ].join( '' )), ' ' + this.progressive + '... ' +\n\t 'You are using ' + options.nice_total_disk_usage + ' of disk space in this Galaxy instance. ' +\n\t ( Galaxy.config.enable_quotas ? 'Your disk quota is: ' + options.quota + '. ' : '' ) +\n\t 'Is your usage more than expected? See the documentation for tips on how to find all of the data in your account.' +\n\t ' \" + this.formattedReference() + \" ', _l([\n 'Collections of datasets are permanent, ordered lists of datasets that can be passed to tools and ',\n 'workflows in order to have analyses done on each member of the entire group. This interface allows ',\n 'you to create a collection and re-order the final collection.'\n ].join( '' )), ' ', _l([\n 'Once your collection is complete, enter a name and ',\n 'click \"Create list\".'\n ].join( '' )), ' ', _l([\n 'Collections of paired datasets are ordered lists of dataset pairs (often forward and reverse reads). ',\n 'These collections can be passed to tools and workflows in order to have analyses done on each member of ',\n 'the entire group. This interface allows you to create a collection, choose which datasets are paired, ',\n 'and re-order the final collection.'\n ].join( '' )), ' ', _l([\n 'Unpaired datasets are shown in the unpaired section ',\n '(hover over the underlined words to highlight below). ',\n 'Paired datasets are shown in the paired section.',\n ' ', _l([\n ' ', _l([\n 'To unpair individual dataset pairs, click the ',\n 'unpair buttons ( ). ',\n 'Click the \"Unpair all\" link to unpair all pairs.'\n ].join( '' )), ' ', _l([\n 'You can include or remove the file extensions (e.g. \".fastq\") from your pair names by toggling the ',\n '\"Remove file extensions from pair names?\" control.'\n ].join( '' )), ' ', _l([\n 'Once your collection is complete, enter a name and ',\n 'click \"Create list\". ',\n '(Note: you do not have to pair all unpaired datasets to finish.)'\n ].join( '' )), ' ', _l([\n 'Pair collections are permanent collections containing two datasets: one forward and one reverse. ',\n 'Often these are forward and reverse reads. The pair collections can be passed to tools and ',\n 'workflows in order to have analyses done on both datasets. This interface allows ',\n 'you to create a pair, name it, and swap which is forward and which reverse.'\n ].join( '' )), ' ', _l([\n 'Once your collection is complete, enter a name and ',\n 'click \"Create list\".'\n ].join( '' )), ' ' + this.progressive + '... ' +\n 'You are using ' + options.nice_total_disk_usage + ' of disk space in this Galaxy instance. ' +\n ( Galaxy.config.enable_quotas ? 'Your disk quota is: ' + options.quota + '. ' : '' ) +\n 'Is your usage more than expected? See the documentation for tips on how to find all of the data in your account.' +\n ' \" + this.formattedReference() + \" ', _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( '' )), ' ', _l([\n\t 'Once your collection is complete, enter a name and ',\n\t 'click \"Create list\".'\n\t ].join( '' )), ' ', _l([\n\t 'Collections of paired datasets are ordered lists of dataset pairs (often forward and reverse reads). ',\n\t 'These collections can be passed to tools and workflows in order to have analyses done on each member of ',\n\t 'the entire group. This interface allows you to create a collection, choose which datasets are paired, ',\n\t 'and re-order the final collection.'\n\t ].join( '' )), ' ', _l([\n\t 'Unpaired datasets are shown in the unpaired section ',\n\t '(hover over the underlined words to highlight below). ',\n\t 'Paired datasets are shown in the paired section.',\n\t ' ', _l([\n\t ' ', _l([\n\t 'To unpair individual dataset pairs, click the ',\n\t 'unpair buttons ( ). ',\n\t 'Click the \"Unpair all\" link to unpair all pairs.'\n\t ].join( '' )), ' ', _l([\n\t 'You can include or remove the file extensions (e.g. \".fastq\") from your pair names by toggling the ',\n\t '\"Remove file extensions from pair names?\" control.'\n\t ].join( '' )), ' ', _l([\n\t 'Once your collection is complete, enter a name and ',\n\t 'click \"Create list\". ',\n\t '(Note: you do not have to pair all unpaired datasets to finish.)'\n\t ].join( '' )), ' ', _l([\n\t 'Pair collections are permanent collections containing two datasets: one forward and one reverse. ',\n\t 'Often these are forward and reverse reads. The pair collections can be passed to tools and ',\n\t 'workflows in order to have analyses done on both datasets. This interface allows ',\n\t 'you to create a pair, name it, and swap which is forward and which reverse.'\n\t ].join( '' )), ' ', _l([\n\t 'Once your collection is complete, enter a name and ',\n\t 'click \"Create list\".'\n\t ].join( '' )), ' ' + this.progressive + '... ' +\n\t 'You are using ' + options.nice_total_disk_usage + ' of disk space in this Galaxy instance. ' +\n\t ( Galaxy.config.enable_quotas ? 'Your disk quota is: ' + options.quota + '. ' : '' ) +\n\t 'Is your usage more than expected? See the documentation for tips on how to find all of the data in your account.' +\n\t ' \" + this.formattedReference() + \" ', _l([\n 'Collections of datasets are permanent, ordered lists of datasets that can be passed to tools and ',\n 'workflows in order to have analyses done on each member of the entire group. This interface allows ',\n 'you to create a collection and re-order the final collection.'\n ].join( '' )), ' ', _l([\n 'Once your collection is complete, enter a name and ',\n 'click \"Create list\".'\n ].join( '' )), ' ', _l([\n 'Collections of paired datasets are ordered lists of dataset pairs (often forward and reverse reads). ',\n 'These collections can be passed to tools and workflows in order to have analyses done on each member of ',\n 'the entire group. This interface allows you to create a collection, choose which datasets are paired, ',\n 'and re-order the final collection.'\n ].join( '' )), ' ', _l([\n 'Unpaired datasets are shown in the unpaired section ',\n '(hover over the underlined words to highlight below). ',\n 'Paired datasets are shown in the paired section.',\n ' ', _l([\n ' ', _l([\n 'To unpair individual dataset pairs, click the ',\n 'unpair buttons ( ). ',\n 'Click the \"Unpair all\" link to unpair all pairs.'\n ].join( '' )), ' ', _l([\n 'You can include or remove the file extensions (e.g. \".fastq\") from your pair names by toggling the ',\n '\"Remove file extensions from pair names?\" control.'\n ].join( '' )), ' ', _l([\n 'Once your collection is complete, enter a name and ',\n 'click \"Create list\". ',\n '(Note: you do not have to pair all unpaired datasets to finish.)'\n ].join( '' )), ' ', _l([\n 'Pair collections are permanent collections containing two datasets: one forward and one reverse. ',\n 'Often these are forward and reverse reads. The pair collections can be passed to tools and ',\n 'workflows in order to have analyses done on both datasets. This interface allows ',\n 'you to create a pair, name it, and swap which is forward and which reverse.'\n ].join( '' )), ' ', _l([\n 'Once your collection is complete, enter a name and ',\n 'click \"Create list\".'\n ].join( '' )), ' ' + this.progressive + '... ' +\n 'You are using ' + options.nice_total_disk_usage + ' of disk space in this Galaxy instance. ' +\n ( Galaxy.config.enable_quotas ? 'Your disk quota is: ' + options.quota + '. ' : '' ) +\n 'Is your usage more than expected? See the documentation for tips on how to find all of the data in your account.' +\n '").attr({id:"content_table",cellpadding:0});this.$el.append(e);var t=this.model.get_metadata("column_names"),i=r("").appendTo(e),n=r("
").appendTo(i);if(t)n.append(" "+t.join(" ")+" ");else for(var s=1;s<=this.model.get_metadata("columns");s++)n.append(""+s+" ");var o=this,a=this.model.get("first_data_chunk");a?this._renderChunk(a):r.when(o.model.get_next_chunk()).then(function(e){o._renderChunk(e)}),this.scroll_elt.scroll(function(){o.attempt_to_fetch()})},scrolled_to_bottom:function(){return!1},_renderCell:function(e,t,i){var n=r("").text(e),s=this.model.get_metadata("column_types");return void 0!==i?n.attr("colspan",i).addClass("stringalign"):s&&t "))})),this.row_count++,i},_renderChunk:function(e){var t=this.$el.find("table");a.each(e.ck_data.split("\n"),function(e,i){""!==e&&t.append(this._renderRow(e))},this)}}),h=d.extend({initialize:function(e){d.prototype.initialize.call(this,e),scroll_elt=a.find(this.$el.parents(),function(e){return"auto"===r(e).css("overflow")}),scroll_elt||(scroll_elt=window),this.scroll_elt=r(scroll_elt)},scrolled_to_bottom:function(){return this.$el.height()-this.scroll_elt.scrollTop()-this.scroll_elt.height()<=0}}),u=d.extend({initialize:function(e){d.prototype.initialize.call(this,e),this.scroll_elt=this.$el.css({position:"relative",overflow:"scroll",height:e.height||"500px"})},scrolled_to_bottom:function(){return this.$el.scrollTop()+this.$el.innerHeight()>=this.el.scrollHeight}}),p=o.View.extend({col:{chrom:null,start:null,end:null},url_viz:null,dataset_id:null,genome_build:null,file_ext:null,initialize:function(e){function t(e,t){for(var i=0;i ").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)},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,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"+JSON.stringify(i.responseJSON)+"
":content+=": "+n}t._showAlert(content,"alert-danger")},events:{"click .more-help":"_clickMoreHelp","click .less-help":"_clickLessHelp","click .main-help":"_toggleHelp","click .header .alert button":"_hideAlert","click .reset":"reset","click .clear-selected":"clearSelectedElements","click .collection-elements":"clearSelectedElements","dragover .collection-elements":"_dragoverElements","drop .collection-elements":"_dropElements","collection-element.dragstart .collection-elements":"_elementDragstart","collection-element.dragend .collection-elements":"_elementDragend","change .collection-name":"_changeName","keydown .collection-name":"_nameCheckForEnter","click .cancel-create":function(e){"function"==typeof this.oncancel&&this.oncancel.call(this)},"click .create-collection":"_clickCreate"},_clickMoreHelp:function(e){e.stopPropagation(),this.$(".main-help").addClass("expanded"),this.$(".more-help").hide()},_clickLessHelp:function(e){e.stopPropagation(),this.$(".main-help").removeClass("expanded"),this.$(".more-help").show()},_toggleHelp:function(e){e.stopPropagation(),this.$(".main-help").toggleClass("expanded"),this.$(".more-help").toggle()},_showAlert:function(e,t){t=t||"alert-danger",this.$(".main-help").hide(),this.$(".header .alert").attr("class","alert alert-dismissable").addClass(t).show().find(".alert-message").html(e)},_hideAlert:function(e){this.$(".main-help").show(),this.$(".header .alert").hide()},reset:function(){this._instanceSetUp(),this._elementsSetUp(),this.render()},clearSelectedElements:function(e){this.$(".collection-elements .collection-element").removeClass("selected"),this.$(".collection-elements-controls > .clear-selected").hide()},_dragoverElements:function(e){e.preventDefault();var t=this.$list();this._checkForAutoscroll(t,e.originalEvent.clientY);var i=this._getNearestElement(e.originalEvent.clientY);this.$(".element-drop-placeholder").remove();var n=r('');i.length?i.before(n):t.append(n)},_checkForAutoscroll:function(e,t){var i=2,n=e.offset(),s=e.scrollTop(),o=t-n.top,a=n.top+e.outerHeight()-t;o>=0&&o","
","<% _.each( problems, function( problem ){ %>","
"].join("")),noElementsLeft:a.template(['<% _.each( problems, function( problem ){ %>","
","<% } 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"),".","",""+JSON.stringify(i.responseJSON)+"
":content+=": "+n}t._showAlert(content,"alert-danger")},events:{"click .more-help":"_clickMoreHelp","click .less-help":"_clickLessHelp","click .main-help":"_toggleHelp","click .header .alert button":"_hideAlert","click .reset":"reset","click .clear-selected":"clearSelectedElements","click .collection-elements":"clearSelectedElements","dragover .collection-elements":"_dragoverElements","drop .collection-elements":"_dropElements","collection-element.dragstart .collection-elements":"_elementDragstart","collection-element.dragend .collection-elements":"_elementDragend","change .collection-name":"_changeName","keydown .collection-name":"_nameCheckForEnter","click .cancel-create":function(e){"function"==typeof this.oncancel&&this.oncancel.call(this)},"click .create-collection":"_clickCreate"},_clickMoreHelp:function(e){e.stopPropagation(),this.$(".main-help").addClass("expanded"),this.$(".more-help").hide()},_clickLessHelp:function(e){e.stopPropagation(),this.$(".main-help").removeClass("expanded"),this.$(".more-help").show()},_toggleHelp:function(e){e.stopPropagation(),this.$(".main-help").toggleClass("expanded"),this.$(".more-help").toggle()},_showAlert:function(e,t){t=t||"alert-danger",this.$(".main-help").hide(),this.$(".header .alert").attr("class","alert alert-dismissable").addClass(t).show().find(".alert-message").html(e)},_hideAlert:function(e){this.$(".main-help").show(),this.$(".header .alert").hide()},reset:function(){this._instanceSetUp(),this._elementsSetUp(),this.render()},clearSelectedElements:function(e){this.$(".collection-elements .collection-element").removeClass("selected"),this.$(".collection-elements-controls > .clear-selected").hide()},_dragoverElements:function(e){e.preventDefault();var t=this.$list();this._checkForAutoscroll(t,e.originalEvent.clientY);var i=this._getNearestElement(e.originalEvent.clientY);this.$(".element-drop-placeholder").remove();var n=r('');i.length?i.before(n):t.append(n)},_checkForAutoscroll:function(e,t){var i=2,n=e.offset(),s=e.scrollTop(),o=t-n.top,a=n.top+e.outerHeight()-t;o>=0&&o","
","<% _.each( problems, function( problem ){ %>","
"].join("")),noElementsLeft:a.template(['<% _.each( problems, function( problem ){ %>","
","<% } 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"),".","","','
","<%= dataset.peek %>
',"<% } %>","<% } %>","");t.append(e),this.row.append(t)},appendHeader:function(){this.$thead.append(this.row),this.row=a(" ")},add:function(e,t,i){var n=a(" ");t&&n.css("width",t),i&&n.css("text-align",i),n.append(e),this.row.append(n)},append:function(e,t){this._commit(e,t,!1)},prepend:function(e,t){this._commit(e,t,!0)},get:function(e){return this.$el.find("#"+e)},del:function(e){var t=this.$tbody.find("#"+e);t.length>0&&(t.remove(),this.row_count--,this._refresh())},delAll:function(){this.$tbody.empty(),this.row_count=0,this._refresh()},value:function(e){this.before=this.$tbody.find(".current").attr("id"),void 0!==e&&(this.$tbody.find("tr").removeClass("current"),e&&this.$tbody.find("#"+e).addClass("current"));var t=this.$tbody.find(".current").attr("id");return void 0===t?null:(t!=this.before&&this.options.onchange&&this.options.onchange(e),t)},size:function(){return this.$tbody.find("tr").length},_commit:function(e,t,i){this.del(e),this.row.attr("id",e),i?this.$tbody.prepend(this.row):this.$tbody.append(this.row),t&&(this.row.hide(),this.row.fadeIn()),this.row=this._row(),this.row_count++,this._refresh()},_row:function(){return a(' ')},_onclick:function(e){var t=this.value(),i=a(e.target).closest("tr").attr("id");""!=i&&i&&t!=i&&(this.options.onconfirm?this.options.onconfirm(i):this.value(i))},_ondblclick:function(e){var t=this.value();t&&this.options.ondblclick&&this.options.ondblclick(t)},_refresh:function(){0==this.row_count?this.$tmessage.show():this.$tmessage.hide()},_template:function(e){return'
",n+=t.help):n+="Tool help is unavailable for this dataset.
",n+="
',c("Set it manually or retry auto-detection"),"","
',c("Undelete it"),"","<% if( view.purgeAllowed ){ %>",'
',c("Permanently remove it from disk"),"","<% } %>","',"<% _.each( visualizations, function( visualization ){ %>",'
","',"
","',"
',"
',"
',"
To pair datasets, you can:","
"].join("")),"","You can filter what is shown in the unpaired sections by:","
"].join("")),"","","
","<% _.each( problems, function( problem ){ %>","
","<% } else if( _.size( elements ) === 0 ){ %>",n("No datasets were selected"),".","<% } else if( _.size( elements ) === 1 ){ %>",n("Only one dataset was selected"),": <%- elements[0].name %>","<% } else if( _.size( elements ) > 2 ){ %>",n("Too many datasets were selected"),': <%- _.pluck( elements, "name" ).join( ", ") %>',"<% } %>","
",n("Two (and only two) elements are needed for the pair"),". ",n("You may need to "),'',n("cancel")," ",n("and reselect new elements"),".","","
',n("Unhide it"),"","','',"
").addClass("tab-navigation nav nav-tabs")).append(a("").addClass("tab-content"))},_template_tab:function(e){var t=a("").addClass("tab-element").attr("id","tab-"+e.id).append(a("").attr("id","tab-title-link-"+e.id)),i=t.find("a");return e.icon&&i.append(a("").addClass("tab-icon fa").addClass(e.icon)),i.append(a("").attr("id","tab-title-text-"+e.id).addClass("tab-title-text").append(e.title)),t}});return{View:t}}.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),i(82),i(81),i(25),i(7),i(26),i(89)],s=function(e,t,i,n,s,l){return o.View.extend({status_classes:{init:"upload-mode fa fa-exclamation text-primary",ready:"upload-mode fa fa-check text-success",running:"upload-mode fa fa-spinner fa-spin",success:"upload-mode fa fa-check",error:"upload-mode fa fa-exclamation-triangle"},initialize:function(e,t){var i=this;this.app=e,this.model=t.model,this.setElement(this._template()),this.$source=this.$(".upload-source"),this.$settings=this.$(".upload-settings"),this.$status=this.$(".upload-status"),this.$text=this.$(".upload-text"),this.$text_content=this.$(".upload-text-content"),this.$info_text=this.$(".upload-info-text"),this.$info_progress=this.$(".upload-info-progress"),this.$file_name=this.$(".upload-file-name"),this.$file_desc=this.$(".upload-file-desc"),this.$file_size=this.$(".upload-file-size"),this.$progress_bar=this.$(".upload-progress-bar"),this.$percentage=this.$(".upload-percentage"),this.uploadinput=this.$el.uploadinput({ondragover:function(){i.model.get("enabled")&&i.$el.addClass("warning")},ondragleave:function(){i.$el.removeClass("warning")},onchange:function(e){"running"!=i.model.get("status")&&e&&e.length>0&&(i.model.reset({file_data:e[0],file_name:e[0].name,file_size:e[0].size,file_mode:e[0].mode||"local"}),i._refreshReady())}}),this.button_menu=new s.ButtonMenu({icon:"fa-caret-down",title:"Select",pull:"left"}),this.$source.append(this.button_menu.$el),this.button_menu.addMenu({icon:"fa-laptop",title:"Choose local file",onclick:function(){i.uploadinput.dialog()}}),this.app.ftp_upload_site&&this.button_menu.addMenu({icon:"fa-folder-open-o",title:"Choose FTP file",onclick:function(){i._showFtp()}}),this.button_menu.addMenu({icon:"fa-edit",title:"Paste/Fetch data",onclick:function(){i.model.reset({file_mode:"new",file_name:"New File"})}}),this.ftp=new n.View({title:"Choose FTP file:",container:this.$source.find(".ui-button-menu"),placement:"right"}),this.settings=new n.View({title:"Upload configuration",container:this.$settings,placement:"bottom"}),this.$text_content.on("change input",function(e){i.model.set({url_paste:a(e.target).val(),file_size:a(e.target).val().length}),i._refreshReady()}),this.$settings.on("click",function(e){i._showSettings()}).on("mousedown",function(e){e.preventDefault()}),this.listenTo(this.model,"change:percentage",function(){i._refreshPercentage()}),this.listenTo(this.model,"change:status",function(){i._refreshStatus()}),this.listenTo(this.model,"change:info",function(){i._refreshInfo()}),this.listenTo(this.model,"change:file_name",function(){i._refreshFileName()}),this.listenTo(this.model,"change:file_mode",function(){i._refreshMode()}),this.listenTo(this.model,"change:file_size",function(){i._refreshFileSize()}),this.listenTo(this.model,"remove",function(){i.remove()}),this.app.collection.on("reset",function(){i.remove()})},render:function(){this.$el.attr("id","upload-row-"+this.model.id),this.$file_name.html(r.escape(this.model.get("file_name")||"-")),this.$file_desc.html(this.model.get("file_desc")||"Unavailable"),this.$file_size.html(e.bytesToString(this.model.get("file_size"))),this.$status.removeClass().addClass(this.status_classes.init)},remove:function(){o.View.prototype.remove.apply(this)},_refreshReady:function(){this.app.collection.each(function(e){e.set("status",e.get("file_size")>0&&"ready"||"init")})},_refreshMode:function(){var e=this.model.get("file_mode");"new"==e?(this.height=this.$el.height(),this.$text.css({width:this.$el.width()-16+"px",top:this.$el.height()-8+"px"}).show(),this.$el.height(this.$el.height()-8+this.$text.height()+16),this.$text_content.val("").trigger("keyup")):(this.$el.height(this.height),this.$text.hide())},_refreshInfo:function(){var e=this.model.get("info");e?this.$info_text.html("Failed: "+e).show():this.$info_text.hide()},_refreshPercentage:function(){var e=parseInt(this.model.get("percentage"));0!=e?this.$progress_bar.css({width:e+"%"}):(this.$progress_bar.addClass("no-transition"),this.$progress_bar.css({width:"0%"}),this.$progress_bar[0].offsetHeight,this.$progress_bar.removeClass("no-transition")),this.$percentage.html(100!=e?e+"%":"Adding to history...")},_refreshStatus:function(){var e=this.model.get("status");this.$status.removeClass().addClass(this.status_classes[e]),this.model.set("enabled","running"!=e),this.$text_content.attr("disabled",!this.model.get("enabled")),this.$el.removeClass("success danger warning"),"running"!=e&&"ready"!=e||this.model.set("percentage",0),this.$source.find(".button")["running"==e?"addClass":"removeClass"]("disabled"),"success"==e&&(this.$el.addClass("success"),this.model.set("percentage",100),this.$percentage.html("100%")),"error"==e?(this.$el.addClass("danger"),this.model.set("percentage",0),this.$info_progress.hide(),this.$info_text.show()):(this.$info_progress.show(),this.$info_text.hide())},_refreshFileName:function(){this.$file_name.html(this.model.get("file_name")||"-")},_refreshFileSize:function(){this.$file_size.html(e.bytesToString(this.model.get("file_size")))},_showFtp:function(){if(this.ftp.visible)this.ftp.hide();else{var e=this;this.ftp.empty(),this.ftp.append(new i({ftp_upload_site:this.app.ftp_upload_site,onchange:function(t){e.ftp.hide(),"running"!=e.model.get("status")&&t&&(e.model.reset({file_mode:"ftp",file_name:t.path,file_size:t.size,file_path:t.path}),e._refreshReady())}}).$el),this.ftp.show()}},_showSettings:function(){this.settings.visible?this.settings.hide():(this.settings.empty(),this.settings.append(new t(this).$el),this.settings.show())},_template:function(){return'
'}})}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(1),i(2))},function(e,t,i){var n,s;(function(o,a,r){n=[i(4),i(53),i(119),i(25),i(26),i(7)],s=function(e,t,i,n,s,l){return o.View.extend({collection:new t.Collection,initialize:function(e){var t=this;this.app=e,this.options=e.options,this.list_extensions=e.list_extensions,this.list_genomes=e.list_genomes,this.ftp_upload_site=e.currentFtp(),this.setElement(this._template()),this.btnStart=new l.Button({title:"Start",onclick:function(){t._eventStart()}}),this.btnClose=new l.Button({title:"Close",onclick:function(){t.app.modal.hide()}}),a.each([this.btnStart,this.btnClose],function(e){t.$(".upload-buttons").prepend(e.$el)}),this.select_extension=new s.View({css:"upload-footer-selection",container:this.$(".upload-footer-extension"),data:a.filter(this.list_extensions,function(e){return e.composite_files}),onchange:function(e){t.collection.reset();var i=a.findWhere(t.list_extensions,{id:e});i&&i.composite_files&&a.each(i.composite_files,function(e){t.collection.add({id:t.collection.size(),file_desc:e.description||e.name})})}}),this.$(".upload-footer-extension-info").on("click",function(e){t._showExtensionInfo({$el:r(e.target),title:t.select_extension.text(),extension:t.select_extension.value(),placement:"top"})}).on("mousedown",function(e){e.preventDefault()}),this.select_genome=new s.View({css:"upload-footer-selection",container:this.$(".upload-footer-genome"),data:this.list_genomes,value:this.options.default_genome}),this.listenTo(this.collection,"add",function(e){t._eventAnnounce(e)}),this.listenTo(this.collection,"change add",function(){t.render()}),this.select_extension.options.onchange(this.select_extension.value()),this.render()},render:function(){var e=this.collection.first();e&&"running"==e.get("status")?(this.select_genome.disable(),this.select_extension.disable()):(this.select_genome.enable(),this.select_extension.enable()),this.collection.where({status:"ready"}).length==this.collection.length&&this.collection.length>0?(this.btnStart.enable(),this.btnStart.$el.addClass("btn-primary")):(this.btnStart.disable(),this.btnStart.$el.removeClass("btn-primary")),this.$(".upload-table")[this.collection.length>0?"show":"hide"]()},_eventAnnounce:function(e){var t=new i(this,{model:e});this.$(".upload-table > tbody:first").append(t.$el),this.$(".upload-table")[this.collection.length>0?"show":"hide"](),t.render()},_eventStart:function(){var e=this;this.collection.each(function(t){t.set({genome:e.select_genome.value(),extension:e.select_extension.value()})}),r.uploadpost({url:this.app.options.nginx_upload_path,data:this.app.toData(this.collection.filter()),success:function(t){e._eventSuccess(t)},error:function(t){e._eventError(t)},progress:function(t){e._eventProgress(t)}})},_eventProgress:function(e){this.collection.each(function(t){t.set("percentage",e)})},_eventSuccess:function(e){this.collection.each(function(e){e.set("status","success")}),Galaxy.currHistoryPanel.refreshContents()},_eventError:function(e){this.collection.each(function(t){t.set({status:"error",info:e})})},_showExtensionInfo:function(e){var t=e.$el,i=e.extension,s=e.title,o=a.findWhere(this.list_extensions,{id:i});this.extension_popup&&this.extension_popup.remove(),this.extension_popup=new n.View({placement:e.placement||"bottom",container:t,destroy:!0}),this.extension_popup.title(s),this.extension_popup.empty(),this.extension_popup.append(this._templateDescription(o)),this.extension_popup.show()},_templateDescription:function(e){if(e.description){var t=e.description;return e.description_url&&(t+=' (read more)'),t}return"There is no description available for this file extension."},_template:function(){return'Description Name Size Settings Status '}})}.apply(t,n),!(void 0!==s&&(e.exports=s))}).call(t,i(3),i(1),i(2))},function(e,t,i){var n,s;(function(o,a,r){n=[i(4),i(53),i(121),i(81),i(25),i(26),i(7),i(89)],s=function(e,t,i,n,s,l,c){return o.View.extend({upload_size:0,collection:new t.Collection,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(e){var t=this;this.app=e,this.options=e.options,this.list_extensions=e.list_extensions,this.list_genomes=e.list_genomes,this.ui_button=e.ui_button,this.ftp_upload_site=e.currentFtp(),this.setElement(this._template()),this.btnLocal=new c.Button({id:"btn-local",title:"Choose local file",onclick:function(){t.uploadbox.select()},icon:"fa fa-laptop"}),this.btnFtp=new c.Button({id:"btn-ftp",title:"Choose FTP file",onclick:function(){t._eventFtp()},icon:"fa fa-folder-open-o"}),this.btnCreate=new c.Button({id:"btn-new",title:"Paste/Fetch data",onclick:function(){t._eventCreate()},icon:"fa fa-edit"}),this.btnStart=new c.Button({id:"btn-start",title:"Start",onclick:function(){t._eventStart()}}),this.btnStop=new c.Button({id:"btn-stop",title:"Pause",onclick:function(){t._eventStop()}}),this.btnReset=new c.Button({id:"btn-reset",title:"Reset",onclick:function(){t._eventReset()}}),this.btnClose=new c.Button({id:"btn-close",title:"Close",onclick:function(){t.app.modal.hide()}}),a.each([this.btnLocal,this.btnFtp,this.btnCreate,this.btnStop,this.btnReset,this.btnStart,this.btnClose],function(e){t.$(".upload-buttons").prepend(e.$el)}),this.uploadbox=this.$(".upload-box").uploadbox({url:this.app.options.nginx_upload_path,announce:function(e,i){t._eventAnnounce(e,i)},initialize:function(e){return t.app.toData([t.collection.get(e)],t.history_id)},progress:function(e,i){t._eventProgress(e,i)},success:function(e,i){t._eventSuccess(e,i)},error:function(e,i){t._eventError(e,i)},complete:function(){t._eventComplete()},ondragover:function(){t.$(".upload-box").addClass("highlight")},ondragleave:function(){t.$(".upload-box").removeClass("highlight")}}),this.ftp=new s.View({title:"FTP files",container:this.btnFtp.$el}),this.select_extension=new l.View({css:"upload-footer-selection",container:this.$(".upload-footer-extension"),data:a.filter(this.list_extensions,function(e){return!e.composite_files}),value:this.options.default_extension,onchange:function(e){t.updateExtension(e)}}),this.$(".upload-footer-extension-info").on("click",function(e){t.showExtensionInfo({$el:r(e.target),title:t.select_extension.text(),extension:t.select_extension.value(),placement:"top"})}).on("mousedown",function(e){e.preventDefault()}),this.select_genome=new l.View({css:"upload-footer-selection",container:this.$(".upload-footer-genome"),data:this.list_genomes,value:this.options.default_genome,onchange:function(e){t.updateGenome(e)}}),this.collection.on("remove",function(e){t._eventRemove(e)}),this._updateScreen()},_eventAnnounce:function(e,n){this.counter.announce++;var s=new t.Model({id:e,file_name:n.name,file_size:n.size,file_mode:n.mode||"local",file_path:n.path,file_data:n});this.collection.add(s);var o=new i(this,{model:s});this.$(".upload-table > tbody:first").append(o.$el),this._updateScreen(),o.render()},_eventProgress:function(e,t){var i=this.collection.get(e);i.set("percentage",t),this.ui_button.model.set("percentage",this._uploadPercentage(t,i.get("file_size")))},_eventSuccess:function(e,t){var i=this.collection.get(e);i.set({percentage:100,status:"success"}),this.ui_button.model.set("percentage",this._uploadPercentage(100,i.get("file_size"))),this.upload_completed+=100*i.get("file_size"),this.counter.announce--,this.counter.success++,this._updateScreen(),Galaxy.currHistoryPanel.refreshContents()},_eventError:function(e,t){var i=this.collection.get(e);i.set({percentage:100,status:"error",info:t}),this.ui_button.model.set({percentage:this._uploadPercentage(100,i.get("file_size")),status:"danger"}),this.upload_completed+=100*i.get("file_size"),this.counter.announce--,this.counter.error++,this._updateScreen()},_eventComplete:function(){this.collection.each(function(e){"queued"==e.get("status")&&e.set("status","init")}),this.counter.running=0,this._updateScreen()},_eventRemove:function(e){var t=e.get("status");"success"==t?this.counter.success--:"error"==t?this.counter.error--:this.counter.announce--,this.uploadbox.remove(e.id),this._updateScreen()},showExtensionInfo:function(e){var t=this,i=e.$el,n=e.extension,o=e.title,r=a.findWhere(t.list_extensions,{id:n});this.extension_popup&&this.extension_popup.remove(),this.extension_popup=new s.View({placement:e.placement||"bottom",container:i}),this.extension_popup.title(o),this.extension_popup.empty(),this.extension_popup.append(this._templateDescription(r)),this.extension_popup.show()},_eventFtp:function(){if(this.ftp.visible)this.ftp.hide();else{this.ftp.empty();var e=this;this.ftp.append(new n({collection:this.collection,ftp_upload_site:this.ftp_upload_site,onadd:function(t){e.uploadbox.add([{mode:"ftp",name:t.path,size:t.size,path:t.path}])},onremove:function(t){e.collection.remove(t)}}).$el),this.ftp.show()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(!(0==this.counter.announce||this.counter.running>0)){var e=this;this.upload_size=0,this.upload_completed=0,this.collection.each(function(t){"init"==t.get("status")&&(t.set("status","queued"),e.upload_size+=t.get("file_size"))}),this.ui_button.model.set({percentage:0,status:"success"}),this.counter.running=this.counter.announce,this.history_id=this.app.currentHistory(),this.uploadbox.start(),this._updateScreen()}},_eventStop:function(){this.counter.running>0&&(this.ui_button.model.set("status","info"),r(".upload-top-info").html("Queue will pause after completing the current file..."),this.uploadbox.stop())},_eventReset:function(){0==this.counter.running&&(this.collection.reset(),this.counter.reset(),this.uploadbox.reset(),this.select_extension.value(this.options.default_extension),this.select_genome.value(this.options.default_genome),this.ui_button.model.set("percentage",0),this._updateScreen())},updateExtension:function(e,t){var i=this;this.collection.each(function(n){"init"!=n.get("status")||n.get("extension")!=i.options.default_extension&&t||n.set("extension",e)})},updateGenome:function(e,t){var i=this;this.collection.each(function(n){"init"!=n.get("status")||n.get("genome")!=i.options.default_genome&&t||n.set("genome",e)})},_updateScreen:function(){var e="";e=0==this.counter.announce?this.uploadbox.compatible()?" ":"Browser does not support Drag & Drop. Try Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.":0==this.counter.running?"You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed.":"Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining.",this.$(".upload-top-info").html(e);var t=0==this.counter.running&&this.counter.announce+this.counter.success+this.counter.error>0,i=0==this.counter.running&&this.counter.announce>0,n=0==this.counter.running,s=this.counter.announce+this.counter.success+this.counter.error>0;this.btnReset[t?"enable":"disable"](),this.btnStart[i?"enable":"disable"](),this.btnStart.$el[i?"addClass":"removeClass"]("btn-primary"),this.btnStop[this.counter.running>0?"enable":"disable"](),this.btnLocal[n?"enable":"disable"](),this.btnFtp[n?"enable":"disable"](),this.btnCreate[n?"enable":"disable"](),this.btnFtp.$el[this.ftp_upload_site?"show":"hide"](),this.$(".upload-table")[s?"show":"hide"](),this.$(".upload-helper")[s?"hide":"show"]()},_uploadPercentage:function(e,t){return(this.upload_completed+e*t)/this.upload_size},_templateDescription:function(e){if(e.description){var t=e.description;return e.description_url&&(t+=' (read more)'),t}return"There is no description available for this file extension."},_template:function(){return'   Name Size Type Genome Settings Status ").addClass("ui-panel-table")),t.use_remote_user||(e._link(e.defs.information),e._link(e.defs.password)),t.enable_communication_server&&e._link(e.defs.communication),e._link(e.defs.permissions),e._link(e.defs.api_key),t.has_user_tool_filters&&e._link(e.defs.toolbox_filters),t.enable_openid&&!t.use_remote_user&&e._link(e.defs.openids),e._link(e.defs.logout),e.$preferences.append(e._templateFooter(i)),e.$el.empty().append(e.$preferences)})},_link:function(i){var n=this,s=a(this._templateRow(i));this.$table.append(s),s.find("a").on("click",function(){i.url?a.ajax({url:Galaxy.root+i.url,type:"GET"}).done(function(s){var o=a.extend({},i,s),r=new e({title:o.title,icon:o.icon,inputs:o.inputs,operations:{submit:new t.ButtonIcon({tooltip:o.submit_tooltip,title:o.submit_title||"Save settings",icon:o.submit_icon||"fa-save",onclick:function(){n._submit(r,o)}}),back:new t.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to user preferences",title:"Preferences",onclick:function(){r.remove(),n.$preferences.show()}})}});n.$preferences.hide(),n.$el.append(r.$el)}).fail(function(e){n.message.update({message:"Failed to load resource "+i.url+".",status:"danger"})}):i.onclick()})},_submit:function(e,t){var i=this;a.ajax({url:t.url,data:JSON.stringify(e.data.create()),type:"PUT",contentType:"application/json"}).done(function(t){var n=!1;e.data.matchModel(t,function(t,i){e.field_list[i].value(t.value),n=!0}),n?e.message.update({message:t.message,status:"success"}):(e.remove(),i.$preferences.show(),i.message.update({message:t.message,status:"success"}))}).fail(function(t){e.message.update({message:t.responseJSON.err_msg,status:"danger"})})},_templateRow:function(e){return'
"},_templateFooter:function(e){return''+e.title+' ").addClass("ui-panel-table")),t.use_remote_user||(e._link(e.defs.information),e._link(e.defs.password)),t.enable_communication_server&&e._link(e.defs.communication),e._link(e.defs.permissions),e._link(e.defs.api_key),t.has_user_tool_filters&&e._link(e.defs.toolbox_filters),t.enable_openid&&!t.use_remote_user&&e._link(e.defs.openids),e._link(e.defs.logout),e.$preferences.append(e._templateFooter(i)),e.$el.empty().append(e.$preferences)})},_link:function(i){var n=this,s=a(this._templateRow(i));this.$table.append(s),s.find("a").on("click",function(){i.url?a.ajax({url:Galaxy.root+i.url,type:"GET"}).done(function(s){var o=a.extend({},i,s),r=new e({title:o.title,icon:o.icon,inputs:o.inputs,operations:{submit:new t.ButtonIcon({tooltip:o.submit_tooltip,title:o.submit_title||"Save settings",icon:o.submit_icon||"fa-save",onclick:function(){n._submit(r,o)}}),back:new t.ButtonIcon({icon:"fa-caret-left",tooltip:"Return to user preferences",title:"Preferences",onclick:function(){r.remove(),n.$preferences.show()}})}});n.$preferences.hide(),n.$el.append(r.$el)}).fail(function(e){n.message.update({message:"Failed to load resource "+i.url+".",status:"danger"})}):i.onclick()})},_submit:function(e,t){var i=this;a.ajax({url:t.url,data:JSON.stringify(e.data.create()),type:"PUT",contentType:"application/json"}).done(function(t){var n=!1;e.data.matchModel(t,function(t,i){e.field_list[i].value(t.value),n=!0}),n?e.message.update({message:t.message,status:"success"}):(e.remove(),i.$preferences.show(),i.message.update({message:t.message,status:"success"}))}).fail(function(t){e.message.update({message:t.responseJSON.err_msg,status:"danger"})})},_templateRow:function(e){return'
"},_templateFooter:function(e){return''+e.title+' ').attr({\n\t id: 'content_table',\n\t cellpadding: 0\n\t });\n\t this.$el.append(data_table);\n\t var column_names = this.model.get_metadata('column_names'),\n\t header_container = $('').appendTo(data_table),\n\t header_row = $('
').appendTo(header_container);\n\t if (column_names) {\n\t header_row.append(' ' + column_names.join(' ') + ' ');\n\t } else {\n\t for (var j = 1; j <= this.model.get_metadata('columns'); j++) {\n\t header_row.append('' + j + ' ');\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.collection = new Backbone.Collection();\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.listenTo( this.collection, 'change add remove reset', 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 && this.collection.where( { visible: true } ).length > 0 ? '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 this.$menu && this.$menu.remove();\n\t if ( this.collection.length > 0 ) {\n\t this.$menu = $( ' ' ).addClass( 'menu dropdown-menu' )\n\t .addClass( 'pull-' + self.model.get( 'pull' ) )\n\t .attr( 'role', 'menu' );\n\t this.$el.append( this.$menu );\n\t }\n\t this.collection.each( function( submodel ) {\n\t var suboptions = submodel.attributes;\n\t if ( suboptions.visible ) {\n\t var $link = $( '' ).addClass( 'dropdown-item' )\n\t .attr( { href : suboptions.href, target : suboptions.target } )\n\t .append( $( '' ).addClass( 'fa' )\n\t .addClass( suboptions.icon )\n\t .css( 'display', suboptions.icon ? 'inline-block' : 'none' ) )\n\t .append( suboptions.title )\n\t .on( 'click', function( e ) {\n\t if ( suboptions.onclick ) {\n\t e.preventDefault();\n\t suboptions.onclick();\n\t }\n\t } );\n\t self.$menu.append( $( '' ).append( $link ) );\n\t suboptions.divider && self.$menu.append( $( '' ).addClass( 'divider' ) );\n\t }\n\t });\n\t },\n\t\n\t /** Add a new menu item */\n\t addMenu: function ( options ) {\n\t this.collection.add( Utils.merge( options, {\n\t title : '',\n\t target : '',\n\t href : '',\n\t onclick : null,\n\t divider : false,\n\t visible : true,\n\t icon : null,\n\t cls : 'button-menu btn-group'\n\t }));\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__(29),\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(Backbone, $, _) {/* This is the regular tool form */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(7), __webpack_require__(8), __webpack_require__(44), __webpack_require__(27) ], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, Ui, Modal, ToolFormBase, Webhooks ) {\n\t var View = Backbone.View.extend({\n\t initialize: function( options ) {\n\t var self = this;\n\t this.modal = parent.Galaxy.modal || new Modal.View();\n\t this.form = new ToolFormBase( Utils.merge({\n\t listen_to_history : true,\n\t always_refresh : false,\n\t customize : function( form ) {\n\t var options = form.model.attributes;\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 form.portlet.disable();\n\t self.submit( options, function() {\n\t execute_btn.unwait();\n\t form.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.push({\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 postchange : function( process, form ) {\n\t var self = this;\n\t var current_state = {\n\t tool_id : form.model.get( 'id' ),\n\t tool_version : form.model.get( 'version' ),\n\t inputs : $.extend(true, {}, form.data.create())\n\t }\n\t form.wait( true );\n\t Galaxy.emit.debug( 'tool-form::postchange()', 'Sending current state.', current_state );\n\t Utils.request({\n\t type : 'POST',\n\t url : Galaxy.root + 'api/tools/' + form.model.get( 'id' ) + '/build',\n\t data : current_state,\n\t success : function( data ) {\n\t form.update( data );\n\t form.wait( false );\n\t Galaxy.emit.debug( 'tool-form::postchange()', 'Received new model.', data );\n\t process.resolve();\n\t },\n\t error : function( response ) {\n\t Galaxy.emit.debug( 'tool-form::postchange()', 'Refresh request failed.', response );\n\t process.reject();\n\t }\n\t });\n\t }\n\t }, options ) );\n\t this.deferred = this.form.deferred;\n\t this.setElement( '' );\n\t this.$el.append( this.form.$el );\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.form.data.create()\n\t }\n\t this.form.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 // Show Webhook if job is running\n\t if ( response.jobs && response.jobs.length > 0 ) {\n\t self.$el.append( $( '', { id: 'webhook-view' } ) );\n\t var WebhookApp = new Webhooks.WebhookView({\n\t urlRoot: Galaxy.root + 'api/webhooks/tool'\n\t });\n\t }\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.form.data.matchResponse( response.err_data );\n\t for ( var input_id in error_messages ) {\n\t self.form.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 : self._templateError( job_def, response && response.err_msg ),\n\t buttons : { 'Close' : function() { self.modal.hide() } }\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.form.data.match( job_input_id );\n\t var input_field = this.form.field_list[ input_id ];\n\t var input_def = this.form.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.form.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.form.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.form.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, 'Invalid success response. No jobs found.' );\n\t }\n\t },\n\t\n\t _templateError: function( response, err_msg ) {\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. ' + ( err_msg || '' ) ) )\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\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __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 '' + 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',\n\t '
',\n\t '',\n\t '
',\n\t '<% _.each( problems, function( problem ){ %>',\n\t '
'\n\t ].join('')),\n\t\n\t /** shown in list when all elements are discarded */\n\t noElementsLeft : _.template([\n\t '<% _.each( problems, function( problem ){ %>',\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 '<%= dataset.peek %>
',\n\t '<% } %>',\n\t '<% } %>',\n\t '');\n\t wrapper.append($el);\n\t this.row.append(wrapper);\n\t },\n\t \n\t // header\n\t appendHeader: function() {\n\t // append header row\n\t this.$thead.append(this.row);\n\t\n\t // row\n\t this.row = $(' ');\n\t },\n\t \n\t // add row cell\n\t add: function($el, width, align) {\n\t var wrapper = $(' ');\n\t if (width) {\n\t wrapper.css('width', width);\n\t }\n\t if (align) {\n\t wrapper.css('text-align', align);\n\t }\n\t wrapper.append($el);\n\t this.row.append(wrapper);\n\t },\n\t \n\t // append\n\t append: function(id, fade) {\n\t this._commit(id, fade, false);\n\t },\n\t \n\t // prepend\n\t prepend: function(id, fade) {\n\t this._commit(id, fade, true);\n\t },\n\t \n\t // get element\n\t get: function(id) {\n\t return this.$el.find('#' + id);\n\t },\n\t \n\t // delete\n\t del: function(id) {\n\t var item = this.$tbody.find('#' + id);\n\t if (item.length > 0) {\n\t item.remove();\n\t this.row_count--;\n\t this._refresh();\n\t }\n\t },\n\t\n\t // delete all\n\t delAll: function() {\n\t this.$tbody.empty();\n\t this.row_count = 0;\n\t this._refresh();\n\t },\n\t \n\t // value\n\t value: function(new_value) {\n\t // get current id/value\n\t this.before = this.$tbody.find('.current').attr('id');\n\t \n\t // check if new_value is defined\n\t if (new_value !== undefined) {\n\t this.$tbody.find('tr').removeClass('current');\n\t if (new_value) {\n\t this.$tbody.find('#' + new_value).addClass('current');\n\t }\n\t }\n\t \n\t // get current id/value\n\t var after = this.$tbody.find('.current').attr('id');\n\t if(after === undefined) {\n\t return null;\n\t } else {\n\t // fire onchange\n\t if (after != this.before && this.options.onchange) {\n\t this.options.onchange(new_value);\n\t }\n\t \n\t // return current value\n\t return after;\n\t }\n\t },\n\t \n\t // size\n\t size: function() {\n\t return this.$tbody.find('tr').length;\n\t },\n\t \n\t // commit\n\t _commit: function(id, fade, prepend) {\n\t // remove previous item with same id\n\t this.del(id);\n\t \n\t // add\n\t this.row.attr('id', id);\n\t \n\t // add row\n\t if (prepend) {\n\t this.$tbody.prepend(this.row);\n\t } else {\n\t this.$tbody.append(this.row);\n\t }\n\t \n\t // fade mode\n\t if (fade) {\n\t this.row.hide();\n\t this.row.fadeIn();\n\t }\n\t \n\t // row\n\t this.row = this._row();\n\t \n\t // row count\n\t this.row_count++;\n\t this._refresh();\n\t },\n\t \n\t // create new row\n\t _row: function() {\n\t return $(' ');\n\t },\n\t \n\t // onclick\n\t _onclick: function(e) {\n\t // get values\n\t var old_value = this.value();\n\t var new_value = $(e.target).closest('tr').attr('id');\n\t if (new_value != ''){\n\t // check equality\n\t if (new_value && old_value != new_value) {\n\t if (this.options.onconfirm) {\n\t this.options.onconfirm(new_value);\n\t } else {\n\t this.value(new_value);\n\t }\n\t }\n\t }\n\t },\n\t\n\t // ondblclick\n\t _ondblclick: function(e) {\n\t var value = this.value();\n\t if (value && this.options.ondblclick) {\n\t this.options.ondblclick(value);\n\t }\n\t },\n\t \n\t // refresh\n\t _refresh: function() {\n\t if (this.row_count == 0) {\n\t this.$tmessage.show();\n\t } else {\n\t this.$tmessage.hide();\n\t }\n\t },\n\t \n\t // load html template\n\t _template: function(options) {\n\t return ' ' +\n\t '' +\n\t '' +\n\t '
' +\n\t '
';\n\t helpString += data.help;\n\t } else {\n\t helpString += 'Tool help is unavailable for this dataset.
';\n\t }\n\t helpString += '
\" target=\"<%- view.linkTarget %>\">',\n\t _l( 'Set it manually or retry auto-detection' ),\n\t '',\n\t '
', _l( 'Undelete it' ), '',\n\t '<% if( view.purgeAllowed ){ %>',\n\t '
',\n\t _l( 'Permanently remove it from disk' ),\n\t '',\n\t '<% } %>',\n\t '',\n\t '<% _.each( visualizations, function( visualization ){ %>',\n\t '
',\n\t '', '
',\n\t '', this._templateOptions( options ), '
'\n\t ].join( '' );\n\t },\n\t\n\t _templateOptions : function( options ){\n\t if( !options.length ){\n\t return '' +\n\t ' ';\n\t },\n\t\n\t /** Template of table */\n\t _templateTable: function() {\n\t return 'Available files: ' +\n\t '' +\n\t '' +\n\t ' ' +\n\t '' +\n\t '' +\n\t '' +\n\t '' +\n\t ' ' + _.escape(options.path) + ' ' +\n\t '' + Utils.bytesToString( options.size ) + ' ' +\n\t '' + options.ctime + ' ' +\n\t '' +\n\t '' +\n\t '
';\n\t },\n\t\n\t /** Template of info message */\n\t _templateInfo: function() {\n\t return '' +\n\t ' ' +\n\t '' +\n\t '' +\n\t '' +\n\t ' Name ' +\n\t 'Size ' +\n\t 'Created ' +\n\t '' ).addClass( 'upload-settings-table ui-table-striped' ).append( '' ) );\n\t this.$cover = this.$( '.upload-settings-cover' );\n\t this.$table = this.$( '.upload-settings-table > tbody' );\n\t this.listenTo ( this.model, 'change', this.render, this );\n\t this.model.trigger( 'change' );\n\t },\n\t\n\t render: function() {\n\t var self = this;\n\t this.$table.empty();\n\t _.each( this.options.parameters, function( parameter ) {\n\t var $checkbox = $( '' ).addClass( 'upload-' + parameter.id + ' upload-icon-button fa' )\n\t .addClass( self.model.get( parameter.id ) && self.options.class_check || self.options.class_uncheck )\n\t .on( 'click', function() {\n\t self.model.get( 'enabled' ) && self.model.set( parameter.id, !self.model.get( parameter.id ) )\n\t });\n\t self.$table.append( $( '
' ).append( $( ' ' ).append( $checkbox ) )\n\t .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/* 83 */,\n/* 84 */,\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, $) {(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/* 86 */\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/* 87 */,\n/* 88 */\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/* 89 */\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/* 90 */\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__( 117 );\n\t CurrentHistoryView = __webpack_require__( 114 ).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 ' ' + 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 // ------------------------------------------------------------------------ rendering\n\t /** render the entire interface */\n\t render : function( speed, callback ){\n\t //this.debug( '-- _render' );\n\t //this.$el.empty().html( PairedCollectionCreator.templates.main() );\n\t this.$el.empty().html( PairedCollectionCreator.templates.main() );\n\t this._renderHeader( speed );\n\t this._renderMiddle( speed );\n\t this._renderFooter( speed );\n\t this._addPluginComponents();\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 //this.debug( '-- _renderHeader' );\n\t var $header = this.$( '.header' ).empty().html( PairedCollectionCreator.templates.header() )\n\t .find( '.help-content' ).prepend( $( PairedCollectionCreator.templates.helpContent() ) );\n\t\n\t this._renderFilters();\n\t return $header;\n\t },\n\t /** fill the filter inputs with the filter values */\n\t _renderFilters : function(){\n\t return this.$( '.forward-column .column-header input' ).val( this.filters[0] )\n\t .add( this.$( '.reverse-column .column-header input' ).val( this.filters[1] ) );\n\t },\n\t\n\t /** render the middle including unpaired and paired sections (which may be hidden) */\n\t _renderMiddle : function( speed, callback ){\n\t var $middle = this.$( '.middle' ).empty().html( PairedCollectionCreator.templates.middle() );\n\t\n\t // (re-) hide the un/paired panels based on instance vars\n\t if( this.unpairedPanelHidden ){\n\t this.$( '.unpaired-columns' ).hide();\n\t } else if( this.pairedPanelHidden ){\n\t this.$( '.paired-columns' ).hide();\n\t }\n\t\n\t this._renderUnpaired();\n\t this._renderPaired();\n\t return $middle;\n\t },\n\t /** render the unpaired section, showing datasets accrd. to filters, update the unpaired counts */\n\t _renderUnpaired : function( speed, callback ){\n\t //this.debug( '-- _renderUnpaired' );\n\t var creator = this,\n\t $fwd, $rev, $prd = [],\n\t split = this._splitByFilters();\n\t // update unpaired counts\n\t this.$( '.forward-column .title' )\n\t .text([ split[0].length, _l( 'unpaired forward' ) ].join( ' ' ));\n\t this.$( '.forward-column .unpaired-info' )\n\t .text( this._renderUnpairedDisplayStr( this.unpaired.length - split[0].length ) );\n\t this.$( '.reverse-column .title' )\n\t .text([ split[1].length, _l( 'unpaired reverse' ) ].join( ' ' ));\n\t this.$( '.reverse-column .unpaired-info' )\n\t .text( this._renderUnpairedDisplayStr( this.unpaired.length - split[1].length ) );\n\t\n\t this.$( '.unpaired-columns .column-datasets' ).empty();\n\t\n\t // show/hide the auto pair button if any unpaired are left\n\t this.$( '.autopair-link' ).toggle( this.unpaired.length !== 0 );\n\t if( this.unpaired.length === 0 ){\n\t this._renderUnpairedEmpty();\n\t return;\n\t }\n\t\n\t // create the dataset dom arrays\n\t $rev = split[1].map( function( dataset, i ){\n\t // if there'll be a fwd dataset across the way, add a button to pair the row\n\t if( ( split[0][ i ] !== undefined )\n\t && ( split[0][ i ] !== dataset ) ){\n\t $prd.push( creator._renderPairButton() );\n\t }\n\t return creator._renderUnpairedDataset( dataset );\n\t });\n\t $fwd = split[0].map( function( dataset ){\n\t return creator._renderUnpairedDataset( dataset );\n\t });\n\t\n\t if( !$fwd.length && !$rev.length ){\n\t this._renderUnpairedNotShown();\n\t return;\n\t }\n\t // add to appropo cols\n\t //TODO: not the best way to render - consider rendering the entire unpaired-columns section in a fragment\n\t // and swapping out that\n\t this.$( '.unpaired-columns .forward-column .column-datasets' ).append( $fwd )\n\t .add( this.$( '.unpaired-columns .paired-column .column-datasets' ).append( $prd ) )\n\t .add( this.$( '.unpaired-columns .reverse-column .column-datasets' ).append( $rev ) );\n\t this._adjUnpairedOnScrollbar();\n\t },\n\t /** return a string to display the count of filtered out datasets */\n\t _renderUnpairedDisplayStr : function( numFiltered ){\n\t return [ '(', numFiltered, ' ', _l( 'filtered out' ), ')' ].join('');\n\t },\n\t /** return an unattached jQuery DOM element to represent an unpaired dataset */\n\t _renderUnpairedDataset : function( dataset ){\n\t //TODO: to underscore template\n\t return $( '')\n\t .attr( 'id', 'dataset-' + dataset.id )\n\t .addClass( 'dataset unpaired' )\n\t .attr( 'draggable', true )\n\t .addClass( dataset.selected? 'selected': '' )\n\t .append( $( '' ).addClass( 'dataset-name' ).text( dataset.name ) )\n\t //??\n\t .data( 'dataset', dataset );\n\t },\n\t /** render the button that may go between unpaired datasets, allowing the user to pair a row */\n\t _renderPairButton : function(){\n\t //TODO: *not* a dataset - don't pretend like it is\n\t return $( '').addClass( 'dataset unpaired' )\n\t .append( $( '' ).addClass( 'dataset-name' ).text( _l( 'Pair these datasets' ) ) );\n\t },\n\t /** a message to display when no unpaired left */\n\t _renderUnpairedEmpty : function(){\n\t //this.debug( '-- renderUnpairedEmpty' );\n\t var $msg = $( '' )\n\t .text( '(' + _l( 'no remaining unpaired datasets' ) + ')' );\n\t this.$( '.unpaired-columns .paired-column .column-datasets' ).empty().prepend( $msg );\n\t return $msg;\n\t },\n\t /** a message to display when no unpaired can be shown with the current filters */\n\t _renderUnpairedNotShown : function(){\n\t //this.debug( '-- renderUnpairedEmpty' );\n\t var $msg = $( '' )\n\t .text( '(' + _l( 'no datasets were found matching the current filters' ) + ')' );\n\t this.$( '.unpaired-columns .paired-column .column-datasets' ).empty().prepend( $msg );\n\t return $msg;\n\t },\n\t /** try to detect if the unpaired section has a scrollbar and adjust left column for better centering of all */\n\t _adjUnpairedOnScrollbar : function(){\n\t var $unpairedColumns = this.$( '.unpaired-columns' ).last(),\n\t $firstDataset = this.$( '.unpaired-columns .reverse-column .dataset' ).first();\n\t if( !$firstDataset.length ){ return; }\n\t var ucRight = $unpairedColumns.offset().left + $unpairedColumns.outerWidth(),\n\t dsRight = $firstDataset.offset().left + $firstDataset.outerWidth(),\n\t rightDiff = Math.floor( ucRight ) - Math.floor( dsRight );\n\t //this.debug( 'rightDiff:', ucRight, '-', dsRight, '=', rightDiff );\n\t this.$( '.unpaired-columns .forward-column' )\n\t .css( 'margin-left', ( rightDiff > 0 )? rightDiff: 0 );\n\t },\n\t\n\t /** render the paired section and update counts of paired datasets */\n\t _renderPaired : function( speed, callback ){\n\t //this.debug( '-- _renderPaired' );\n\t this.$( '.paired-column-title .title' ).text([ this.paired.length, _l( 'paired' ) ].join( ' ' ) );\n\t // show/hide the unpair all link\n\t this.$( '.unpair-all-link' ).toggle( this.paired.length !== 0 );\n\t if( this.paired.length === 0 ){\n\t this._renderPairedEmpty();\n\t return;\n\t //TODO: would be best to return here (the $columns)\n\t } else {\n\t // show/hide 'remove extensions link' when any paired and they seem to have extensions\n\t this.$( '.remove-extensions-link' ).show();\n\t }\n\t\n\t this.$( '.paired-columns .column-datasets' ).empty();\n\t var creator = this;\n\t this.paired.forEach( function( pair, i ){\n\t //TODO: cache these?\n\t var pairView = new PairView({ pair: pair });\n\t creator.$( '.paired-columns .column-datasets' )\n\t .append( pairView.render().$el )\n\t .append([\n\t ''\n\t ].join( '' ));\n\t });\n\t },\n\t /** a message to display when none paired */\n\t _renderPairedEmpty : function(){\n\t var $msg = $( '' )\n\t .text( '(' + _l( 'no paired datasets yet' ) + ')' );\n\t this.$( '.paired-columns .column-datasets' ).empty().prepend( $msg );\n\t return $msg;\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( PairedCollectionCreator.templates.footer() );\n\t this.$( '.remove-extensions' ).prop( 'checked', this.removeExtensions );\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._chooseFiltersPopover( '.choose-filters-link' );\n\t this.$( '.help-content i' ).hoverhighlight( '.collection-creator', 'rgba( 64, 255, 255, 1.0 )' );\n\t },\n\t\n\t /** build a filter selection popover allowing selection of common filter pairs */\n\t _chooseFiltersPopover : function( selector ){\n\t function filterChoice( val1, val2 ){\n\t return [\n\t ''\n\t ].join('');\n\t }\n\t var $popoverContent = $( _.template([\n\t '
',\n\t '',\n\t _l( 'Auto-pair' ),\n\t '',\n\t '',\n\t '
',\n\t '
',\n\t '
',\n\t '
',\n\t '
',\n\t 'To pair datasets, you can:',\n\t '
'\n\t ].join( '' )), '',\n\t 'You can filter what is shown in the unpaired sections by:',\n\t '
'\n\t ].join( '' )), '',\n\t '',\n\t '
',\n\t '<% _.each( problems, function( problem ){ %>',\n\t '
',\n\t '<% } else if( _.size( elements ) === 0 ){ %>',\n\t _l( 'No datasets were selected' ), '.',\n\t '<% } else if( _.size( elements ) === 1 ){ %>',\n\t _l( 'Only one dataset was selected' ), ': <%- elements[0].name %>',\n\t '<% } else if( _.size( elements ) > 2 ){ %>',\n\t _l( 'Too many datasets were selected' ),\n\t ': <%- _.pluck( elements, \"name\" ).join( \", \") %>',\n\t '<% } %>',\n\t '
',\n\t _l( 'Two (and only two) elements are needed for the pair' ), '. ',\n\t _l( 'You may need to ' ),\n\t '', _l( 'cancel' ), ' ',\n\t _l( 'and reselect new elements' ), '.',\n\t '',\n\t '
', _l( 'Unhide it' ), '',\n\t '',\n\t '',\n\t '
' ).addClass( 'tab-navigation nav nav-tabs' ) )\n\t .append( $( '' ).addClass( 'tab-content' ) );\n\t },\n\t\n\t /** Tab template */\n\t _template_tab: function( options ) {\n\t var $tmpl = $( '' ).addClass( 'tab-element' )\n\t .attr( 'id', 'tab-' + options.id )\n\t .append( $( '' ).attr( 'id', 'tab-title-link-' + options.id ) );\n\t var $href = $tmpl.find( 'a' );\n\t options.icon && $href.append( $( '' ).addClass( 'tab-icon fa' ).addClass( options.icon ) );\n\t $href.append( $( '' ).attr( 'id', 'tab-title-text-' + options.id )\n\t .addClass( 'tab-title-text' )\n\t .append( options.title ) );\n\t return $tmpl;\n\t }\n\t });\n\t\n\t return { View : View }\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)))\n\n/***/ },\n/* 119 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $, _) {/** Renders the composite upload row view */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(82), __webpack_require__(81), __webpack_require__(25), __webpack_require__(7), __webpack_require__(26), __webpack_require__(89) ], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, UploadSettings, UploadFtp, Popover, Ui, Select ) {\n\t return Backbone.View.extend({\n\t /** Dictionary of upload states and associated icons */\n\t status_classes : {\n\t init : 'upload-mode fa fa-exclamation text-primary',\n\t ready : 'upload-mode fa fa-check text-success',\n\t running : 'upload-mode fa fa-spinner fa-spin',\n\t success : 'upload-mode fa fa-check',\n\t error : 'upload-mode fa fa-exclamation-triangle'\n\t },\n\t\n\t initialize: function( app, options ) {\n\t var self = this;\n\t this.app = app;\n\t this.model = options.model;\n\t this.setElement( this._template() );\n\t this.$source = this.$( '.upload-source' );\n\t this.$settings = this.$( '.upload-settings' );\n\t this.$status = this.$( '.upload-status' );\n\t this.$text = this.$( '.upload-text' );\n\t this.$text_content = this.$( '.upload-text-content' );\n\t this.$info_text = this.$( '.upload-info-text' );\n\t this.$info_progress = this.$( '.upload-info-progress' );\n\t this.$file_name = this.$( '.upload-file-name' );\n\t this.$file_desc = this.$( '.upload-file-desc' );\n\t this.$file_size = this.$( '.upload-file-size' );\n\t this.$progress_bar = this.$( '.upload-progress-bar' );\n\t this.$percentage = this.$( '.upload-percentage' );\n\t\n\t // build upload functions\n\t this.uploadinput = this.$el.uploadinput({\n\t ondragover: function() {\n\t self.model.get( 'enabled' ) && self.$el.addClass( 'warning' );\n\t },\n\t ondragleave: function() {\n\t self.$el.removeClass( 'warning' );\n\t },\n\t onchange: function( files ) {\n\t if ( self.model.get( 'status' ) != 'running' && files && files.length > 0 ) {\n\t self.model.reset({\n\t 'file_data': files[ 0 ],\n\t 'file_name': files[ 0 ].name,\n\t 'file_size': files[ 0 ].size,\n\t 'file_mode': files[ 0 ].mode || 'local'\n\t });\n\t self._refreshReady();\n\t }\n\t }\n\t });\n\t\n\t // source selection popup\n\t this.button_menu = new Ui.ButtonMenu({\n\t icon : 'fa-caret-down',\n\t title : 'Select',\n\t pull : 'left'\n\t });\n\t this.$source.append( this.button_menu.$el );\n\t this.button_menu.addMenu({\n\t icon : 'fa-laptop',\n\t title : 'Choose local file',\n\t onclick : function() { self.uploadinput.dialog() }\n\t });\n\t if ( this.app.ftp_upload_site ) {\n\t this.button_menu.addMenu({\n\t icon : 'fa-folder-open-o',\n\t title : 'Choose FTP file',\n\t onclick : function() { self._showFtp() }\n\t });\n\t }\n\t this.button_menu.addMenu({\n\t icon : 'fa-edit',\n\t title : 'Paste/Fetch data',\n\t onclick : function() {\n\t self.model.reset( { 'file_mode': 'new', 'file_name': 'New File' } );\n\t }\n\t });\n\t\n\t // add ftp file viewer\n\t this.ftp = new Popover.View({\n\t title : 'Choose FTP file:',\n\t container : this.$source.find( '.ui-button-menu' ),\n\t placement : 'right'\n\t });\n\t\n\t // append popup to settings icon\n\t this.settings = new Popover.View({\n\t title : 'Upload configuration',\n\t container : this.$settings,\n\t placement : 'bottom'\n\t });\n\t\n\t // handle text editing event\n\t this.$text_content.on( 'change input', function( e ) {\n\t self.model.set( { 'url_paste': $( e.target ).val(),\n\t 'file_size': $( e.target ).val().length } );\n\t self._refreshReady();\n\t });\n\t\n\t // handle settings popover\n\t this.$settings.on( 'click', function( e ) { self._showSettings() } )\n\t .on( 'mousedown', function( e ) { e.preventDefault() } );\n\t\n\t // model events\n\t this.listenTo( this.model, 'change:percentage', function() { self._refreshPercentage() } );\n\t this.listenTo( this.model, 'change:status', function() { self._refreshStatus() } );\n\t this.listenTo( this.model, 'change:info', function() { self._refreshInfo() } );\n\t this.listenTo( this.model, 'change:file_name', function() { self._refreshFileName() } );\n\t this.listenTo( this.model, 'change:file_mode', function() { self._refreshMode() } );\n\t this.listenTo( this.model, 'change:file_size', function() { self._refreshFileSize() } );\n\t this.listenTo( this.model, 'remove', function() { self.remove() } );\n\t this.app.collection.on( 'reset', function() { self.remove() } );\n\t },\n\t\n\t render: function() {\n\t this.$el.attr( 'id', 'upload-row-' + this.model.id );\n\t this.$file_name.html( _.escape( this.model.get( 'file_name' ) || '-' ) );\n\t this.$file_desc.html( this.model.get( 'file_desc' ) || 'Unavailable' );\n\t this.$file_size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );\n\t this.$status.removeClass().addClass( this.status_classes.init );\n\t },\n\t\n\t /** Remove view */\n\t remove: function() {\n\t // call the base class remove method\n\t Backbone.View.prototype.remove.apply( this );\n\t },\n\t\n\t //\n\t // handle model events\n\t //\n\t\n\t /** Refresh ready or not states */\n\t _refreshReady: function() {\n\t this.app.collection.each( function( model ) {\n\t model.set( 'status', ( model.get( 'file_size' ) > 0 ) && 'ready' || 'init' );\n\t });\n\t },\n\t\n\t /** Refresh mode and e.g. show/hide textarea field */\n\t _refreshMode: function() {\n\t var file_mode = this.model.get( 'file_mode' );\n\t if ( file_mode == 'new' ) {\n\t this.height = this.$el.height();\n\t this.$text.css( { 'width' : this.$el.width() - 16 + 'px',\n\t 'top' : this.$el.height() - 8 + 'px' } ).show();\n\t this.$el.height( this.$el.height() - 8 + this.$text.height() + 16 );\n\t this.$text_content.val( '' ).trigger( 'keyup' );\n\t } else {\n\t this.$el.height( this.height );\n\t this.$text.hide();\n\t }\n\t },\n\t\n\t /** Refresh information */\n\t _refreshInfo: function() {\n\t var info = this.model.get( 'info' );\n\t if ( info ) {\n\t this.$info_text.html( 'Failed: ' + info ).show();\n\t } else {\n\t this.$info_text.hide();\n\t }\n\t },\n\t\n\t /** Refresh percentage */\n\t _refreshPercentage : function() {\n\t var percentage = parseInt( this.model.get( 'percentage' ) );\n\t if ( percentage != 0 ) {\n\t this.$progress_bar.css( { width : percentage + '%' } );\n\t } else {\n\t this.$progress_bar.addClass( 'no-transition' );\n\t this.$progress_bar.css( { width : '0%' } );\n\t this.$progress_bar[ 0 ].offsetHeight;\n\t this.$progress_bar.removeClass( 'no-transition' );\n\t }\n\t this.$percentage.html( percentage != 100 ? percentage + '%' : 'Adding to history...' );\n\t },\n\t\n\t /** Refresh status */\n\t _refreshStatus : function() {\n\t var status = this.model.get( 'status' );\n\t this.$status.removeClass().addClass( this.status_classes[ status ] );\n\t this.model.set( 'enabled', status != 'running' );\n\t this.$text_content.attr( 'disabled', !this.model.get( 'enabled' ) );\n\t this.$el.removeClass( 'success danger warning' );\n\t if ( status == 'running' || status == 'ready' ) {\n\t this.model.set( 'percentage', 0 );\n\t }\n\t this.$source.find( '.button' )[ status == 'running' ? 'addClass' : 'removeClass' ]( 'disabled' );\n\t if ( status == 'success' ) {\n\t this.$el.addClass( 'success' );\n\t this.model.set( 'percentage', 100 );\n\t this.$percentage.html( '100%' );\n\t }\n\t if ( status == 'error' ) {\n\t this.$el.addClass( 'danger' );\n\t this.model.set( 'percentage', 0 );\n\t this.$info_progress.hide();\n\t this.$info_text.show();\n\t } else {\n\t this.$info_progress.show();\n\t this.$info_text.hide();\n\t }\n\t },\n\t\n\t /** File name */\n\t _refreshFileName: function() {\n\t this.$file_name.html( this.model.get( 'file_name' ) || '-' );\n\t },\n\t\n\t /** File size */\n\t _refreshFileSize: function() {\n\t this.$file_size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );\n\t },\n\t\n\t /** Show/hide ftp popup */\n\t _showFtp: function() {\n\t if ( !this.ftp.visible ) {\n\t var self = this;\n\t this.ftp.empty();\n\t this.ftp.append( ( new UploadFtp( {\n\t ftp_upload_site : this.app.ftp_upload_site,\n\t onchange : function( ftp_file ) {\n\t self.ftp.hide();\n\t if ( self.model.get( 'status' ) != 'running' && ftp_file ) {\n\t self.model.reset({\n\t 'file_mode': 'ftp',\n\t 'file_name': ftp_file.path,\n\t 'file_size': ftp_file.size,\n\t 'file_path': ftp_file.path\n\t });\n\t self._refreshReady();\n\t }\n\t }\n\t } ) ).$el );\n\t this.ftp.show();\n\t } else {\n\t this.ftp.hide();\n\t }\n\t },\n\t\n\t /** Show/hide settings popup */\n\t _showSettings : function() {\n\t if ( !this.settings.visible ) {\n\t this.settings.empty();\n\t this.settings.append( ( new UploadSettings( this ) ).$el );\n\t this.settings.show();\n\t } else {\n\t this.settings.hide();\n\t }\n\t },\n\t\n\t /** Template */\n\t _template: function() {\n\t return '
' +\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), __webpack_require__(2)))\n\n/***/ },\n/* 120 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _, $) {/** Renders contents of the composite uploader */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(53), __webpack_require__(119), __webpack_require__(25), __webpack_require__(26), __webpack_require__(7)], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, UploadModel, UploadRow, Popover, Select, Ui ) {\n\t return Backbone.View.extend({\n\t collection: new UploadModel.Collection(),\n\t initialize: function(app) {\n\t var self = this;\n\t this.app = app;\n\t this.options = app.options;\n\t this.list_extensions = app.list_extensions;\n\t this.list_genomes = app.list_genomes;\n\t this.ftp_upload_site = app.currentFtp();\n\t this.setElement( this._template() );\n\t\n\t // create button section\n\t this.btnStart = new Ui.Button( { title: 'Start', onclick: function() { self._eventStart() } } );\n\t this.btnClose = new Ui.Button( { title: 'Close', onclick: function() { self.app.modal.hide() } } );\n\t\n\t // append buttons to dom\n\t _.each( [ this.btnStart, this.btnClose ], function( button ) {\n\t self.$( '.upload-buttons' ).prepend( button.$el );\n\t });\n\t\n\t // select extension\n\t this.select_extension = new Select.View({\n\t css : 'upload-footer-selection',\n\t container : this.$( '.upload-footer-extension' ),\n\t data : _.filter( this.list_extensions, function( ext ) { return ext.composite_files } ),\n\t onchange : function( extension ) {\n\t self.collection.reset();\n\t var details = _.findWhere( self.list_extensions, { id : extension } );\n\t if ( details && details.composite_files ) {\n\t _.each( details.composite_files, function( item ) {\n\t self.collection.add( { id : self.collection.size(),\n\t file_desc : item.description || item.name } );\n\t } );\n\t }\n\t }\n\t });\n\t\n\t // handle extension info popover\n\t this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {\n\t self._showExtensionInfo({\n\t $el : $( e.target ),\n\t title : self.select_extension.text(),\n\t extension : self.select_extension.value(),\n\t placement : 'top'\n\t });\n\t }).on( 'mousedown', function( e ) { e.preventDefault() } );\n\t\n\t // genome extension\n\t this.select_genome = new Select.View({\n\t css : 'upload-footer-selection',\n\t container : this.$( '.upload-footer-genome' ),\n\t data : this.list_genomes,\n\t value : this.options.default_genome\n\t });\n\t\n\t // listener for collection triggers on change in composite datatype and extension selection\n\t this.listenTo( this.collection, 'add', function ( model ) { self._eventAnnounce( model ) } );\n\t this.listenTo( this.collection, 'change add', function() { self.render() } );\n\t this.select_extension.options.onchange( this.select_extension.value() );\n\t this.render();\n\t },\n\t\n\t render: function () {\n\t var model = this.collection.first();\n\t if ( model && model.get( 'status' ) == 'running' ) {\n\t this.select_genome.disable();\n\t this.select_extension.disable();\n\t } else {\n\t this.select_genome.enable();\n\t this.select_extension.enable();\n\t }\n\t if ( this.collection.where( { status : 'ready' } ).length == this.collection.length && this.collection.length > 0 ) {\n\t this.btnStart.enable();\n\t this.btnStart.$el.addClass( 'btn-primary' );\n\t } else {\n\t this.btnStart.disable();\n\t this.btnStart.$el.removeClass( 'btn-primary' );\n\t }\n\t this.$( '.upload-table' )[ this.collection.length > 0 ? 'show' : 'hide' ]();\n\t },\n\t\n\t //\n\t // upload events / process pipeline\n\t //\n\t\n\t /** Builds the basic ui with placeholder rows for each composite data type file */\n\t _eventAnnounce: function( model ) {\n\t var upload_row = new UploadRow( this, { model : model } );\n\t this.$( '.upload-table > tbody:first' ).append( upload_row.$el );\n\t this.$( '.upload-table' )[ this.collection.length > 0 ? 'show' : 'hide' ]();\n\t upload_row.render();\n\t },\n\t\n\t /** Start upload process */\n\t _eventStart: function() {\n\t var self = this;\n\t this.collection.each( function( model ) {\n\t model.set( { 'genome' : self.select_genome.value(),\n\t 'extension': self.select_extension.value() } );\n\t });\n\t $.uploadpost({\n\t url : this.app.options.nginx_upload_path,\n\t data : this.app.toData( this.collection.filter() ),\n\t success : function( message ) { self._eventSuccess( message ) },\n\t error : function( message ) { self._eventError( message ) },\n\t progress : function( percentage ) { self._eventProgress( percentage ) }\n\t });\n\t },\n\t\n\t /** Refresh progress state */\n\t _eventProgress: function( percentage ) {\n\t this.collection.each( function( it ) { it.set( 'percentage', percentage ) } );\n\t },\n\t\n\t /** Refresh success state */\n\t _eventSuccess: function( message ) {\n\t this.collection.each( function( it ) { it.set('status', 'success') } );\n\t Galaxy.currHistoryPanel.refreshContents();\n\t },\n\t\n\t /** Refresh error state */\n\t _eventError: function( message ) {\n\t this.collection.each( function( it ) { it.set( { 'status': 'error', 'info': message } ) } );\n\t },\n\t\n\t /** Display extension info popup */\n\t _showExtensionInfo: function(options) {\n\t var self = this;\n\t var $el = options.$el;\n\t var extension = options.extension;\n\t var title = options.title;\n\t var description = _.findWhere(this.list_extensions, { id : extension });\n\t this.extension_popup && this.extension_popup.remove();\n\t this.extension_popup = new Popover.View({\n\t placement: options.placement || 'bottom',\n\t container: $el,\n\t destroy: true\n\t });\n\t this.extension_popup.title( title );\n\t this.extension_popup.empty();\n\t this.extension_popup.append( this._templateDescription( description ) );\n\t this.extension_popup.show();\n\t },\n\t\n\t /* Template for extensions description */\n\t _templateDescription: function( options ) {\n\t if ( options.description ) {\n\t var tmpl = options.description;\n\t if ( options.description_url ) {\n\t tmpl += ' (read more)';\n\t }\n\t return tmpl;\n\t } else {\n\t return 'There is no description available for this file extension.';\n\t }\n\t },\n\t\n\t /** Load html template */\n\t _template: function() {\n\t return '' +\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 '' +\n\t ' ' +\n\t '' +\n\t '' +\n\t '' +\n\t ' ' +\n\t ' Description ' +\n\t 'Name ' +\n\t 'Size ' +\n\t 'Settings ' +\n\t 'Status ' +\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), __webpack_require__(2)))\n\n/***/ },\n/* 122 */\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _, $) {/** Renders contents of the default uploader */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(53), __webpack_require__(121), __webpack_require__(81), __webpack_require__(25), __webpack_require__(26), __webpack_require__(7), __webpack_require__(89)], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, UploadModel, UploadRow, UploadFtp, Popover, Select, Ui ) {\n\t return Backbone.View.extend({\n\t // current upload size in bytes\n\t upload_size: 0,\n\t\n\t // contains upload row models\n\t collection : new UploadModel.Collection(),\n\t\n\t // keeps track of the current uploader state\n\t counter : {\n\t announce : 0,\n\t success : 0,\n\t error : 0,\n\t running : 0,\n\t reset : function() { this.announce = this.success = this.error = this.running = 0 }\n\t },\n\t\n\t initialize : function( app ) {\n\t var self = this;\n\t this.app = app;\n\t this.options = app.options;\n\t this.list_extensions = app.list_extensions;\n\t this.list_genomes = app.list_genomes;\n\t this.ui_button = app.ui_button;\n\t this.ftp_upload_site = app.currentFtp();\n\t this.setElement( this._template() );\n\t\n\t // append buttons to dom\n\t this.btnLocal = new Ui.Button( { id: 'btn-local', title: 'Choose local file', onclick: function() { self.uploadbox.select() }, icon: 'fa fa-laptop' } );\n\t this.btnFtp = new Ui.Button( { id: 'btn-ftp', title: 'Choose FTP file', onclick: function() { self._eventFtp() }, icon: 'fa fa-folder-open-o' } );\n\t this.btnCreate = new Ui.Button( { id: 'btn-new', title: 'Paste/Fetch data', onclick: function() { self._eventCreate() }, icon: 'fa fa-edit' } );\n\t this.btnStart = new Ui.Button( { id: 'btn-start', title: 'Start', onclick: function() { self._eventStart() } } );\n\t this.btnStop = new Ui.Button( { id: 'btn-stop', title: 'Pause', onclick: function() { self._eventStop() } } );\n\t this.btnReset = new Ui.Button( { id: 'btn-reset', title: 'Reset', onclick: function() { self._eventReset() } } );\n\t this.btnClose = new Ui.Button( { id: 'btn-close', title: 'Close', onclick: function() { self.app.modal.hide() } } );\n\t _.each( [ this.btnLocal, this.btnFtp, this.btnCreate, this.btnStop, this.btnReset, this.btnStart, this.btnClose ], function( button ) {\n\t self.$( '.upload-buttons' ).prepend( button.$el );\n\t });\n\t\n\t // file upload\n\t this.uploadbox = this.$( '.upload-box' ).uploadbox({\n\t url : this.app.options.nginx_upload_path,\n\t announce : function( index, file ) { self._eventAnnounce( index, file ) },\n\t initialize : function( index ) { return self.app.toData( [ self.collection.get( index ) ], self.history_id ) },\n\t progress : function( index, percentage ) { self._eventProgress( index, percentage ) },\n\t success : function( index, message ) { self._eventSuccess( index, message ) },\n\t error : function( index, message ) { self._eventError( index, message ) },\n\t complete : function() { self._eventComplete() },\n\t ondragover : function() { self.$( '.upload-box' ).addClass( 'highlight' ) },\n\t ondragleave : function() { self.$( '.upload-box' ).removeClass( 'highlight' ) }\n\t });\n\t\n\t // add ftp file viewer\n\t this.ftp = new Popover.View( { title: 'FTP files', container: this.btnFtp.$el } );\n\t\n\t // select extension\n\t this.select_extension = new Select.View({\n\t css : 'upload-footer-selection',\n\t container : this.$( '.upload-footer-extension' ),\n\t data : _.filter( this.list_extensions, function( ext ) { return !ext.composite_files } ),\n\t value : this.options.default_extension,\n\t onchange : function( extension ) { self.updateExtension( extension ) }\n\t });\n\t\n\t // handle extension info popover\n\t this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {\n\t self.showExtensionInfo({\n\t $el : $( e.target ),\n\t title : self.select_extension.text(),\n\t extension : self.select_extension.value(),\n\t placement : 'top'\n\t });\n\t }).on( 'mousedown', function( e ) { e.preventDefault() } );\n\t\n\t // genome extension\n\t this.select_genome = new Select.View({\n\t css : 'upload-footer-selection',\n\t container : this.$( '.upload-footer-genome' ),\n\t data : this.list_genomes,\n\t value : this.options.default_genome,\n\t onchange : function( genome ) { self.updateGenome(genome) }\n\t });\n\t\n\t // events\n\t this.collection.on( 'remove', function( model ) { self._eventRemove( model ) } );\n\t this._updateScreen();\n\t },\n\t\n\t /** A new file has been dropped/selected through the uploadbox plugin */\n\t _eventAnnounce: function( index, file ) {\n\t this.counter.announce++;\n\t var new_model = new UploadModel.Model({\n\t id : index,\n\t file_name : file.name,\n\t file_size : file.size,\n\t file_mode : file.mode || 'local',\n\t file_path : file.path,\n\t file_data : file\n\t });\n\t this.collection.add( new_model );\n\t var upload_row = new UploadRow( this, { model: new_model } );\n\t this.$( '.upload-table > tbody:first' ).append( upload_row.$el );\n\t this._updateScreen();\n\t upload_row.render();\n\t },\n\t\n\t /** Progress */\n\t _eventProgress: function( index, percentage ) {\n\t var it = this.collection.get( index );\n\t it.set( 'percentage', percentage );\n\t this.ui_button.model.set( 'percentage', this._uploadPercentage( percentage, it.get( 'file_size' ) ) );\n\t },\n\t\n\t /** Success */\n\t _eventSuccess: function( index, message ) {\n\t var it = this.collection.get( index );\n\t it.set( { 'percentage': 100, 'status': 'success' } );\n\t this.ui_button.model.set( 'percentage', this._uploadPercentage( 100, it.get( 'file_size' ) ) );\n\t this.upload_completed += it.get( 'file_size' ) * 100;\n\t this.counter.announce--;\n\t this.counter.success++;\n\t this._updateScreen();\n\t Galaxy.currHistoryPanel.refreshContents();\n\t },\n\t\n\t /** Error */\n\t _eventError: function( index, message ) {\n\t var it = this.collection.get( index );\n\t it.set( { 'percentage': 100, 'status': 'error', 'info': message } );\n\t this.ui_button.model.set( { 'percentage': this._uploadPercentage( 100, it.get( 'file_size' ) ), 'status': 'danger' } );\n\t this.upload_completed += it.get( 'file_size' ) * 100;\n\t this.counter.announce--;\n\t this.counter.error++;\n\t this._updateScreen();\n\t },\n\t\n\t /** Queue is done */\n\t _eventComplete: function() {\n\t this.collection.each( function( model ) { model.get( 'status' ) == 'queued' && model.set( 'status', 'init' ) } );\n\t this.counter.running = 0;\n\t this._updateScreen();\n\t },\n\t\n\t /** Remove model from upload list */\n\t _eventRemove: function( model ) {\n\t var status = model.get( 'status' );\n\t if ( status == 'success' ) {\n\t this.counter.success--;\n\t } else if ( status == 'error' ) {\n\t this.counter.error--;\n\t } else {\n\t this.counter.announce--;\n\t }\n\t this.uploadbox.remove( model.id );\n\t this._updateScreen();\n\t },\n\t\n\t //\n\t // events triggered by this view\n\t //\n\t\n\t /** [public] display extension info popup */\n\t showExtensionInfo: function( options ) {\n\t var self = this;\n\t var $el = options.$el;\n\t var extension = options.extension;\n\t var title = options.title;\n\t var description = _.findWhere( self.list_extensions, { 'id': extension } );\n\t this.extension_popup && this.extension_popup.remove();\n\t this.extension_popup = new Popover.View({ placement: options.placement || 'bottom', container: $el } );\n\t this.extension_popup.title( title );\n\t this.extension_popup.empty();\n\t this.extension_popup.append( this._templateDescription( description ) );\n\t this.extension_popup.show();\n\t },\n\t\n\t /** Show/hide ftp popup */\n\t _eventFtp: function() {\n\t if ( !this.ftp.visible ) {\n\t this.ftp.empty();\n\t var self = this;\n\t this.ftp.append( ( new UploadFtp({\n\t collection : this.collection,\n\t ftp_upload_site : this.ftp_upload_site,\n\t onadd : function( ftp_file ) {\n\t self.uploadbox.add([{\n\t mode: 'ftp',\n\t name: ftp_file.path,\n\t size: ftp_file.size,\n\t path: ftp_file.path\n\t }]);\n\t },\n\t onremove: function( model_index ) {\n\t self.collection.remove( model_index );\n\t }\n\t } ) ).$el );\n\t this.ftp.show();\n\t } else {\n\t this.ftp.hide();\n\t }\n\t },\n\t\n\t /** Create a new file */\n\t _eventCreate: function (){\n\t this.uploadbox.add( [ { name: 'New File', size: 0, mode: 'new' } ] );\n\t },\n\t\n\t /** Start upload process */\n\t _eventStart: function() {\n\t if ( this.counter.announce == 0 || this.counter.running > 0 ) {\n\t return;\n\t }\n\t var self = this;\n\t this.upload_size = 0;\n\t this.upload_completed = 0;\n\t this.collection.each( function( model ) {\n\t if( model.get( 'status' ) == 'init' ) {\n\t model.set( 'status', 'queued' );\n\t self.upload_size += model.get( 'file_size' );\n\t }\n\t });\n\t this.ui_button.model.set( { 'percentage': 0, 'status': 'success' } );\n\t this.counter.running = this.counter.announce;\n\t this.history_id = this.app.currentHistory();\n\t this.uploadbox.start();\n\t this._updateScreen();\n\t },\n\t\n\t /** Pause upload process */\n\t _eventStop: function() {\n\t if ( this.counter.running > 0 ) {\n\t this.ui_button.model.set( 'status', 'info' );\n\t $( '.upload-top-info' ).html( 'Queue will pause after completing the current file...' );\n\t this.uploadbox.stop();\n\t }\n\t },\n\t\n\t /** Remove all */\n\t _eventReset: function() {\n\t if ( this.counter.running == 0 ){\n\t this.collection.reset();\n\t this.counter.reset();\n\t this.uploadbox.reset();\n\t this.select_extension.value( this.options.default_extension );\n\t this.select_genome.value( this.options.default_genome );\n\t this.ui_button.model.set( 'percentage', 0 );\n\t this._updateScreen();\n\t }\n\t },\n\t\n\t /** Update extension for all models */\n\t updateExtension: function( extension, defaults_only ) {\n\t var self = this;\n\t this.collection.each( function( model ) {\n\t if ( model.get( 'status' ) == 'init' && ( model.get( 'extension' ) == self.options.default_extension || !defaults_only ) ) {\n\t model.set( 'extension', extension );\n\t }\n\t });\n\t },\n\t\n\t /** Update genome for all models */\n\t updateGenome: function( genome, defaults_only ) {\n\t var self = this;\n\t this.collection.each( function( model ) {\n\t if ( model.get( 'status' ) == 'init' && ( model.get( 'genome' ) == self.options.default_genome || !defaults_only ) ) {\n\t model.set( 'genome', genome );\n\t }\n\t });\n\t },\n\t\n\t /** Set screen */\n\t _updateScreen: function () {\n\t var message = '';\n\t if( this.counter.announce == 0 ) {\n\t if (this.uploadbox.compatible()) {\n\t message = ' ';\n\t } else {\n\t message = 'Browser does not support Drag & Drop. Try Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.';\n\t }\n\t } else {\n\t if ( this.counter.running == 0 ) {\n\t message = 'You added ' + this.counter.announce + ' file(s) to the queue. Add more files or click \\'Start\\' to proceed.';\n\t } else {\n\t message = 'Please wait...' + this.counter.announce + ' out of ' + this.counter.running + ' remaining.';\n\t }\n\t }\n\t this.$( '.upload-top-info' ).html( message );\n\t var enable_reset = this.counter.running == 0 && this.counter.announce + this.counter.success + this.counter.error > 0;\n\t var enable_start = this.counter.running == 0 && this.counter.announce > 0;\n\t var enable_sources = this.counter.running == 0;\n\t var show_table = this.counter.announce + this.counter.success + this.counter.error > 0;\n\t this.btnReset[ enable_reset ? 'enable' : 'disable' ]();\n\t this.btnStart[ enable_start ? 'enable' : 'disable' ]();\n\t this.btnStart.$el[ enable_start ? 'addClass' : 'removeClass' ]( 'btn-primary' );\n\t this.btnStop[ this.counter.running > 0 ? 'enable' : 'disable' ]();\n\t this.btnLocal[ enable_sources ? 'enable' : 'disable' ]();\n\t this.btnFtp[ enable_sources ? 'enable' : 'disable' ]();\n\t this.btnCreate[ enable_sources ? 'enable' : 'disable' ]();\n\t this.btnFtp.$el[ this.ftp_upload_site ? 'show' : 'hide' ]();\n\t this.$( '.upload-table' )[ show_table ? 'show' : 'hide' ]();\n\t this.$( '.upload-helper' )[ show_table ? 'hide' : 'show' ]();\n\t },\n\t\n\t /** Calculate percentage of all queued uploads */\n\t _uploadPercentage: function( percentage, size ) {\n\t return ( this.upload_completed + ( percentage * size ) ) / this.upload_size;\n\t },\n\t\n\t /** Template for extensions description */\n\t _templateDescription: function( options ) {\n\t if ( options.description ) {\n\t var tmpl = options.description;\n\t if ( options.description_url ) {\n\t tmpl += ' (read more)';\n\t }\n\t return tmpl;\n\t } else {\n\t return 'There is no description available for this file extension.';\n\t }\n\t },\n\t\n\t /** Template */\n\t _template: function() {\n\t return '' +\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 '
' +\n\t '' +\n\t ' ' +\n\t '' +\n\t '' +\n\t 'Name ' +\n\t 'Size ' +\n\t 'Type ' +\n\t 'Genome ' +\n\t 'Settings ' +\n\t 'Status ' +\n\t '' +\n\t ' ' ).addClass( 'ui-panel-table' ) );\n\t if( !config.use_remote_user ) {\n\t self._link( self.defs.information );\n\t self._link( self.defs.password );\n\t }\n\t if( config.enable_communication_server ) {\n\t self._link( self.defs.communication );\n\t }\n\t self._link( self.defs.permissions );\n\t self._link( self.defs.api_key );\n\t if( config.has_user_tool_filters ) {\n\t self._link( self.defs.toolbox_filters );\n\t }\n\t if( config.enable_openid && !config.use_remote_user ) {\n\t self._link( self.defs.openids );\n\t }\n\t self._link( self.defs.logout );\n\t self.$preferences.append( self._templateFooter( data ) );\n\t self.$el.empty().append( self.$preferences );\n\t });\n\t },\n\t\n\t _link: function( page ) {\n\t var self = this;\n\t var $page_item = $( this._templateRow( page ) );\n\t this.$table.append( $page_item );\n\t $page_item.find( 'a' ).on( 'click', function() {\n\t if ( page.url ) {\n\t $.ajax({\n\t url : Galaxy.root + page.url,\n\t type : 'GET'\n\t }).done( function( response ) {\n\t var options = $.extend( {}, page, response );\n\t var form = new Form({\n\t title : options.title,\n\t icon : options.icon,\n\t inputs : options.inputs,\n\t operations: {\n\t 'submit': new Ui.ButtonIcon({\n\t tooltip : options.submit_tooltip,\n\t title : options.submit_title || 'Save settings',\n\t icon : options.submit_icon || 'fa-save',\n\t onclick : function() { self._submit( form, options ) }\n\t }),\n\t 'back': new Ui.ButtonIcon({\n\t icon : 'fa-caret-left',\n\t tooltip : 'Return to user preferences',\n\t title : 'Preferences',\n\t onclick : function() { form.remove(); self.$preferences.show(); }\n\t })\n\t }\n\t });\n\t self.$preferences.hide();\n\t self.$el.append( form.$el );\n\t }).fail( function( response ) {\n\t self.message.update( { message: 'Failed to load resource ' + page.url + '.', status: 'danger' } );\n\t })\n\t } else {\n\t page.onclick();\n\t }\n\t });\n\t },\n\t\n\t _submit: function( form, options ) {\n\t var self = this;\n\t $.ajax( {\n\t url : options.url,\n\t data : JSON.stringify(form.data.create()),\n\t type : 'PUT',\n\t contentType : 'application/json'\n\t }).done( function( response ) {\n\t var updated_values = false;\n\t form.data.matchModel( response, function ( input, input_id ) {\n\t form.field_list[ input_id ].value( input.value );\n\t updated_values = true;\n\t });\n\t if ( updated_values ) {\n\t form.message.update( { message: response.message, status: 'success' } );\n\t } else {\n\t form.remove();\n\t self.$preferences.show();\n\t self.message.update( { message: response.message, status: 'success' } );\n\t }\n\t }).fail( function( response ) {\n\t form.message.update( { message: response.responseJSON.err_msg, status: 'danger' } );\n\t });\n\t },\n\t\n\t _templateRow: function( options ) {\n\t return '
' +\n\t ' ';\n\t },\n\t\n\t _templateFooter: function( options ) {\n\t return '' +\n\t ' ' +\n\t '' +\n\t '' + options.title + '' +\n\t ' ' +\n\t '').attr({\n id: 'content_table',\n cellpadding: 0\n });\n this.$el.append(data_table);\n var column_names = this.model.get_metadata('column_names'),\n header_container = $('').appendTo(data_table),\n header_row = $('
').appendTo(header_container);\n if (column_names) {\n header_row.append(' ' + column_names.join(' ') + ' ');\n } else {\n for (var j = 1; j <= this.model.get_metadata('columns'); j++) {\n header_row.append('' + j + ' ');\n }\n }\n\n // Render first chunk.\n var self = this,\n first_chunk = this.model.get('first_data_chunk');\n if (first_chunk) {\n // First chunk is bootstrapped, so render now.\n this._renderChunk(first_chunk);\n }\n else {\n // No bootstrapping, so get first chunk and then render.\n $.when(self.model.get_next_chunk()).then(function(result) {\n self._renderChunk(result);\n });\n }\n\n // -- Show new chunks during scrolling. --\n\n // Set up chunk loading when scrolling using the scrolling element.\n this.scroll_elt.scroll(function(){\n self.attempt_to_fetch();\n });\n },\n\n /**\n * Returns true if user has scrolled to the bottom of the view.\n */\n scrolled_to_bottom: function() {\n return false;\n },\n\n // -- Helper functions. --\n\n _renderCell: function(cell_contents, index, colspan) {\n var $cell = $('').text(cell_contents);\n var column_types = this.model.get_metadata('column_types');\n if (colspan !== undefined) {\n $cell.attr('colspan', colspan).addClass('stringalign');\n } else if (column_types) {\n if (index < column_types.length) {\n if (column_types[index] === 'str' || column_types[index] === 'list') {\n /* Left align all str columns, right align the rest */\n $cell.addClass('stringalign');\n }\n }\n }\n return $cell;\n },\n\n _renderRow: function(line) {\n // Check length of cells to ensure this is a complete row.\n var cells = line.split('\\t'),\n row = $(' '),\n num_columns = this.model.get_metadata('columns');\n\n if (this.row_count % 2 !== 0) {\n row.addClass('dark_row');\n }\n\n if (cells.length === num_columns) {\n _.each(cells, function(cell_contents, index) {\n row.append(this._renderCell(cell_contents, index));\n }, this);\n }\n else if (cells.length > num_columns) {\n // SAM file or like format with optional metadata included.\n _.each(cells.slice(0, num_columns - 1), function(cell_contents, index) {\n row.append(this._renderCell(cell_contents, index));\n }, this);\n row.append(this._renderCell(cells.slice(num_columns - 1).join('\\t'), num_columns - 1));\n }\n else if (cells.length === 1){\n // Comment line, just return the one cell.\n row.append(this._renderCell(line, 0, num_columns));\n }\n else {\n // cells.length is greater than one, but less than num_columns. Render cells and pad tds.\n // Possibly a SAM file or like format with optional metadata missing.\n // Could also be a tabular file with a line with missing columns.\n _.each(cells, function(cell_contents, index) {\n row.append(this._renderCell(cell_contents, index));\n }, this);\n _.each(_.range(num_columns - cells.length), function(){\n row.append($(' '));\n });\n }\n\n this.row_count++;\n return row;\n },\n\n _renderChunk: function(chunk) {\n var data_table = this.$el.find('table');\n _.each(chunk.ck_data.split('\\n'), function(line, index) {\n if (line !== ''){\n data_table.append(this._renderRow(line));\n }\n }, this);\n }\n});\n\n/**\n * Tabular view that is placed at the top level of page. Scrolling occurs\n * view top-level elements outside of view.\n */\nvar TopLevelTabularDatasetChunkedView = TabularDatasetChunkedView.extend({\n\n initialize: function(options) {\n TabularDatasetChunkedView.prototype.initialize.call(this, options);\n\n // Scrolling happens in top-level elements.\n scroll_elt = _.find(this.$el.parents(), function(p) {\n return $(p).css('overflow') === 'auto';\n });\n\n // If no scrolling element found, use window.\n if (!scroll_elt) { scroll_elt = window; }\n\n // Wrap scrolling element for easy access.\n this.scroll_elt = $(scroll_elt);\n },\n\n /**\n * Returns true if user has scrolled to the bottom of the view.\n */\n scrolled_to_bottom: function() {\n return (this.$el.height() - this.scroll_elt.scrollTop() - this.scroll_elt.height() <= 0);\n }\n\n});\n\n/**\n * Tabular view tnat is embedded in a page. Scrolling occurs in view's el.\n */\nvar EmbeddedTabularDatasetChunkedView = TabularDatasetChunkedView.extend({\n\n initialize: function(options) {\n TabularDatasetChunkedView.prototype.initialize.call(this, options);\n\n // Because view is embedded, set up div to do scrolling.\n this.scroll_elt = this.$el.css({\n position: 'relative',\n overflow: 'scroll',\n height: options.height || '500px'\n });\n },\n\n /**\n * Returns true if user has scrolled to the bottom of the view.\n */\n scrolled_to_bottom: function() {\n return this.$el.scrollTop() + this.$el.innerHeight() >= this.el.scrollHeight;\n }\n\n});\n\n// button for trackster visualization\nvar TabularButtonTracksterView = Backbone.View.extend({\n\n // gene region columns\n col: {\n chrom : null,\n start : null,\n end : null\n },\n\n // url for trackster\n url_viz : null,\n\n // dataset id\n dataset_id : null,\n\n // database key\n genome_build: null,\n\n // data type\n file_ext : null,\n\n // backbone initialize\n initialize: function (options) {\n // check if environment is available\n var Galaxy = parent.Galaxy;\n\n // link galaxy modal or create one\n if (Galaxy && Galaxy.modal) {\n this.modal = Galaxy.modal;\n }\n\n // link galaxy frames\n if (Galaxy && Galaxy.frame) {\n this.frame = Galaxy.frame;\n }\n\n // check\n if (!this.modal || !this.frame) {\n return;\n }\n\n // model/metadata\n var model = options.model;\n var metadata = model.get('metadata');\n\n // check for datatype\n if (!model.get('file_ext')) {\n return;\n }\n\n // get data type\n this.file_ext = model.get('file_ext');\n\n // check for bed-file format\n if (this.file_ext == 'bed')\n {\n // verify that metadata exists\n if (metadata.get('chromCol') && metadata.get('startCol') && metadata.get('endCol'))\n {\n // read in columns\n this.col.chrom = metadata.get('chromCol') - 1;\n this.col.start = metadata.get('startCol') - 1;\n this.col.end = metadata.get('endCol') - 1;\n } else {\n console.log('TabularButtonTrackster : Bed-file metadata incomplete.');\n return;\n }\n }\n\n // check for vcf-file format\n if (this.file_ext == 'vcf')\n {\n // search array\n function search (str, array) {\n for (var j = 0; j < array.length; j++)\n if (array[j].match(str)) return j;\n return -1;\n };\n\n // load\n this.col.chrom = search('Chrom', metadata.get('column_names'));\n this.col.start = search('Pos', metadata.get('column_names'));\n this.col.end = null;\n\n // verify that metadata exists\n if (this.col.chrom == -1 || this.col.start == -1) {\n console.log('TabularButtonTrackster : VCF-file metadata incomplete.');\n return;\n }\n }\n\n // check\n if(this.col.chrom === undefined) {\n return;\n }\n\n // get dataset id\n if (model.id) {\n this.dataset_id = model.id;\n } else {\n console.log('TabularButtonTrackster : Dataset identification is missing.');\n return;\n }\n\n // get url\n if (model.get('url_viz')) {\n this.url_viz = model.get('url_viz');\n } else {\n console.log('TabularButtonTrackster : Url for visualization controller is missing.');\n return;\n }\n\n // get genome_build / database key\n if (model.get('genome_build')) {\n this.genome_build = model.get('genome_build');\n }\n\n // create the icon\n var btn_viz = new mod_icon_btn.IconButtonView({\n model : new mod_icon_btn.IconButton({\n title : 'Visualize',\n icon_class : 'chart_curve',\n id : 'btn_viz'\n })\n });\n\n // set element\n this.setElement(options.$el);\n\n // add to element\n this.$el.append(btn_viz.render().$el);\n\n // hide the button\n this.hide();\n },\n\n // backbone events\n events:\n {\n 'mouseover tr' : 'show',\n 'mouseleave' : 'hide'\n },\n\n // show button\n show: function (e) {\n // is numeric\n function is_numeric(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n };\n\n // check\n if(this.col.chrom === null)\n return;\n\n // get selected data line\n var row = $(e.target).parent();\n\n // verify that location has been found\n var chrom = row.children().eq(this.col.chrom).html();\n var start = row.children().eq(this.col.start).html();\n\n // end is optional\n var end = this.col.end ? row.children().eq(this.col.end).html() : start;\n\n // double check location\n if (!chrom.match(\"^#\") && chrom !== \"\" && is_numeric(start)) {\n\n // get target gene region\n var btn_viz_pars = {\n dataset_id : this.dataset_id,\n gene_region : chrom + \":\" + start + \"-\" + end\n };\n\n // get button position\n var offset = row.offset();\n var left = offset.left - 10;\n var top = offset.top - $(window).scrollTop() + 3;\n\n // update css\n $('#btn_viz').css({'position': 'fixed', 'top': top + 'px', 'left': left + 'px'});\n $('#btn_viz').off('click');\n $('#btn_viz').click(this.create_trackster_action(this.url_viz, btn_viz_pars, this.genome_build));\n\n // show the button\n $('#btn_viz').show();\n } else {\n // hide the button\n $('#btn_viz').hide();\n }\n },\n\n // hide button\n hide: function () {\n this.$el.find('#btn_viz').hide();\n },\n\n // create action\n create_trackster_action : function (vis_url, dataset_params, dbkey) {\n // link this\n var self = this;\n\n // create function\n return function() {\n var listTracksParams = {};\n if (dbkey) {\n listTracksParams[ 'f-dbkey' ] = dbkey;\n }\n $.ajax({\n url: vis_url + '/list_tracks?' + $.param( listTracksParams ),\n dataType: 'html',\n error: function() {\n // show error message\n self.modal.show({\n title : 'Something went wrong!',\n body : 'Unfortunately we could not add this dataset to the track browser. Please try again or contact us.',\n buttons : {\n 'Cancel': function(){\n self.modal.hide();\n }\n }\n });\n },\n success: function(table_html) {\n self.modal.show({\n title : 'View Data in a New or Saved Visualization',\n buttons :{\n 'Cancel': function(){\n self.modal.hide();\n },\n 'View in saved visualization': function(){\n // show modal with saved visualizations\n self.modal.show(\n {\n title : 'Add Data to Saved Visualization',\n body : table_html,\n buttons : {\n 'Cancel': function(){\n self.modal.hide();\n },\n 'Add to visualization': function(){\n // hide\n self.modal.hide();\n\n // search selected fields\n self.modal.$el.find('input[name=id]:checked').each(function(){\n // get visualization id\n var vis_id = $(this).val();\n dataset_params.id = vis_id;\n\n // add widget\n self.frame.add({\n title : 'Trackster',\n type : 'url',\n content : vis_url + '/trackster?' + $.param(dataset_params)\n });\n });\n }\n }\n });\n },\n 'View in new visualization': function(){\n // hide\n self.modal.hide();\n\n // add widget\n self.frame.add({\n title : 'Trackster',\n type : 'url',\n content : vis_url + '/trackster?' + $.param(dataset_params)\n });\n }\n }\n });\n }\n });\n return false;\n };\n }\n});\n\n// -- Utility functions. --\n\n/**\n * Create a model, attach it to a view, render view, and attach it to a parent element.\n */\nvar createModelAndView = function(model, view, model_config, parent_elt) {\n // Create model, view.\n var a_view = new view({\n model: new model(model_config)\n });\n\n // Render view and add to parent element.\n a_view.render();\n if (parent_elt) {\n parent_elt.append(a_view.$el);\n }\n\n return a_view;\n};\n\n/**\n * Create a tabular dataset chunked view (and requisite tabular dataset model)\n * and appends to parent_elt.\n */\nvar createTabularDatasetChunkedView = function(options) {\n // If no model, create and set model from dataset config.\n if (!options.model) {\n options.model = new TabularDataset(options.dataset_config);\n }\n\n var parent_elt = options.parent_elt;\n var embedded = options.embedded;\n\n // Clean up options so that only needed options are passed to view.\n delete options.embedded;\n delete options.parent_elt;\n delete options.dataset_config;\n\n // Create and set up view.\n var view = (embedded ? new EmbeddedTabularDatasetChunkedView(options) :\n new TopLevelTabularDatasetChunkedView(options));\n view.render();\n\n if (parent_elt) {\n parent_elt.append(view.$el);\n // If we're sticking this in another element, once it's appended check\n // to make sure we've filled enough space.\n // Without this, the scroll elements don't work.\n view.expand_to_container();\n }\n\n return view;\n};\n\nreturn {\n Dataset: Dataset,\n TabularDataset: TabularDataset,\n DatasetCollection: DatasetCollection,\n TabularDatasetChunkedView: TabularDatasetChunkedView,\n createTabularDatasetChunkedView: createTabularDatasetChunkedView\n};\n\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/dataset/data.js\n// module id = 11\n// module chunks = 0 1 3","define([\n], function(){\n\n'use strict';\n//==============================================================================\n/** Map of possible HDA/collection/job states to their string equivalents.\n * A port of galaxy.model.Dataset.states.\n */\nvar STATES = {\n // NOT ready states\n /** is uploading and not ready */\n UPLOAD : 'upload',\n /** the job that will produce the dataset queued in the runner */\n QUEUED : 'queued',\n /** the job that will produce the dataset is running */\n RUNNING : 'running',\n /** metadata for the dataset is being discovered/set */\n SETTING_METADATA : 'setting_metadata',\n\n // ready states\n /** was created without a tool */\n NEW : 'new',\n /** has no data */\n EMPTY : 'empty',\n /** has successfully completed running */\n OK : 'ok',\n\n /** the job that will produce the dataset paused */\n PAUSED : 'paused',\n /** metadata discovery/setting failed or errored (but otherwise ok) */\n FAILED_METADATA : 'failed_metadata',\n//TODO: not in trans.app.model.Dataset.states - is in database\n /** not accessible to the current user (i.e. due to permissions) */\n NOT_VIEWABLE : 'noPermission',\n /** deleted while uploading */\n DISCARDED : 'discarded',\n /** the tool producing this dataset failed */\n ERROR : 'error'\n};\n\nSTATES.READY_STATES = [\n STATES.OK,\n STATES.EMPTY,\n STATES.PAUSED,\n STATES.FAILED_METADATA,\n STATES.NOT_VIEWABLE,\n STATES.DISCARDED,\n STATES.ERROR\n];\n\nSTATES.NOT_READY_STATES = [\n STATES.UPLOAD,\n STATES.QUEUED,\n STATES.RUNNING,\n STATES.SETTING_METADATA,\n STATES.NEW\n];\n\n\n//==============================================================================\n return STATES;\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/dataset/states.js\n// module id = 12\n// module chunks = 3","/** This module contains all button views. */\ndefine( [ 'utils/utils' ], function( Utils ) {\n /** This renders the default button which is used e.g. at the bottom of the upload modal. */\n var ButtonDefault = Backbone.View.extend({\n initialize: function( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : Utils.uid(),\n title : '',\n floating : 'right',\n icon : '',\n cls : 'btn btn-default',\n wait : false,\n wait_text : 'Sending...',\n wait_cls : 'btn btn-info',\n disabled : false,\n percentage : -1\n }).set( options );\n this.setElement( $( '' ).attr( 'type', 'button' )\n .append( this.$icon = $( '' ) )\n .append( this.$title = $( '' ) )\n .append( this.$progress = $( '' ).append( this.$progress_bar = $( '' ) ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.render();\n },\n\n render: function() {\n var self = this;\n var options = this.model.attributes;\n this.$el.removeClass()\n .addClass( 'ui-button-default' )\n .addClass( options.disabled && 'disabled' )\n .attr( 'id', options.id )\n .attr( 'disabled', options.disabled )\n .css( 'float', options.floating )\n .off( 'click' ).on( 'click' , function() {\n $( '.tooltip' ).hide();\n options.onclick && !self.disabled && options.onclick();\n })\n .tooltip( { title: options.tooltip, placement: 'bottom' } );\n this.$progress.addClass( 'progress' ).css( 'display', options.percentage !== -1 ? 'block' : 'none' );\n this.$progress_bar.addClass( 'progress-bar' ).css( { width : options.percentage + '%' } );\n this.$icon.removeClass().addClass( 'icon fa' );\n this.$title.removeClass().addClass( 'title' );\n if ( options.wait ) {\n this.$el.addClass( options.wait_cls ).prop( 'disabled', true );\n this.$icon.addClass( 'fa-spinner fa-spin ui-margin-right' );\n this.$title.html( options.wait_text );\n } else {\n this.$el.addClass( options.cls );\n this.$icon.addClass( options.icon );\n this.$title.html( options.title );\n options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n }\n },\n\n /** Show button */\n show: function() {\n this.$el.show();\n },\n\n /** Hide button */\n hide: function() {\n this.$el.hide();\n },\n\n /** Disable button */\n disable: function() {\n this.model.set( 'disabled', true );\n },\n\n /** Enable button */\n enable: function() {\n this.model.set( 'disabled', false );\n },\n\n /** Show spinner to indicate that the button is not ready to be clicked */\n wait: function() {\n this.model.set( 'wait', true );\n },\n\n /** Hide spinner to indicate that the button is ready to be clicked */\n unwait: function() {\n this.model.set( 'wait', false );\n },\n\n /** Change icon */\n setIcon: function( icon ) {\n this.model.set( 'icon', icon );\n }\n });\n\n /** This button allows the right-click/open-in-new-tab feature, its used e.g. for panel buttons. */\n var ButtonLink = ButtonDefault.extend({\n initialize: function( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : Utils.uid(),\n title : '',\n icon : '',\n cls : ''\n }).set( options );\n this.setElement( $( '' ).append( this.$icon = $( '' ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.render();\n },\n\n render: function() {\n var options = this.model.attributes;\n this.$el.removeClass()\n .addClass( options.cls )\n .attr( { id : options.id,\n href : options.href || 'javascript:void(0)',\n title : options.title,\n target : options.target || '_top',\n disabled : options.disabled } )\n .off( 'click' ).on( 'click' , function() {\n options.onclick && !options.disabled && options.onclick();\n });\n this.$icon.removeClass().addClass( options.icon );\n }\n });\n\n /** The check button is used in the tool form and allows to distinguish between multiple states e.g. all, partially and nothing selected. */\n var ButtonCheck = Backbone.View.extend({\n initialize: function( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : Utils.uid(),\n title : 'Select/Unselect all',\n icons : [ 'fa-square-o', 'fa-minus-square-o', 'fa-check-square-o' ],\n value : 0,\n onchange : function(){}\n }).set( options );\n this.setElement( $( '' ).append( this.$icon = $( '' ) )\n .append( this.$title = $( '' ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.render();\n },\n\n render: function( options ) {\n var self = this;\n var options = this.model.attributes;\n this.$el.addClass( 'ui-button-check' )\n .off( 'click' ).on('click', function() {\n self.model.set( 'value', ( self.model.get( 'value' ) === 0 && 2 ) || 0 );\n options.onclick && options.onclick();\n });\n this.$title.html( options.title );\n this.$icon.removeClass()\n .addClass( 'icon fa ui-margin-right' )\n .addClass( options.icons[ options.value ] );\n },\n\n /* Sets a new value and/or returns the value.\n * @param{Integer} new_val - Set a new value 0=unchecked, 1=partial and 2=checked.\n * OR:\n * @param{Integer} new_val - Number of selected options.\n * @param{Integer} total - Total number of available options.\n */\n value: function ( new_val, total ) {\n if ( new_val !== undefined ) {\n if ( total && new_val !== 0 ) {\n new_val = ( new_val !== total ) && 1 || 2;\n }\n this.model.set( 'value', new_val );\n this.model.get( 'onchange' )( this.model.get( 'value' ) );\n }\n return this.model.get( 'value' );\n }\n });\n\n /** This renders a differently styled, more compact button version. */\n var ButtonIcon = ButtonDefault.extend({\n initialize: function( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : Utils.uid(),\n title : '',\n floating : 'right',\n icon : '',\n cls : 'ui-button-icon',\n disabled : false\n }).set( options );\n this.setElement( $( '' ).append( this.$button = $( '' ).append( this.$icon = $( '' ) )\n .append( this.$title = $( '' ) ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.render();\n },\n\n render : function( options ) {\n var self = this;\n var options = this.model.attributes;\n this.$el.removeClass()\n .addClass( options.cls )\n .addClass( options.disabled && 'disabled' )\n .attr( 'disabled', options.disabled )\n .attr( 'id', options.id )\n .css( 'float', options.floating )\n .off( 'click' ).on( 'click', function() {\n $( '.tooltip' ).hide();\n !options.disabled && options.onclick && options.onclick();\n });\n this.$button.addClass( 'button' ).tooltip( { title: options.tooltip, placement: 'bottom' } );\n this.$icon.removeClass().addClass( 'icon fa' ).addClass( options.icon );\n this.$title.addClass( 'title' ).html( options.title );\n options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n }\n });\n\n /** This class creates a button with dropdown menu. */\n var ButtonMenu = ButtonDefault.extend({\n $menu: null,\n initialize: function ( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : '',\n title : '',\n floating : 'right',\n pull : 'right',\n icon : null,\n onclick : null,\n cls : 'ui-button-icon ui-button-menu',\n tooltip : '',\n target : '',\n href : '',\n onunload : null,\n visible : true,\n tag : ''\n }).set( options );\n this.collection = new Backbone.Collection();\n this.setElement( $( '' ).append( this.$root = $( '' ).append( this.$icon = $( '' ) )\n .append( this.$title = $( '' ) ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.listenTo( this.collection, 'change add remove reset', this.render, this );\n this.render();\n },\n\n render: function() {\n var self = this;\n var options = this.model.attributes;\n this.$el.removeClass()\n .addClass( 'dropdown' )\n .addClass( options.cls )\n .attr( 'id', options.id )\n .css( { float : options.floating,\n display : options.visible && this.collection.where( { visible: true } ).length > 0 ? 'block' : 'none' } );\n this.$root.addClass( 'root button dropdown-toggle' )\n .attr( 'data-toggle', 'dropdown' )\n .tooltip( { title: options.tooltip, placement: 'bottom' } )\n .off( 'click' ).on( 'click', function( e ) {\n $( '.tooltip' ).hide();\n e.preventDefault();\n options.onclick && options.onclick();\n } );\n this.$icon.removeClass().addClass( 'icon fa' ).addClass( options.icon );\n this.$title.removeClass().addClass( 'title' ).html( options.title );\n options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n this.$menu && this.$menu.remove();\n if ( this.collection.length > 0 ) {\n this.$menu = $( ' ' ).addClass( 'menu dropdown-menu' )\n .addClass( 'pull-' + self.model.get( 'pull' ) )\n .attr( 'role', 'menu' );\n this.$el.append( this.$menu );\n }\n this.collection.each( function( submodel ) {\n var suboptions = submodel.attributes;\n if ( suboptions.visible ) {\n var $link = $( '' ).addClass( 'dropdown-item' )\n .attr( { href : suboptions.href, target : suboptions.target } )\n .append( $( '' ).addClass( 'fa' )\n .addClass( suboptions.icon )\n .css( 'display', suboptions.icon ? 'inline-block' : 'none' ) )\n .append( suboptions.title )\n .on( 'click', function( e ) {\n if ( suboptions.onclick ) {\n e.preventDefault();\n suboptions.onclick();\n }\n } );\n self.$menu.append( $( '' ).append( $link ) );\n suboptions.divider && self.$menu.append( $( '' ).addClass( 'divider' ) );\n }\n });\n },\n\n /** Add a new menu item */\n addMenu: function ( options ) {\n this.collection.add( Utils.merge( options, {\n title : '',\n target : '',\n href : '',\n onclick : null,\n divider : false,\n visible : true,\n icon : null,\n cls : 'button-menu btn-group'\n }));\n }\n });\n\n return {\n ButtonDefault : ButtonDefault,\n ButtonLink : ButtonLink,\n ButtonIcon : ButtonIcon,\n ButtonCheck : ButtonCheck,\n ButtonMenu : ButtonMenu\n }\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/ui/ui-buttons.js\n// module id = 13\n// module chunks = 0 3","define([\n //jquery\n //backbone\n], function(){\n//=============================================================================\n/**\n * backbone model for icon buttons\n */\nvar IconButton = Backbone.Model.extend({\n defaults: {\n title : \"\",\n icon_class : \"\",\n on_click : null,\n menu_options : null,\n is_menu_button : true,\n id : null,\n href : null,\n target : null,\n enabled : true,\n visible : true,\n tooltip_config : {}\n }\n});\n\n/**\n * backbone view for icon buttons\n */\nvar IconButtonView = Backbone.View.extend({\n\n initialize : function(){\n // better rendering this way\n this.model.attributes.tooltip_config = { placement : 'bottom' };\n this.model.bind( 'change', this.render, this );\n },\n\n render : function( ){\n // hide tooltip\n this.$el.tooltip( 'hide' );\n\n var new_elem = this.template( this.model.toJSON() );\n // configure tooltip\n new_elem.tooltip( this.model.get( 'tooltip_config' ));\n this.$el.replaceWith( new_elem );\n this.setElement( new_elem );\n return this;\n },\n\n events : {\n 'click' : 'click'\n },\n\n click : function( event ){\n // if on_click pass to that function\n if( _.isFunction( this.model.get( 'on_click' ) ) ){\n this.model.get( 'on_click' )( event );\n return false;\n }\n // otherwise, bubble up ( to href or whatever )\n return true;\n },\n\n // generate html element\n template: function( options ){\n var buffer = 'title=\"' + options.title + '\" class=\"icon-button';\n\n if( options.is_menu_button ){\n buffer += ' menu-button';\n }\n\n buffer += ' ' + options.icon_class;\n\n if( !options.enabled ){\n buffer += '_disabled';\n }\n\n // close class tag\n buffer += '\"';\n\n if( options.id ){\n buffer += ' id=\"' + options.id + '\"';\n }\n\n buffer += ' href=\"' + options.href + '\"';\n // add target for href\n if( options.target ){\n buffer += ' target=\"' + options.target + '\"';\n }\n // set visibility\n if( !options.visible ){\n buffer += ' style=\"display: none;\"';\n }\n\n // enabled/disabled\n if ( options.enabled ){\n buffer = '';\n } else {\n buffer = '';\n }\n\n // return element\n return $( buffer );\n }\n} );\n\n// define collection\nvar IconButtonCollection = Backbone.Collection.extend({\n model: IconButton\n});\n\n/**\n * menu with multiple icon buttons\n * views are not needed nor used for individual buttons\n */\nvar IconButtonMenuView = Backbone.View.extend({\n\n tagName: 'div',\n\n initialize: function(){\n this.render();\n },\n\n render: function(){\n // initialize icon buttons\n var self = this;\n this.collection.each(function(button){\n // create and add icon button to menu\n var elt = $('')\n .attr('href', 'javascript:void(0)')\n .attr('title', button.attributes.title)\n .addClass('icon-button menu-button')\n .addClass(button.attributes.icon_class)\n .appendTo(self.$el)\n .click(button.attributes.on_click);\n\n // configure tooltip\n if (button.attributes.tooltip_config){\n elt.tooltip(button.attributes.tooltip_config);\n }\n\n // add popup menu to icon\n var menu_options = button.get('options');\n if (menu_options){\n make_popupmenu(elt, menu_options);\n }\n });\n\n // return\n return this;\n }\n});\n\n/**\n * Returns an IconButtonMenuView for the provided configuration.\n * Configuration is a list of dictionaries where each dictionary\n * defines an icon button. Each dictionary must have the following\n * elements: icon_class, title, and on_click.\n */\nvar create_icon_buttons_menu = function(config, global_config)\n{\n // initialize global configuration\n if (!global_config) global_config = {};\n\n // create and initialize menu\n var buttons = new IconButtonCollection(\n _.map(config, function(button_config){\n return new IconButton(_.extend(button_config, global_config));\n })\n );\n\n // return menu\n return new IconButtonMenuView( {collection: buttons} );\n};\n\n\n//=============================================================================\n return {\n IconButton : IconButton,\n IconButtonView : IconButtonView,\n IconButtonCollection : IconButtonCollection,\n IconButtonMenuView : IconButtonMenuView,\n create_icon_buttons_menu: create_icon_buttons_menu\n };\n})\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/ui/icon-button.js\n// module id = 14\n// module chunks = 0 1 3","// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js\n// Uses AMD or browser globals to create a jQuery plugin.\n(function (factory) {\n if (typeof define === 'function' && define.amd) {\n //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will\n // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still\n // relying on jquery being loaded globally* in order to preserve plugins.\n define([ 'jquery' ], factory);\n } else {\n // Browser globals\n factory(jQuery);\n }\n\n}(function ( jQuery ) {\n'use_strict';\n\nvar $ = jQuery;\n\n// ============================================================================\n/**\n * Make an element with text editable: (a) when user clicks on text, a textbox/area\n * is provided for editing; (b) when enter key pressed, element's text is set and on_finish\n * is called.\n */\n$.fn.make_text_editable = function(config_dict) {\n // Get config options.\n var num_cols = (\"num_cols\" in config_dict ? config_dict.num_cols : 30),\n num_rows = (\"num_rows\" in config_dict ? config_dict.num_rows : 4),\n use_textarea = (\"use_textarea\" in config_dict ? config_dict.use_textarea : false),\n on_finish = (\"on_finish\" in config_dict ? config_dict.on_finish : null),\n help_text = (\"help_text\" in config_dict ? config_dict.help_text : null);\n\n // Add element behavior.\n var container = $(this);\n container.addClass(\"editable-text\").click(function(e) {\n // If there's already an input element, editing is active, so do nothing.\n if ($(this).children(\":input\").length > 0) {\n return;\n }\n\n container.removeClass(\"editable-text\");\n\n // Handler for setting element text.\n var set_text = function(new_text) {\n container.find(\":input\").remove();\n\n if (new_text !== \"\") {\n container.text(new_text);\n } else {\n // No text; need a line so that there is a click target.\n container.html(\"
\");\n }\n container.addClass(\"editable-text\");\n\n if (on_finish) {\n on_finish(new_text);\n }\n };\n\n // Create input element(s) for editing.\n var cur_text = (\"cur_text\" in config_dict ? config_dict.cur_text : container.text() ),\n input_elt, button_elt;\n\n if (use_textarea) {\n input_elt = $(\"\")\n .attr({ rows: num_rows, cols: num_cols }).text($.trim(cur_text))\n .keyup(function(e) {\n if (e.keyCode === 27) {\n // Escape key.\n set_text(cur_text);\n }\n });\n button_elt = $(\"\").text(\"Done\").click(function() {\n set_text(input_elt.val());\n // Return false so that click does not propogate to container.\n return false;\n });\n }\n else {\n input_elt = $(\"\").attr({ value: $.trim(cur_text), size: num_cols })\n .blur(function() {\n set_text(cur_text);\n }).keyup(function(e) {\n if (e.keyCode === 27) {\n // Escape key.\n $(this).trigger(\"blur\");\n } else if (e.keyCode === 13) {\n // Enter key.\n set_text($(this).val());\n }\n\n // Do not propogate event to avoid unwanted side effects.\n e.stopPropagation();\n });\n }\n\n // Replace text with input object(s) and focus & select.\n container.text(\"\");\n container.append(input_elt);\n if (button_elt) {\n container.append(button_elt);\n }\n input_elt.focus();\n input_elt.select();\n\n // Do not propogate to elements below b/c that blurs input and prevents it from being used.\n e.stopPropagation();\n });\n\n // Add help text if there some.\n if (help_text) {\n container.attr(\"title\", help_text).tooltip();\n }\n\n return container;\n};\n\n// ============================================================================\n}));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/ui/editable-text.js\n// module id = 15\n// module chunks = 0 3","define(function(){\n\n/**\n * Stringifies a number adding commas for digit grouping as per North America.\n */\nfunction commatize( number ) {\n number += ''; // Convert to string\n var rgx = /(\\d+)(\\d{3})/;\n while (rgx.test(number)) {\n number = number.replace(rgx, '$1' + ',' + '$2');\n }\n return number;\n}\n\n/**\n * Helper to determine if object is jQuery deferred.\n */\nvar is_deferred = function ( d ) {\n return ('promise' in d);\n};\n\n/**\n * Implementation of a server-state based deferred. Server is repeatedly polled, and when\n * condition is met, deferred is resolved.\n */\nvar ServerStateDeferred = Backbone.Model.extend({\n defaults: {\n ajax_settings: {},\n interval: 1000,\n success_fn: function(result) { return true; }\n },\n\n /**\n * Returns a deferred that resolves when success function returns true.\n */\n go: function() {\n var deferred = $.Deferred(),\n self = this,\n ajax_settings = self.get('ajax_settings'),\n success_fn = self.get('success_fn'),\n interval = self.get('interval'),\n _go = function() {\n $.ajax(ajax_settings).success(function(result) {\n if (success_fn(result)) {\n // Result is good, so resolve.\n deferred.resolve(result);\n }\n else {\n // Result not good, try again.\n setTimeout(_go, interval);\n }\n });\n };\n _go();\n return deferred;\n }\n});\n\n/**\n * Returns a random color in hexadecimal format that is sufficiently different from a single color\n * or set of colors.\n * @param colors a color or list of colors in the format '#RRGGBB'\n */\nvar get_random_color = function(colors) {\n // Default for colors is white.\n if (!colors) { colors = \"#ffffff\"; }\n\n // If needed, create list of colors.\n if ( typeof(colors) === \"string\" ) {\n colors = [ colors ];\n }\n\n // Convert colors to numbers.\n for (var i = 0; i < colors.length; i++) {\n colors[i] = parseInt( colors[i].slice(1), 16 );\n }\n\n // -- Perceived brightness and difference formulas are from\n // -- http://www.w3.org/WAI/ER/WD-AERT/#color-contrast\n\n // Compute perceived color brightness (based on RGB-YIQ transformation):\n var brightness = function(r, g, b) {\n return ( (r * 299) + (g * 587) + (b * 114) ) / 1000;\n };\n\n // Compute color difference:\n var difference = function(r1, g1, b1, r2, g2, b2) {\n return ( Math.max(r1, r2) - Math.min(r1, r2) ) +\n ( Math.max(g1, g2) - Math.min(g1, g2) ) +\n ( Math.max(b1, b2) - Math.min(b1, b2) );\n };\n\n // Create new random color.\n var new_color, nr, ng, nb,\n other_color, or, og, ob,\n n_brightness, o_brightness,\n diff, ok = false,\n num_tries = 0;\n do {\n // New color is never white b/c random in [0,1)\n new_color = Math.round( Math.random() * 0xffffff );\n nr = ( new_color & 0xff0000 ) >> 16;\n ng = ( new_color & 0x00ff00 ) >> 8;\n nb = new_color & 0x0000ff;\n n_brightness = brightness(nr, ng, nb);\n ok = true;\n for (i = 0; i < colors.length; i++) {\n other_color = colors[i];\n or = ( other_color & 0xff0000 ) >> 16;\n og = ( other_color & 0x00ff00 ) >> 8;\n ob = other_color & 0x0000ff;\n o_brightness = brightness(or, og, ob);\n diff = difference(nr, ng, nb, or, og, ob);\n // These thresholds may need to be adjusted. Brightness difference range is 125;\n // color difference range is 500.\n if ( ( Math.abs(n_brightness - o_brightness) < 40 ) ||\n ( diff < 200 ) ) {\n ok = false;\n break;\n }\n }\n\n num_tries++\n; } while (!ok && num_tries <= 10 );\n\n // Add 0x1000000 to left pad number with 0s.\n return '#' + ( 0x1000000 + new_color ).toString(16).substr(1,6);\n};\n\nreturn {\n commatize: commatize,\n is_deferred: is_deferred,\n ServerStateDeferred : ServerStateDeferred,\n get_random_color : get_random_color\n};\n\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/viz/trackster/util.js\n// module id = 16\n// module chunks = 0 3","define([\n \"libs/bibtex\",\n \"mvc/base-mvc\",\n \"utils/localization\"\n], function( parseBibtex, baseMVC, _l ){\n/* global Backbone */\n// we use amd here to require, but bibtex uses a global or commonjs pattern.\n// webpack will load via commonjs and plain requirejs will load as global. Check both\nparseBibtex = parseBibtex || window.BibtexParser;\n\nvar logNamespace = 'citation';\n//==============================================================================\n/** @class model for tool citations.\n * @name Citation\n * @augments Backbone.Model\n */\nvar Citation = Backbone.Model.extend( baseMVC.LoggableMixin ).extend({\n _logNamespace : logNamespace,\n\n defaults : {\n content: ''\n },\n\n initialize: function() {\n var parsed;\n try {\n // TODO: to model.parse/.validate\n parsed = parseBibtex( this.attributes.content );\n } catch( err ){\n return;\n }\n // bibtex returns successfully parsed in .entries and any parsing errors in .errors\n if( parsed.errors.length ){\n // the gen. form of these errors seems to be [ line, col, char, error message ]\n var errors = parsed.errors.reduce( function( all, current ){ return all + '; ' + current; });\n // throw new Error( 'Error parsing bibtex: ' + errors );\n this.log( 'Error parsing bibtex: ' + errors );\n }\n\n this._fields = {};\n this.entry = _.first( parsed.entries );\n if( this.entry ){\n var rawFields = this.entry.Fields;\n for( var key in rawFields ){\n var value = rawFields[ key ];\n var lowerKey = key.toLowerCase();\n this._fields[ lowerKey ] = value;\n }\n }\n },\n entryType: function() {\n return this.entry? this.entry.EntryType : undefined;\n },\n fields: function() {\n return this._fields;\n }\n});\n\n\n//==============================================================================\n/** @class Backbone collection of citations.\n */\nvar BaseCitationCollection = Backbone.Collection.extend( baseMVC.LoggableMixin ).extend( {\n _logNamespace : logNamespace,\n\n /** root api url */\n urlRoot : Galaxy.root + 'api',\n partial : true, // Assume some tools in history/workflow may not be properly annotated yet.\n model : Citation,\n} );\n\nvar HistoryCitationCollection = BaseCitationCollection.extend( {\n /** complete api url */\n url : function() {\n return this.urlRoot + '/histories/' + this.history_id + '/citations';\n }\n} );\n\nvar ToolCitationCollection = BaseCitationCollection.extend( {\n /** complete api url */\n url : function() {\n return this.urlRoot + '/tools/' + this.tool_id + '/citations';\n },\n partial : false, // If a tool has citations, assume they are complete.\n} );\n\n\n//==============================================================================\n\nreturn {\n Citation : Citation,\n HistoryCitationCollection : HistoryCitationCollection,\n ToolCitationCollection: ToolCitationCollection\n};\n\n\n});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/citation/citation-model.js\n// module id = 17\n// module chunks = 0 3","/* This is the regular tool form */\ndefine([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form-base', 'mvc/webhooks' ],\n function( Utils, Ui, Modal, ToolFormBase, Webhooks ) {\n var View = Backbone.View.extend({\n initialize: function( options ) {\n var self = this;\n this.modal = parent.Galaxy.modal || new Modal.View();\n this.form = new ToolFormBase( Utils.merge({\n listen_to_history : true,\n always_refresh : false,\n customize : function( form ) {\n var options = form.model.attributes;\n // build execute button\n options.buttons = {\n execute: execute_btn = new Ui.Button({\n icon : 'fa-check',\n tooltip : 'Execute: ' + options.name + ' (' + options.version + ')',\n title : 'Execute',\n cls : 'ui-button btn btn-primary',\n floating : 'clear',\n onclick : function() {\n execute_btn.wait();\n form.portlet.disable();\n self.submit( options, function() {\n execute_btn.unwait();\n form.portlet.enable();\n } );\n }\n })\n }\n // remap feature\n if ( options.job_id && options.job_remap ) {\n options.inputs.push({\n label : 'Resume dependencies from this job',\n name : 'rerun_remap_job_id',\n type : 'select',\n display : 'radio',\n ignore : '__ignore__',\n value : '__ignore__',\n options : [ [ 'Yes', options.job_id ], [ 'No', '__ignore__' ] ],\n 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 });\n }\n },\n postchange : function( process, form ) {\n var self = this;\n var current_state = {\n tool_id : form.model.get( 'id' ),\n tool_version : form.model.get( 'version' ),\n inputs : $.extend(true, {}, form.data.create())\n }\n form.wait( true );\n Galaxy.emit.debug( 'tool-form::postchange()', 'Sending current state.', current_state );\n Utils.request({\n type : 'POST',\n url : Galaxy.root + 'api/tools/' + form.model.get( 'id' ) + '/build',\n data : current_state,\n success : function( data ) {\n form.update( data );\n form.wait( false );\n Galaxy.emit.debug( 'tool-form::postchange()', 'Received new model.', data );\n process.resolve();\n },\n error : function( response ) {\n Galaxy.emit.debug( 'tool-form::postchange()', 'Refresh request failed.', response );\n process.reject();\n }\n });\n }\n }, options ) );\n this.deferred = this.form.deferred;\n this.setElement( '' );\n this.$el.append( this.form.$el );\n },\n\n /** Submit a regular job.\n * @param{dict} options - Specifies tool id and version\n * @param{function} callback - Called when request has completed\n */\n submit: function( options, callback ) {\n var self = this;\n var job_def = {\n tool_id : options.id,\n tool_version : options.version,\n inputs : this.form.data.create()\n }\n this.form.trigger( 'reset' );\n if ( !self.validate( job_def ) ) {\n Galaxy.emit.debug( 'tool-form::submit()', 'Submission canceled. Validation failed.' );\n callback && callback();\n return;\n }\n if ( options.action !== Galaxy.root + 'tool_runner/index' ) {\n var $f = $( '' ).attr( { action: options.action, method: options.method, enctype: options.enctype } );\n _.each( job_def.inputs, function( value, key ) { $f.append( $( '' ).attr( { 'name': key, 'value': value } ) ) } );\n $f.hide().appendTo( 'body' ).submit().remove();\n callback && callback();\n return;\n }\n Galaxy.emit.debug( 'tool-form::submit()', 'Validation complete.', job_def );\n Utils.request({\n type : 'POST',\n url : Galaxy.root + 'api/tools',\n data : job_def,\n success : function( response ) {\n callback && callback();\n self.$el.children().hide();\n self.$el.append( self._templateSuccess( response ) );\n // Show Webhook if job is running\n if ( response.jobs && response.jobs.length > 0 ) {\n self.$el.append( $( '', { id: 'webhook-view' } ) );\n var WebhookApp = new Webhooks.WebhookView({\n urlRoot: Galaxy.root + 'api/webhooks/tool'\n });\n }\n parent.Galaxy && parent.Galaxy.currHistoryPanel && parent.Galaxy.currHistoryPanel.refreshContents();\n },\n error : function( response ) {\n callback && callback();\n Galaxy.emit.debug( 'tool-form::submit', 'Submission failed.', response );\n var input_found = false;\n if ( response && response.err_data ) {\n var error_messages = self.form.data.matchResponse( response.err_data );\n for ( var input_id in error_messages ) {\n self.form.highlight( input_id, error_messages[ input_id ]);\n input_found = true;\n break;\n }\n }\n if ( !input_found ) {\n self.modal.show({\n title : 'Job submission failed',\n body : self._templateError( job_def, response && response.err_msg ),\n buttons : { 'Close' : function() { self.modal.hide() } }\n });\n }\n }\n });\n },\n\n /** Validate job dictionary.\n * @param{dict} job_def - Job execution dictionary\n */\n validate: function( job_def ) {\n var job_inputs = job_def.inputs;\n var batch_n = -1;\n var batch_src = null;\n for ( var job_input_id in job_inputs ) {\n var input_value = job_inputs[ job_input_id ];\n var input_id = this.form.data.match( job_input_id );\n var input_field = this.form.field_list[ input_id ];\n var input_def = this.form.input_list[ input_id ];\n if ( !input_id || !input_def || !input_field ) {\n Galaxy.emit.debug('tool-form::validate()', 'Retrieving input objects failed.');\n continue;\n }\n if ( !input_def.optional && input_value == null ) {\n this.form.highlight( input_id );\n return false;\n }\n if ( input_value && input_value.batch ) {\n var n = input_value.values.length;\n var src = n > 0 && input_value.values[ 0 ] && input_value.values[ 0 ].src;\n if ( src ) {\n if ( batch_src === null ) {\n batch_src = src;\n } else if ( batch_src !== src ) {\n this.form.highlight( input_id, 'Please select either dataset or dataset list fields for all batch mode fields.' );\n return false;\n }\n }\n if ( batch_n === -1 ) {\n batch_n = n;\n } else if ( batch_n !== n ) {\n this.form.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 return false;\n }\n }\n }\n return true;\n },\n\n _templateSuccess: function( response ) {\n if ( response.jobs && response.jobs.length > 0 ) {\n var njobs = response.jobs.length;\n var njobs_text = njobs == 1 ? '1 job has' : njobs + ' jobs have';\n var $message = $( '' ).addClass( 'donemessagelarge' )\n .append( $( '' ).text( njobs_text + ' been successfully added to the queue - resulting in the following datasets:' ) );\n _.each( response.outputs, function( output ) {\n $message.append( $( '' ).addClass( 'messagerow' ).append( $( '' ).text( output.hid + ': ' + output.name ) ) );\n });\n $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 return $message;\n } else {\n return this._templateError( response, 'Invalid success response. No jobs found.' );\n }\n },\n\n _templateError: function( response, err_msg ) {\n return $( '' ).addClass( 'errormessagelarge' )\n .append( $( '' ).text( 'The server could not complete the request. Please contact the Galaxy Team if this error persists. ' + ( err_msg || '' ) ) )\n .append( $( '' ).text( JSON.stringify( response, null, 4 ) ) );\n }\n });\n\n return {\n View: View\n };\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/tool/tool-form.js\n// module id = 18\n// module chunks = 0 3","// dependencies\ndefine(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc'], function(Utils, Portlet, Ui) {\n\n// ui list element\nvar View = Backbone.View.extend({\n // create portlet to keep track of selected list elements\n initialize : function(options) {\n // link this\n var self = this;\n\n // initialize options\n this.options = options;\n this.name = options.name || 'element';\n this.multiple = options.multiple || false;\n\n // create message handler\n this.message = new Ui.Message();\n\n // create portlet\n this.portlet = new Portlet.View({ cls: 'ui-portlet-section' });\n\n // create select field containing the options which can be inserted into the list\n this.select = new Ui.Select.View({ optional : options.optional });\n\n // create insert new list element button\n this.button = new Ui.ButtonIcon({\n icon : 'fa fa-sign-in',\n floating : 'left',\n tooltip : 'Insert new ' + this.name,\n onclick : function() {\n self.add({\n id : self.select.value(),\n name : self.select.text()\n });\n }\n });\n\n // build main element\n this.setElement(this._template(options));\n this.$('.ui-list-message').append(this.message.$el);\n this.$('.ui-list-portlet').append(this.portlet.$el);\n this.$('.ui-list-button').append(this.button.$el);\n this.$('.ui-list-select').append(this.select.$el);\n },\n\n /** Return/Set currently selected list elements */\n value: function(val) {\n // set new value\n if (val !== undefined) {\n this.portlet.empty();\n if ($.isArray(val)) {\n for (var i in val) {\n var v = val[i];\n var v_id = null;\n var v_name = null;\n if ($.type(v) != 'string') {\n v_id = v.id;\n v_name = v.name;\n } else {\n v_id = v_name = v;\n }\n if (v_id != null) {\n this.add({\n id : v_id,\n name : v_name\n });\n }\n }\n }\n this._refresh();\n }\n // get current value\n var lst = [];\n this.$('.ui-list-id').each(function() {\n lst.push({\n id : $(this).prop('id'),\n name : $(this).find('.ui-list-name').html()\n });\n });\n if (lst.length == 0) {\n return null;\n }\n return lst;\n },\n\n /** Add row */\n add: function(options) {\n var self = this;\n if (this.$('[id=\"' + options.id + '\"]').length === 0) {\n if (!Utils.isEmpty(options.id)) {\n var $el = $(this._templateRow({\n id : options.id,\n name : options.name\n }));\n $el.on('click', function() {\n $el.remove();\n self._refresh();\n });\n $el.on('mouseover', function() {\n $el.addClass('portlet-highlight');\n });\n $el.on('mouseout', function() {\n $el.removeClass('portlet-highlight');\n });\n this.portlet.append($el);\n this._refresh();\n } else {\n this.message.update({ message: 'Please select a valid ' + this.name + '.', status: 'danger' });\n }\n } else {\n this.message.update({ message: 'This ' + this.name + ' is already in the list.' });\n }\n },\n\n /** Update available options */\n update: function(options) {\n this.select.update(options);\n },\n\n /** Refresh view */\n _refresh: function() {\n if (this.$('.ui-list-id').length > 0) {\n !this.multiple && this.button.disable();\n this.$('.ui-list-portlet').show();\n } else {\n this.button.enable();\n this.$('.ui-list-portlet').hide();\n }\n this.options.onchange && this.options.onchange();\n },\n\n /** Main Template */\n _template: function(options) {\n return '' + JSON.stringify( xhr.responseJSON ) + '
';\n } else {\n content += ': ' + message;\n }\n }\n creator._showAlert( content, 'alert-danger' );\n },\n\n events : {\n // header\n 'click .more-help' : '_clickMoreHelp',\n 'click .less-help' : '_clickLessHelp',\n 'click .main-help' : '_toggleHelp',\n 'click .header .alert button' : '_hideAlert',\n\n 'click .reset' : 'reset',\n 'click .clear-selected' : 'clearSelectedElements',\n\n // elements - selection\n 'click .collection-elements' : 'clearSelectedElements',\n\n // elements - drop target\n // 'dragenter .collection-elements': '_dragenterElements',\n // 'dragleave .collection-elements': '_dragleaveElements',\n 'dragover .collection-elements' : '_dragoverElements',\n 'drop .collection-elements' : '_dropElements',\n\n // these bubble up from the elements as custom events\n 'collection-element.dragstart .collection-elements' : '_elementDragstart',\n 'collection-element.dragend .collection-elements' : '_elementDragend',\n\n // footer\n 'change .collection-name' : '_changeName',\n 'keydown .collection-name' : '_nameCheckForEnter',\n 'click .cancel-create' : function( ev ){\n if( typeof this.oncancel === 'function' ){\n this.oncancel.call( this );\n }\n },\n 'click .create-collection' : '_clickCreate'//,\n },\n\n // ........................................................................ header\n /** expand help */\n _clickMoreHelp : function( ev ){\n ev.stopPropagation();\n this.$( '.main-help' ).addClass( 'expanded' );\n this.$( '.more-help' ).hide();\n },\n /** collapse help */\n _clickLessHelp : function( ev ){\n ev.stopPropagation();\n this.$( '.main-help' ).removeClass( 'expanded' );\n this.$( '.more-help' ).show();\n },\n /** toggle help */\n _toggleHelp : function( ev ){\n ev.stopPropagation();\n this.$( '.main-help' ).toggleClass( 'expanded' );\n this.$( '.more-help' ).toggle();\n },\n\n /** show an alert on the top of the interface containing message (alertClass is bootstrap's alert-*) */\n _showAlert : function( message, alertClass ){\n alertClass = alertClass || 'alert-danger';\n this.$( '.main-help' ).hide();\n this.$( '.header .alert' )\n .attr( 'class', 'alert alert-dismissable' ).addClass( alertClass ).show()\n .find( '.alert-message' ).html( message );\n },\n /** hide the alerts at the top */\n _hideAlert : function( message ){\n this.$( '.main-help' ).show();\n this.$( '.header .alert' ).hide();\n },\n\n // ........................................................................ elements\n /** reset all data to the initial state */\n reset : function(){\n this._instanceSetUp();\n this._elementsSetUp();\n this.render();\n },\n\n /** deselect all elements */\n clearSelectedElements : function( ev ){\n this.$( '.collection-elements .collection-element' ).removeClass( 'selected' );\n this.$( '.collection-elements-controls > .clear-selected' ).hide();\n },\n\n //_dragenterElements : function( ev ){\n // //this.debug( '_dragenterElements:', ev );\n //},\n//TODO: if selected are dragged out of the list area - remove the placeholder - cuz it won't work anyway\n // _dragleaveElements : function( ev ){\n // //this.debug( '_dragleaveElements:', ev );\n // },\n\n /** track the mouse drag over the list adding a placeholder to show where the drop would occur */\n _dragoverElements : function( ev ){\n //this.debug( '_dragoverElements:', ev );\n ev.preventDefault();\n\n var $list = this.$list();\n this._checkForAutoscroll( $list, ev.originalEvent.clientY );\n var $nearest = this._getNearestElement( ev.originalEvent.clientY );\n\n //TODO: no need to re-create - move instead\n this.$( '.element-drop-placeholder' ).remove();\n var $placeholder = $( '' );\n if( !$nearest.length ){\n $list.append( $placeholder );\n } else {\n $nearest.before( $placeholder );\n }\n },\n\n /** If the mouse is near enough to the list's top or bottom, scroll the list */\n _checkForAutoscroll : function( $element, y ){\n var AUTOSCROLL_SPEED = 2,\n offset = $element.offset(),\n scrollTop = $element.scrollTop(),\n upperDist = y - offset.top,\n lowerDist = ( offset.top + $element.outerHeight() ) - y;\n if( upperDist >= 0 && upperDist < this.autoscrollDist ){\n $element.scrollTop( scrollTop - AUTOSCROLL_SPEED );\n } else if( lowerDist >= 0 && lowerDist < this.autoscrollDist ){\n $element.scrollTop( scrollTop + AUTOSCROLL_SPEED );\n }\n },\n\n /** get the nearest element based on the mouse's Y coordinate.\n * If the y is at the end of the list, return an empty jQuery object.\n */\n _getNearestElement : function( y ){\n var WIGGLE = 4,\n lis = this.$( '.collection-elements li.collection-element' ).toArray();\n for( var i=0; i',\n '
',\n '<% _.each( problems, function( problem ){ %>',\n '
'\n ].join('')),\n\n /** shown in list when all elements are discarded */\n noElementsLeft : _.template([\n '<% _.each( problems, function( problem ){ %>',\n '
',\n '<% } else if( _.size( elements ) < 1 ){ %>',\n _l( 'No datasets were selected' ), '.',\n '<% } %>',\n '
',\n _l( 'At least one element is needed for the collection' ), '. ',\n _l( 'You may need to ' ),\n '', _l( 'cancel' ), ' ',\n _l( 'and reselect new elements' ), '.',\n '',\n '',\n '
',\n '<%= dataset.peek %>
',\n '<% } %>',\n '<% } %>',\n '');\n wrapper.append($el);\n this.row.append(wrapper);\n },\n \n // header\n appendHeader: function() {\n // append header row\n this.$thead.append(this.row);\n\n // row\n this.row = $(' ');\n },\n \n // add row cell\n add: function($el, width, align) {\n var wrapper = $(' ');\n if (width) {\n wrapper.css('width', width);\n }\n if (align) {\n wrapper.css('text-align', align);\n }\n wrapper.append($el);\n this.row.append(wrapper);\n },\n \n // append\n append: function(id, fade) {\n this._commit(id, fade, false);\n },\n \n // prepend\n prepend: function(id, fade) {\n this._commit(id, fade, true);\n },\n \n // get element\n get: function(id) {\n return this.$el.find('#' + id);\n },\n \n // delete\n del: function(id) {\n var item = this.$tbody.find('#' + id);\n if (item.length > 0) {\n item.remove();\n this.row_count--;\n this._refresh();\n }\n },\n\n // delete all\n delAll: function() {\n this.$tbody.empty();\n this.row_count = 0;\n this._refresh();\n },\n \n // value\n value: function(new_value) {\n // get current id/value\n this.before = this.$tbody.find('.current').attr('id');\n \n // check if new_value is defined\n if (new_value !== undefined) {\n this.$tbody.find('tr').removeClass('current');\n if (new_value) {\n this.$tbody.find('#' + new_value).addClass('current');\n }\n }\n \n // get current id/value\n var after = this.$tbody.find('.current').attr('id');\n if(after === undefined) {\n return null;\n } else {\n // fire onchange\n if (after != this.before && this.options.onchange) {\n this.options.onchange(new_value);\n }\n \n // return current value\n return after;\n }\n },\n \n // size\n size: function() {\n return this.$tbody.find('tr').length;\n },\n \n // commit\n _commit: function(id, fade, prepend) {\n // remove previous item with same id\n this.del(id);\n \n // add\n this.row.attr('id', id);\n \n // add row\n if (prepend) {\n this.$tbody.prepend(this.row);\n } else {\n this.$tbody.append(this.row);\n }\n \n // fade mode\n if (fade) {\n this.row.hide();\n this.row.fadeIn();\n }\n \n // row\n this.row = this._row();\n \n // row count\n this.row_count++;\n this._refresh();\n },\n \n // create new row\n _row: function() {\n return $(' ');\n },\n \n // onclick\n _onclick: function(e) {\n // get values\n var old_value = this.value();\n var new_value = $(e.target).closest('tr').attr('id');\n if (new_value != ''){\n // check equality\n if (new_value && old_value != new_value) {\n if (this.options.onconfirm) {\n this.options.onconfirm(new_value);\n } else {\n this.value(new_value);\n }\n }\n }\n },\n\n // ondblclick\n _ondblclick: function(e) {\n var value = this.value();\n if (value && this.options.ondblclick) {\n this.options.ondblclick(value);\n }\n },\n \n // refresh\n _refresh: function() {\n if (this.row_count == 0) {\n this.$tmessage.show();\n } else {\n this.$tmessage.hide();\n }\n },\n \n // load html template\n _template: function(options) {\n return ' ' +\n '' +\n '' +\n '
' +\n '
';\n helpString += data.help;\n } else {\n helpString += 'Tool help is unavailable for this dataset.
';\n }\n helpString += '
\" target=\"<%- view.linkTarget %>\">',\n _l( 'Set it manually or retry auto-detection' ),\n '',\n '
', _l( 'Undelete it' ), '',\n '<% if( view.purgeAllowed ){ %>',\n '
',\n _l( 'Permanently remove it from disk' ),\n '',\n '<% } %>',\n '',\n '<% _.each( visualizations, function( visualization ){ %>',\n '
',\n '', '
',\n '', this._templateOptions( options ), '
'\n ].join( '' );\n },\n\n _templateOptions : function( options ){\n if( !options.length ){\n return '' +\n ' ';\n },\n\n /** Template of table */\n _templateTable: function() {\n return 'Available files: ' +\n '' +\n '' +\n ' ' +\n '' +\n '' +\n '' +\n '' +\n ' ' + _.escape(options.path) + ' ' +\n '' + Utils.bytesToString( options.size ) + ' ' +\n '' + options.ctime + ' ' +\n '' +\n '' +\n '
';\n },\n\n /** Template of info message */\n _templateInfo: function() {\n return '' +\n ' ' +\n '' +\n '' +\n '' +\n ' Name ' +\n 'Size ' +\n 'Created ' +\n '' ).addClass( 'upload-settings-table ui-table-striped' ).append( '' ) );\n this.$cover = this.$( '.upload-settings-cover' );\n this.$table = this.$( '.upload-settings-table > tbody' );\n this.listenTo ( this.model, 'change', this.render, this );\n this.model.trigger( 'change' );\n },\n\n render: function() {\n var self = this;\n this.$table.empty();\n _.each( this.options.parameters, function( parameter ) {\n var $checkbox = $( '' ).addClass( 'upload-' + parameter.id + ' upload-icon-button fa' )\n .addClass( self.model.get( parameter.id ) && self.options.class_check || self.options.class_uncheck )\n .on( 'click', function() {\n self.model.get( 'enabled' ) && self.model.set( parameter.id, !self.model.get( parameter.id ) )\n });\n self.$table.append( $( '
' ).append( $( ' ' ).append( $checkbox ) )\n .append( $( ' ' ).append( parameter.title ) ) )\n });\n this.$cover[ this.model.get( 'enabled' ) && 'hide' || 'show' ]();\n }\n });\n});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/upload/upload-settings.js\n// module id = 82\n// module chunks = 3","(function (factory) {\n if (typeof define === 'function' && define.amd) {\n define([], factory);\n } else {\n // Browser globals\n factory(jQuery);\n }\n\n}(function () {\n//=============================================================================\n\n jQuery.fn.extend({\n hoverhighlight : function $hoverhighlight( scope, color ){\n scope = scope || 'body';\n if( !this.length ){ return this; }\n\n $( this ).each( function(){\n var $this = $( this ),\n targetSelector = $this.data( 'target' );\n\n if( targetSelector ){\n $this.mouseover( function( ev ){\n $( targetSelector, scope ).css({\n background: color\n });\n })\n .mouseout( function( ev ){\n $( targetSelector ).css({\n background: ''\n });\n });\n }\n });\n return this;\n }\n });\n}));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/ui/hoverhighlight.js\n// module id = 85\n// module chunks = 3","// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js\n// Uses AMD or browser globals to create a jQuery plugin.\n(function (factory) {\n if (typeof define === 'function' && define.amd) {\n //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will\n // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still\n // relying on jquery being loaded globally* in order to preserve plugins.\n define([], factory);\n } else {\n // Browser globals\n factory(jQuery);\n }\n\n}(function () {\n var _l = window._l || function( s ){ return s; };\n\n //TODO: consolidate with tool menu functionality, use there\n\n /** searchInput: (jQuery plugin)\n * Creates a search input, a clear button, and loading indicator\n * within the selected node.\n *\n * When the user either presses return or enters some minimal number\n * of characters, a callback is called. Pressing ESC when the input\n * is focused will clear the input and call a separate callback.\n */\n function searchInput( parentNode, options ){\n var KEYCODE_ESC = 27,\n KEYCODE_RETURN = 13,\n $parentNode = $( parentNode ),\n firstSearch = true,\n defaults = {\n initialVal : '',\n name : 'search',\n placeholder : 'search',\n classes : '',\n onclear : function(){},\n onfirstsearch : null,\n onsearch : function( inputVal ){},\n minSearchLen : 0,\n escWillClear : true,\n oninit : function(){}\n };\n\n // .................................................................... input rendering and events\n // visually clear the search, trigger an event, and call the callback\n function clearSearchInput( event ){\n var $input = $( this ).parent().children( 'input' );\n $input.val( '' ).trigger( 'searchInput.clear' ).blur();\n options.onclear();\n }\n\n // search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)\n function search( event, searchTerms ){\n if( !searchTerms ){\n return clearSearchInput();\n }\n $( this ).trigger( 'search.search', searchTerms );\n if( typeof options.onfirstsearch === 'function' && firstSearch ){\n firstSearch = false;\n options.onfirstsearch( searchTerms );\n } else {\n options.onsearch( searchTerms );\n }\n }\n\n // .................................................................... input rendering and events\n function inputTemplate(){\n // class search-query is bootstrap 2.3 style that now lives in base.less\n return [ '' ].join( '' );\n }\n\n // the search input that responds to keyboard events and displays the search value\n function $input(){\n return $( inputTemplate() )\n // select all text on a focus\n .focus( function( event ){\n $( this ).select();\n })\n // attach behaviors to esc, return if desired, search on some min len string\n .keyup( function( event ){\n event.preventDefault();\n event.stopPropagation();\n\n // esc key will clear if desired\n if( event.which === KEYCODE_ESC && options.escWillClear ){\n clearSearchInput.call( this, event );\n\n } else {\n var searchTerms = $( this ).val();\n // return key or the search string len > minSearchLen (if not 0) triggers search\n if( ( event.which === KEYCODE_RETURN )\n || ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){\n search.call( this, event, searchTerms );\n }\n }\n })\n .val( options.initialVal );\n }\n\n // .................................................................... clear button rendering and events\n // a button for clearing the search bar, placed on the right hand side\n function $clearBtn(){\n return $([ '' ].join('') )\n .tooltip({ placement: 'bottom' })\n .click( function( event ){\n clearSearchInput.call( this, event );\n });\n }\n\n // .................................................................... loadingIndicator rendering\n // a button for clearing the search bar, placed on the right hand side\n function $loadingIndicator(){\n return $([ '' ].join('') )\n .hide().tooltip({ placement: 'bottom' });\n }\n\n // .................................................................... commands\n // visually swap the load, clear buttons\n function toggleLoadingIndicator(){\n $parentNode.find( '.search-loading' ).toggle();\n $parentNode.find( '.search-clear' ).toggle();\n }\n\n // .................................................................... init\n // string command (not constructor)\n if( jQuery.type( options ) === 'string' ){\n if( options === 'toggle-loading' ){\n toggleLoadingIndicator();\n }\n return $parentNode;\n }\n\n // initial render\n if( jQuery.type( options ) === 'object' ){\n options = jQuery.extend( true, {}, defaults, options );\n }\n //NOTE: prepended\n return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);\n }\n\n // as jq plugin\n jQuery.fn.extend({\n searchInput : function $searchInput( options ){\n return this.each( function(){\n return searchInput( this, options );\n });\n }\n });\n}));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/ui/search-input.js\n// module id = 86\n// module chunks = 3","define([], function(){\n// Alphanumeric/natural sort fn\nfunction naturalSort(a, b) {\n // setup temp-scope variables for comparison evauluation\n var re = /(-?[0-9\\.]+)/g,\n x = a.toString().toLowerCase() || '',\n y = b.toString().toLowerCase() || '',\n nC = String.fromCharCode(0),\n xN = x.replace( re, nC + '$1' + nC ).split(nC),\n yN = y.replace( re, nC + '$1' + nC ).split(nC),\n xD = (new Date(x)).getTime(),\n yD = xD ? (new Date(y)).getTime() : null;\n // natural sorting of dates\n if ( yD ) {\n if ( xD < yD ) { return -1; }\n else if ( xD > yD ) { return 1; }\n }\n // natural sorting through split numeric strings and default strings\n var oFxNcL, oFyNcL;\n for ( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {\n oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];\n oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];\n if (oFxNcL < oFyNcL) { return -1; }\n else if (oFxNcL > oFyNcL) { return 1; }\n }\n return 0;\n}\n\nreturn naturalSort;\n})\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/utils/natural-sort.js\n// module id = 88\n// module chunks = 3","/*\n galaxy upload plugins - requires FormData and XMLHttpRequest\n*/\n;(function($){\n // add event properties\n jQuery.event.props.push(\"dataTransfer\");\n\n /**\n Posts file data to the API\n */\n $.uploadpost = function (config) {\n // parse options\n var cnf = $.extend({}, {\n data : {},\n success : function() {},\n error : function() {},\n progress : function() {},\n url : null,\n maxfilesize : 2048,\n error_filesize : 'File exceeds 2GB. Please use a FTP client.',\n error_default : 'Please make sure the file is available.',\n error_server : 'Upload request failed.',\n error_login : 'Uploads require you to log in.'\n }, config);\n\n // link data\n var data = cnf.data;\n\n // check errors\n if (data.error_message) {\n cnf.error(data.error_message);\n return;\n }\n\n // construct form data\n var form = new FormData();\n for (var key in data.payload) {\n form.append(key, data.payload[key]);\n }\n\n // add files to submission\n var sizes = 0;\n for (var key in data.files) {\n var d = data.files[key];\n form.append(d.name, d.file, d.file.name);\n sizes += d.file.size;\n }\n\n // check file size, unless it's an ftp file\n if (sizes > 1048576 * cnf.maxfilesize) {\n cnf.error(cnf.error_filesize);\n return;\n }\n\n // prepare request\n xhr = new XMLHttpRequest();\n xhr.open('POST', cnf.url, true);\n xhr.setRequestHeader('Accept', 'application/json');\n xhr.setRequestHeader('Cache-Control', 'no-cache');\n xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\n // captures state changes\n xhr.onreadystatechange = function() {\n // check for request completed, server connection closed\n if (xhr.readyState == xhr.DONE) {\n // parse response\n var response = null;\n if (xhr.responseText) {\n try {\n response = jQuery.parseJSON(xhr.responseText);\n } catch (e) {\n response = xhr.responseText;\n }\n }\n // pass any error to the error option\n if (xhr.status < 200 || xhr.status > 299) {\n var text = xhr.statusText;\n if (xhr.status == 403) {\n text = cnf.error_login;\n } else if (xhr.status == 0) {\n text = cnf.error_server;\n } else if (!text) {\n text = cnf.error_default;\n }\n cnf.error(text + ' (' + xhr.status + ')');\n } else {\n cnf.success(response);\n }\n }\n }\n\n // prepare upload progress\n xhr.upload.addEventListener('progress', function(e) {\n if (e.lengthComputable) {\n cnf.progress(Math.round((e.loaded * 100) / e.total));\n }\n }, false);\n\n // send request\n Galaxy.emit.debug('uploadbox::uploadpost()', 'Posting following data.', cnf);\n xhr.send(form);\n }\n\n /**\n Handles the upload events drag/drop etc.\n */\n $.fn.uploadinput = function(options) {\n // initialize\n var el = this;\n var opts = $.extend({}, {\n ondragover : function() {},\n ondragleave : function() {},\n onchange : function() {},\n multiple : false\n }, options);\n\n // append hidden upload field\n var $input = $('');\n el.append($input.change(function (e) {\n opts.onchange(e.target.files);\n $(this).val('');\n }));\n\n // drag/drop events\n el.on('drop', function (e) {\n opts.ondragleave(e);\n if(e.dataTransfer) {\n opts.onchange(e.dataTransfer.files);\n e.preventDefault();\n }\n });\n el.on('dragover', function (e) {\n e.preventDefault();\n opts.ondragover(e);\n });\n el.on('dragleave', function (e) {\n e.stopPropagation();\n opts.ondragleave(e);\n });\n\n // exports\n return {\n dialog: function () {\n $input.trigger('click');\n }\n }\n }\n\n /**\n Handles the upload queue and events such as drag/drop etc.\n */\n $.fn.uploadbox = function(options) {\n // parse options\n var opts = $.extend({}, {\n dragover : function() {},\n dragleave : function() {},\n announce : function(d) {},\n initialize : function(d) {},\n progress : function(d, m) {},\n success : function(d, m) {},\n error : function(d, m) { alert(m); },\n complete : function() {}\n }, options);\n\n // file queue\n var queue = {};\n\n // queue index/length counter\n var queue_index = 0;\n var queue_length = 0;\n\n // indicates if queue is currently running\n var queue_running = false;\n var queue_stop = false;\n\n // element\n var uploadinput = $(this).uploadinput({\n multiple : true,\n onchange : function(files) { add(files); },\n ondragover : options.ondragover,\n ondragleave : options.ondragleave\n });\n\n // add new files to upload queue\n function add(files) {\n if (files && files.length && !queue_running) {\n var current_index = queue_index;\n _.each(files, function(file, key) {\n if (file.mode !== 'new' && _.filter(queue, function(f) {\n return f.name === file.name && f.size === file.size;\n }).length) {\n file.duplicate = true;\n }\n });\n _.each(files, function(file) {\n if (!file.duplicate) {\n var index = String(queue_index++);\n queue[index] = file;\n opts.announce(index, queue[index]);\n queue_length++;\n }\n });\n return current_index;\n }\n }\n\n // remove file from queue\n function remove(index) {\n if (queue[index]) {\n delete queue[index];\n queue_length--;\n }\n }\n\n // process an upload, recursive\n function process() {\n // validate\n if (queue_length == 0 || queue_stop) {\n queue_stop = false;\n queue_running = false;\n opts.complete();\n return;\n } else {\n queue_running = true;\n }\n\n // get an identifier from the queue\n var index = -1;\n for (var key in queue) {\n index = key;\n break;\n }\n\n // get current file from queue\n var file = queue[index];\n\n // remove from queue\n remove(index)\n\n // create and submit data\n $.uploadpost({\n url : opts.url,\n data : opts.initialize(index),\n success : function(message) { opts.success(index, message); process();},\n error : function(message) { opts.error(index, message); process();},\n progress : function(percentage) { opts.progress(index, percentage); }\n });\n }\n\n /*\n public interface\n */\n\n // open file browser for selection\n function select() {\n uploadinput.dialog();\n }\n\n // remove all entries from queue\n function reset(index) {\n for (index in queue) {\n remove(index);\n }\n }\n\n // initiate upload process\n function start() {\n if (!queue_running) {\n queue_running = true;\n process();\n }\n }\n\n // stop upload process\n function stop() {\n queue_stop = true;\n }\n\n // set options\n function configure(options) {\n opts = $.extend({}, opts, options);\n return opts;\n }\n\n // verify browser compatibility\n function compatible() {\n return window.File && window.FormData && window.XMLHttpRequest && window.FileList;\n }\n\n // export functions\n return {\n 'select' : select,\n 'add' : add,\n 'remove' : remove,\n 'start' : start,\n 'stop' : stop,\n 'reset' : reset,\n 'configure' : configure,\n 'compatible' : compatible\n };\n }\n})(jQuery);\n\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/utils/uploadbox.js\n// module id = 89\n// module chunks = 3","var RightPanel = require( 'layout/panel' ).RightPanel,\n Ui = require( 'mvc/ui/ui-misc' ),\n historyOptionsMenu = require( 'mvc/history/options-menu' );\n CurrentHistoryView = require( 'mvc/history/history-view-edit-current' ).CurrentHistoryView,\n _l = require( 'utils/localization' );\n\n/** the right hand panel in the analysis page that shows the current history */\nvar HistoryPanel = RightPanel.extend({\n\n title : _l( 'History' ),\n\n initialize : function( options ){\n RightPanel.prototype.initialize.call( this, options );\n this.options = _.pick( options, 'userIsAnonymous', 'allow_user_dataset_purge', 'galaxyRoot' );\n\n // view of the current history\n this.historyView = new CurrentHistoryView({\n className : CurrentHistoryView.prototype.className + ' middle',\n purgeAllowed : options.allow_user_dataset_purge,\n linkTarget : 'galaxy_main'\n });\n },\n\n /** override to change footer selector */\n $toggleButton : function(){\n return this.$( '.footer > .panel-collapse' );\n },\n\n render : function(){\n RightPanel.prototype.render.call( this );\n this.optionsMenu = historyOptionsMenu( this.$( '#history-options-button' ), {\n anonymous : this.options.userIsAnonymous,\n purgeAllowed : this.options.allow_user_dataset_purge,\n root : this.options.galaxyRoot\n });\n this.$( '> .header .buttons [title]' ).tooltip({ placement: 'bottom' });\n this.historyView.setElement( this.$( '.history-panel' ) );\n this.$el.attr( 'class', 'history-right-panel' );\n },\n\n /** override to add buttons */\n _templateHeader: function( data ){\n var historyUrl = this.options.galaxyRoot + 'history';\n var multiUrl = this.options.galaxyRoot + 'history/view_multiple';\n return [\n ' ' + JSON.stringify( xhr.responseJSON ) + '
';\n } else {\n content += ': ' + message;\n }\n }\n creator._showAlert( content, 'alert-danger' );\n },\n\n // ------------------------------------------------------------------------ rendering\n /** render the entire interface */\n render : function( speed, callback ){\n //this.debug( '-- _render' );\n //this.$el.empty().html( PairedCollectionCreator.templates.main() );\n this.$el.empty().html( PairedCollectionCreator.templates.main() );\n this._renderHeader( speed );\n this._renderMiddle( speed );\n this._renderFooter( speed );\n this._addPluginComponents();\n this.trigger( 'rendered', this );\n return this;\n },\n\n /** render the header section */\n _renderHeader : function( speed, callback ){\n //this.debug( '-- _renderHeader' );\n var $header = this.$( '.header' ).empty().html( PairedCollectionCreator.templates.header() )\n .find( '.help-content' ).prepend( $( PairedCollectionCreator.templates.helpContent() ) );\n\n this._renderFilters();\n return $header;\n },\n /** fill the filter inputs with the filter values */\n _renderFilters : function(){\n return this.$( '.forward-column .column-header input' ).val( this.filters[0] )\n .add( this.$( '.reverse-column .column-header input' ).val( this.filters[1] ) );\n },\n\n /** render the middle including unpaired and paired sections (which may be hidden) */\n _renderMiddle : function( speed, callback ){\n var $middle = this.$( '.middle' ).empty().html( PairedCollectionCreator.templates.middle() );\n\n // (re-) hide the un/paired panels based on instance vars\n if( this.unpairedPanelHidden ){\n this.$( '.unpaired-columns' ).hide();\n } else if( this.pairedPanelHidden ){\n this.$( '.paired-columns' ).hide();\n }\n\n this._renderUnpaired();\n this._renderPaired();\n return $middle;\n },\n /** render the unpaired section, showing datasets accrd. to filters, update the unpaired counts */\n _renderUnpaired : function( speed, callback ){\n //this.debug( '-- _renderUnpaired' );\n var creator = this,\n $fwd, $rev, $prd = [],\n split = this._splitByFilters();\n // update unpaired counts\n this.$( '.forward-column .title' )\n .text([ split[0].length, _l( 'unpaired forward' ) ].join( ' ' ));\n this.$( '.forward-column .unpaired-info' )\n .text( this._renderUnpairedDisplayStr( this.unpaired.length - split[0].length ) );\n this.$( '.reverse-column .title' )\n .text([ split[1].length, _l( 'unpaired reverse' ) ].join( ' ' ));\n this.$( '.reverse-column .unpaired-info' )\n .text( this._renderUnpairedDisplayStr( this.unpaired.length - split[1].length ) );\n\n this.$( '.unpaired-columns .column-datasets' ).empty();\n\n // show/hide the auto pair button if any unpaired are left\n this.$( '.autopair-link' ).toggle( this.unpaired.length !== 0 );\n if( this.unpaired.length === 0 ){\n this._renderUnpairedEmpty();\n return;\n }\n\n // create the dataset dom arrays\n $rev = split[1].map( function( dataset, i ){\n // if there'll be a fwd dataset across the way, add a button to pair the row\n if( ( split[0][ i ] !== undefined )\n && ( split[0][ i ] !== dataset ) ){\n $prd.push( creator._renderPairButton() );\n }\n return creator._renderUnpairedDataset( dataset );\n });\n $fwd = split[0].map( function( dataset ){\n return creator._renderUnpairedDataset( dataset );\n });\n\n if( !$fwd.length && !$rev.length ){\n this._renderUnpairedNotShown();\n return;\n }\n // add to appropo cols\n //TODO: not the best way to render - consider rendering the entire unpaired-columns section in a fragment\n // and swapping out that\n this.$( '.unpaired-columns .forward-column .column-datasets' ).append( $fwd )\n .add( this.$( '.unpaired-columns .paired-column .column-datasets' ).append( $prd ) )\n .add( this.$( '.unpaired-columns .reverse-column .column-datasets' ).append( $rev ) );\n this._adjUnpairedOnScrollbar();\n },\n /** return a string to display the count of filtered out datasets */\n _renderUnpairedDisplayStr : function( numFiltered ){\n return [ '(', numFiltered, ' ', _l( 'filtered out' ), ')' ].join('');\n },\n /** return an unattached jQuery DOM element to represent an unpaired dataset */\n _renderUnpairedDataset : function( dataset ){\n //TODO: to underscore template\n return $( '')\n .attr( 'id', 'dataset-' + dataset.id )\n .addClass( 'dataset unpaired' )\n .attr( 'draggable', true )\n .addClass( dataset.selected? 'selected': '' )\n .append( $( '' ).addClass( 'dataset-name' ).text( dataset.name ) )\n //??\n .data( 'dataset', dataset );\n },\n /** render the button that may go between unpaired datasets, allowing the user to pair a row */\n _renderPairButton : function(){\n //TODO: *not* a dataset - don't pretend like it is\n return $( '').addClass( 'dataset unpaired' )\n .append( $( '' ).addClass( 'dataset-name' ).text( _l( 'Pair these datasets' ) ) );\n },\n /** a message to display when no unpaired left */\n _renderUnpairedEmpty : function(){\n //this.debug( '-- renderUnpairedEmpty' );\n var $msg = $( '' )\n .text( '(' + _l( 'no remaining unpaired datasets' ) + ')' );\n this.$( '.unpaired-columns .paired-column .column-datasets' ).empty().prepend( $msg );\n return $msg;\n },\n /** a message to display when no unpaired can be shown with the current filters */\n _renderUnpairedNotShown : function(){\n //this.debug( '-- renderUnpairedEmpty' );\n var $msg = $( '' )\n .text( '(' + _l( 'no datasets were found matching the current filters' ) + ')' );\n this.$( '.unpaired-columns .paired-column .column-datasets' ).empty().prepend( $msg );\n return $msg;\n },\n /** try to detect if the unpaired section has a scrollbar and adjust left column for better centering of all */\n _adjUnpairedOnScrollbar : function(){\n var $unpairedColumns = this.$( '.unpaired-columns' ).last(),\n $firstDataset = this.$( '.unpaired-columns .reverse-column .dataset' ).first();\n if( !$firstDataset.length ){ return; }\n var ucRight = $unpairedColumns.offset().left + $unpairedColumns.outerWidth(),\n dsRight = $firstDataset.offset().left + $firstDataset.outerWidth(),\n rightDiff = Math.floor( ucRight ) - Math.floor( dsRight );\n //this.debug( 'rightDiff:', ucRight, '-', dsRight, '=', rightDiff );\n this.$( '.unpaired-columns .forward-column' )\n .css( 'margin-left', ( rightDiff > 0 )? rightDiff: 0 );\n },\n\n /** render the paired section and update counts of paired datasets */\n _renderPaired : function( speed, callback ){\n //this.debug( '-- _renderPaired' );\n this.$( '.paired-column-title .title' ).text([ this.paired.length, _l( 'paired' ) ].join( ' ' ) );\n // show/hide the unpair all link\n this.$( '.unpair-all-link' ).toggle( this.paired.length !== 0 );\n if( this.paired.length === 0 ){\n this._renderPairedEmpty();\n return;\n //TODO: would be best to return here (the $columns)\n } else {\n // show/hide 'remove extensions link' when any paired and they seem to have extensions\n this.$( '.remove-extensions-link' ).show();\n }\n\n this.$( '.paired-columns .column-datasets' ).empty();\n var creator = this;\n this.paired.forEach( function( pair, i ){\n //TODO: cache these?\n var pairView = new PairView({ pair: pair });\n creator.$( '.paired-columns .column-datasets' )\n .append( pairView.render().$el )\n .append([\n ''\n ].join( '' ));\n });\n },\n /** a message to display when none paired */\n _renderPairedEmpty : function(){\n var $msg = $( '' )\n .text( '(' + _l( 'no paired datasets yet' ) + ')' );\n this.$( '.paired-columns .column-datasets' ).empty().prepend( $msg );\n return $msg;\n },\n\n /** render the footer, completion controls, and cancel controls */\n _renderFooter : function( speed, callback ){\n var $footer = this.$( '.footer' ).empty().html( PairedCollectionCreator.templates.footer() );\n this.$( '.remove-extensions' ).prop( 'checked', this.removeExtensions );\n if( typeof this.oncancel === 'function' ){\n this.$( '.cancel-create.btn' ).show();\n }\n return $footer;\n },\n\n /** add any jQuery/bootstrap/custom plugins to elements rendered */\n _addPluginComponents : function(){\n this._chooseFiltersPopover( '.choose-filters-link' );\n this.$( '.help-content i' ).hoverhighlight( '.collection-creator', 'rgba( 64, 255, 255, 1.0 )' );\n },\n\n /** build a filter selection popover allowing selection of common filter pairs */\n _chooseFiltersPopover : function( selector ){\n function filterChoice( val1, val2 ){\n return [\n ''\n ].join('');\n }\n var $popoverContent = $( _.template([\n '
',\n '',\n _l( 'Auto-pair' ),\n '',\n '',\n '
',\n '
',\n '
',\n '
To pair datasets, you can:',\n '
'\n ].join( '' )), '',\n 'You can filter what is shown in the unpaired sections by:',\n '
'\n ].join( '' )), '',\n '',\n '
',\n '<% _.each( problems, function( problem ){ %>',\n '
',\n '<% } else if( _.size( elements ) === 0 ){ %>',\n _l( 'No datasets were selected' ), '.',\n '<% } else if( _.size( elements ) === 1 ){ %>',\n _l( 'Only one dataset was selected' ), ': <%- elements[0].name %>',\n '<% } else if( _.size( elements ) > 2 ){ %>',\n _l( 'Too many datasets were selected' ),\n ': <%- _.pluck( elements, \"name\" ).join( \", \") %>',\n '<% } %>',\n '
',\n _l( 'Two (and only two) elements are needed for the pair' ), '. ',\n _l( 'You may need to ' ),\n '', _l( 'cancel' ), ' ',\n _l( 'and reselect new elements' ), '.',\n '',\n '
', _l( 'Unhide it' ), '',\n '',\n '',\n '
' ).addClass( 'tab-navigation nav nav-tabs' ) )\n .append( $( '' ).addClass( 'tab-content' ) );\n },\n\n /** Tab template */\n _template_tab: function( options ) {\n var $tmpl = $( '' ).addClass( 'tab-element' )\n .attr( 'id', 'tab-' + options.id )\n .append( $( '' ).attr( 'id', 'tab-title-link-' + options.id ) );\n var $href = $tmpl.find( 'a' );\n options.icon && $href.append( $( '' ).addClass( 'tab-icon fa' ).addClass( options.icon ) );\n $href.append( $( '' ).attr( 'id', 'tab-title-text-' + options.id )\n .addClass( 'tab-title-text' )\n .append( options.title ) );\n return $tmpl;\n }\n });\n\n return { View : View }\n});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/ui/ui-tabs.js\n// module id = 118\n// module chunks = 3","/** Renders the composite upload row view */\ndefine([ 'utils/utils', 'mvc/upload/upload-settings', 'mvc/upload/upload-ftp', 'mvc/ui/ui-popover', 'mvc/ui/ui-misc', 'mvc/ui/ui-select', 'utils/uploadbox' ],\nfunction( Utils, UploadSettings, UploadFtp, Popover, Ui, Select ) {\n return Backbone.View.extend({\n /** Dictionary of upload states and associated icons */\n status_classes : {\n init : 'upload-mode fa fa-exclamation text-primary',\n ready : 'upload-mode fa fa-check text-success',\n running : 'upload-mode fa fa-spinner fa-spin',\n success : 'upload-mode fa fa-check',\n error : 'upload-mode fa fa-exclamation-triangle'\n },\n\n initialize: function( app, options ) {\n var self = this;\n this.app = app;\n this.model = options.model;\n this.setElement( this._template() );\n this.$source = this.$( '.upload-source' );\n this.$settings = this.$( '.upload-settings' );\n this.$status = this.$( '.upload-status' );\n this.$text = this.$( '.upload-text' );\n this.$text_content = this.$( '.upload-text-content' );\n this.$info_text = this.$( '.upload-info-text' );\n this.$info_progress = this.$( '.upload-info-progress' );\n this.$file_name = this.$( '.upload-file-name' );\n this.$file_desc = this.$( '.upload-file-desc' );\n this.$file_size = this.$( '.upload-file-size' );\n this.$progress_bar = this.$( '.upload-progress-bar' );\n this.$percentage = this.$( '.upload-percentage' );\n\n // build upload functions\n this.uploadinput = this.$el.uploadinput({\n ondragover: function() {\n self.model.get( 'enabled' ) && self.$el.addClass( 'warning' );\n },\n ondragleave: function() {\n self.$el.removeClass( 'warning' );\n },\n onchange: function( files ) {\n if ( self.model.get( 'status' ) != 'running' && files && files.length > 0 ) {\n self.model.reset({\n 'file_data': files[ 0 ],\n 'file_name': files[ 0 ].name,\n 'file_size': files[ 0 ].size,\n 'file_mode': files[ 0 ].mode || 'local'\n });\n self._refreshReady();\n }\n }\n });\n\n // source selection popup\n this.button_menu = new Ui.ButtonMenu({\n icon : 'fa-caret-down',\n title : 'Select',\n pull : 'left'\n });\n this.$source.append( this.button_menu.$el );\n this.button_menu.addMenu({\n icon : 'fa-laptop',\n title : 'Choose local file',\n onclick : function() { self.uploadinput.dialog() }\n });\n if ( this.app.ftp_upload_site ) {\n this.button_menu.addMenu({\n icon : 'fa-folder-open-o',\n title : 'Choose FTP file',\n onclick : function() { self._showFtp() }\n });\n }\n this.button_menu.addMenu({\n icon : 'fa-edit',\n title : 'Paste/Fetch data',\n onclick : function() {\n self.model.reset( { 'file_mode': 'new', 'file_name': 'New File' } );\n }\n });\n\n // add ftp file viewer\n this.ftp = new Popover.View({\n title : 'Choose FTP file:',\n container : this.$source.find( '.ui-button-menu' ),\n placement : 'right'\n });\n\n // append popup to settings icon\n this.settings = new Popover.View({\n title : 'Upload configuration',\n container : this.$settings,\n placement : 'bottom'\n });\n\n // handle text editing event\n this.$text_content.on( 'change input', function( e ) {\n self.model.set( { 'url_paste': $( e.target ).val(),\n 'file_size': $( e.target ).val().length } );\n self._refreshReady();\n });\n\n // handle settings popover\n this.$settings.on( 'click', function( e ) { self._showSettings() } )\n .on( 'mousedown', function( e ) { e.preventDefault() } );\n\n // model events\n this.listenTo( this.model, 'change:percentage', function() { self._refreshPercentage() } );\n this.listenTo( this.model, 'change:status', function() { self._refreshStatus() } );\n this.listenTo( this.model, 'change:info', function() { self._refreshInfo() } );\n this.listenTo( this.model, 'change:file_name', function() { self._refreshFileName() } );\n this.listenTo( this.model, 'change:file_mode', function() { self._refreshMode() } );\n this.listenTo( this.model, 'change:file_size', function() { self._refreshFileSize() } );\n this.listenTo( this.model, 'remove', function() { self.remove() } );\n this.app.collection.on( 'reset', function() { self.remove() } );\n },\n\n render: function() {\n this.$el.attr( 'id', 'upload-row-' + this.model.id );\n this.$file_name.html( _.escape( this.model.get( 'file_name' ) || '-' ) );\n this.$file_desc.html( this.model.get( 'file_desc' ) || 'Unavailable' );\n this.$file_size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );\n this.$status.removeClass().addClass( this.status_classes.init );\n },\n\n /** Remove view */\n remove: function() {\n // call the base class remove method\n Backbone.View.prototype.remove.apply( this );\n },\n\n //\n // handle model events\n //\n\n /** Refresh ready or not states */\n _refreshReady: function() {\n this.app.collection.each( function( model ) {\n model.set( 'status', ( model.get( 'file_size' ) > 0 ) && 'ready' || 'init' );\n });\n },\n\n /** Refresh mode and e.g. show/hide textarea field */\n _refreshMode: function() {\n var file_mode = this.model.get( 'file_mode' );\n if ( file_mode == 'new' ) {\n this.height = this.$el.height();\n this.$text.css( { 'width' : this.$el.width() - 16 + 'px',\n 'top' : this.$el.height() - 8 + 'px' } ).show();\n this.$el.height( this.$el.height() - 8 + this.$text.height() + 16 );\n this.$text_content.val( '' ).trigger( 'keyup' );\n } else {\n this.$el.height( this.height );\n this.$text.hide();\n }\n },\n\n /** Refresh information */\n _refreshInfo: function() {\n var info = this.model.get( 'info' );\n if ( info ) {\n this.$info_text.html( 'Failed: ' + info ).show();\n } else {\n this.$info_text.hide();\n }\n },\n\n /** Refresh percentage */\n _refreshPercentage : function() {\n var percentage = parseInt( this.model.get( 'percentage' ) );\n if ( percentage != 0 ) {\n this.$progress_bar.css( { width : percentage + '%' } );\n } else {\n this.$progress_bar.addClass( 'no-transition' );\n this.$progress_bar.css( { width : '0%' } );\n this.$progress_bar[ 0 ].offsetHeight;\n this.$progress_bar.removeClass( 'no-transition' );\n }\n this.$percentage.html( percentage != 100 ? percentage + '%' : 'Adding to history...' );\n },\n\n /** Refresh status */\n _refreshStatus : function() {\n var status = this.model.get( 'status' );\n this.$status.removeClass().addClass( this.status_classes[ status ] );\n this.model.set( 'enabled', status != 'running' );\n this.$text_content.attr( 'disabled', !this.model.get( 'enabled' ) );\n this.$el.removeClass( 'success danger warning' );\n if ( status == 'running' || status == 'ready' ) {\n this.model.set( 'percentage', 0 );\n }\n this.$source.find( '.button' )[ status == 'running' ? 'addClass' : 'removeClass' ]( 'disabled' );\n if ( status == 'success' ) {\n this.$el.addClass( 'success' );\n this.model.set( 'percentage', 100 );\n this.$percentage.html( '100%' );\n }\n if ( status == 'error' ) {\n this.$el.addClass( 'danger' );\n this.model.set( 'percentage', 0 );\n this.$info_progress.hide();\n this.$info_text.show();\n } else {\n this.$info_progress.show();\n this.$info_text.hide();\n }\n },\n\n /** File name */\n _refreshFileName: function() {\n this.$file_name.html( this.model.get( 'file_name' ) || '-' );\n },\n\n /** File size */\n _refreshFileSize: function() {\n this.$file_size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );\n },\n\n /** Show/hide ftp popup */\n _showFtp: function() {\n if ( !this.ftp.visible ) {\n var self = this;\n this.ftp.empty();\n this.ftp.append( ( new UploadFtp( {\n ftp_upload_site : this.app.ftp_upload_site,\n onchange : function( ftp_file ) {\n self.ftp.hide();\n if ( self.model.get( 'status' ) != 'running' && ftp_file ) {\n self.model.reset({\n 'file_mode': 'ftp',\n 'file_name': ftp_file.path,\n 'file_size': ftp_file.size,\n 'file_path': ftp_file.path\n });\n self._refreshReady();\n }\n }\n } ) ).$el );\n this.ftp.show();\n } else {\n this.ftp.hide();\n }\n },\n\n /** Show/hide settings popup */\n _showSettings : function() {\n if ( !this.settings.visible ) {\n this.settings.empty();\n this.settings.append( ( new UploadSettings( this ) ).$el );\n this.settings.show();\n } else {\n this.settings.hide();\n }\n },\n\n /** Template */\n _template: function() {\n return '
' +\n ' ';\n }\n });\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/upload/composite/composite-row.js\n// module id = 119\n// module chunks = 3","/** Renders contents of the composite uploader */\ndefine([ 'utils/utils', 'mvc/upload/upload-model', 'mvc/upload/composite/composite-row', 'mvc/ui/ui-popover', 'mvc/ui/ui-select', 'mvc/ui/ui-misc'],\nfunction( Utils, UploadModel, UploadRow, Popover, Select, Ui ) {\n return Backbone.View.extend({\n collection: new UploadModel.Collection(),\n initialize: function(app) {\n var self = this;\n this.app = app;\n this.options = app.options;\n this.list_extensions = app.list_extensions;\n this.list_genomes = app.list_genomes;\n this.ftp_upload_site = app.currentFtp();\n this.setElement( this._template() );\n\n // create button section\n this.btnStart = new Ui.Button( { title: 'Start', onclick: function() { self._eventStart() } } );\n this.btnClose = new Ui.Button( { title: 'Close', onclick: function() { self.app.modal.hide() } } );\n\n // append buttons to dom\n _.each( [ this.btnStart, this.btnClose ], function( button ) {\n self.$( '.upload-buttons' ).prepend( button.$el );\n });\n\n // select extension\n this.select_extension = new Select.View({\n css : 'upload-footer-selection',\n container : this.$( '.upload-footer-extension' ),\n data : _.filter( this.list_extensions, function( ext ) { return ext.composite_files } ),\n onchange : function( extension ) {\n self.collection.reset();\n var details = _.findWhere( self.list_extensions, { id : extension } );\n if ( details && details.composite_files ) {\n _.each( details.composite_files, function( item ) {\n self.collection.add( { id : self.collection.size(),\n file_desc : item.description || item.name } );\n } );\n }\n }\n });\n\n // handle extension info popover\n this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {\n self._showExtensionInfo({\n $el : $( e.target ),\n title : self.select_extension.text(),\n extension : self.select_extension.value(),\n placement : 'top'\n });\n }).on( 'mousedown', function( e ) { e.preventDefault() } );\n\n // genome extension\n this.select_genome = new Select.View({\n css : 'upload-footer-selection',\n container : this.$( '.upload-footer-genome' ),\n data : this.list_genomes,\n value : this.options.default_genome\n });\n\n // listener for collection triggers on change in composite datatype and extension selection\n this.listenTo( this.collection, 'add', function ( model ) { self._eventAnnounce( model ) } );\n this.listenTo( this.collection, 'change add', function() { self.render() } );\n this.select_extension.options.onchange( this.select_extension.value() );\n this.render();\n },\n\n render: function () {\n var model = this.collection.first();\n if ( model && model.get( 'status' ) == 'running' ) {\n this.select_genome.disable();\n this.select_extension.disable();\n } else {\n this.select_genome.enable();\n this.select_extension.enable();\n }\n if ( this.collection.where( { status : 'ready' } ).length == this.collection.length && this.collection.length > 0 ) {\n this.btnStart.enable();\n this.btnStart.$el.addClass( 'btn-primary' );\n } else {\n this.btnStart.disable();\n this.btnStart.$el.removeClass( 'btn-primary' );\n }\n this.$( '.upload-table' )[ this.collection.length > 0 ? 'show' : 'hide' ]();\n },\n\n //\n // upload events / process pipeline\n //\n\n /** Builds the basic ui with placeholder rows for each composite data type file */\n _eventAnnounce: function( model ) {\n var upload_row = new UploadRow( this, { model : model } );\n this.$( '.upload-table > tbody:first' ).append( upload_row.$el );\n this.$( '.upload-table' )[ this.collection.length > 0 ? 'show' : 'hide' ]();\n upload_row.render();\n },\n\n /** Start upload process */\n _eventStart: function() {\n var self = this;\n this.collection.each( function( model ) {\n model.set( { 'genome' : self.select_genome.value(),\n 'extension': self.select_extension.value() } );\n });\n $.uploadpost({\n url : this.app.options.nginx_upload_path,\n data : this.app.toData( this.collection.filter() ),\n success : function( message ) { self._eventSuccess( message ) },\n error : function( message ) { self._eventError( message ) },\n progress : function( percentage ) { self._eventProgress( percentage ) }\n });\n },\n\n /** Refresh progress state */\n _eventProgress: function( percentage ) {\n this.collection.each( function( it ) { it.set( 'percentage', percentage ) } );\n },\n\n /** Refresh success state */\n _eventSuccess: function( message ) {\n this.collection.each( function( it ) { it.set('status', 'success') } );\n Galaxy.currHistoryPanel.refreshContents();\n },\n\n /** Refresh error state */\n _eventError: function( message ) {\n this.collection.each( function( it ) { it.set( { 'status': 'error', 'info': message } ) } );\n },\n\n /** Display extension info popup */\n _showExtensionInfo: function(options) {\n var self = this;\n var $el = options.$el;\n var extension = options.extension;\n var title = options.title;\n var description = _.findWhere(this.list_extensions, { id : extension });\n this.extension_popup && this.extension_popup.remove();\n this.extension_popup = new Popover.View({\n placement: options.placement || 'bottom',\n container: $el,\n destroy: true\n });\n this.extension_popup.title( title );\n this.extension_popup.empty();\n this.extension_popup.append( this._templateDescription( description ) );\n this.extension_popup.show();\n },\n\n /* Template for extensions description */\n _templateDescription: function( options ) {\n if ( options.description ) {\n var tmpl = options.description;\n if ( options.description_url ) {\n tmpl += ' (read more)';\n }\n return tmpl;\n } else {\n return 'There is no description available for this file extension.';\n }\n },\n\n /** Load html template */\n _template: function() {\n return '' +\n ' ' +\n '' +\n ' ' +\n '' +\n '' +\n ' ' +\n '' +\n '' +\n ' ' +\n '' +\n '' +\n ' ' +\n '' +\n ' ' +\n ' ' +\n '' +\n '' +\n '
' +\n '' +\n ' ' +\n '' +\n '' +\n '' +\n ' ' +\n ' Description ' +\n 'Name ' +\n 'Size ' +\n 'Settings ' +\n 'Status ' +\n '' +\n ' ';\n }\n });\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/upload/default/default-row.js\n// module id = 121\n// module chunks = 3","/** Renders contents of the default uploader */\ndefine([ 'utils/utils', 'mvc/upload/upload-model', 'mvc/upload/default/default-row', 'mvc/upload/upload-ftp', 'mvc/ui/ui-popover', 'mvc/ui/ui-select', 'mvc/ui/ui-misc', 'utils/uploadbox'],\nfunction( Utils, UploadModel, UploadRow, UploadFtp, Popover, Select, Ui ) {\n return Backbone.View.extend({\n // current upload size in bytes\n upload_size: 0,\n\n // contains upload row models\n collection : new UploadModel.Collection(),\n\n // keeps track of the current uploader state\n counter : {\n announce : 0,\n success : 0,\n error : 0,\n running : 0,\n reset : function() { this.announce = this.success = this.error = this.running = 0 }\n },\n\n initialize : function( app ) {\n var self = this;\n this.app = app;\n this.options = app.options;\n this.list_extensions = app.list_extensions;\n this.list_genomes = app.list_genomes;\n this.ui_button = app.ui_button;\n this.ftp_upload_site = app.currentFtp();\n this.setElement( this._template() );\n\n // append buttons to dom\n this.btnLocal = new Ui.Button( { id: 'btn-local', title: 'Choose local file', onclick: function() { self.uploadbox.select() }, icon: 'fa fa-laptop' } );\n this.btnFtp = new Ui.Button( { id: 'btn-ftp', title: 'Choose FTP file', onclick: function() { self._eventFtp() }, icon: 'fa fa-folder-open-o' } );\n this.btnCreate = new Ui.Button( { id: 'btn-new', title: 'Paste/Fetch data', onclick: function() { self._eventCreate() }, icon: 'fa fa-edit' } );\n this.btnStart = new Ui.Button( { id: 'btn-start', title: 'Start', onclick: function() { self._eventStart() } } );\n this.btnStop = new Ui.Button( { id: 'btn-stop', title: 'Pause', onclick: function() { self._eventStop() } } );\n this.btnReset = new Ui.Button( { id: 'btn-reset', title: 'Reset', onclick: function() { self._eventReset() } } );\n this.btnClose = new Ui.Button( { id: 'btn-close', title: 'Close', onclick: function() { self.app.modal.hide() } } );\n _.each( [ this.btnLocal, this.btnFtp, this.btnCreate, this.btnStop, this.btnReset, this.btnStart, this.btnClose ], function( button ) {\n self.$( '.upload-buttons' ).prepend( button.$el );\n });\n\n // file upload\n this.uploadbox = this.$( '.upload-box' ).uploadbox({\n url : this.app.options.nginx_upload_path,\n announce : function( index, file ) { self._eventAnnounce( index, file ) },\n initialize : function( index ) { return self.app.toData( [ self.collection.get( index ) ], self.history_id ) },\n progress : function( index, percentage ) { self._eventProgress( index, percentage ) },\n success : function( index, message ) { self._eventSuccess( index, message ) },\n error : function( index, message ) { self._eventError( index, message ) },\n complete : function() { self._eventComplete() },\n ondragover : function() { self.$( '.upload-box' ).addClass( 'highlight' ) },\n ondragleave : function() { self.$( '.upload-box' ).removeClass( 'highlight' ) }\n });\n\n // add ftp file viewer\n this.ftp = new Popover.View( { title: 'FTP files', container: this.btnFtp.$el } );\n\n // select extension\n this.select_extension = new Select.View({\n css : 'upload-footer-selection',\n container : this.$( '.upload-footer-extension' ),\n data : _.filter( this.list_extensions, function( ext ) { return !ext.composite_files } ),\n value : this.options.default_extension,\n onchange : function( extension ) { self.updateExtension( extension ) }\n });\n\n // handle extension info popover\n this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {\n self.showExtensionInfo({\n $el : $( e.target ),\n title : self.select_extension.text(),\n extension : self.select_extension.value(),\n placement : 'top'\n });\n }).on( 'mousedown', function( e ) { e.preventDefault() } );\n\n // genome extension\n this.select_genome = new Select.View({\n css : 'upload-footer-selection',\n container : this.$( '.upload-footer-genome' ),\n data : this.list_genomes,\n value : this.options.default_genome,\n onchange : function( genome ) { self.updateGenome(genome) }\n });\n\n // events\n this.collection.on( 'remove', function( model ) { self._eventRemove( model ) } );\n this._updateScreen();\n },\n\n /** A new file has been dropped/selected through the uploadbox plugin */\n _eventAnnounce: function( index, file ) {\n this.counter.announce++;\n var new_model = new UploadModel.Model({\n id : index,\n file_name : file.name,\n file_size : file.size,\n file_mode : file.mode || 'local',\n file_path : file.path,\n file_data : file\n });\n this.collection.add( new_model );\n var upload_row = new UploadRow( this, { model: new_model } );\n this.$( '.upload-table > tbody:first' ).append( upload_row.$el );\n this._updateScreen();\n upload_row.render();\n },\n\n /** Progress */\n _eventProgress: function( index, percentage ) {\n var it = this.collection.get( index );\n it.set( 'percentage', percentage );\n this.ui_button.model.set( 'percentage', this._uploadPercentage( percentage, it.get( 'file_size' ) ) );\n },\n\n /** Success */\n _eventSuccess: function( index, message ) {\n var it = this.collection.get( index );\n it.set( { 'percentage': 100, 'status': 'success' } );\n this.ui_button.model.set( 'percentage', this._uploadPercentage( 100, it.get( 'file_size' ) ) );\n this.upload_completed += it.get( 'file_size' ) * 100;\n this.counter.announce--;\n this.counter.success++;\n this._updateScreen();\n Galaxy.currHistoryPanel.refreshContents();\n },\n\n /** Error */\n _eventError: function( index, message ) {\n var it = this.collection.get( index );\n it.set( { 'percentage': 100, 'status': 'error', 'info': message } );\n this.ui_button.model.set( { 'percentage': this._uploadPercentage( 100, it.get( 'file_size' ) ), 'status': 'danger' } );\n this.upload_completed += it.get( 'file_size' ) * 100;\n this.counter.announce--;\n this.counter.error++;\n this._updateScreen();\n },\n\n /** Queue is done */\n _eventComplete: function() {\n this.collection.each( function( model ) { model.get( 'status' ) == 'queued' && model.set( 'status', 'init' ) } );\n this.counter.running = 0;\n this._updateScreen();\n },\n\n /** Remove model from upload list */\n _eventRemove: function( model ) {\n var status = model.get( 'status' );\n if ( status == 'success' ) {\n this.counter.success--;\n } else if ( status == 'error' ) {\n this.counter.error--;\n } else {\n this.counter.announce--;\n }\n this.uploadbox.remove( model.id );\n this._updateScreen();\n },\n\n //\n // events triggered by this view\n //\n\n /** [public] display extension info popup */\n showExtensionInfo: function( options ) {\n var self = this;\n var $el = options.$el;\n var extension = options.extension;\n var title = options.title;\n var description = _.findWhere( self.list_extensions, { 'id': extension } );\n this.extension_popup && this.extension_popup.remove();\n this.extension_popup = new Popover.View({ placement: options.placement || 'bottom', container: $el } );\n this.extension_popup.title( title );\n this.extension_popup.empty();\n this.extension_popup.append( this._templateDescription( description ) );\n this.extension_popup.show();\n },\n\n /** Show/hide ftp popup */\n _eventFtp: function() {\n if ( !this.ftp.visible ) {\n this.ftp.empty();\n var self = this;\n this.ftp.append( ( new UploadFtp({\n collection : this.collection,\n ftp_upload_site : this.ftp_upload_site,\n onadd : function( ftp_file ) {\n self.uploadbox.add([{\n mode: 'ftp',\n name: ftp_file.path,\n size: ftp_file.size,\n path: ftp_file.path\n }]);\n },\n onremove: function( model_index ) {\n self.collection.remove( model_index );\n }\n } ) ).$el );\n this.ftp.show();\n } else {\n this.ftp.hide();\n }\n },\n\n /** Create a new file */\n _eventCreate: function (){\n this.uploadbox.add( [ { name: 'New File', size: 0, mode: 'new' } ] );\n },\n\n /** Start upload process */\n _eventStart: function() {\n if ( this.counter.announce == 0 || this.counter.running > 0 ) {\n return;\n }\n var self = this;\n this.upload_size = 0;\n this.upload_completed = 0;\n this.collection.each( function( model ) {\n if( model.get( 'status' ) == 'init' ) {\n model.set( 'status', 'queued' );\n self.upload_size += model.get( 'file_size' );\n }\n });\n this.ui_button.model.set( { 'percentage': 0, 'status': 'success' } );\n this.counter.running = this.counter.announce;\n this.history_id = this.app.currentHistory();\n this.uploadbox.start();\n this._updateScreen();\n },\n\n /** Pause upload process */\n _eventStop: function() {\n if ( this.counter.running > 0 ) {\n this.ui_button.model.set( 'status', 'info' );\n $( '.upload-top-info' ).html( 'Queue will pause after completing the current file...' );\n this.uploadbox.stop();\n }\n },\n\n /** Remove all */\n _eventReset: function() {\n if ( this.counter.running == 0 ){\n this.collection.reset();\n this.counter.reset();\n this.uploadbox.reset();\n this.select_extension.value( this.options.default_extension );\n this.select_genome.value( this.options.default_genome );\n this.ui_button.model.set( 'percentage', 0 );\n this._updateScreen();\n }\n },\n\n /** Update extension for all models */\n updateExtension: function( extension, defaults_only ) {\n var self = this;\n this.collection.each( function( model ) {\n if ( model.get( 'status' ) == 'init' && ( model.get( 'extension' ) == self.options.default_extension || !defaults_only ) ) {\n model.set( 'extension', extension );\n }\n });\n },\n\n /** Update genome for all models */\n updateGenome: function( genome, defaults_only ) {\n var self = this;\n this.collection.each( function( model ) {\n if ( model.get( 'status' ) == 'init' && ( model.get( 'genome' ) == self.options.default_genome || !defaults_only ) ) {\n model.set( 'genome', genome );\n }\n });\n },\n\n /** Set screen */\n _updateScreen: function () {\n var message = '';\n if( this.counter.announce == 0 ) {\n if (this.uploadbox.compatible()) {\n message = ' ';\n } else {\n message = 'Browser does not support Drag & Drop. Try Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.';\n }\n } else {\n if ( this.counter.running == 0 ) {\n message = 'You added ' + this.counter.announce + ' file(s) to the queue. Add more files or click \\'Start\\' to proceed.';\n } else {\n message = 'Please wait...' + this.counter.announce + ' out of ' + this.counter.running + ' remaining.';\n }\n }\n this.$( '.upload-top-info' ).html( message );\n var enable_reset = this.counter.running == 0 && this.counter.announce + this.counter.success + this.counter.error > 0;\n var enable_start = this.counter.running == 0 && this.counter.announce > 0;\n var enable_sources = this.counter.running == 0;\n var show_table = this.counter.announce + this.counter.success + this.counter.error > 0;\n this.btnReset[ enable_reset ? 'enable' : 'disable' ]();\n this.btnStart[ enable_start ? 'enable' : 'disable' ]();\n this.btnStart.$el[ enable_start ? 'addClass' : 'removeClass' ]( 'btn-primary' );\n this.btnStop[ this.counter.running > 0 ? 'enable' : 'disable' ]();\n this.btnLocal[ enable_sources ? 'enable' : 'disable' ]();\n this.btnFtp[ enable_sources ? 'enable' : 'disable' ]();\n this.btnCreate[ enable_sources ? 'enable' : 'disable' ]();\n this.btnFtp.$el[ this.ftp_upload_site ? 'show' : 'hide' ]();\n this.$( '.upload-table' )[ show_table ? 'show' : 'hide' ]();\n this.$( '.upload-helper' )[ show_table ? 'hide' : 'show' ]();\n },\n\n /** Calculate percentage of all queued uploads */\n _uploadPercentage: function( percentage, size ) {\n return ( this.upload_completed + ( percentage * size ) ) / this.upload_size;\n },\n\n /** Template for extensions description */\n _templateDescription: function( options ) {\n if ( options.description ) {\n var tmpl = options.description;\n if ( options.description_url ) {\n tmpl += ' (read more)';\n }\n return tmpl;\n } else {\n return 'There is no description available for this file extension.';\n }\n },\n\n /** Template */\n _template: function() {\n return '' +\n ' ' +\n '' +\n ' ' +\n '' +\n '  ' +\n '' +\n ' ' +\n '' +\n ' ' +\n '' +\n ' ' +\n ' ' +\n '' +\n '' +\n ' ' +\n '' +\n '
' +\n '' +\n '
' +\n '' +\n ' ' +\n '' +\n '' +\n 'Name ' +\n 'Size ' +\n 'Type ' +\n 'Genome ' +\n 'Settings ' +\n 'Status ' +\n '' +\n ' ' ).addClass( 'ui-panel-table' ) );\n if( !config.use_remote_user ) {\n self._link( self.defs.information );\n self._link( self.defs.password );\n }\n if( config.enable_communication_server ) {\n self._link( self.defs.communication );\n }\n self._link( self.defs.permissions );\n self._link( self.defs.api_key );\n if( config.has_user_tool_filters ) {\n self._link( self.defs.toolbox_filters );\n }\n if( config.enable_openid && !config.use_remote_user ) {\n self._link( self.defs.openids );\n }\n self._link( self.defs.logout );\n self.$preferences.append( self._templateFooter( data ) );\n self.$el.empty().append( self.$preferences );\n });\n },\n\n _link: function( page ) {\n var self = this;\n var $page_item = $( this._templateRow( page ) );\n this.$table.append( $page_item );\n $page_item.find( 'a' ).on( 'click', function() {\n if ( page.url ) {\n $.ajax({\n url : Galaxy.root + page.url,\n type : 'GET'\n }).done( function( response ) {\n var options = $.extend( {}, page, response );\n var form = new Form({\n title : options.title,\n icon : options.icon,\n inputs : options.inputs,\n operations: {\n 'submit': new Ui.ButtonIcon({\n tooltip : options.submit_tooltip,\n title : options.submit_title || 'Save settings',\n icon : options.submit_icon || 'fa-save',\n onclick : function() { self._submit( form, options ) }\n }),\n 'back': new Ui.ButtonIcon({\n icon : 'fa-caret-left',\n tooltip : 'Return to user preferences',\n title : 'Preferences',\n onclick : function() { form.remove(); self.$preferences.show(); }\n })\n }\n });\n self.$preferences.hide();\n self.$el.append( form.$el );\n }).fail( function( response ) {\n self.message.update( { message: 'Failed to load resource ' + page.url + '.', status: 'danger' } );\n })\n } else {\n page.onclick();\n }\n });\n },\n\n _submit: function( form, options ) {\n var self = this;\n $.ajax( {\n url : options.url,\n data : JSON.stringify(form.data.create()),\n type : 'PUT',\n contentType : 'application/json'\n }).done( function( response ) {\n var updated_values = false;\n form.data.matchModel( response, function ( input, input_id ) {\n form.field_list[ input_id ].value( input.value );\n updated_values = true;\n });\n if ( updated_values ) {\n form.message.update( { message: response.message, status: 'success' } );\n } else {\n form.remove();\n self.$preferences.show();\n self.message.update( { message: response.message, status: 'success' } );\n }\n }).fail( function( response ) {\n form.message.update( { message: response.responseJSON.err_msg, status: 'danger' } );\n });\n },\n\n _templateRow: function( options ) {\n return '
' +\n ' ';\n },\n\n _templateFooter: function( options ) {\n return '' +\n ' ' +\n '' +\n '' + options.title + '' +\n ' ' +\n '').attr({\n\t id: 'content_table',\n\t cellpadding: 0\n\t });\n\t this.$el.append(data_table);\n\t var column_names = this.model.get_metadata('column_names'),\n\t header_container = $('').appendTo(data_table),\n\t header_row = $('
').appendTo(header_container);\n\t if (column_names) {\n\t header_row.append(' ' + column_names.join(' ') + ' ');\n\t } else {\n\t for (var j = 1; j <= this.model.get_metadata('columns'); j++) {\n\t header_row.append('' + j + ' ');\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.collection = new Backbone.Collection();\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.listenTo( this.collection, 'change add remove reset', 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 && this.collection.where( { visible: true } ).length > 0 ? '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 this.$menu && this.$menu.remove();\n\t if ( this.collection.length > 0 ) {\n\t this.$menu = $( ' ' ).addClass( 'menu dropdown-menu' )\n\t .addClass( 'pull-' + self.model.get( 'pull' ) )\n\t .attr( 'role', 'menu' );\n\t this.$el.append( this.$menu );\n\t }\n\t this.collection.each( function( submodel ) {\n\t var suboptions = submodel.attributes;\n\t if ( suboptions.visible ) {\n\t var $link = $( '' ).addClass( 'dropdown-item' )\n\t .attr( { href : suboptions.href, target : suboptions.target } )\n\t .append( $( '' ).addClass( 'fa' )\n\t .addClass( suboptions.icon )\n\t .css( 'display', suboptions.icon ? 'inline-block' : 'none' ) )\n\t .append( suboptions.title )\n\t .on( 'click', function( e ) {\n\t if ( suboptions.onclick ) {\n\t e.preventDefault();\n\t suboptions.onclick();\n\t }\n\t } );\n\t self.$menu.append( $( '' ).append( $link ) );\n\t suboptions.divider && self.$menu.append( $( '' ).addClass( 'divider' ) );\n\t }\n\t });\n\t },\n\t\n\t /** Add a new menu item */\n\t addMenu: function ( options ) {\n\t this.collection.add( Utils.merge( options, {\n\t title : '',\n\t target : '',\n\t href : '',\n\t onclick : null,\n\t divider : false,\n\t visible : true,\n\t icon : null,\n\t cls : 'button-menu btn-group'\n\t }));\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__(29),\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(Backbone, $, _) {/* This is the regular tool form */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(7), __webpack_require__(8), __webpack_require__(44), __webpack_require__(27) ], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, Ui, Modal, ToolFormBase, Webhooks ) {\n\t var View = Backbone.View.extend({\n\t initialize: function( options ) {\n\t var self = this;\n\t this.modal = parent.Galaxy.modal || new Modal.View();\n\t this.form = new ToolFormBase( Utils.merge({\n\t listen_to_history : true,\n\t always_refresh : false,\n\t customize : function( form ) {\n\t var options = form.model.attributes;\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 form.portlet.disable();\n\t self.submit( options, function() {\n\t execute_btn.unwait();\n\t form.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.push({\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 postchange : function( process, form ) {\n\t var self = this;\n\t var current_state = {\n\t tool_id : form.model.get( 'id' ),\n\t tool_version : form.model.get( 'version' ),\n\t inputs : $.extend(true, {}, form.data.create())\n\t }\n\t form.wait( true );\n\t Galaxy.emit.debug( 'tool-form::postchange()', 'Sending current state.', current_state );\n\t Utils.request({\n\t type : 'POST',\n\t url : Galaxy.root + 'api/tools/' + form.model.get( 'id' ) + '/build',\n\t data : current_state,\n\t success : function( data ) {\n\t form.update( data );\n\t form.wait( false );\n\t Galaxy.emit.debug( 'tool-form::postchange()', 'Received new model.', data );\n\t process.resolve();\n\t },\n\t error : function( response ) {\n\t Galaxy.emit.debug( 'tool-form::postchange()', 'Refresh request failed.', response );\n\t process.reject();\n\t }\n\t });\n\t }\n\t }, options ) );\n\t this.deferred = this.form.deferred;\n\t this.setElement( '' );\n\t this.$el.append( this.form.$el );\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.form.data.create()\n\t }\n\t this.form.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 // Show Webhook if job is running\n\t if ( response.jobs && response.jobs.length > 0 ) {\n\t self.$el.append( $( '', { id: 'webhook-view' } ) );\n\t var WebhookApp = new Webhooks.WebhookView({\n\t urlRoot: Galaxy.root + 'api/webhooks/tool'\n\t });\n\t }\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.form.data.matchResponse( response.err_data );\n\t for ( var input_id in error_messages ) {\n\t self.form.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 : self._templateError( job_def, response && response.err_msg ),\n\t buttons : { 'Close' : function() { self.modal.hide() } }\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.form.data.match( job_input_id );\n\t var input_field = this.form.field_list[ input_id ];\n\t var input_def = this.form.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.form.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.form.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.form.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, 'Invalid success response. No jobs found.' );\n\t }\n\t },\n\t\n\t _templateError: function( response, err_msg ) {\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. ' + ( err_msg || '' ) ) )\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\n\t/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __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 '' + 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',\n\t '
',\n\t '',\n\t '
',\n\t '<% _.each( problems, function( problem ){ %>',\n\t '
'\n\t ].join('')),\n\t\n\t /** shown in list when all elements are discarded */\n\t noElementsLeft : _.template([\n\t '<% _.each( problems, function( problem ){ %>',\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 '<%= dataset.peek %>
',\n\t '<% } %>',\n\t '<% } %>',\n\t '');\n\t wrapper.append($el);\n\t this.row.append(wrapper);\n\t },\n\t \n\t // header\n\t appendHeader: function() {\n\t // append header row\n\t this.$thead.append(this.row);\n\t\n\t // row\n\t this.row = $(' ');\n\t },\n\t \n\t // add row cell\n\t add: function($el, width, align) {\n\t var wrapper = $(' ');\n\t if (width) {\n\t wrapper.css('width', width);\n\t }\n\t if (align) {\n\t wrapper.css('text-align', align);\n\t }\n\t wrapper.append($el);\n\t this.row.append(wrapper);\n\t },\n\t \n\t // append\n\t append: function(id, fade) {\n\t this._commit(id, fade, false);\n\t },\n\t \n\t // prepend\n\t prepend: function(id, fade) {\n\t this._commit(id, fade, true);\n\t },\n\t \n\t // get element\n\t get: function(id) {\n\t return this.$el.find('#' + id);\n\t },\n\t \n\t // delete\n\t del: function(id) {\n\t var item = this.$tbody.find('#' + id);\n\t if (item.length > 0) {\n\t item.remove();\n\t this.row_count--;\n\t this._refresh();\n\t }\n\t },\n\t\n\t // delete all\n\t delAll: function() {\n\t this.$tbody.empty();\n\t this.row_count = 0;\n\t this._refresh();\n\t },\n\t \n\t // value\n\t value: function(new_value) {\n\t // get current id/value\n\t this.before = this.$tbody.find('.current').attr('id');\n\t \n\t // check if new_value is defined\n\t if (new_value !== undefined) {\n\t this.$tbody.find('tr').removeClass('current');\n\t if (new_value) {\n\t this.$tbody.find('#' + new_value).addClass('current');\n\t }\n\t }\n\t \n\t // get current id/value\n\t var after = this.$tbody.find('.current').attr('id');\n\t if(after === undefined) {\n\t return null;\n\t } else {\n\t // fire onchange\n\t if (after != this.before && this.options.onchange) {\n\t this.options.onchange(new_value);\n\t }\n\t \n\t // return current value\n\t return after;\n\t }\n\t },\n\t \n\t // size\n\t size: function() {\n\t return this.$tbody.find('tr').length;\n\t },\n\t \n\t // commit\n\t _commit: function(id, fade, prepend) {\n\t // remove previous item with same id\n\t this.del(id);\n\t \n\t // add\n\t this.row.attr('id', id);\n\t \n\t // add row\n\t if (prepend) {\n\t this.$tbody.prepend(this.row);\n\t } else {\n\t this.$tbody.append(this.row);\n\t }\n\t \n\t // fade mode\n\t if (fade) {\n\t this.row.hide();\n\t this.row.fadeIn();\n\t }\n\t \n\t // row\n\t this.row = this._row();\n\t \n\t // row count\n\t this.row_count++;\n\t this._refresh();\n\t },\n\t \n\t // create new row\n\t _row: function() {\n\t return $(' ');\n\t },\n\t \n\t // onclick\n\t _onclick: function(e) {\n\t // get values\n\t var old_value = this.value();\n\t var new_value = $(e.target).closest('tr').attr('id');\n\t if (new_value != ''){\n\t // check equality\n\t if (new_value && old_value != new_value) {\n\t if (this.options.onconfirm) {\n\t this.options.onconfirm(new_value);\n\t } else {\n\t this.value(new_value);\n\t }\n\t }\n\t }\n\t },\n\t\n\t // ondblclick\n\t _ondblclick: function(e) {\n\t var value = this.value();\n\t if (value && this.options.ondblclick) {\n\t this.options.ondblclick(value);\n\t }\n\t },\n\t \n\t // refresh\n\t _refresh: function() {\n\t if (this.row_count == 0) {\n\t this.$tmessage.show();\n\t } else {\n\t this.$tmessage.hide();\n\t }\n\t },\n\t \n\t // load html template\n\t _template: function(options) {\n\t return ' ' +\n\t '' +\n\t '' +\n\t '
' +\n\t '
';\n\t helpString += data.help;\n\t } else {\n\t helpString += 'Tool help is unavailable for this dataset.
';\n\t }\n\t helpString += '
\" target=\"<%- view.linkTarget %>\">',\n\t _l( 'Set it manually or retry auto-detection' ),\n\t '',\n\t '
', _l( 'Undelete it' ), '',\n\t '<% if( view.purgeAllowed ){ %>',\n\t '
',\n\t _l( 'Permanently remove it from disk' ),\n\t '',\n\t '<% } %>',\n\t '',\n\t '<% _.each( visualizations, function( visualization ){ %>',\n\t '
',\n\t '', '
',\n\t '', this._templateOptions( options ), '
'\n\t ].join( '' );\n\t },\n\t\n\t _templateOptions : function( options ){\n\t if( !options.length ){\n\t return '' +\n\t ' ';\n\t },\n\t\n\t /** Template of table */\n\t _templateTable: function() {\n\t return 'Available files: ' +\n\t '' +\n\t '' +\n\t ' ' +\n\t '' +\n\t '' +\n\t '' +\n\t '' +\n\t ' ' + _.escape(options.path) + ' ' +\n\t '' + Utils.bytesToString( options.size ) + ' ' +\n\t '' + options.ctime + ' ' +\n\t '' +\n\t '' +\n\t '
';\n\t },\n\t\n\t /** Template of info message */\n\t _templateInfo: function() {\n\t return '' +\n\t ' ' +\n\t '' +\n\t '' +\n\t '' +\n\t ' Name ' +\n\t 'Size ' +\n\t 'Created ' +\n\t '' ).addClass( 'upload-settings-table ui-table-striped' ).append( '' ) );\n\t this.$cover = this.$( '.upload-settings-cover' );\n\t this.$table = this.$( '.upload-settings-table > tbody' );\n\t this.listenTo ( this.model, 'change', this.render, this );\n\t this.model.trigger( 'change' );\n\t },\n\t\n\t render: function() {\n\t var self = this;\n\t this.$table.empty();\n\t _.each( this.options.parameters, function( parameter ) {\n\t var $checkbox = $( '' ).addClass( 'upload-' + parameter.id + ' upload-icon-button fa' )\n\t .addClass( self.model.get( parameter.id ) && self.options.class_check || self.options.class_uncheck )\n\t .on( 'click', function() {\n\t self.model.get( 'enabled' ) && self.model.set( parameter.id, !self.model.get( parameter.id ) )\n\t });\n\t self.$table.append( $( '
' ).append( $( ' ' ).append( $checkbox ) )\n\t .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/* 83 */,\n/* 84 */,\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, $) {(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/* 86 */\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/* 87 */,\n/* 88 */\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/* 89 */\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/* 90 */\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__( 117 );\n\t CurrentHistoryView = __webpack_require__( 114 ).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 ' ' + 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 // ------------------------------------------------------------------------ rendering\n\t /** render the entire interface */\n\t render : function( speed, callback ){\n\t //this.debug( '-- _render' );\n\t //this.$el.empty().html( PairedCollectionCreator.templates.main() );\n\t this.$el.empty().html( PairedCollectionCreator.templates.main() );\n\t this._renderHeader( speed );\n\t this._renderMiddle( speed );\n\t this._renderFooter( speed );\n\t this._addPluginComponents();\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 //this.debug( '-- _renderHeader' );\n\t var $header = this.$( '.header' ).empty().html( PairedCollectionCreator.templates.header() )\n\t .find( '.help-content' ).prepend( $( PairedCollectionCreator.templates.helpContent() ) );\n\t\n\t this._renderFilters();\n\t return $header;\n\t },\n\t /** fill the filter inputs with the filter values */\n\t _renderFilters : function(){\n\t return this.$( '.forward-column .column-header input' ).val( this.filters[0] )\n\t .add( this.$( '.reverse-column .column-header input' ).val( this.filters[1] ) );\n\t },\n\t\n\t /** render the middle including unpaired and paired sections (which may be hidden) */\n\t _renderMiddle : function( speed, callback ){\n\t var $middle = this.$( '.middle' ).empty().html( PairedCollectionCreator.templates.middle() );\n\t\n\t // (re-) hide the un/paired panels based on instance vars\n\t if( this.unpairedPanelHidden ){\n\t this.$( '.unpaired-columns' ).hide();\n\t } else if( this.pairedPanelHidden ){\n\t this.$( '.paired-columns' ).hide();\n\t }\n\t\n\t this._renderUnpaired();\n\t this._renderPaired();\n\t return $middle;\n\t },\n\t /** render the unpaired section, showing datasets accrd. to filters, update the unpaired counts */\n\t _renderUnpaired : function( speed, callback ){\n\t //this.debug( '-- _renderUnpaired' );\n\t var creator = this,\n\t $fwd, $rev, $prd = [],\n\t split = this._splitByFilters();\n\t // update unpaired counts\n\t this.$( '.forward-column .title' )\n\t .text([ split[0].length, _l( 'unpaired forward' ) ].join( ' ' ));\n\t this.$( '.forward-column .unpaired-info' )\n\t .text( this._renderUnpairedDisplayStr( this.unpaired.length - split[0].length ) );\n\t this.$( '.reverse-column .title' )\n\t .text([ split[1].length, _l( 'unpaired reverse' ) ].join( ' ' ));\n\t this.$( '.reverse-column .unpaired-info' )\n\t .text( this._renderUnpairedDisplayStr( this.unpaired.length - split[1].length ) );\n\t\n\t this.$( '.unpaired-columns .column-datasets' ).empty();\n\t\n\t // show/hide the auto pair button if any unpaired are left\n\t this.$( '.autopair-link' ).toggle( this.unpaired.length !== 0 );\n\t if( this.unpaired.length === 0 ){\n\t this._renderUnpairedEmpty();\n\t return;\n\t }\n\t\n\t // create the dataset dom arrays\n\t $rev = split[1].map( function( dataset, i ){\n\t // if there'll be a fwd dataset across the way, add a button to pair the row\n\t if( ( split[0][ i ] !== undefined )\n\t && ( split[0][ i ] !== dataset ) ){\n\t $prd.push( creator._renderPairButton() );\n\t }\n\t return creator._renderUnpairedDataset( dataset );\n\t });\n\t $fwd = split[0].map( function( dataset ){\n\t return creator._renderUnpairedDataset( dataset );\n\t });\n\t\n\t if( !$fwd.length && !$rev.length ){\n\t this._renderUnpairedNotShown();\n\t return;\n\t }\n\t // add to appropo cols\n\t //TODO: not the best way to render - consider rendering the entire unpaired-columns section in a fragment\n\t // and swapping out that\n\t this.$( '.unpaired-columns .forward-column .column-datasets' ).append( $fwd )\n\t .add( this.$( '.unpaired-columns .paired-column .column-datasets' ).append( $prd ) )\n\t .add( this.$( '.unpaired-columns .reverse-column .column-datasets' ).append( $rev ) );\n\t this._adjUnpairedOnScrollbar();\n\t },\n\t /** return a string to display the count of filtered out datasets */\n\t _renderUnpairedDisplayStr : function( numFiltered ){\n\t return [ '(', numFiltered, ' ', _l( 'filtered out' ), ')' ].join('');\n\t },\n\t /** return an unattached jQuery DOM element to represent an unpaired dataset */\n\t _renderUnpairedDataset : function( dataset ){\n\t //TODO: to underscore template\n\t return $( '')\n\t .attr( 'id', 'dataset-' + dataset.id )\n\t .addClass( 'dataset unpaired' )\n\t .attr( 'draggable', true )\n\t .addClass( dataset.selected? 'selected': '' )\n\t .append( $( '' ).addClass( 'dataset-name' ).text( dataset.name ) )\n\t //??\n\t .data( 'dataset', dataset );\n\t },\n\t /** render the button that may go between unpaired datasets, allowing the user to pair a row */\n\t _renderPairButton : function(){\n\t //TODO: *not* a dataset - don't pretend like it is\n\t return $( '').addClass( 'dataset unpaired' )\n\t .append( $( '' ).addClass( 'dataset-name' ).text( _l( 'Pair these datasets' ) ) );\n\t },\n\t /** a message to display when no unpaired left */\n\t _renderUnpairedEmpty : function(){\n\t //this.debug( '-- renderUnpairedEmpty' );\n\t var $msg = $( '' )\n\t .text( '(' + _l( 'no remaining unpaired datasets' ) + ')' );\n\t this.$( '.unpaired-columns .paired-column .column-datasets' ).empty().prepend( $msg );\n\t return $msg;\n\t },\n\t /** a message to display when no unpaired can be shown with the current filters */\n\t _renderUnpairedNotShown : function(){\n\t //this.debug( '-- renderUnpairedEmpty' );\n\t var $msg = $( '' )\n\t .text( '(' + _l( 'no datasets were found matching the current filters' ) + ')' );\n\t this.$( '.unpaired-columns .paired-column .column-datasets' ).empty().prepend( $msg );\n\t return $msg;\n\t },\n\t /** try to detect if the unpaired section has a scrollbar and adjust left column for better centering of all */\n\t _adjUnpairedOnScrollbar : function(){\n\t var $unpairedColumns = this.$( '.unpaired-columns' ).last(),\n\t $firstDataset = this.$( '.unpaired-columns .reverse-column .dataset' ).first();\n\t if( !$firstDataset.length ){ return; }\n\t var ucRight = $unpairedColumns.offset().left + $unpairedColumns.outerWidth(),\n\t dsRight = $firstDataset.offset().left + $firstDataset.outerWidth(),\n\t rightDiff = Math.floor( ucRight ) - Math.floor( dsRight );\n\t //this.debug( 'rightDiff:', ucRight, '-', dsRight, '=', rightDiff );\n\t this.$( '.unpaired-columns .forward-column' )\n\t .css( 'margin-left', ( rightDiff > 0 )? rightDiff: 0 );\n\t },\n\t\n\t /** render the paired section and update counts of paired datasets */\n\t _renderPaired : function( speed, callback ){\n\t //this.debug( '-- _renderPaired' );\n\t this.$( '.paired-column-title .title' ).text([ this.paired.length, _l( 'paired' ) ].join( ' ' ) );\n\t // show/hide the unpair all link\n\t this.$( '.unpair-all-link' ).toggle( this.paired.length !== 0 );\n\t if( this.paired.length === 0 ){\n\t this._renderPairedEmpty();\n\t return;\n\t //TODO: would be best to return here (the $columns)\n\t } else {\n\t // show/hide 'remove extensions link' when any paired and they seem to have extensions\n\t this.$( '.remove-extensions-link' ).show();\n\t }\n\t\n\t this.$( '.paired-columns .column-datasets' ).empty();\n\t var creator = this;\n\t this.paired.forEach( function( pair, i ){\n\t //TODO: cache these?\n\t var pairView = new PairView({ pair: pair });\n\t creator.$( '.paired-columns .column-datasets' )\n\t .append( pairView.render().$el )\n\t .append([\n\t ''\n\t ].join( '' ));\n\t });\n\t },\n\t /** a message to display when none paired */\n\t _renderPairedEmpty : function(){\n\t var $msg = $( '' )\n\t .text( '(' + _l( 'no paired datasets yet' ) + ')' );\n\t this.$( '.paired-columns .column-datasets' ).empty().prepend( $msg );\n\t return $msg;\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( PairedCollectionCreator.templates.footer() );\n\t this.$( '.remove-extensions' ).prop( 'checked', this.removeExtensions );\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._chooseFiltersPopover( '.choose-filters-link' );\n\t this.$( '.help-content i' ).hoverhighlight( '.collection-creator', 'rgba( 64, 255, 255, 1.0 )' );\n\t },\n\t\n\t /** build a filter selection popover allowing selection of common filter pairs */\n\t _chooseFiltersPopover : function( selector ){\n\t function filterChoice( val1, val2 ){\n\t return [\n\t ''\n\t ].join('');\n\t }\n\t var $popoverContent = $( _.template([\n\t '
',\n\t '',\n\t _l( 'Auto-pair' ),\n\t '',\n\t '',\n\t '
',\n\t '
',\n\t '
',\n\t '
',\n\t '
',\n\t 'To pair datasets, you can:',\n\t '
'\n\t ].join( '' )), '',\n\t 'You can filter what is shown in the unpaired sections by:',\n\t '
'\n\t ].join( '' )), '',\n\t '',\n\t '
',\n\t '<% _.each( problems, function( problem ){ %>',\n\t '
',\n\t '<% } else if( _.size( elements ) === 0 ){ %>',\n\t _l( 'No datasets were selected' ), '.',\n\t '<% } else if( _.size( elements ) === 1 ){ %>',\n\t _l( 'Only one dataset was selected' ), ': <%- elements[0].name %>',\n\t '<% } else if( _.size( elements ) > 2 ){ %>',\n\t _l( 'Too many datasets were selected' ),\n\t ': <%- _.pluck( elements, \"name\" ).join( \", \") %>',\n\t '<% } %>',\n\t '
',\n\t _l( 'Two (and only two) elements are needed for the pair' ), '. ',\n\t _l( 'You may need to ' ),\n\t '', _l( 'cancel' ), ' ',\n\t _l( 'and reselect new elements' ), '.',\n\t '',\n\t '
', _l( 'Unhide it' ), '',\n\t '',\n\t '',\n\t '
' ).addClass( 'tab-navigation nav nav-tabs' ) )\n\t .append( $( '' ).addClass( 'tab-content' ) );\n\t },\n\t\n\t /** Tab template */\n\t _template_tab: function( options ) {\n\t var $tmpl = $( '' ).addClass( 'tab-element' )\n\t .attr( 'id', 'tab-' + options.id )\n\t .append( $( '' ).attr( 'id', 'tab-title-link-' + options.id ) );\n\t var $href = $tmpl.find( 'a' );\n\t options.icon && $href.append( $( '' ).addClass( 'tab-icon fa' ).addClass( options.icon ) );\n\t $href.append( $( '' ).attr( 'id', 'tab-title-text-' + options.id )\n\t .addClass( 'tab-title-text' )\n\t .append( options.title ) );\n\t return $tmpl;\n\t }\n\t });\n\t\n\t return { View : View }\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)))\n\n/***/ }),\n/* 119 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, $, _) {/** Renders the composite upload row view */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(82), __webpack_require__(81), __webpack_require__(25), __webpack_require__(7), __webpack_require__(26), __webpack_require__(89) ], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, UploadSettings, UploadFtp, Popover, Ui, Select ) {\n\t return Backbone.View.extend({\n\t /** Dictionary of upload states and associated icons */\n\t status_classes : {\n\t init : 'upload-mode fa fa-exclamation text-primary',\n\t ready : 'upload-mode fa fa-check text-success',\n\t running : 'upload-mode fa fa-spinner fa-spin',\n\t success : 'upload-mode fa fa-check',\n\t error : 'upload-mode fa fa-exclamation-triangle'\n\t },\n\t\n\t initialize: function( app, options ) {\n\t var self = this;\n\t this.app = app;\n\t this.model = options.model;\n\t this.setElement( this._template() );\n\t this.$source = this.$( '.upload-source' );\n\t this.$settings = this.$( '.upload-settings' );\n\t this.$status = this.$( '.upload-status' );\n\t this.$text = this.$( '.upload-text' );\n\t this.$text_content = this.$( '.upload-text-content' );\n\t this.$info_text = this.$( '.upload-info-text' );\n\t this.$info_progress = this.$( '.upload-info-progress' );\n\t this.$file_name = this.$( '.upload-file-name' );\n\t this.$file_desc = this.$( '.upload-file-desc' );\n\t this.$file_size = this.$( '.upload-file-size' );\n\t this.$progress_bar = this.$( '.upload-progress-bar' );\n\t this.$percentage = this.$( '.upload-percentage' );\n\t\n\t // build upload functions\n\t this.uploadinput = this.$el.uploadinput({\n\t ondragover: function() {\n\t self.model.get( 'enabled' ) && self.$el.addClass( 'warning' );\n\t },\n\t ondragleave: function() {\n\t self.$el.removeClass( 'warning' );\n\t },\n\t onchange: function( files ) {\n\t if ( self.model.get( 'status' ) != 'running' && files && files.length > 0 ) {\n\t self.model.reset({\n\t 'file_data': files[ 0 ],\n\t 'file_name': files[ 0 ].name,\n\t 'file_size': files[ 0 ].size,\n\t 'file_mode': files[ 0 ].mode || 'local'\n\t });\n\t self._refreshReady();\n\t }\n\t }\n\t });\n\t\n\t // source selection popup\n\t this.button_menu = new Ui.ButtonMenu({\n\t icon : 'fa-caret-down',\n\t title : 'Select',\n\t pull : 'left'\n\t });\n\t this.$source.append( this.button_menu.$el );\n\t this.button_menu.addMenu({\n\t icon : 'fa-laptop',\n\t title : 'Choose local file',\n\t onclick : function() { self.uploadinput.dialog() }\n\t });\n\t if ( this.app.ftp_upload_site ) {\n\t this.button_menu.addMenu({\n\t icon : 'fa-folder-open-o',\n\t title : 'Choose FTP file',\n\t onclick : function() { self._showFtp() }\n\t });\n\t }\n\t this.button_menu.addMenu({\n\t icon : 'fa-edit',\n\t title : 'Paste/Fetch data',\n\t onclick : function() {\n\t self.model.reset( { 'file_mode': 'new', 'file_name': 'New File' } );\n\t }\n\t });\n\t\n\t // add ftp file viewer\n\t this.ftp = new Popover.View({\n\t title : 'Choose FTP file:',\n\t container : this.$source.find( '.ui-button-menu' ),\n\t placement : 'right'\n\t });\n\t\n\t // append popup to settings icon\n\t this.settings = new Popover.View({\n\t title : 'Upload configuration',\n\t container : this.$settings,\n\t placement : 'bottom'\n\t });\n\t\n\t // handle text editing event\n\t this.$text_content.on( 'change input', function( e ) {\n\t self.model.set( { 'url_paste': $( e.target ).val(),\n\t 'file_size': $( e.target ).val().length } );\n\t self._refreshReady();\n\t });\n\t\n\t // handle settings popover\n\t this.$settings.on( 'click', function( e ) { self._showSettings() } )\n\t .on( 'mousedown', function( e ) { e.preventDefault() } );\n\t\n\t // model events\n\t this.listenTo( this.model, 'change:percentage', function() { self._refreshPercentage() } );\n\t this.listenTo( this.model, 'change:status', function() { self._refreshStatus() } );\n\t this.listenTo( this.model, 'change:info', function() { self._refreshInfo() } );\n\t this.listenTo( this.model, 'change:file_name', function() { self._refreshFileName() } );\n\t this.listenTo( this.model, 'change:file_mode', function() { self._refreshMode() } );\n\t this.listenTo( this.model, 'change:file_size', function() { self._refreshFileSize() } );\n\t this.listenTo( this.model, 'remove', function() { self.remove() } );\n\t this.app.collection.on( 'reset', function() { self.remove() } );\n\t },\n\t\n\t render: function() {\n\t this.$el.attr( 'id', 'upload-row-' + this.model.id );\n\t this.$file_name.html( _.escape( this.model.get( 'file_name' ) || '-' ) );\n\t this.$file_desc.html( this.model.get( 'file_desc' ) || 'Unavailable' );\n\t this.$file_size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );\n\t this.$status.removeClass().addClass( this.status_classes.init );\n\t },\n\t\n\t /** Remove view */\n\t remove: function() {\n\t // call the base class remove method\n\t Backbone.View.prototype.remove.apply( this );\n\t },\n\t\n\t //\n\t // handle model events\n\t //\n\t\n\t /** Refresh ready or not states */\n\t _refreshReady: function() {\n\t this.app.collection.each( function( model ) {\n\t model.set( 'status', ( model.get( 'file_size' ) > 0 ) && 'ready' || 'init' );\n\t });\n\t },\n\t\n\t /** Refresh mode and e.g. show/hide textarea field */\n\t _refreshMode: function() {\n\t var file_mode = this.model.get( 'file_mode' );\n\t if ( file_mode == 'new' ) {\n\t this.height = this.$el.height();\n\t this.$text.css( { 'width' : this.$el.width() - 16 + 'px',\n\t 'top' : this.$el.height() - 8 + 'px' } ).show();\n\t this.$el.height( this.$el.height() - 8 + this.$text.height() + 16 );\n\t this.$text_content.val( '' ).trigger( 'keyup' );\n\t } else {\n\t this.$el.height( this.height );\n\t this.$text.hide();\n\t }\n\t },\n\t\n\t /** Refresh information */\n\t _refreshInfo: function() {\n\t var info = this.model.get( 'info' );\n\t if ( info ) {\n\t this.$info_text.html( 'Failed: ' + info ).show();\n\t } else {\n\t this.$info_text.hide();\n\t }\n\t },\n\t\n\t /** Refresh percentage */\n\t _refreshPercentage : function() {\n\t var percentage = parseInt( this.model.get( 'percentage' ) );\n\t if ( percentage != 0 ) {\n\t this.$progress_bar.css( { width : percentage + '%' } );\n\t } else {\n\t this.$progress_bar.addClass( 'no-transition' );\n\t this.$progress_bar.css( { width : '0%' } );\n\t this.$progress_bar[ 0 ].offsetHeight;\n\t this.$progress_bar.removeClass( 'no-transition' );\n\t }\n\t this.$percentage.html( percentage != 100 ? percentage + '%' : 'Adding to history...' );\n\t },\n\t\n\t /** Refresh status */\n\t _refreshStatus : function() {\n\t var status = this.model.get( 'status' );\n\t this.$status.removeClass().addClass( this.status_classes[ status ] );\n\t this.model.set( 'enabled', status != 'running' );\n\t this.$text_content.attr( 'disabled', !this.model.get( 'enabled' ) );\n\t this.$el.removeClass( 'success danger warning' );\n\t if ( status == 'running' || status == 'ready' ) {\n\t this.model.set( 'percentage', 0 );\n\t }\n\t this.$source.find( '.button' )[ status == 'running' ? 'addClass' : 'removeClass' ]( 'disabled' );\n\t if ( status == 'success' ) {\n\t this.$el.addClass( 'success' );\n\t this.model.set( 'percentage', 100 );\n\t this.$percentage.html( '100%' );\n\t }\n\t if ( status == 'error' ) {\n\t this.$el.addClass( 'danger' );\n\t this.model.set( 'percentage', 0 );\n\t this.$info_progress.hide();\n\t this.$info_text.show();\n\t } else {\n\t this.$info_progress.show();\n\t this.$info_text.hide();\n\t }\n\t },\n\t\n\t /** File name */\n\t _refreshFileName: function() {\n\t this.$file_name.html( this.model.get( 'file_name' ) || '-' );\n\t },\n\t\n\t /** File size */\n\t _refreshFileSize: function() {\n\t this.$file_size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );\n\t },\n\t\n\t /** Show/hide ftp popup */\n\t _showFtp: function() {\n\t if ( !this.ftp.visible ) {\n\t var self = this;\n\t this.ftp.empty();\n\t this.ftp.append( ( new UploadFtp( {\n\t ftp_upload_site : this.app.ftp_upload_site,\n\t onchange : function( ftp_file ) {\n\t self.ftp.hide();\n\t if ( self.model.get( 'status' ) != 'running' && ftp_file ) {\n\t self.model.reset({\n\t 'file_mode': 'ftp',\n\t 'file_name': ftp_file.path,\n\t 'file_size': ftp_file.size,\n\t 'file_path': ftp_file.path\n\t });\n\t self._refreshReady();\n\t }\n\t }\n\t } ) ).$el );\n\t this.ftp.show();\n\t } else {\n\t this.ftp.hide();\n\t }\n\t },\n\t\n\t /** Show/hide settings popup */\n\t _showSettings : function() {\n\t if ( !this.settings.visible ) {\n\t this.settings.empty();\n\t this.settings.append( ( new UploadSettings( this ) ).$el );\n\t this.settings.show();\n\t } else {\n\t this.settings.hide();\n\t }\n\t },\n\t\n\t /** Template */\n\t _template: function() {\n\t return '
' +\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), __webpack_require__(2)))\n\n/***/ }),\n/* 120 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _, $) {/** Renders contents of the composite uploader */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(53), __webpack_require__(119), __webpack_require__(25), __webpack_require__(26), __webpack_require__(7)], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, UploadModel, UploadRow, Popover, Select, Ui ) {\n\t return Backbone.View.extend({\n\t collection: new UploadModel.Collection(),\n\t initialize: function(app) {\n\t var self = this;\n\t this.app = app;\n\t this.options = app.options;\n\t this.list_extensions = app.list_extensions;\n\t this.list_genomes = app.list_genomes;\n\t this.ftp_upload_site = app.currentFtp();\n\t this.setElement( this._template() );\n\t\n\t // create button section\n\t this.btnStart = new Ui.Button( { title: 'Start', onclick: function() { self._eventStart() } } );\n\t this.btnClose = new Ui.Button( { title: 'Close', onclick: function() { self.app.modal.hide() } } );\n\t\n\t // append buttons to dom\n\t _.each( [ this.btnStart, this.btnClose ], function( button ) {\n\t self.$( '.upload-buttons' ).prepend( button.$el );\n\t });\n\t\n\t // select extension\n\t this.select_extension = new Select.View({\n\t css : 'upload-footer-selection',\n\t container : this.$( '.upload-footer-extension' ),\n\t data : _.filter( this.list_extensions, function( ext ) { return ext.composite_files } ),\n\t onchange : function( extension ) {\n\t self.collection.reset();\n\t var details = _.findWhere( self.list_extensions, { id : extension } );\n\t if ( details && details.composite_files ) {\n\t _.each( details.composite_files, function( item ) {\n\t self.collection.add( { id : self.collection.size(),\n\t file_desc : item.description || item.name } );\n\t } );\n\t }\n\t }\n\t });\n\t\n\t // handle extension info popover\n\t this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {\n\t self._showExtensionInfo({\n\t $el : $( e.target ),\n\t title : self.select_extension.text(),\n\t extension : self.select_extension.value(),\n\t placement : 'top'\n\t });\n\t }).on( 'mousedown', function( e ) { e.preventDefault() } );\n\t\n\t // genome extension\n\t this.select_genome = new Select.View({\n\t css : 'upload-footer-selection',\n\t container : this.$( '.upload-footer-genome' ),\n\t data : this.list_genomes,\n\t value : this.options.default_genome\n\t });\n\t\n\t // listener for collection triggers on change in composite datatype and extension selection\n\t this.listenTo( this.collection, 'add', function ( model ) { self._eventAnnounce( model ) } );\n\t this.listenTo( this.collection, 'change add', function() { self.render() } );\n\t this.select_extension.options.onchange( this.select_extension.value() );\n\t this.render();\n\t },\n\t\n\t render: function () {\n\t var model = this.collection.first();\n\t if ( model && model.get( 'status' ) == 'running' ) {\n\t this.select_genome.disable();\n\t this.select_extension.disable();\n\t } else {\n\t this.select_genome.enable();\n\t this.select_extension.enable();\n\t }\n\t if ( this.collection.where( { status : 'ready' } ).length == this.collection.length && this.collection.length > 0 ) {\n\t this.btnStart.enable();\n\t this.btnStart.$el.addClass( 'btn-primary' );\n\t } else {\n\t this.btnStart.disable();\n\t this.btnStart.$el.removeClass( 'btn-primary' );\n\t }\n\t this.$( '.upload-table' )[ this.collection.length > 0 ? 'show' : 'hide' ]();\n\t },\n\t\n\t //\n\t // upload events / process pipeline\n\t //\n\t\n\t /** Builds the basic ui with placeholder rows for each composite data type file */\n\t _eventAnnounce: function( model ) {\n\t var upload_row = new UploadRow( this, { model : model } );\n\t this.$( '.upload-table > tbody:first' ).append( upload_row.$el );\n\t this.$( '.upload-table' )[ this.collection.length > 0 ? 'show' : 'hide' ]();\n\t upload_row.render();\n\t },\n\t\n\t /** Start upload process */\n\t _eventStart: function() {\n\t var self = this;\n\t this.collection.each( function( model ) {\n\t model.set( { 'genome' : self.select_genome.value(),\n\t 'extension': self.select_extension.value() } );\n\t });\n\t $.uploadpost({\n\t url : this.app.options.nginx_upload_path,\n\t data : this.app.toData( this.collection.filter() ),\n\t success : function( message ) { self._eventSuccess( message ) },\n\t error : function( message ) { self._eventError( message ) },\n\t progress : function( percentage ) { self._eventProgress( percentage ) }\n\t });\n\t },\n\t\n\t /** Refresh progress state */\n\t _eventProgress: function( percentage ) {\n\t this.collection.each( function( it ) { it.set( 'percentage', percentage ) } );\n\t },\n\t\n\t /** Refresh success state */\n\t _eventSuccess: function( message ) {\n\t this.collection.each( function( it ) { it.set('status', 'success') } );\n\t Galaxy.currHistoryPanel.refreshContents();\n\t },\n\t\n\t /** Refresh error state */\n\t _eventError: function( message ) {\n\t this.collection.each( function( it ) { it.set( { 'status': 'error', 'info': message } ) } );\n\t },\n\t\n\t /** Display extension info popup */\n\t _showExtensionInfo: function(options) {\n\t var self = this;\n\t var $el = options.$el;\n\t var extension = options.extension;\n\t var title = options.title;\n\t var description = _.findWhere(this.list_extensions, { id : extension });\n\t this.extension_popup && this.extension_popup.remove();\n\t this.extension_popup = new Popover.View({\n\t placement: options.placement || 'bottom',\n\t container: $el,\n\t destroy: true\n\t });\n\t this.extension_popup.title( title );\n\t this.extension_popup.empty();\n\t this.extension_popup.append( this._templateDescription( description ) );\n\t this.extension_popup.show();\n\t },\n\t\n\t /* Template for extensions description */\n\t _templateDescription: function( options ) {\n\t if ( options.description ) {\n\t var tmpl = options.description;\n\t if ( options.description_url ) {\n\t tmpl += ' (read more)';\n\t }\n\t return tmpl;\n\t } else {\n\t return 'There is no description available for this file extension.';\n\t }\n\t },\n\t\n\t /** Load html template */\n\t _template: function() {\n\t return '' +\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 '' +\n\t ' ' +\n\t '' +\n\t '' +\n\t '' +\n\t ' ' +\n\t ' Description ' +\n\t 'Name ' +\n\t 'Size ' +\n\t 'Settings ' +\n\t 'Status ' +\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), __webpack_require__(2)))\n\n/***/ }),\n/* 122 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(Backbone, _, $) {/** Renders contents of the default uploader */\n\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(4), __webpack_require__(53), __webpack_require__(121), __webpack_require__(81), __webpack_require__(25), __webpack_require__(26), __webpack_require__(7), __webpack_require__(89)], __WEBPACK_AMD_DEFINE_RESULT__ = function( Utils, UploadModel, UploadRow, UploadFtp, Popover, Select, Ui ) {\n\t return Backbone.View.extend({\n\t // current upload size in bytes\n\t upload_size: 0,\n\t\n\t // contains upload row models\n\t collection : new UploadModel.Collection(),\n\t\n\t // keeps track of the current uploader state\n\t counter : {\n\t announce : 0,\n\t success : 0,\n\t error : 0,\n\t running : 0,\n\t reset : function() { this.announce = this.success = this.error = this.running = 0 }\n\t },\n\t\n\t initialize : function( app ) {\n\t var self = this;\n\t this.app = app;\n\t this.options = app.options;\n\t this.list_extensions = app.list_extensions;\n\t this.list_genomes = app.list_genomes;\n\t this.ui_button = app.ui_button;\n\t this.ftp_upload_site = app.currentFtp();\n\t this.setElement( this._template() );\n\t\n\t // append buttons to dom\n\t this.btnLocal = new Ui.Button( { id: 'btn-local', title: 'Choose local file', onclick: function() { self.uploadbox.select() }, icon: 'fa fa-laptop' } );\n\t this.btnFtp = new Ui.Button( { id: 'btn-ftp', title: 'Choose FTP file', onclick: function() { self._eventFtp() }, icon: 'fa fa-folder-open-o' } );\n\t this.btnCreate = new Ui.Button( { id: 'btn-new', title: 'Paste/Fetch data', onclick: function() { self._eventCreate() }, icon: 'fa fa-edit' } );\n\t this.btnStart = new Ui.Button( { id: 'btn-start', title: 'Start', onclick: function() { self._eventStart() } } );\n\t this.btnStop = new Ui.Button( { id: 'btn-stop', title: 'Pause', onclick: function() { self._eventStop() } } );\n\t this.btnReset = new Ui.Button( { id: 'btn-reset', title: 'Reset', onclick: function() { self._eventReset() } } );\n\t this.btnClose = new Ui.Button( { id: 'btn-close', title: 'Close', onclick: function() { self.app.modal.hide() } } );\n\t _.each( [ this.btnLocal, this.btnFtp, this.btnCreate, this.btnStop, this.btnReset, this.btnStart, this.btnClose ], function( button ) {\n\t self.$( '.upload-buttons' ).prepend( button.$el );\n\t });\n\t\n\t // file upload\n\t this.uploadbox = this.$( '.upload-box' ).uploadbox({\n\t url : this.app.options.nginx_upload_path,\n\t announce : function( index, file ) { self._eventAnnounce( index, file ) },\n\t initialize : function( index ) { return self.app.toData( [ self.collection.get( index ) ], self.history_id ) },\n\t progress : function( index, percentage ) { self._eventProgress( index, percentage ) },\n\t success : function( index, message ) { self._eventSuccess( index, message ) },\n\t error : function( index, message ) { self._eventError( index, message ) },\n\t complete : function() { self._eventComplete() },\n\t ondragover : function() { self.$( '.upload-box' ).addClass( 'highlight' ) },\n\t ondragleave : function() { self.$( '.upload-box' ).removeClass( 'highlight' ) }\n\t });\n\t\n\t // add ftp file viewer\n\t this.ftp = new Popover.View( { title: 'FTP files', container: this.btnFtp.$el } );\n\t\n\t // select extension\n\t this.select_extension = new Select.View({\n\t css : 'upload-footer-selection',\n\t container : this.$( '.upload-footer-extension' ),\n\t data : _.filter( this.list_extensions, function( ext ) { return !ext.composite_files } ),\n\t value : this.options.default_extension,\n\t onchange : function( extension ) { self.updateExtension( extension ) }\n\t });\n\t\n\t // handle extension info popover\n\t this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {\n\t self.showExtensionInfo({\n\t $el : $( e.target ),\n\t title : self.select_extension.text(),\n\t extension : self.select_extension.value(),\n\t placement : 'top'\n\t });\n\t }).on( 'mousedown', function( e ) { e.preventDefault() } );\n\t\n\t // genome extension\n\t this.select_genome = new Select.View({\n\t css : 'upload-footer-selection',\n\t container : this.$( '.upload-footer-genome' ),\n\t data : this.list_genomes,\n\t value : this.options.default_genome,\n\t onchange : function( genome ) { self.updateGenome(genome) }\n\t });\n\t\n\t // events\n\t this.collection.on( 'remove', function( model ) { self._eventRemove( model ) } );\n\t this._updateScreen();\n\t },\n\t\n\t /** A new file has been dropped/selected through the uploadbox plugin */\n\t _eventAnnounce: function( index, file ) {\n\t this.counter.announce++;\n\t var new_model = new UploadModel.Model({\n\t id : index,\n\t file_name : file.name,\n\t file_size : file.size,\n\t file_mode : file.mode || 'local',\n\t file_path : file.path,\n\t file_data : file\n\t });\n\t this.collection.add( new_model );\n\t var upload_row = new UploadRow( this, { model: new_model } );\n\t this.$( '.upload-table > tbody:first' ).append( upload_row.$el );\n\t this._updateScreen();\n\t upload_row.render();\n\t },\n\t\n\t /** Progress */\n\t _eventProgress: function( index, percentage ) {\n\t var it = this.collection.get( index );\n\t it.set( 'percentage', percentage );\n\t this.ui_button.model.set( 'percentage', this._uploadPercentage( percentage, it.get( 'file_size' ) ) );\n\t },\n\t\n\t /** Success */\n\t _eventSuccess: function( index, message ) {\n\t var it = this.collection.get( index );\n\t it.set( { 'percentage': 100, 'status': 'success' } );\n\t this.ui_button.model.set( 'percentage', this._uploadPercentage( 100, it.get( 'file_size' ) ) );\n\t this.upload_completed += it.get( 'file_size' ) * 100;\n\t this.counter.announce--;\n\t this.counter.success++;\n\t this._updateScreen();\n\t Galaxy.currHistoryPanel.refreshContents();\n\t },\n\t\n\t /** Error */\n\t _eventError: function( index, message ) {\n\t var it = this.collection.get( index );\n\t it.set( { 'percentage': 100, 'status': 'error', 'info': message } );\n\t this.ui_button.model.set( { 'percentage': this._uploadPercentage( 100, it.get( 'file_size' ) ), 'status': 'danger' } );\n\t this.upload_completed += it.get( 'file_size' ) * 100;\n\t this.counter.announce--;\n\t this.counter.error++;\n\t this._updateScreen();\n\t },\n\t\n\t /** Queue is done */\n\t _eventComplete: function() {\n\t this.collection.each( function( model ) { model.get( 'status' ) == 'queued' && model.set( 'status', 'init' ) } );\n\t this.counter.running = 0;\n\t this._updateScreen();\n\t },\n\t\n\t /** Remove model from upload list */\n\t _eventRemove: function( model ) {\n\t var status = model.get( 'status' );\n\t if ( status == 'success' ) {\n\t this.counter.success--;\n\t } else if ( status == 'error' ) {\n\t this.counter.error--;\n\t } else {\n\t this.counter.announce--;\n\t }\n\t this.uploadbox.remove( model.id );\n\t this._updateScreen();\n\t },\n\t\n\t //\n\t // events triggered by this view\n\t //\n\t\n\t /** [public] display extension info popup */\n\t showExtensionInfo: function( options ) {\n\t var self = this;\n\t var $el = options.$el;\n\t var extension = options.extension;\n\t var title = options.title;\n\t var description = _.findWhere( self.list_extensions, { 'id': extension } );\n\t this.extension_popup && this.extension_popup.remove();\n\t this.extension_popup = new Popover.View({ placement: options.placement || 'bottom', container: $el } );\n\t this.extension_popup.title( title );\n\t this.extension_popup.empty();\n\t this.extension_popup.append( this._templateDescription( description ) );\n\t this.extension_popup.show();\n\t },\n\t\n\t /** Show/hide ftp popup */\n\t _eventFtp: function() {\n\t if ( !this.ftp.visible ) {\n\t this.ftp.empty();\n\t var self = this;\n\t this.ftp.append( ( new UploadFtp({\n\t collection : this.collection,\n\t ftp_upload_site : this.ftp_upload_site,\n\t onadd : function( ftp_file ) {\n\t self.uploadbox.add([{\n\t mode: 'ftp',\n\t name: ftp_file.path,\n\t size: ftp_file.size,\n\t path: ftp_file.path\n\t }]);\n\t },\n\t onremove: function( model_index ) {\n\t self.collection.remove( model_index );\n\t }\n\t } ) ).$el );\n\t this.ftp.show();\n\t } else {\n\t this.ftp.hide();\n\t }\n\t },\n\t\n\t /** Create a new file */\n\t _eventCreate: function (){\n\t this.uploadbox.add( [ { name: 'New File', size: 0, mode: 'new' } ] );\n\t },\n\t\n\t /** Start upload process */\n\t _eventStart: function() {\n\t if ( this.counter.announce == 0 || this.counter.running > 0 ) {\n\t return;\n\t }\n\t var self = this;\n\t this.upload_size = 0;\n\t this.upload_completed = 0;\n\t this.collection.each( function( model ) {\n\t if( model.get( 'status' ) == 'init' ) {\n\t model.set( 'status', 'queued' );\n\t self.upload_size += model.get( 'file_size' );\n\t }\n\t });\n\t this.ui_button.model.set( { 'percentage': 0, 'status': 'success' } );\n\t this.counter.running = this.counter.announce;\n\t this.history_id = this.app.currentHistory();\n\t this.uploadbox.start();\n\t this._updateScreen();\n\t },\n\t\n\t /** Pause upload process */\n\t _eventStop: function() {\n\t if ( this.counter.running > 0 ) {\n\t this.ui_button.model.set( 'status', 'info' );\n\t $( '.upload-top-info' ).html( 'Queue will pause after completing the current file...' );\n\t this.uploadbox.stop();\n\t }\n\t },\n\t\n\t /** Remove all */\n\t _eventReset: function() {\n\t if ( this.counter.running == 0 ){\n\t this.collection.reset();\n\t this.counter.reset();\n\t this.uploadbox.reset();\n\t this.select_extension.value( this.options.default_extension );\n\t this.select_genome.value( this.options.default_genome );\n\t this.ui_button.model.set( 'percentage', 0 );\n\t this._updateScreen();\n\t }\n\t },\n\t\n\t /** Update extension for all models */\n\t updateExtension: function( extension, defaults_only ) {\n\t var self = this;\n\t this.collection.each( function( model ) {\n\t if ( model.get( 'status' ) == 'init' && ( model.get( 'extension' ) == self.options.default_extension || !defaults_only ) ) {\n\t model.set( 'extension', extension );\n\t }\n\t });\n\t },\n\t\n\t /** Update genome for all models */\n\t updateGenome: function( genome, defaults_only ) {\n\t var self = this;\n\t this.collection.each( function( model ) {\n\t if ( model.get( 'status' ) == 'init' && ( model.get( 'genome' ) == self.options.default_genome || !defaults_only ) ) {\n\t model.set( 'genome', genome );\n\t }\n\t });\n\t },\n\t\n\t /** Set screen */\n\t _updateScreen: function () {\n\t var message = '';\n\t if( this.counter.announce == 0 ) {\n\t if (this.uploadbox.compatible()) {\n\t message = ' ';\n\t } else {\n\t message = 'Browser does not support Drag & Drop. Try Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.';\n\t }\n\t } else {\n\t if ( this.counter.running == 0 ) {\n\t message = 'You added ' + this.counter.announce + ' file(s) to the queue. Add more files or click \\'Start\\' to proceed.';\n\t } else {\n\t message = 'Please wait...' + this.counter.announce + ' out of ' + this.counter.running + ' remaining.';\n\t }\n\t }\n\t this.$( '.upload-top-info' ).html( message );\n\t var enable_reset = this.counter.running == 0 && this.counter.announce + this.counter.success + this.counter.error > 0;\n\t var enable_start = this.counter.running == 0 && this.counter.announce > 0;\n\t var enable_sources = this.counter.running == 0;\n\t var show_table = this.counter.announce + this.counter.success + this.counter.error > 0;\n\t this.btnReset[ enable_reset ? 'enable' : 'disable' ]();\n\t this.btnStart[ enable_start ? 'enable' : 'disable' ]();\n\t this.btnStart.$el[ enable_start ? 'addClass' : 'removeClass' ]( 'btn-primary' );\n\t this.btnStop[ this.counter.running > 0 ? 'enable' : 'disable' ]();\n\t this.btnLocal[ enable_sources ? 'enable' : 'disable' ]();\n\t this.btnFtp[ enable_sources ? 'enable' : 'disable' ]();\n\t this.btnCreate[ enable_sources ? 'enable' : 'disable' ]();\n\t this.btnFtp.$el[ this.ftp_upload_site ? 'show' : 'hide' ]();\n\t this.$( '.upload-table' )[ show_table ? 'show' : 'hide' ]();\n\t this.$( '.upload-helper' )[ show_table ? 'hide' : 'show' ]();\n\t },\n\t\n\t /** Calculate percentage of all queued uploads */\n\t _uploadPercentage: function( percentage, size ) {\n\t return ( this.upload_completed + ( percentage * size ) ) / this.upload_size;\n\t },\n\t\n\t /** Template for extensions description */\n\t _templateDescription: function( options ) {\n\t if ( options.description ) {\n\t var tmpl = options.description;\n\t if ( options.description_url ) {\n\t tmpl += ' (read more)';\n\t }\n\t return tmpl;\n\t } else {\n\t return 'There is no description available for this file extension.';\n\t }\n\t },\n\t\n\t /** Template */\n\t _template: function() {\n\t return '' +\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 '
' +\n\t '' +\n\t ' ' +\n\t '' +\n\t '' +\n\t 'Name ' +\n\t 'Size ' +\n\t 'Type ' +\n\t 'Genome ' +\n\t 'Settings ' +\n\t 'Status ' +\n\t '' +\n\t ' ' ).addClass( 'ui-panel-table' ) );\n\t if( !config.use_remote_user ) {\n\t self._link( self.defs.information );\n\t self._link( self.defs.password );\n\t }\n\t if( config.enable_communication_server ) {\n\t self._link( self.defs.communication );\n\t }\n\t self._link( self.defs.permissions );\n\t self._link( self.defs.api_key );\n\t if( config.has_user_tool_filters ) {\n\t self._link( self.defs.toolbox_filters );\n\t }\n\t if( config.enable_openid && !config.use_remote_user ) {\n\t self._link( self.defs.openids );\n\t }\n\t self._link( self.defs.logout );\n\t self.$preferences.append( self._templateFooter( data ) );\n\t self.$el.empty().append( self.$preferences );\n\t });\n\t },\n\t\n\t _link: function( page ) {\n\t var self = this;\n\t var $page_item = $( this._templateRow( page ) );\n\t this.$table.append( $page_item );\n\t $page_item.find( 'a' ).on( 'click', function() {\n\t if ( page.url ) {\n\t $.ajax({\n\t url : Galaxy.root + page.url,\n\t type : 'GET'\n\t }).done( function( response ) {\n\t var options = $.extend( {}, page, response );\n\t var form = new Form({\n\t title : options.title,\n\t icon : options.icon,\n\t inputs : options.inputs,\n\t operations: {\n\t 'submit': new Ui.ButtonIcon({\n\t tooltip : options.submit_tooltip,\n\t title : options.submit_title || 'Save settings',\n\t icon : options.submit_icon || 'fa-save',\n\t onclick : function() { self._submit( form, options ) }\n\t }),\n\t 'back': new Ui.ButtonIcon({\n\t icon : 'fa-caret-left',\n\t tooltip : 'Return to user preferences',\n\t title : 'Preferences',\n\t onclick : function() { form.remove(); self.$preferences.show(); }\n\t })\n\t }\n\t });\n\t self.$preferences.hide();\n\t self.$el.append( form.$el );\n\t }).fail( function( response ) {\n\t self.message.update( { message: 'Failed to load resource ' + page.url + '.', status: 'danger' } );\n\t })\n\t } else {\n\t page.onclick();\n\t }\n\t });\n\t },\n\t\n\t _submit: function( form, options ) {\n\t var self = this;\n\t $.ajax( {\n\t url : options.url,\n\t data : JSON.stringify(form.data.create()),\n\t type : 'PUT',\n\t contentType : 'application/json'\n\t }).done( function( response ) {\n\t var updated_values = false;\n\t form.data.matchModel( response, function ( input, input_id ) {\n\t form.field_list[ input_id ].value( input.value );\n\t updated_values = true;\n\t });\n\t if ( updated_values ) {\n\t form.message.update( { message: response.message, status: 'success' } );\n\t } else {\n\t form.remove();\n\t self.$preferences.show();\n\t self.message.update( { message: response.message, status: 'success' } );\n\t }\n\t }).fail( function( response ) {\n\t form.message.update( { message: response.responseJSON.err_msg, status: 'danger' } );\n\t });\n\t },\n\t\n\t _templateRow: function( options ) {\n\t return '
' +\n\t ' ';\n\t },\n\t\n\t _templateFooter: function( options ) {\n\t return '' +\n\t ' ' +\n\t '' +\n\t '' + options.title + '' +\n\t ' ' +\n\t '').attr({\n id: 'content_table',\n cellpadding: 0\n });\n this.$el.append(data_table);\n var column_names = this.model.get_metadata('column_names'),\n header_container = $('').appendTo(data_table),\n header_row = $('
').appendTo(header_container);\n if (column_names) {\n header_row.append(' ' + column_names.join(' ') + ' ');\n } else {\n for (var j = 1; j <= this.model.get_metadata('columns'); j++) {\n header_row.append('' + j + ' ');\n }\n }\n\n // Render first chunk.\n var self = this,\n first_chunk = this.model.get('first_data_chunk');\n if (first_chunk) {\n // First chunk is bootstrapped, so render now.\n this._renderChunk(first_chunk);\n }\n else {\n // No bootstrapping, so get first chunk and then render.\n $.when(self.model.get_next_chunk()).then(function(result) {\n self._renderChunk(result);\n });\n }\n\n // -- Show new chunks during scrolling. --\n\n // Set up chunk loading when scrolling using the scrolling element.\n this.scroll_elt.scroll(function(){\n self.attempt_to_fetch();\n });\n },\n\n /**\n * Returns true if user has scrolled to the bottom of the view.\n */\n scrolled_to_bottom: function() {\n return false;\n },\n\n // -- Helper functions. --\n\n _renderCell: function(cell_contents, index, colspan) {\n var $cell = $('').text(cell_contents);\n var column_types = this.model.get_metadata('column_types');\n if (colspan !== undefined) {\n $cell.attr('colspan', colspan).addClass('stringalign');\n } else if (column_types) {\n if (index < column_types.length) {\n if (column_types[index] === 'str' || column_types[index] === 'list') {\n /* Left align all str columns, right align the rest */\n $cell.addClass('stringalign');\n }\n }\n }\n return $cell;\n },\n\n _renderRow: function(line) {\n // Check length of cells to ensure this is a complete row.\n var cells = line.split('\\t'),\n row = $(' '),\n num_columns = this.model.get_metadata('columns');\n\n if (this.row_count % 2 !== 0) {\n row.addClass('dark_row');\n }\n\n if (cells.length === num_columns) {\n _.each(cells, function(cell_contents, index) {\n row.append(this._renderCell(cell_contents, index));\n }, this);\n }\n else if (cells.length > num_columns) {\n // SAM file or like format with optional metadata included.\n _.each(cells.slice(0, num_columns - 1), function(cell_contents, index) {\n row.append(this._renderCell(cell_contents, index));\n }, this);\n row.append(this._renderCell(cells.slice(num_columns - 1).join('\\t'), num_columns - 1));\n }\n else if (cells.length === 1){\n // Comment line, just return the one cell.\n row.append(this._renderCell(line, 0, num_columns));\n }\n else {\n // cells.length is greater than one, but less than num_columns. Render cells and pad tds.\n // Possibly a SAM file or like format with optional metadata missing.\n // Could also be a tabular file with a line with missing columns.\n _.each(cells, function(cell_contents, index) {\n row.append(this._renderCell(cell_contents, index));\n }, this);\n _.each(_.range(num_columns - cells.length), function(){\n row.append($(' '));\n });\n }\n\n this.row_count++;\n return row;\n },\n\n _renderChunk: function(chunk) {\n var data_table = this.$el.find('table');\n _.each(chunk.ck_data.split('\\n'), function(line, index) {\n if (line !== ''){\n data_table.append(this._renderRow(line));\n }\n }, this);\n }\n});\n\n/**\n * Tabular view that is placed at the top level of page. Scrolling occurs\n * view top-level elements outside of view.\n */\nvar TopLevelTabularDatasetChunkedView = TabularDatasetChunkedView.extend({\n\n initialize: function(options) {\n TabularDatasetChunkedView.prototype.initialize.call(this, options);\n\n // Scrolling happens in top-level elements.\n scroll_elt = _.find(this.$el.parents(), function(p) {\n return $(p).css('overflow') === 'auto';\n });\n\n // If no scrolling element found, use window.\n if (!scroll_elt) { scroll_elt = window; }\n\n // Wrap scrolling element for easy access.\n this.scroll_elt = $(scroll_elt);\n },\n\n /**\n * Returns true if user has scrolled to the bottom of the view.\n */\n scrolled_to_bottom: function() {\n return (this.$el.height() - this.scroll_elt.scrollTop() - this.scroll_elt.height() <= 0);\n }\n\n});\n\n/**\n * Tabular view tnat is embedded in a page. Scrolling occurs in view's el.\n */\nvar EmbeddedTabularDatasetChunkedView = TabularDatasetChunkedView.extend({\n\n initialize: function(options) {\n TabularDatasetChunkedView.prototype.initialize.call(this, options);\n\n // Because view is embedded, set up div to do scrolling.\n this.scroll_elt = this.$el.css({\n position: 'relative',\n overflow: 'scroll',\n height: options.height || '500px'\n });\n },\n\n /**\n * Returns true if user has scrolled to the bottom of the view.\n */\n scrolled_to_bottom: function() {\n return this.$el.scrollTop() + this.$el.innerHeight() >= this.el.scrollHeight;\n }\n\n});\n\n// button for trackster visualization\nvar TabularButtonTracksterView = Backbone.View.extend({\n\n // gene region columns\n col: {\n chrom : null,\n start : null,\n end : null\n },\n\n // url for trackster\n url_viz : null,\n\n // dataset id\n dataset_id : null,\n\n // database key\n genome_build: null,\n\n // data type\n file_ext : null,\n\n // backbone initialize\n initialize: function (options) {\n // check if environment is available\n var Galaxy = parent.Galaxy;\n\n // link galaxy modal or create one\n if (Galaxy && Galaxy.modal) {\n this.modal = Galaxy.modal;\n }\n\n // link galaxy frames\n if (Galaxy && Galaxy.frame) {\n this.frame = Galaxy.frame;\n }\n\n // check\n if (!this.modal || !this.frame) {\n return;\n }\n\n // model/metadata\n var model = options.model;\n var metadata = model.get('metadata');\n\n // check for datatype\n if (!model.get('file_ext')) {\n return;\n }\n\n // get data type\n this.file_ext = model.get('file_ext');\n\n // check for bed-file format\n if (this.file_ext == 'bed')\n {\n // verify that metadata exists\n if (metadata.get('chromCol') && metadata.get('startCol') && metadata.get('endCol'))\n {\n // read in columns\n this.col.chrom = metadata.get('chromCol') - 1;\n this.col.start = metadata.get('startCol') - 1;\n this.col.end = metadata.get('endCol') - 1;\n } else {\n console.log('TabularButtonTrackster : Bed-file metadata incomplete.');\n return;\n }\n }\n\n // check for vcf-file format\n if (this.file_ext == 'vcf')\n {\n // search array\n function search (str, array) {\n for (var j = 0; j < array.length; j++)\n if (array[j].match(str)) return j;\n return -1;\n };\n\n // load\n this.col.chrom = search('Chrom', metadata.get('column_names'));\n this.col.start = search('Pos', metadata.get('column_names'));\n this.col.end = null;\n\n // verify that metadata exists\n if (this.col.chrom == -1 || this.col.start == -1) {\n console.log('TabularButtonTrackster : VCF-file metadata incomplete.');\n return;\n }\n }\n\n // check\n if(this.col.chrom === undefined) {\n return;\n }\n\n // get dataset id\n if (model.id) {\n this.dataset_id = model.id;\n } else {\n console.log('TabularButtonTrackster : Dataset identification is missing.');\n return;\n }\n\n // get url\n if (model.get('url_viz')) {\n this.url_viz = model.get('url_viz');\n } else {\n console.log('TabularButtonTrackster : Url for visualization controller is missing.');\n return;\n }\n\n // get genome_build / database key\n if (model.get('genome_build')) {\n this.genome_build = model.get('genome_build');\n }\n\n // create the icon\n var btn_viz = new mod_icon_btn.IconButtonView({\n model : new mod_icon_btn.IconButton({\n title : 'Visualize',\n icon_class : 'chart_curve',\n id : 'btn_viz'\n })\n });\n\n // set element\n this.setElement(options.$el);\n\n // add to element\n this.$el.append(btn_viz.render().$el);\n\n // hide the button\n this.hide();\n },\n\n // backbone events\n events:\n {\n 'mouseover tr' : 'show',\n 'mouseleave' : 'hide'\n },\n\n // show button\n show: function (e) {\n // is numeric\n function is_numeric(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n };\n\n // check\n if(this.col.chrom === null)\n return;\n\n // get selected data line\n var row = $(e.target).parent();\n\n // verify that location has been found\n var chrom = row.children().eq(this.col.chrom).html();\n var start = row.children().eq(this.col.start).html();\n\n // end is optional\n var end = this.col.end ? row.children().eq(this.col.end).html() : start;\n\n // double check location\n if (!chrom.match(\"^#\") && chrom !== \"\" && is_numeric(start)) {\n\n // get target gene region\n var btn_viz_pars = {\n dataset_id : this.dataset_id,\n gene_region : chrom + \":\" + start + \"-\" + end\n };\n\n // get button position\n var offset = row.offset();\n var left = offset.left - 10;\n var top = offset.top - $(window).scrollTop() + 3;\n\n // update css\n $('#btn_viz').css({'position': 'fixed', 'top': top + 'px', 'left': left + 'px'});\n $('#btn_viz').off('click');\n $('#btn_viz').click(this.create_trackster_action(this.url_viz, btn_viz_pars, this.genome_build));\n\n // show the button\n $('#btn_viz').show();\n } else {\n // hide the button\n $('#btn_viz').hide();\n }\n },\n\n // hide button\n hide: function () {\n this.$el.find('#btn_viz').hide();\n },\n\n // create action\n create_trackster_action : function (vis_url, dataset_params, dbkey) {\n // link this\n var self = this;\n\n // create function\n return function() {\n var listTracksParams = {};\n if (dbkey) {\n listTracksParams[ 'f-dbkey' ] = dbkey;\n }\n $.ajax({\n url: vis_url + '/list_tracks?' + $.param( listTracksParams ),\n dataType: 'html',\n error: function() {\n // show error message\n self.modal.show({\n title : 'Something went wrong!',\n body : 'Unfortunately we could not add this dataset to the track browser. Please try again or contact us.',\n buttons : {\n 'Cancel': function(){\n self.modal.hide();\n }\n }\n });\n },\n success: function(table_html) {\n self.modal.show({\n title : 'View Data in a New or Saved Visualization',\n buttons :{\n 'Cancel': function(){\n self.modal.hide();\n },\n 'View in saved visualization': function(){\n // show modal with saved visualizations\n self.modal.show(\n {\n title : 'Add Data to Saved Visualization',\n body : table_html,\n buttons : {\n 'Cancel': function(){\n self.modal.hide();\n },\n 'Add to visualization': function(){\n // hide\n self.modal.hide();\n\n // search selected fields\n self.modal.$el.find('input[name=id]:checked').each(function(){\n // get visualization id\n var vis_id = $(this).val();\n dataset_params.id = vis_id;\n\n // add widget\n self.frame.add({\n title : 'Trackster',\n type : 'url',\n content : vis_url + '/trackster?' + $.param(dataset_params)\n });\n });\n }\n }\n });\n },\n 'View in new visualization': function(){\n // hide\n self.modal.hide();\n\n // add widget\n self.frame.add({\n title : 'Trackster',\n type : 'url',\n content : vis_url + '/trackster?' + $.param(dataset_params)\n });\n }\n }\n });\n }\n });\n return false;\n };\n }\n});\n\n// -- Utility functions. --\n\n/**\n * Create a model, attach it to a view, render view, and attach it to a parent element.\n */\nvar createModelAndView = function(model, view, model_config, parent_elt) {\n // Create model, view.\n var a_view = new view({\n model: new model(model_config)\n });\n\n // Render view and add to parent element.\n a_view.render();\n if (parent_elt) {\n parent_elt.append(a_view.$el);\n }\n\n return a_view;\n};\n\n/**\n * Create a tabular dataset chunked view (and requisite tabular dataset model)\n * and appends to parent_elt.\n */\nvar createTabularDatasetChunkedView = function(options) {\n // If no model, create and set model from dataset config.\n if (!options.model) {\n options.model = new TabularDataset(options.dataset_config);\n }\n\n var parent_elt = options.parent_elt;\n var embedded = options.embedded;\n\n // Clean up options so that only needed options are passed to view.\n delete options.embedded;\n delete options.parent_elt;\n delete options.dataset_config;\n\n // Create and set up view.\n var view = (embedded ? new EmbeddedTabularDatasetChunkedView(options) :\n new TopLevelTabularDatasetChunkedView(options));\n view.render();\n\n if (parent_elt) {\n parent_elt.append(view.$el);\n // If we're sticking this in another element, once it's appended check\n // to make sure we've filled enough space.\n // Without this, the scroll elements don't work.\n view.expand_to_container();\n }\n\n return view;\n};\n\nreturn {\n Dataset: Dataset,\n TabularDataset: TabularDataset,\n DatasetCollection: DatasetCollection,\n TabularDatasetChunkedView: TabularDatasetChunkedView,\n createTabularDatasetChunkedView: createTabularDatasetChunkedView\n};\n\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/dataset/data.js\n// module id = 11\n// module chunks = 0 1 3","define([\n], function(){\n\n'use strict';\n//==============================================================================\n/** Map of possible HDA/collection/job states to their string equivalents.\n * A port of galaxy.model.Dataset.states.\n */\nvar STATES = {\n // NOT ready states\n /** is uploading and not ready */\n UPLOAD : 'upload',\n /** the job that will produce the dataset queued in the runner */\n QUEUED : 'queued',\n /** the job that will produce the dataset is running */\n RUNNING : 'running',\n /** metadata for the dataset is being discovered/set */\n SETTING_METADATA : 'setting_metadata',\n\n // ready states\n /** was created without a tool */\n NEW : 'new',\n /** has no data */\n EMPTY : 'empty',\n /** has successfully completed running */\n OK : 'ok',\n\n /** the job that will produce the dataset paused */\n PAUSED : 'paused',\n /** metadata discovery/setting failed or errored (but otherwise ok) */\n FAILED_METADATA : 'failed_metadata',\n//TODO: not in trans.app.model.Dataset.states - is in database\n /** not accessible to the current user (i.e. due to permissions) */\n NOT_VIEWABLE : 'noPermission',\n /** deleted while uploading */\n DISCARDED : 'discarded',\n /** the tool producing this dataset failed */\n ERROR : 'error'\n};\n\nSTATES.READY_STATES = [\n STATES.OK,\n STATES.EMPTY,\n STATES.PAUSED,\n STATES.FAILED_METADATA,\n STATES.NOT_VIEWABLE,\n STATES.DISCARDED,\n STATES.ERROR\n];\n\nSTATES.NOT_READY_STATES = [\n STATES.UPLOAD,\n STATES.QUEUED,\n STATES.RUNNING,\n STATES.SETTING_METADATA,\n STATES.NEW\n];\n\n\n//==============================================================================\n return STATES;\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/dataset/states.js\n// module id = 12\n// module chunks = 3","/** This module contains all button views. */\ndefine( [ 'utils/utils' ], function( Utils ) {\n /** This renders the default button which is used e.g. at the bottom of the upload modal. */\n var ButtonDefault = Backbone.View.extend({\n initialize: function( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : Utils.uid(),\n title : '',\n floating : 'right',\n icon : '',\n cls : 'btn btn-default',\n wait : false,\n wait_text : 'Sending...',\n wait_cls : 'btn btn-info',\n disabled : false,\n percentage : -1\n }).set( options );\n this.setElement( $( '' ).attr( 'type', 'button' )\n .append( this.$icon = $( '' ) )\n .append( this.$title = $( '' ) )\n .append( this.$progress = $( '' ).append( this.$progress_bar = $( '' ) ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.render();\n },\n\n render: function() {\n var self = this;\n var options = this.model.attributes;\n this.$el.removeClass()\n .addClass( 'ui-button-default' )\n .addClass( options.disabled && 'disabled' )\n .attr( 'id', options.id )\n .attr( 'disabled', options.disabled )\n .css( 'float', options.floating )\n .off( 'click' ).on( 'click' , function() {\n $( '.tooltip' ).hide();\n options.onclick && !self.disabled && options.onclick();\n })\n .tooltip( { title: options.tooltip, placement: 'bottom' } );\n this.$progress.addClass( 'progress' ).css( 'display', options.percentage !== -1 ? 'block' : 'none' );\n this.$progress_bar.addClass( 'progress-bar' ).css( { width : options.percentage + '%' } );\n this.$icon.removeClass().addClass( 'icon fa' );\n this.$title.removeClass().addClass( 'title' );\n if ( options.wait ) {\n this.$el.addClass( options.wait_cls ).prop( 'disabled', true );\n this.$icon.addClass( 'fa-spinner fa-spin ui-margin-right' );\n this.$title.html( options.wait_text );\n } else {\n this.$el.addClass( options.cls );\n this.$icon.addClass( options.icon );\n this.$title.html( options.title );\n options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n }\n },\n\n /** Show button */\n show: function() {\n this.$el.show();\n },\n\n /** Hide button */\n hide: function() {\n this.$el.hide();\n },\n\n /** Disable button */\n disable: function() {\n this.model.set( 'disabled', true );\n },\n\n /** Enable button */\n enable: function() {\n this.model.set( 'disabled', false );\n },\n\n /** Show spinner to indicate that the button is not ready to be clicked */\n wait: function() {\n this.model.set( 'wait', true );\n },\n\n /** Hide spinner to indicate that the button is ready to be clicked */\n unwait: function() {\n this.model.set( 'wait', false );\n },\n\n /** Change icon */\n setIcon: function( icon ) {\n this.model.set( 'icon', icon );\n }\n });\n\n /** This button allows the right-click/open-in-new-tab feature, its used e.g. for panel buttons. */\n var ButtonLink = ButtonDefault.extend({\n initialize: function( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : Utils.uid(),\n title : '',\n icon : '',\n cls : ''\n }).set( options );\n this.setElement( $( '' ).append( this.$icon = $( '' ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.render();\n },\n\n render: function() {\n var options = this.model.attributes;\n this.$el.removeClass()\n .addClass( options.cls )\n .attr( { id : options.id,\n href : options.href || 'javascript:void(0)',\n title : options.title,\n target : options.target || '_top',\n disabled : options.disabled } )\n .off( 'click' ).on( 'click' , function() {\n options.onclick && !options.disabled && options.onclick();\n });\n this.$icon.removeClass().addClass( options.icon );\n }\n });\n\n /** The check button is used in the tool form and allows to distinguish between multiple states e.g. all, partially and nothing selected. */\n var ButtonCheck = Backbone.View.extend({\n initialize: function( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : Utils.uid(),\n title : 'Select/Unselect all',\n icons : [ 'fa-square-o', 'fa-minus-square-o', 'fa-check-square-o' ],\n value : 0,\n onchange : function(){}\n }).set( options );\n this.setElement( $( '' ).append( this.$icon = $( '' ) )\n .append( this.$title = $( '' ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.render();\n },\n\n render: function( options ) {\n var self = this;\n var options = this.model.attributes;\n this.$el.addClass( 'ui-button-check' )\n .off( 'click' ).on('click', function() {\n self.model.set( 'value', ( self.model.get( 'value' ) === 0 && 2 ) || 0 );\n options.onclick && options.onclick();\n });\n this.$title.html( options.title );\n this.$icon.removeClass()\n .addClass( 'icon fa ui-margin-right' )\n .addClass( options.icons[ options.value ] );\n },\n\n /* Sets a new value and/or returns the value.\n * @param{Integer} new_val - Set a new value 0=unchecked, 1=partial and 2=checked.\n * OR:\n * @param{Integer} new_val - Number of selected options.\n * @param{Integer} total - Total number of available options.\n */\n value: function ( new_val, total ) {\n if ( new_val !== undefined ) {\n if ( total && new_val !== 0 ) {\n new_val = ( new_val !== total ) && 1 || 2;\n }\n this.model.set( 'value', new_val );\n this.model.get( 'onchange' )( this.model.get( 'value' ) );\n }\n return this.model.get( 'value' );\n }\n });\n\n /** This renders a differently styled, more compact button version. */\n var ButtonIcon = ButtonDefault.extend({\n initialize: function( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : Utils.uid(),\n title : '',\n floating : 'right',\n icon : '',\n cls : 'ui-button-icon',\n disabled : false\n }).set( options );\n this.setElement( $( '' ).append( this.$button = $( '' ).append( this.$icon = $( '' ) )\n .append( this.$title = $( '' ) ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.render();\n },\n\n render : function( options ) {\n var self = this;\n var options = this.model.attributes;\n this.$el.removeClass()\n .addClass( options.cls )\n .addClass( options.disabled && 'disabled' )\n .attr( 'disabled', options.disabled )\n .attr( 'id', options.id )\n .css( 'float', options.floating )\n .off( 'click' ).on( 'click', function() {\n $( '.tooltip' ).hide();\n !options.disabled && options.onclick && options.onclick();\n });\n this.$button.addClass( 'button' ).tooltip( { title: options.tooltip, placement: 'bottom' } );\n this.$icon.removeClass().addClass( 'icon fa' ).addClass( options.icon );\n this.$title.addClass( 'title' ).html( options.title );\n options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n }\n });\n\n /** This class creates a button with dropdown menu. */\n var ButtonMenu = ButtonDefault.extend({\n $menu: null,\n initialize: function ( options ) {\n this.model = options && options.model || new Backbone.Model({\n id : '',\n title : '',\n floating : 'right',\n pull : 'right',\n icon : null,\n onclick : null,\n cls : 'ui-button-icon ui-button-menu',\n tooltip : '',\n target : '',\n href : '',\n onunload : null,\n visible : true,\n tag : ''\n }).set( options );\n this.collection = new Backbone.Collection();\n this.setElement( $( '' ).append( this.$root = $( '' ).append( this.$icon = $( '' ) )\n .append( this.$title = $( '' ) ) ) );\n this.listenTo( this.model, 'change', this.render, this );\n this.listenTo( this.collection, 'change add remove reset', this.render, this );\n this.render();\n },\n\n render: function() {\n var self = this;\n var options = this.model.attributes;\n this.$el.removeClass()\n .addClass( 'dropdown' )\n .addClass( options.cls )\n .attr( 'id', options.id )\n .css( { float : options.floating,\n display : options.visible && this.collection.where( { visible: true } ).length > 0 ? 'block' : 'none' } );\n this.$root.addClass( 'root button dropdown-toggle' )\n .attr( 'data-toggle', 'dropdown' )\n .tooltip( { title: options.tooltip, placement: 'bottom' } )\n .off( 'click' ).on( 'click', function( e ) {\n $( '.tooltip' ).hide();\n e.preventDefault();\n options.onclick && options.onclick();\n } );\n this.$icon.removeClass().addClass( 'icon fa' ).addClass( options.icon );\n this.$title.removeClass().addClass( 'title' ).html( options.title );\n options.icon && options.title && this.$icon.addClass( 'ui-margin-right' );\n this.$menu && this.$menu.remove();\n if ( this.collection.length > 0 ) {\n this.$menu = $( ' ' ).addClass( 'menu dropdown-menu' )\n .addClass( 'pull-' + self.model.get( 'pull' ) )\n .attr( 'role', 'menu' );\n this.$el.append( this.$menu );\n }\n this.collection.each( function( submodel ) {\n var suboptions = submodel.attributes;\n if ( suboptions.visible ) {\n var $link = $( '' ).addClass( 'dropdown-item' )\n .attr( { href : suboptions.href, target : suboptions.target } )\n .append( $( '' ).addClass( 'fa' )\n .addClass( suboptions.icon )\n .css( 'display', suboptions.icon ? 'inline-block' : 'none' ) )\n .append( suboptions.title )\n .on( 'click', function( e ) {\n if ( suboptions.onclick ) {\n e.preventDefault();\n suboptions.onclick();\n }\n } );\n self.$menu.append( $( '' ).append( $link ) );\n suboptions.divider && self.$menu.append( $( '' ).addClass( 'divider' ) );\n }\n });\n },\n\n /** Add a new menu item */\n addMenu: function ( options ) {\n this.collection.add( Utils.merge( options, {\n title : '',\n target : '',\n href : '',\n onclick : null,\n divider : false,\n visible : true,\n icon : null,\n cls : 'button-menu btn-group'\n }));\n }\n });\n\n return {\n ButtonDefault : ButtonDefault,\n ButtonLink : ButtonLink,\n ButtonIcon : ButtonIcon,\n ButtonCheck : ButtonCheck,\n ButtonMenu : ButtonMenu\n }\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/ui/ui-buttons.js\n// module id = 13\n// module chunks = 0 3","define([\n //jquery\n //backbone\n], function(){\n//=============================================================================\n/**\n * backbone model for icon buttons\n */\nvar IconButton = Backbone.Model.extend({\n defaults: {\n title : \"\",\n icon_class : \"\",\n on_click : null,\n menu_options : null,\n is_menu_button : true,\n id : null,\n href : null,\n target : null,\n enabled : true,\n visible : true,\n tooltip_config : {}\n }\n});\n\n/**\n * backbone view for icon buttons\n */\nvar IconButtonView = Backbone.View.extend({\n\n initialize : function(){\n // better rendering this way\n this.model.attributes.tooltip_config = { placement : 'bottom' };\n this.model.bind( 'change', this.render, this );\n },\n\n render : function( ){\n // hide tooltip\n this.$el.tooltip( 'hide' );\n\n var new_elem = this.template( this.model.toJSON() );\n // configure tooltip\n new_elem.tooltip( this.model.get( 'tooltip_config' ));\n this.$el.replaceWith( new_elem );\n this.setElement( new_elem );\n return this;\n },\n\n events : {\n 'click' : 'click'\n },\n\n click : function( event ){\n // if on_click pass to that function\n if( _.isFunction( this.model.get( 'on_click' ) ) ){\n this.model.get( 'on_click' )( event );\n return false;\n }\n // otherwise, bubble up ( to href or whatever )\n return true;\n },\n\n // generate html element\n template: function( options ){\n var buffer = 'title=\"' + options.title + '\" class=\"icon-button';\n\n if( options.is_menu_button ){\n buffer += ' menu-button';\n }\n\n buffer += ' ' + options.icon_class;\n\n if( !options.enabled ){\n buffer += '_disabled';\n }\n\n // close class tag\n buffer += '\"';\n\n if( options.id ){\n buffer += ' id=\"' + options.id + '\"';\n }\n\n buffer += ' href=\"' + options.href + '\"';\n // add target for href\n if( options.target ){\n buffer += ' target=\"' + options.target + '\"';\n }\n // set visibility\n if( !options.visible ){\n buffer += ' style=\"display: none;\"';\n }\n\n // enabled/disabled\n if ( options.enabled ){\n buffer = '';\n } else {\n buffer = '';\n }\n\n // return element\n return $( buffer );\n }\n} );\n\n// define collection\nvar IconButtonCollection = Backbone.Collection.extend({\n model: IconButton\n});\n\n/**\n * menu with multiple icon buttons\n * views are not needed nor used for individual buttons\n */\nvar IconButtonMenuView = Backbone.View.extend({\n\n tagName: 'div',\n\n initialize: function(){\n this.render();\n },\n\n render: function(){\n // initialize icon buttons\n var self = this;\n this.collection.each(function(button){\n // create and add icon button to menu\n var elt = $('')\n .attr('href', 'javascript:void(0)')\n .attr('title', button.attributes.title)\n .addClass('icon-button menu-button')\n .addClass(button.attributes.icon_class)\n .appendTo(self.$el)\n .click(button.attributes.on_click);\n\n // configure tooltip\n if (button.attributes.tooltip_config){\n elt.tooltip(button.attributes.tooltip_config);\n }\n\n // add popup menu to icon\n var menu_options = button.get('options');\n if (menu_options){\n make_popupmenu(elt, menu_options);\n }\n });\n\n // return\n return this;\n }\n});\n\n/**\n * Returns an IconButtonMenuView for the provided configuration.\n * Configuration is a list of dictionaries where each dictionary\n * defines an icon button. Each dictionary must have the following\n * elements: icon_class, title, and on_click.\n */\nvar create_icon_buttons_menu = function(config, global_config)\n{\n // initialize global configuration\n if (!global_config) global_config = {};\n\n // create and initialize menu\n var buttons = new IconButtonCollection(\n _.map(config, function(button_config){\n return new IconButton(_.extend(button_config, global_config));\n })\n );\n\n // return menu\n return new IconButtonMenuView( {collection: buttons} );\n};\n\n\n//=============================================================================\n return {\n IconButton : IconButton,\n IconButtonView : IconButtonView,\n IconButtonCollection : IconButtonCollection,\n IconButtonMenuView : IconButtonMenuView,\n create_icon_buttons_menu: create_icon_buttons_menu\n };\n})\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/ui/icon-button.js\n// module id = 14\n// module chunks = 0 1 3","// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js\n// Uses AMD or browser globals to create a jQuery plugin.\n(function (factory) {\n if (typeof define === 'function' && define.amd) {\n //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will\n // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still\n // relying on jquery being loaded globally* in order to preserve plugins.\n define([ 'jquery' ], factory);\n } else {\n // Browser globals\n factory(jQuery);\n }\n\n}(function ( jQuery ) {\n'use_strict';\n\nvar $ = jQuery;\n\n// ============================================================================\n/**\n * Make an element with text editable: (a) when user clicks on text, a textbox/area\n * is provided for editing; (b) when enter key pressed, element's text is set and on_finish\n * is called.\n */\n$.fn.make_text_editable = function(config_dict) {\n // Get config options.\n var num_cols = (\"num_cols\" in config_dict ? config_dict.num_cols : 30),\n num_rows = (\"num_rows\" in config_dict ? config_dict.num_rows : 4),\n use_textarea = (\"use_textarea\" in config_dict ? config_dict.use_textarea : false),\n on_finish = (\"on_finish\" in config_dict ? config_dict.on_finish : null),\n help_text = (\"help_text\" in config_dict ? config_dict.help_text : null);\n\n // Add element behavior.\n var container = $(this);\n container.addClass(\"editable-text\").click(function(e) {\n // If there's already an input element, editing is active, so do nothing.\n if ($(this).children(\":input\").length > 0) {\n return;\n }\n\n container.removeClass(\"editable-text\");\n\n // Handler for setting element text.\n var set_text = function(new_text) {\n container.find(\":input\").remove();\n\n if (new_text !== \"\") {\n container.text(new_text);\n } else {\n // No text; need a line so that there is a click target.\n container.html(\"
\");\n }\n container.addClass(\"editable-text\");\n\n if (on_finish) {\n on_finish(new_text);\n }\n };\n\n // Create input element(s) for editing.\n var cur_text = (\"cur_text\" in config_dict ? config_dict.cur_text : container.text() ),\n input_elt, button_elt;\n\n if (use_textarea) {\n input_elt = $(\"\")\n .attr({ rows: num_rows, cols: num_cols }).text($.trim(cur_text))\n .keyup(function(e) {\n if (e.keyCode === 27) {\n // Escape key.\n set_text(cur_text);\n }\n });\n button_elt = $(\"\").text(\"Done\").click(function() {\n set_text(input_elt.val());\n // Return false so that click does not propogate to container.\n return false;\n });\n }\n else {\n input_elt = $(\"\").attr({ value: $.trim(cur_text), size: num_cols })\n .blur(function() {\n set_text(cur_text);\n }).keyup(function(e) {\n if (e.keyCode === 27) {\n // Escape key.\n $(this).trigger(\"blur\");\n } else if (e.keyCode === 13) {\n // Enter key.\n set_text($(this).val());\n }\n\n // Do not propogate event to avoid unwanted side effects.\n e.stopPropagation();\n });\n }\n\n // Replace text with input object(s) and focus & select.\n container.text(\"\");\n container.append(input_elt);\n if (button_elt) {\n container.append(button_elt);\n }\n input_elt.focus();\n input_elt.select();\n\n // Do not propogate to elements below b/c that blurs input and prevents it from being used.\n e.stopPropagation();\n });\n\n // Add help text if there some.\n if (help_text) {\n container.attr(\"title\", help_text).tooltip();\n }\n\n return container;\n};\n\n// ============================================================================\n}));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/ui/editable-text.js\n// module id = 15\n// module chunks = 0 3","define(function(){\n\n/**\n * Stringifies a number adding commas for digit grouping as per North America.\n */\nfunction commatize( number ) {\n number += ''; // Convert to string\n var rgx = /(\\d+)(\\d{3})/;\n while (rgx.test(number)) {\n number = number.replace(rgx, '$1' + ',' + '$2');\n }\n return number;\n}\n\n/**\n * Helper to determine if object is jQuery deferred.\n */\nvar is_deferred = function ( d ) {\n return ('promise' in d);\n};\n\n/**\n * Implementation of a server-state based deferred. Server is repeatedly polled, and when\n * condition is met, deferred is resolved.\n */\nvar ServerStateDeferred = Backbone.Model.extend({\n defaults: {\n ajax_settings: {},\n interval: 1000,\n success_fn: function(result) { return true; }\n },\n\n /**\n * Returns a deferred that resolves when success function returns true.\n */\n go: function() {\n var deferred = $.Deferred(),\n self = this,\n ajax_settings = self.get('ajax_settings'),\n success_fn = self.get('success_fn'),\n interval = self.get('interval'),\n _go = function() {\n $.ajax(ajax_settings).success(function(result) {\n if (success_fn(result)) {\n // Result is good, so resolve.\n deferred.resolve(result);\n }\n else {\n // Result not good, try again.\n setTimeout(_go, interval);\n }\n });\n };\n _go();\n return deferred;\n }\n});\n\n/**\n * Returns a random color in hexadecimal format that is sufficiently different from a single color\n * or set of colors.\n * @param colors a color or list of colors in the format '#RRGGBB'\n */\nvar get_random_color = function(colors) {\n // Default for colors is white.\n if (!colors) { colors = \"#ffffff\"; }\n\n // If needed, create list of colors.\n if ( typeof(colors) === \"string\" ) {\n colors = [ colors ];\n }\n\n // Convert colors to numbers.\n for (var i = 0; i < colors.length; i++) {\n colors[i] = parseInt( colors[i].slice(1), 16 );\n }\n\n // -- Perceived brightness and difference formulas are from\n // -- http://www.w3.org/WAI/ER/WD-AERT/#color-contrast\n\n // Compute perceived color brightness (based on RGB-YIQ transformation):\n var brightness = function(r, g, b) {\n return ( (r * 299) + (g * 587) + (b * 114) ) / 1000;\n };\n\n // Compute color difference:\n var difference = function(r1, g1, b1, r2, g2, b2) {\n return ( Math.max(r1, r2) - Math.min(r1, r2) ) +\n ( Math.max(g1, g2) - Math.min(g1, g2) ) +\n ( Math.max(b1, b2) - Math.min(b1, b2) );\n };\n\n // Create new random color.\n var new_color, nr, ng, nb,\n other_color, or, og, ob,\n n_brightness, o_brightness,\n diff, ok = false,\n num_tries = 0;\n do {\n // New color is never white b/c random in [0,1)\n new_color = Math.round( Math.random() * 0xffffff );\n nr = ( new_color & 0xff0000 ) >> 16;\n ng = ( new_color & 0x00ff00 ) >> 8;\n nb = new_color & 0x0000ff;\n n_brightness = brightness(nr, ng, nb);\n ok = true;\n for (i = 0; i < colors.length; i++) {\n other_color = colors[i];\n or = ( other_color & 0xff0000 ) >> 16;\n og = ( other_color & 0x00ff00 ) >> 8;\n ob = other_color & 0x0000ff;\n o_brightness = brightness(or, og, ob);\n diff = difference(nr, ng, nb, or, og, ob);\n // These thresholds may need to be adjusted. Brightness difference range is 125;\n // color difference range is 500.\n if ( ( Math.abs(n_brightness - o_brightness) < 40 ) ||\n ( diff < 200 ) ) {\n ok = false;\n break;\n }\n }\n\n num_tries++\n; } while (!ok && num_tries <= 10 );\n\n // Add 0x1000000 to left pad number with 0s.\n return '#' + ( 0x1000000 + new_color ).toString(16).substr(1,6);\n};\n\nreturn {\n commatize: commatize,\n is_deferred: is_deferred,\n ServerStateDeferred : ServerStateDeferred,\n get_random_color : get_random_color\n};\n\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/viz/trackster/util.js\n// module id = 16\n// module chunks = 0 3","define([\n \"libs/bibtex\",\n \"mvc/base-mvc\",\n \"utils/localization\"\n], function( parseBibtex, baseMVC, _l ){\n/* global Backbone */\n// we use amd here to require, but bibtex uses a global or commonjs pattern.\n// webpack will load via commonjs and plain requirejs will load as global. Check both\nparseBibtex = parseBibtex || window.BibtexParser;\n\nvar logNamespace = 'citation';\n//==============================================================================\n/** @class model for tool citations.\n * @name Citation\n * @augments Backbone.Model\n */\nvar Citation = Backbone.Model.extend( baseMVC.LoggableMixin ).extend({\n _logNamespace : logNamespace,\n\n defaults : {\n content: ''\n },\n\n initialize: function() {\n var parsed;\n try {\n // TODO: to model.parse/.validate\n parsed = parseBibtex( this.attributes.content );\n } catch( err ){\n return;\n }\n // bibtex returns successfully parsed in .entries and any parsing errors in .errors\n if( parsed.errors.length ){\n // the gen. form of these errors seems to be [ line, col, char, error message ]\n var errors = parsed.errors.reduce( function( all, current ){ return all + '; ' + current; });\n // throw new Error( 'Error parsing bibtex: ' + errors );\n this.log( 'Error parsing bibtex: ' + errors );\n }\n\n this._fields = {};\n this.entry = _.first( parsed.entries );\n if( this.entry ){\n var rawFields = this.entry.Fields;\n for( var key in rawFields ){\n var value = rawFields[ key ];\n var lowerKey = key.toLowerCase();\n this._fields[ lowerKey ] = value;\n }\n }\n },\n entryType: function() {\n return this.entry? this.entry.EntryType : undefined;\n },\n fields: function() {\n return this._fields;\n }\n});\n\n\n//==============================================================================\n/** @class Backbone collection of citations.\n */\nvar BaseCitationCollection = Backbone.Collection.extend( baseMVC.LoggableMixin ).extend( {\n _logNamespace : logNamespace,\n\n /** root api url */\n urlRoot : Galaxy.root + 'api',\n partial : true, // Assume some tools in history/workflow may not be properly annotated yet.\n model : Citation,\n} );\n\nvar HistoryCitationCollection = BaseCitationCollection.extend( {\n /** complete api url */\n url : function() {\n return this.urlRoot + '/histories/' + this.history_id + '/citations';\n }\n} );\n\nvar ToolCitationCollection = BaseCitationCollection.extend( {\n /** complete api url */\n url : function() {\n return this.urlRoot + '/tools/' + this.tool_id + '/citations';\n },\n partial : false, // If a tool has citations, assume they are complete.\n} );\n\n\n//==============================================================================\n\nreturn {\n Citation : Citation,\n HistoryCitationCollection : HistoryCitationCollection,\n ToolCitationCollection: ToolCitationCollection\n};\n\n\n});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/citation/citation-model.js\n// module id = 17\n// module chunks = 0 3","/* This is the regular tool form */\ndefine([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal', 'mvc/tool/tool-form-base', 'mvc/webhooks' ],\n function( Utils, Ui, Modal, ToolFormBase, Webhooks ) {\n var View = Backbone.View.extend({\n initialize: function( options ) {\n var self = this;\n this.modal = parent.Galaxy.modal || new Modal.View();\n this.form = new ToolFormBase( Utils.merge({\n listen_to_history : true,\n always_refresh : false,\n customize : function( form ) {\n var options = form.model.attributes;\n // build execute button\n options.buttons = {\n execute: execute_btn = new Ui.Button({\n icon : 'fa-check',\n tooltip : 'Execute: ' + options.name + ' (' + options.version + ')',\n title : 'Execute',\n cls : 'ui-button btn btn-primary',\n floating : 'clear',\n onclick : function() {\n execute_btn.wait();\n form.portlet.disable();\n self.submit( options, function() {\n execute_btn.unwait();\n form.portlet.enable();\n } );\n }\n })\n }\n // remap feature\n if ( options.job_id && options.job_remap ) {\n options.inputs.push({\n label : 'Resume dependencies from this job',\n name : 'rerun_remap_job_id',\n type : 'select',\n display : 'radio',\n ignore : '__ignore__',\n value : '__ignore__',\n options : [ [ 'Yes', options.job_id ], [ 'No', '__ignore__' ] ],\n 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 });\n }\n },\n postchange : function( process, form ) {\n var self = this;\n var current_state = {\n tool_id : form.model.get( 'id' ),\n tool_version : form.model.get( 'version' ),\n inputs : $.extend(true, {}, form.data.create())\n }\n form.wait( true );\n Galaxy.emit.debug( 'tool-form::postchange()', 'Sending current state.', current_state );\n Utils.request({\n type : 'POST',\n url : Galaxy.root + 'api/tools/' + form.model.get( 'id' ) + '/build',\n data : current_state,\n success : function( data ) {\n form.update( data );\n form.wait( false );\n Galaxy.emit.debug( 'tool-form::postchange()', 'Received new model.', data );\n process.resolve();\n },\n error : function( response ) {\n Galaxy.emit.debug( 'tool-form::postchange()', 'Refresh request failed.', response );\n process.reject();\n }\n });\n }\n }, options ) );\n this.deferred = this.form.deferred;\n this.setElement( '' );\n this.$el.append( this.form.$el );\n },\n\n /** Submit a regular job.\n * @param{dict} options - Specifies tool id and version\n * @param{function} callback - Called when request has completed\n */\n submit: function( options, callback ) {\n var self = this;\n var job_def = {\n tool_id : options.id,\n tool_version : options.version,\n inputs : this.form.data.create()\n }\n this.form.trigger( 'reset' );\n if ( !self.validate( job_def ) ) {\n Galaxy.emit.debug( 'tool-form::submit()', 'Submission canceled. Validation failed.' );\n callback && callback();\n return;\n }\n if ( options.action !== Galaxy.root + 'tool_runner/index' ) {\n var $f = $( '' ).attr( { action: options.action, method: options.method, enctype: options.enctype } );\n _.each( job_def.inputs, function( value, key ) { $f.append( $( '' ).attr( { 'name': key, 'value': value } ) ) } );\n $f.hide().appendTo( 'body' ).submit().remove();\n callback && callback();\n return;\n }\n Galaxy.emit.debug( 'tool-form::submit()', 'Validation complete.', job_def );\n Utils.request({\n type : 'POST',\n url : Galaxy.root + 'api/tools',\n data : job_def,\n success : function( response ) {\n callback && callback();\n self.$el.children().hide();\n self.$el.append( self._templateSuccess( response ) );\n // Show Webhook if job is running\n if ( response.jobs && response.jobs.length > 0 ) {\n self.$el.append( $( '', { id: 'webhook-view' } ) );\n var WebhookApp = new Webhooks.WebhookView({\n urlRoot: Galaxy.root + 'api/webhooks/tool'\n });\n }\n parent.Galaxy && parent.Galaxy.currHistoryPanel && parent.Galaxy.currHistoryPanel.refreshContents();\n },\n error : function( response ) {\n callback && callback();\n Galaxy.emit.debug( 'tool-form::submit', 'Submission failed.', response );\n var input_found = false;\n if ( response && response.err_data ) {\n var error_messages = self.form.data.matchResponse( response.err_data );\n for ( var input_id in error_messages ) {\n self.form.highlight( input_id, error_messages[ input_id ]);\n input_found = true;\n break;\n }\n }\n if ( !input_found ) {\n self.modal.show({\n title : 'Job submission failed',\n body : self._templateError( job_def, response && response.err_msg ),\n buttons : { 'Close' : function() { self.modal.hide() } }\n });\n }\n }\n });\n },\n\n /** Validate job dictionary.\n * @param{dict} job_def - Job execution dictionary\n */\n validate: function( job_def ) {\n var job_inputs = job_def.inputs;\n var batch_n = -1;\n var batch_src = null;\n for ( var job_input_id in job_inputs ) {\n var input_value = job_inputs[ job_input_id ];\n var input_id = this.form.data.match( job_input_id );\n var input_field = this.form.field_list[ input_id ];\n var input_def = this.form.input_list[ input_id ];\n if ( !input_id || !input_def || !input_field ) {\n Galaxy.emit.debug('tool-form::validate()', 'Retrieving input objects failed.');\n continue;\n }\n if ( !input_def.optional && input_value == null ) {\n this.form.highlight( input_id );\n return false;\n }\n if ( input_value && input_value.batch ) {\n var n = input_value.values.length;\n var src = n > 0 && input_value.values[ 0 ] && input_value.values[ 0 ].src;\n if ( src ) {\n if ( batch_src === null ) {\n batch_src = src;\n } else if ( batch_src !== src ) {\n this.form.highlight( input_id, 'Please select either dataset or dataset list fields for all batch mode fields.' );\n return false;\n }\n }\n if ( batch_n === -1 ) {\n batch_n = n;\n } else if ( batch_n !== n ) {\n this.form.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 return false;\n }\n }\n }\n return true;\n },\n\n _templateSuccess: function( response ) {\n if ( response.jobs && response.jobs.length > 0 ) {\n var njobs = response.jobs.length;\n var njobs_text = njobs == 1 ? '1 job has' : njobs + ' jobs have';\n var $message = $( '' ).addClass( 'donemessagelarge' )\n .append( $( '' ).text( njobs_text + ' been successfully added to the queue - resulting in the following datasets:' ) );\n _.each( response.outputs, function( output ) {\n $message.append( $( '' ).addClass( 'messagerow' ).append( $( '' ).text( output.hid + ': ' + output.name ) ) );\n });\n $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 return $message;\n } else {\n return this._templateError( response, 'Invalid success response. No jobs found.' );\n }\n },\n\n _templateError: function( response, err_msg ) {\n return $( '' ).addClass( 'errormessagelarge' )\n .append( $( '' ).text( 'The server could not complete the request. Please contact the Galaxy Team if this error persists. ' + ( err_msg || '' ) ) )\n .append( $( '' ).text( JSON.stringify( response, null, 4 ) ) );\n }\n });\n\n return {\n View: View\n };\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/tool/tool-form.js\n// module id = 18\n// module chunks = 0 3","// dependencies\ndefine(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc'], function(Utils, Portlet, Ui) {\n\n// ui list element\nvar View = Backbone.View.extend({\n // create portlet to keep track of selected list elements\n initialize : function(options) {\n // link this\n var self = this;\n\n // initialize options\n this.options = options;\n this.name = options.name || 'element';\n this.multiple = options.multiple || false;\n\n // create message handler\n this.message = new Ui.Message();\n\n // create portlet\n this.portlet = new Portlet.View({ cls: 'ui-portlet-section' });\n\n // create select field containing the options which can be inserted into the list\n this.select = new Ui.Select.View({ optional : options.optional });\n\n // create insert new list element button\n this.button = new Ui.ButtonIcon({\n icon : 'fa fa-sign-in',\n floating : 'left',\n tooltip : 'Insert new ' + this.name,\n onclick : function() {\n self.add({\n id : self.select.value(),\n name : self.select.text()\n });\n }\n });\n\n // build main element\n this.setElement(this._template(options));\n this.$('.ui-list-message').append(this.message.$el);\n this.$('.ui-list-portlet').append(this.portlet.$el);\n this.$('.ui-list-button').append(this.button.$el);\n this.$('.ui-list-select').append(this.select.$el);\n },\n\n /** Return/Set currently selected list elements */\n value: function(val) {\n // set new value\n if (val !== undefined) {\n this.portlet.empty();\n if ($.isArray(val)) {\n for (var i in val) {\n var v = val[i];\n var v_id = null;\n var v_name = null;\n if ($.type(v) != 'string') {\n v_id = v.id;\n v_name = v.name;\n } else {\n v_id = v_name = v;\n }\n if (v_id != null) {\n this.add({\n id : v_id,\n name : v_name\n });\n }\n }\n }\n this._refresh();\n }\n // get current value\n var lst = [];\n this.$('.ui-list-id').each(function() {\n lst.push({\n id : $(this).prop('id'),\n name : $(this).find('.ui-list-name').html()\n });\n });\n if (lst.length == 0) {\n return null;\n }\n return lst;\n },\n\n /** Add row */\n add: function(options) {\n var self = this;\n if (this.$('[id=\"' + options.id + '\"]').length === 0) {\n if (!Utils.isEmpty(options.id)) {\n var $el = $(this._templateRow({\n id : options.id,\n name : options.name\n }));\n $el.on('click', function() {\n $el.remove();\n self._refresh();\n });\n $el.on('mouseover', function() {\n $el.addClass('portlet-highlight');\n });\n $el.on('mouseout', function() {\n $el.removeClass('portlet-highlight');\n });\n this.portlet.append($el);\n this._refresh();\n } else {\n this.message.update({ message: 'Please select a valid ' + this.name + '.', status: 'danger' });\n }\n } else {\n this.message.update({ message: 'This ' + this.name + ' is already in the list.' });\n }\n },\n\n /** Update available options */\n update: function(options) {\n this.select.update(options);\n },\n\n /** Refresh view */\n _refresh: function() {\n if (this.$('.ui-list-id').length > 0) {\n !this.multiple && this.button.disable();\n this.$('.ui-list-portlet').show();\n } else {\n this.button.enable();\n this.$('.ui-list-portlet').hide();\n }\n this.options.onchange && this.options.onchange();\n },\n\n /** Main Template */\n _template: function(options) {\n return '' + JSON.stringify( xhr.responseJSON ) + '
';\n } else {\n content += ': ' + message;\n }\n }\n creator._showAlert( content, 'alert-danger' );\n },\n\n events : {\n // header\n 'click .more-help' : '_clickMoreHelp',\n 'click .less-help' : '_clickLessHelp',\n 'click .main-help' : '_toggleHelp',\n 'click .header .alert button' : '_hideAlert',\n\n 'click .reset' : 'reset',\n 'click .clear-selected' : 'clearSelectedElements',\n\n // elements - selection\n 'click .collection-elements' : 'clearSelectedElements',\n\n // elements - drop target\n // 'dragenter .collection-elements': '_dragenterElements',\n // 'dragleave .collection-elements': '_dragleaveElements',\n 'dragover .collection-elements' : '_dragoverElements',\n 'drop .collection-elements' : '_dropElements',\n\n // these bubble up from the elements as custom events\n 'collection-element.dragstart .collection-elements' : '_elementDragstart',\n 'collection-element.dragend .collection-elements' : '_elementDragend',\n\n // footer\n 'change .collection-name' : '_changeName',\n 'keydown .collection-name' : '_nameCheckForEnter',\n 'click .cancel-create' : function( ev ){\n if( typeof this.oncancel === 'function' ){\n this.oncancel.call( this );\n }\n },\n 'click .create-collection' : '_clickCreate'//,\n },\n\n // ........................................................................ header\n /** expand help */\n _clickMoreHelp : function( ev ){\n ev.stopPropagation();\n this.$( '.main-help' ).addClass( 'expanded' );\n this.$( '.more-help' ).hide();\n },\n /** collapse help */\n _clickLessHelp : function( ev ){\n ev.stopPropagation();\n this.$( '.main-help' ).removeClass( 'expanded' );\n this.$( '.more-help' ).show();\n },\n /** toggle help */\n _toggleHelp : function( ev ){\n ev.stopPropagation();\n this.$( '.main-help' ).toggleClass( 'expanded' );\n this.$( '.more-help' ).toggle();\n },\n\n /** show an alert on the top of the interface containing message (alertClass is bootstrap's alert-*) */\n _showAlert : function( message, alertClass ){\n alertClass = alertClass || 'alert-danger';\n this.$( '.main-help' ).hide();\n this.$( '.header .alert' )\n .attr( 'class', 'alert alert-dismissable' ).addClass( alertClass ).show()\n .find( '.alert-message' ).html( message );\n },\n /** hide the alerts at the top */\n _hideAlert : function( message ){\n this.$( '.main-help' ).show();\n this.$( '.header .alert' ).hide();\n },\n\n // ........................................................................ elements\n /** reset all data to the initial state */\n reset : function(){\n this._instanceSetUp();\n this._elementsSetUp();\n this.render();\n },\n\n /** deselect all elements */\n clearSelectedElements : function( ev ){\n this.$( '.collection-elements .collection-element' ).removeClass( 'selected' );\n this.$( '.collection-elements-controls > .clear-selected' ).hide();\n },\n\n //_dragenterElements : function( ev ){\n // //this.debug( '_dragenterElements:', ev );\n //},\n//TODO: if selected are dragged out of the list area - remove the placeholder - cuz it won't work anyway\n // _dragleaveElements : function( ev ){\n // //this.debug( '_dragleaveElements:', ev );\n // },\n\n /** track the mouse drag over the list adding a placeholder to show where the drop would occur */\n _dragoverElements : function( ev ){\n //this.debug( '_dragoverElements:', ev );\n ev.preventDefault();\n\n var $list = this.$list();\n this._checkForAutoscroll( $list, ev.originalEvent.clientY );\n var $nearest = this._getNearestElement( ev.originalEvent.clientY );\n\n //TODO: no need to re-create - move instead\n this.$( '.element-drop-placeholder' ).remove();\n var $placeholder = $( '' );\n if( !$nearest.length ){\n $list.append( $placeholder );\n } else {\n $nearest.before( $placeholder );\n }\n },\n\n /** If the mouse is near enough to the list's top or bottom, scroll the list */\n _checkForAutoscroll : function( $element, y ){\n var AUTOSCROLL_SPEED = 2,\n offset = $element.offset(),\n scrollTop = $element.scrollTop(),\n upperDist = y - offset.top,\n lowerDist = ( offset.top + $element.outerHeight() ) - y;\n if( upperDist >= 0 && upperDist < this.autoscrollDist ){\n $element.scrollTop( scrollTop - AUTOSCROLL_SPEED );\n } else if( lowerDist >= 0 && lowerDist < this.autoscrollDist ){\n $element.scrollTop( scrollTop + AUTOSCROLL_SPEED );\n }\n },\n\n /** get the nearest element based on the mouse's Y coordinate.\n * If the y is at the end of the list, return an empty jQuery object.\n */\n _getNearestElement : function( y ){\n var WIGGLE = 4,\n lis = this.$( '.collection-elements li.collection-element' ).toArray();\n for( var i=0; i',\n '
',\n '<% _.each( problems, function( problem ){ %>',\n '
'\n ].join('')),\n\n /** shown in list when all elements are discarded */\n noElementsLeft : _.template([\n '<% _.each( problems, function( problem ){ %>',\n '
',\n '<% } else if( _.size( elements ) < 1 ){ %>',\n _l( 'No datasets were selected' ), '.',\n '<% } %>',\n '
',\n _l( 'At least one element is needed for the collection' ), '. ',\n _l( 'You may need to ' ),\n '', _l( 'cancel' ), ' ',\n _l( 'and reselect new elements' ), '.',\n '',\n '',\n '
',\n '<%= dataset.peek %>
',\n '<% } %>',\n '<% } %>',\n '');\n wrapper.append($el);\n this.row.append(wrapper);\n },\n \n // header\n appendHeader: function() {\n // append header row\n this.$thead.append(this.row);\n\n // row\n this.row = $(' ');\n },\n \n // add row cell\n add: function($el, width, align) {\n var wrapper = $(' ');\n if (width) {\n wrapper.css('width', width);\n }\n if (align) {\n wrapper.css('text-align', align);\n }\n wrapper.append($el);\n this.row.append(wrapper);\n },\n \n // append\n append: function(id, fade) {\n this._commit(id, fade, false);\n },\n \n // prepend\n prepend: function(id, fade) {\n this._commit(id, fade, true);\n },\n \n // get element\n get: function(id) {\n return this.$el.find('#' + id);\n },\n \n // delete\n del: function(id) {\n var item = this.$tbody.find('#' + id);\n if (item.length > 0) {\n item.remove();\n this.row_count--;\n this._refresh();\n }\n },\n\n // delete all\n delAll: function() {\n this.$tbody.empty();\n this.row_count = 0;\n this._refresh();\n },\n \n // value\n value: function(new_value) {\n // get current id/value\n this.before = this.$tbody.find('.current').attr('id');\n \n // check if new_value is defined\n if (new_value !== undefined) {\n this.$tbody.find('tr').removeClass('current');\n if (new_value) {\n this.$tbody.find('#' + new_value).addClass('current');\n }\n }\n \n // get current id/value\n var after = this.$tbody.find('.current').attr('id');\n if(after === undefined) {\n return null;\n } else {\n // fire onchange\n if (after != this.before && this.options.onchange) {\n this.options.onchange(new_value);\n }\n \n // return current value\n return after;\n }\n },\n \n // size\n size: function() {\n return this.$tbody.find('tr').length;\n },\n \n // commit\n _commit: function(id, fade, prepend) {\n // remove previous item with same id\n this.del(id);\n \n // add\n this.row.attr('id', id);\n \n // add row\n if (prepend) {\n this.$tbody.prepend(this.row);\n } else {\n this.$tbody.append(this.row);\n }\n \n // fade mode\n if (fade) {\n this.row.hide();\n this.row.fadeIn();\n }\n \n // row\n this.row = this._row();\n \n // row count\n this.row_count++;\n this._refresh();\n },\n \n // create new row\n _row: function() {\n return $(' ');\n },\n \n // onclick\n _onclick: function(e) {\n // get values\n var old_value = this.value();\n var new_value = $(e.target).closest('tr').attr('id');\n if (new_value != ''){\n // check equality\n if (new_value && old_value != new_value) {\n if (this.options.onconfirm) {\n this.options.onconfirm(new_value);\n } else {\n this.value(new_value);\n }\n }\n }\n },\n\n // ondblclick\n _ondblclick: function(e) {\n var value = this.value();\n if (value && this.options.ondblclick) {\n this.options.ondblclick(value);\n }\n },\n \n // refresh\n _refresh: function() {\n if (this.row_count == 0) {\n this.$tmessage.show();\n } else {\n this.$tmessage.hide();\n }\n },\n \n // load html template\n _template: function(options) {\n return ' ' +\n '' +\n '' +\n '
' +\n '
';\n helpString += data.help;\n } else {\n helpString += 'Tool help is unavailable for this dataset.
';\n }\n helpString += '
\" target=\"<%- view.linkTarget %>\">',\n _l( 'Set it manually or retry auto-detection' ),\n '',\n '
', _l( 'Undelete it' ), '',\n '<% if( view.purgeAllowed ){ %>',\n '
',\n _l( 'Permanently remove it from disk' ),\n '',\n '<% } %>',\n '',\n '<% _.each( visualizations, function( visualization ){ %>',\n '
',\n '', '
',\n '', this._templateOptions( options ), '
'\n ].join( '' );\n },\n\n _templateOptions : function( options ){\n if( !options.length ){\n return '' +\n ' ';\n },\n\n /** Template of table */\n _templateTable: function() {\n return 'Available files: ' +\n '' +\n '' +\n ' ' +\n '' +\n '' +\n '' +\n '' +\n ' ' + _.escape(options.path) + ' ' +\n '' + Utils.bytesToString( options.size ) + ' ' +\n '' + options.ctime + ' ' +\n '' +\n '' +\n '
';\n },\n\n /** Template of info message */\n _templateInfo: function() {\n return '' +\n ' ' +\n '' +\n '' +\n '' +\n ' Name ' +\n 'Size ' +\n 'Created ' +\n '' ).addClass( 'upload-settings-table ui-table-striped' ).append( '' ) );\n this.$cover = this.$( '.upload-settings-cover' );\n this.$table = this.$( '.upload-settings-table > tbody' );\n this.listenTo ( this.model, 'change', this.render, this );\n this.model.trigger( 'change' );\n },\n\n render: function() {\n var self = this;\n this.$table.empty();\n _.each( this.options.parameters, function( parameter ) {\n var $checkbox = $( '' ).addClass( 'upload-' + parameter.id + ' upload-icon-button fa' )\n .addClass( self.model.get( parameter.id ) && self.options.class_check || self.options.class_uncheck )\n .on( 'click', function() {\n self.model.get( 'enabled' ) && self.model.set( parameter.id, !self.model.get( parameter.id ) )\n });\n self.$table.append( $( '
' ).append( $( ' ' ).append( $checkbox ) )\n .append( $( ' ' ).append( parameter.title ) ) )\n });\n this.$cover[ this.model.get( 'enabled' ) && 'hide' || 'show' ]();\n }\n });\n});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/upload/upload-settings.js\n// module id = 82\n// module chunks = 3","(function (factory) {\n if (typeof define === 'function' && define.amd) {\n define([], factory);\n } else {\n // Browser globals\n factory(jQuery);\n }\n\n}(function () {\n//=============================================================================\n\n jQuery.fn.extend({\n hoverhighlight : function $hoverhighlight( scope, color ){\n scope = scope || 'body';\n if( !this.length ){ return this; }\n\n $( this ).each( function(){\n var $this = $( this ),\n targetSelector = $this.data( 'target' );\n\n if( targetSelector ){\n $this.mouseover( function( ev ){\n $( targetSelector, scope ).css({\n background: color\n });\n })\n .mouseout( function( ev ){\n $( targetSelector ).css({\n background: ''\n });\n });\n }\n });\n return this;\n }\n });\n}));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/ui/hoverhighlight.js\n// module id = 85\n// module chunks = 3","// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js\n// Uses AMD or browser globals to create a jQuery plugin.\n(function (factory) {\n if (typeof define === 'function' && define.amd) {\n //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will\n // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still\n // relying on jquery being loaded globally* in order to preserve plugins.\n define([], factory);\n } else {\n // Browser globals\n factory(jQuery);\n }\n\n}(function () {\n var _l = window._l || function( s ){ return s; };\n\n //TODO: consolidate with tool menu functionality, use there\n\n /** searchInput: (jQuery plugin)\n * Creates a search input, a clear button, and loading indicator\n * within the selected node.\n *\n * When the user either presses return or enters some minimal number\n * of characters, a callback is called. Pressing ESC when the input\n * is focused will clear the input and call a separate callback.\n */\n function searchInput( parentNode, options ){\n var KEYCODE_ESC = 27,\n KEYCODE_RETURN = 13,\n $parentNode = $( parentNode ),\n firstSearch = true,\n defaults = {\n initialVal : '',\n name : 'search',\n placeholder : 'search',\n classes : '',\n onclear : function(){},\n onfirstsearch : null,\n onsearch : function( inputVal ){},\n minSearchLen : 0,\n escWillClear : true,\n oninit : function(){}\n };\n\n // .................................................................... input rendering and events\n // visually clear the search, trigger an event, and call the callback\n function clearSearchInput( event ){\n var $input = $( this ).parent().children( 'input' );\n $input.val( '' ).trigger( 'searchInput.clear' ).blur();\n options.onclear();\n }\n\n // search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)\n function search( event, searchTerms ){\n if( !searchTerms ){\n return clearSearchInput();\n }\n $( this ).trigger( 'search.search', searchTerms );\n if( typeof options.onfirstsearch === 'function' && firstSearch ){\n firstSearch = false;\n options.onfirstsearch( searchTerms );\n } else {\n options.onsearch( searchTerms );\n }\n }\n\n // .................................................................... input rendering and events\n function inputTemplate(){\n // class search-query is bootstrap 2.3 style that now lives in base.less\n return [ '' ].join( '' );\n }\n\n // the search input that responds to keyboard events and displays the search value\n function $input(){\n return $( inputTemplate() )\n // select all text on a focus\n .focus( function( event ){\n $( this ).select();\n })\n // attach behaviors to esc, return if desired, search on some min len string\n .keyup( function( event ){\n event.preventDefault();\n event.stopPropagation();\n\n // esc key will clear if desired\n if( event.which === KEYCODE_ESC && options.escWillClear ){\n clearSearchInput.call( this, event );\n\n } else {\n var searchTerms = $( this ).val();\n // return key or the search string len > minSearchLen (if not 0) triggers search\n if( ( event.which === KEYCODE_RETURN )\n || ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){\n search.call( this, event, searchTerms );\n }\n }\n })\n .val( options.initialVal );\n }\n\n // .................................................................... clear button rendering and events\n // a button for clearing the search bar, placed on the right hand side\n function $clearBtn(){\n return $([ '' ].join('') )\n .tooltip({ placement: 'bottom' })\n .click( function( event ){\n clearSearchInput.call( this, event );\n });\n }\n\n // .................................................................... loadingIndicator rendering\n // a button for clearing the search bar, placed on the right hand side\n function $loadingIndicator(){\n return $([ '' ].join('') )\n .hide().tooltip({ placement: 'bottom' });\n }\n\n // .................................................................... commands\n // visually swap the load, clear buttons\n function toggleLoadingIndicator(){\n $parentNode.find( '.search-loading' ).toggle();\n $parentNode.find( '.search-clear' ).toggle();\n }\n\n // .................................................................... init\n // string command (not constructor)\n if( jQuery.type( options ) === 'string' ){\n if( options === 'toggle-loading' ){\n toggleLoadingIndicator();\n }\n return $parentNode;\n }\n\n // initial render\n if( jQuery.type( options ) === 'object' ){\n options = jQuery.extend( true, {}, defaults, options );\n }\n //NOTE: prepended\n return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);\n }\n\n // as jq plugin\n jQuery.fn.extend({\n searchInput : function $searchInput( options ){\n return this.each( function(){\n return searchInput( this, options );\n });\n }\n });\n}));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/ui/search-input.js\n// module id = 86\n// module chunks = 3","define([], function(){\n// Alphanumeric/natural sort fn\nfunction naturalSort(a, b) {\n // setup temp-scope variables for comparison evauluation\n var re = /(-?[0-9\\.]+)/g,\n x = a.toString().toLowerCase() || '',\n y = b.toString().toLowerCase() || '',\n nC = String.fromCharCode(0),\n xN = x.replace( re, nC + '$1' + nC ).split(nC),\n yN = y.replace( re, nC + '$1' + nC ).split(nC),\n xD = (new Date(x)).getTime(),\n yD = xD ? (new Date(y)).getTime() : null;\n // natural sorting of dates\n if ( yD ) {\n if ( xD < yD ) { return -1; }\n else if ( xD > yD ) { return 1; }\n }\n // natural sorting through split numeric strings and default strings\n var oFxNcL, oFyNcL;\n for ( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {\n oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];\n oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];\n if (oFxNcL < oFyNcL) { return -1; }\n else if (oFxNcL > oFyNcL) { return 1; }\n }\n return 0;\n}\n\nreturn naturalSort;\n})\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/utils/natural-sort.js\n// module id = 88\n// module chunks = 3","/*\n galaxy upload plugins - requires FormData and XMLHttpRequest\n*/\n;(function($){\n // add event properties\n jQuery.event.props.push(\"dataTransfer\");\n\n /**\n Posts file data to the API\n */\n $.uploadpost = function (config) {\n // parse options\n var cnf = $.extend({}, {\n data : {},\n success : function() {},\n error : function() {},\n progress : function() {},\n url : null,\n maxfilesize : 2048,\n error_filesize : 'File exceeds 2GB. Please use a FTP client.',\n error_default : 'Please make sure the file is available.',\n error_server : 'Upload request failed.',\n error_login : 'Uploads require you to log in.'\n }, config);\n\n // link data\n var data = cnf.data;\n\n // check errors\n if (data.error_message) {\n cnf.error(data.error_message);\n return;\n }\n\n // construct form data\n var form = new FormData();\n for (var key in data.payload) {\n form.append(key, data.payload[key]);\n }\n\n // add files to submission\n var sizes = 0;\n for (var key in data.files) {\n var d = data.files[key];\n form.append(d.name, d.file, d.file.name);\n sizes += d.file.size;\n }\n\n // check file size, unless it's an ftp file\n if (sizes > 1048576 * cnf.maxfilesize) {\n cnf.error(cnf.error_filesize);\n return;\n }\n\n // prepare request\n xhr = new XMLHttpRequest();\n xhr.open('POST', cnf.url, true);\n xhr.setRequestHeader('Accept', 'application/json');\n xhr.setRequestHeader('Cache-Control', 'no-cache');\n xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\n // captures state changes\n xhr.onreadystatechange = function() {\n // check for request completed, server connection closed\n if (xhr.readyState == xhr.DONE) {\n // parse response\n var response = null;\n if (xhr.responseText) {\n try {\n response = jQuery.parseJSON(xhr.responseText);\n } catch (e) {\n response = xhr.responseText;\n }\n }\n // pass any error to the error option\n if (xhr.status < 200 || xhr.status > 299) {\n var text = xhr.statusText;\n if (xhr.status == 403) {\n text = cnf.error_login;\n } else if (xhr.status == 0) {\n text = cnf.error_server;\n } else if (!text) {\n text = cnf.error_default;\n }\n cnf.error(text + ' (' + xhr.status + ')');\n } else {\n cnf.success(response);\n }\n }\n }\n\n // prepare upload progress\n xhr.upload.addEventListener('progress', function(e) {\n if (e.lengthComputable) {\n cnf.progress(Math.round((e.loaded * 100) / e.total));\n }\n }, false);\n\n // send request\n Galaxy.emit.debug('uploadbox::uploadpost()', 'Posting following data.', cnf);\n xhr.send(form);\n }\n\n /**\n Handles the upload events drag/drop etc.\n */\n $.fn.uploadinput = function(options) {\n // initialize\n var el = this;\n var opts = $.extend({}, {\n ondragover : function() {},\n ondragleave : function() {},\n onchange : function() {},\n multiple : false\n }, options);\n\n // append hidden upload field\n var $input = $('');\n el.append($input.change(function (e) {\n opts.onchange(e.target.files);\n $(this).val('');\n }));\n\n // drag/drop events\n el.on('drop', function (e) {\n opts.ondragleave(e);\n if(e.dataTransfer) {\n opts.onchange(e.dataTransfer.files);\n e.preventDefault();\n }\n });\n el.on('dragover', function (e) {\n e.preventDefault();\n opts.ondragover(e);\n });\n el.on('dragleave', function (e) {\n e.stopPropagation();\n opts.ondragleave(e);\n });\n\n // exports\n return {\n dialog: function () {\n $input.trigger('click');\n }\n }\n }\n\n /**\n Handles the upload queue and events such as drag/drop etc.\n */\n $.fn.uploadbox = function(options) {\n // parse options\n var opts = $.extend({}, {\n dragover : function() {},\n dragleave : function() {},\n announce : function(d) {},\n initialize : function(d) {},\n progress : function(d, m) {},\n success : function(d, m) {},\n error : function(d, m) { alert(m); },\n complete : function() {}\n }, options);\n\n // file queue\n var queue = {};\n\n // queue index/length counter\n var queue_index = 0;\n var queue_length = 0;\n\n // indicates if queue is currently running\n var queue_running = false;\n var queue_stop = false;\n\n // element\n var uploadinput = $(this).uploadinput({\n multiple : true,\n onchange : function(files) { add(files); },\n ondragover : options.ondragover,\n ondragleave : options.ondragleave\n });\n\n // add new files to upload queue\n function add(files) {\n if (files && files.length && !queue_running) {\n var current_index = queue_index;\n _.each(files, function(file, key) {\n if (file.mode !== 'new' && _.filter(queue, function(f) {\n return f.name === file.name && f.size === file.size;\n }).length) {\n file.duplicate = true;\n }\n });\n _.each(files, function(file) {\n if (!file.duplicate) {\n var index = String(queue_index++);\n queue[index] = file;\n opts.announce(index, queue[index]);\n queue_length++;\n }\n });\n return current_index;\n }\n }\n\n // remove file from queue\n function remove(index) {\n if (queue[index]) {\n delete queue[index];\n queue_length--;\n }\n }\n\n // process an upload, recursive\n function process() {\n // validate\n if (queue_length == 0 || queue_stop) {\n queue_stop = false;\n queue_running = false;\n opts.complete();\n return;\n } else {\n queue_running = true;\n }\n\n // get an identifier from the queue\n var index = -1;\n for (var key in queue) {\n index = key;\n break;\n }\n\n // get current file from queue\n var file = queue[index];\n\n // remove from queue\n remove(index)\n\n // create and submit data\n $.uploadpost({\n url : opts.url,\n data : opts.initialize(index),\n success : function(message) { opts.success(index, message); process();},\n error : function(message) { opts.error(index, message); process();},\n progress : function(percentage) { opts.progress(index, percentage); }\n });\n }\n\n /*\n public interface\n */\n\n // open file browser for selection\n function select() {\n uploadinput.dialog();\n }\n\n // remove all entries from queue\n function reset(index) {\n for (index in queue) {\n remove(index);\n }\n }\n\n // initiate upload process\n function start() {\n if (!queue_running) {\n queue_running = true;\n process();\n }\n }\n\n // stop upload process\n function stop() {\n queue_stop = true;\n }\n\n // set options\n function configure(options) {\n opts = $.extend({}, opts, options);\n return opts;\n }\n\n // verify browser compatibility\n function compatible() {\n return window.File && window.FormData && window.XMLHttpRequest && window.FileList;\n }\n\n // export functions\n return {\n 'select' : select,\n 'add' : add,\n 'remove' : remove,\n 'start' : start,\n 'stop' : stop,\n 'reset' : reset,\n 'configure' : configure,\n 'compatible' : compatible\n };\n }\n})(jQuery);\n\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/utils/uploadbox.js\n// module id = 89\n// module chunks = 3","var RightPanel = require( 'layout/panel' ).RightPanel,\n Ui = require( 'mvc/ui/ui-misc' ),\n historyOptionsMenu = require( 'mvc/history/options-menu' );\n CurrentHistoryView = require( 'mvc/history/history-view-edit-current' ).CurrentHistoryView,\n _l = require( 'utils/localization' );\n\n/** the right hand panel in the analysis page that shows the current history */\nvar HistoryPanel = RightPanel.extend({\n\n title : _l( 'History' ),\n\n initialize : function( options ){\n RightPanel.prototype.initialize.call( this, options );\n this.options = _.pick( options, 'userIsAnonymous', 'allow_user_dataset_purge', 'galaxyRoot' );\n\n // view of the current history\n this.historyView = new CurrentHistoryView({\n className : CurrentHistoryView.prototype.className + ' middle',\n purgeAllowed : options.allow_user_dataset_purge,\n linkTarget : 'galaxy_main'\n });\n },\n\n /** override to change footer selector */\n $toggleButton : function(){\n return this.$( '.footer > .panel-collapse' );\n },\n\n render : function(){\n RightPanel.prototype.render.call( this );\n this.optionsMenu = historyOptionsMenu( this.$( '#history-options-button' ), {\n anonymous : this.options.userIsAnonymous,\n purgeAllowed : this.options.allow_user_dataset_purge,\n root : this.options.galaxyRoot\n });\n this.$( '> .header .buttons [title]' ).tooltip({ placement: 'bottom' });\n this.historyView.setElement( this.$( '.history-panel' ) );\n this.$el.attr( 'class', 'history-right-panel' );\n },\n\n /** override to add buttons */\n _templateHeader: function( data ){\n var historyUrl = this.options.galaxyRoot + 'history';\n var multiUrl = this.options.galaxyRoot + 'history/view_multiple';\n return [\n ' ' + JSON.stringify( xhr.responseJSON ) + '
';\n } else {\n content += ': ' + message;\n }\n }\n creator._showAlert( content, 'alert-danger' );\n },\n\n // ------------------------------------------------------------------------ rendering\n /** render the entire interface */\n render : function( speed, callback ){\n //this.debug( '-- _render' );\n //this.$el.empty().html( PairedCollectionCreator.templates.main() );\n this.$el.empty().html( PairedCollectionCreator.templates.main() );\n this._renderHeader( speed );\n this._renderMiddle( speed );\n this._renderFooter( speed );\n this._addPluginComponents();\n this.trigger( 'rendered', this );\n return this;\n },\n\n /** render the header section */\n _renderHeader : function( speed, callback ){\n //this.debug( '-- _renderHeader' );\n var $header = this.$( '.header' ).empty().html( PairedCollectionCreator.templates.header() )\n .find( '.help-content' ).prepend( $( PairedCollectionCreator.templates.helpContent() ) );\n\n this._renderFilters();\n return $header;\n },\n /** fill the filter inputs with the filter values */\n _renderFilters : function(){\n return this.$( '.forward-column .column-header input' ).val( this.filters[0] )\n .add( this.$( '.reverse-column .column-header input' ).val( this.filters[1] ) );\n },\n\n /** render the middle including unpaired and paired sections (which may be hidden) */\n _renderMiddle : function( speed, callback ){\n var $middle = this.$( '.middle' ).empty().html( PairedCollectionCreator.templates.middle() );\n\n // (re-) hide the un/paired panels based on instance vars\n if( this.unpairedPanelHidden ){\n this.$( '.unpaired-columns' ).hide();\n } else if( this.pairedPanelHidden ){\n this.$( '.paired-columns' ).hide();\n }\n\n this._renderUnpaired();\n this._renderPaired();\n return $middle;\n },\n /** render the unpaired section, showing datasets accrd. to filters, update the unpaired counts */\n _renderUnpaired : function( speed, callback ){\n //this.debug( '-- _renderUnpaired' );\n var creator = this,\n $fwd, $rev, $prd = [],\n split = this._splitByFilters();\n // update unpaired counts\n this.$( '.forward-column .title' )\n .text([ split[0].length, _l( 'unpaired forward' ) ].join( ' ' ));\n this.$( '.forward-column .unpaired-info' )\n .text( this._renderUnpairedDisplayStr( this.unpaired.length - split[0].length ) );\n this.$( '.reverse-column .title' )\n .text([ split[1].length, _l( 'unpaired reverse' ) ].join( ' ' ));\n this.$( '.reverse-column .unpaired-info' )\n .text( this._renderUnpairedDisplayStr( this.unpaired.length - split[1].length ) );\n\n this.$( '.unpaired-columns .column-datasets' ).empty();\n\n // show/hide the auto pair button if any unpaired are left\n this.$( '.autopair-link' ).toggle( this.unpaired.length !== 0 );\n if( this.unpaired.length === 0 ){\n this._renderUnpairedEmpty();\n return;\n }\n\n // create the dataset dom arrays\n $rev = split[1].map( function( dataset, i ){\n // if there'll be a fwd dataset across the way, add a button to pair the row\n if( ( split[0][ i ] !== undefined )\n && ( split[0][ i ] !== dataset ) ){\n $prd.push( creator._renderPairButton() );\n }\n return creator._renderUnpairedDataset( dataset );\n });\n $fwd = split[0].map( function( dataset ){\n return creator._renderUnpairedDataset( dataset );\n });\n\n if( !$fwd.length && !$rev.length ){\n this._renderUnpairedNotShown();\n return;\n }\n // add to appropo cols\n //TODO: not the best way to render - consider rendering the entire unpaired-columns section in a fragment\n // and swapping out that\n this.$( '.unpaired-columns .forward-column .column-datasets' ).append( $fwd )\n .add( this.$( '.unpaired-columns .paired-column .column-datasets' ).append( $prd ) )\n .add( this.$( '.unpaired-columns .reverse-column .column-datasets' ).append( $rev ) );\n this._adjUnpairedOnScrollbar();\n },\n /** return a string to display the count of filtered out datasets */\n _renderUnpairedDisplayStr : function( numFiltered ){\n return [ '(', numFiltered, ' ', _l( 'filtered out' ), ')' ].join('');\n },\n /** return an unattached jQuery DOM element to represent an unpaired dataset */\n _renderUnpairedDataset : function( dataset ){\n //TODO: to underscore template\n return $( '')\n .attr( 'id', 'dataset-' + dataset.id )\n .addClass( 'dataset unpaired' )\n .attr( 'draggable', true )\n .addClass( dataset.selected? 'selected': '' )\n .append( $( '' ).addClass( 'dataset-name' ).text( dataset.name ) )\n //??\n .data( 'dataset', dataset );\n },\n /** render the button that may go between unpaired datasets, allowing the user to pair a row */\n _renderPairButton : function(){\n //TODO: *not* a dataset - don't pretend like it is\n return $( '').addClass( 'dataset unpaired' )\n .append( $( '' ).addClass( 'dataset-name' ).text( _l( 'Pair these datasets' ) ) );\n },\n /** a message to display when no unpaired left */\n _renderUnpairedEmpty : function(){\n //this.debug( '-- renderUnpairedEmpty' );\n var $msg = $( '' )\n .text( '(' + _l( 'no remaining unpaired datasets' ) + ')' );\n this.$( '.unpaired-columns .paired-column .column-datasets' ).empty().prepend( $msg );\n return $msg;\n },\n /** a message to display when no unpaired can be shown with the current filters */\n _renderUnpairedNotShown : function(){\n //this.debug( '-- renderUnpairedEmpty' );\n var $msg = $( '' )\n .text( '(' + _l( 'no datasets were found matching the current filters' ) + ')' );\n this.$( '.unpaired-columns .paired-column .column-datasets' ).empty().prepend( $msg );\n return $msg;\n },\n /** try to detect if the unpaired section has a scrollbar and adjust left column for better centering of all */\n _adjUnpairedOnScrollbar : function(){\n var $unpairedColumns = this.$( '.unpaired-columns' ).last(),\n $firstDataset = this.$( '.unpaired-columns .reverse-column .dataset' ).first();\n if( !$firstDataset.length ){ return; }\n var ucRight = $unpairedColumns.offset().left + $unpairedColumns.outerWidth(),\n dsRight = $firstDataset.offset().left + $firstDataset.outerWidth(),\n rightDiff = Math.floor( ucRight ) - Math.floor( dsRight );\n //this.debug( 'rightDiff:', ucRight, '-', dsRight, '=', rightDiff );\n this.$( '.unpaired-columns .forward-column' )\n .css( 'margin-left', ( rightDiff > 0 )? rightDiff: 0 );\n },\n\n /** render the paired section and update counts of paired datasets */\n _renderPaired : function( speed, callback ){\n //this.debug( '-- _renderPaired' );\n this.$( '.paired-column-title .title' ).text([ this.paired.length, _l( 'paired' ) ].join( ' ' ) );\n // show/hide the unpair all link\n this.$( '.unpair-all-link' ).toggle( this.paired.length !== 0 );\n if( this.paired.length === 0 ){\n this._renderPairedEmpty();\n return;\n //TODO: would be best to return here (the $columns)\n } else {\n // show/hide 'remove extensions link' when any paired and they seem to have extensions\n this.$( '.remove-extensions-link' ).show();\n }\n\n this.$( '.paired-columns .column-datasets' ).empty();\n var creator = this;\n this.paired.forEach( function( pair, i ){\n //TODO: cache these?\n var pairView = new PairView({ pair: pair });\n creator.$( '.paired-columns .column-datasets' )\n .append( pairView.render().$el )\n .append([\n ''\n ].join( '' ));\n });\n },\n /** a message to display when none paired */\n _renderPairedEmpty : function(){\n var $msg = $( '' )\n .text( '(' + _l( 'no paired datasets yet' ) + ')' );\n this.$( '.paired-columns .column-datasets' ).empty().prepend( $msg );\n return $msg;\n },\n\n /** render the footer, completion controls, and cancel controls */\n _renderFooter : function( speed, callback ){\n var $footer = this.$( '.footer' ).empty().html( PairedCollectionCreator.templates.footer() );\n this.$( '.remove-extensions' ).prop( 'checked', this.removeExtensions );\n if( typeof this.oncancel === 'function' ){\n this.$( '.cancel-create.btn' ).show();\n }\n return $footer;\n },\n\n /** add any jQuery/bootstrap/custom plugins to elements rendered */\n _addPluginComponents : function(){\n this._chooseFiltersPopover( '.choose-filters-link' );\n this.$( '.help-content i' ).hoverhighlight( '.collection-creator', 'rgba( 64, 255, 255, 1.0 )' );\n },\n\n /** build a filter selection popover allowing selection of common filter pairs */\n _chooseFiltersPopover : function( selector ){\n function filterChoice( val1, val2 ){\n return [\n ''\n ].join('');\n }\n var $popoverContent = $( _.template([\n '
',\n '',\n _l( 'Auto-pair' ),\n '',\n '',\n '
',\n '
',\n '
',\n '
To pair datasets, you can:',\n '
'\n ].join( '' )), '',\n 'You can filter what is shown in the unpaired sections by:',\n '
'\n ].join( '' )), '',\n '',\n '
',\n '<% _.each( problems, function( problem ){ %>',\n '
',\n '<% } else if( _.size( elements ) === 0 ){ %>',\n _l( 'No datasets were selected' ), '.',\n '<% } else if( _.size( elements ) === 1 ){ %>',\n _l( 'Only one dataset was selected' ), ': <%- elements[0].name %>',\n '<% } else if( _.size( elements ) > 2 ){ %>',\n _l( 'Too many datasets were selected' ),\n ': <%- _.pluck( elements, \"name\" ).join( \", \") %>',\n '<% } %>',\n '
',\n _l( 'Two (and only two) elements are needed for the pair' ), '. ',\n _l( 'You may need to ' ),\n '', _l( 'cancel' ), ' ',\n _l( 'and reselect new elements' ), '.',\n '',\n '
', _l( 'Unhide it' ), '',\n '',\n '',\n '
' ).addClass( 'tab-navigation nav nav-tabs' ) )\n .append( $( '' ).addClass( 'tab-content' ) );\n },\n\n /** Tab template */\n _template_tab: function( options ) {\n var $tmpl = $( '' ).addClass( 'tab-element' )\n .attr( 'id', 'tab-' + options.id )\n .append( $( '' ).attr( 'id', 'tab-title-link-' + options.id ) );\n var $href = $tmpl.find( 'a' );\n options.icon && $href.append( $( '' ).addClass( 'tab-icon fa' ).addClass( options.icon ) );\n $href.append( $( '' ).attr( 'id', 'tab-title-text-' + options.id )\n .addClass( 'tab-title-text' )\n .append( options.title ) );\n return $tmpl;\n }\n });\n\n return { View : View }\n});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/ui/ui-tabs.js\n// module id = 118\n// module chunks = 3","/** Renders the composite upload row view */\ndefine([ 'utils/utils', 'mvc/upload/upload-settings', 'mvc/upload/upload-ftp', 'mvc/ui/ui-popover', 'mvc/ui/ui-misc', 'mvc/ui/ui-select', 'utils/uploadbox' ],\nfunction( Utils, UploadSettings, UploadFtp, Popover, Ui, Select ) {\n return Backbone.View.extend({\n /** Dictionary of upload states and associated icons */\n status_classes : {\n init : 'upload-mode fa fa-exclamation text-primary',\n ready : 'upload-mode fa fa-check text-success',\n running : 'upload-mode fa fa-spinner fa-spin',\n success : 'upload-mode fa fa-check',\n error : 'upload-mode fa fa-exclamation-triangle'\n },\n\n initialize: function( app, options ) {\n var self = this;\n this.app = app;\n this.model = options.model;\n this.setElement( this._template() );\n this.$source = this.$( '.upload-source' );\n this.$settings = this.$( '.upload-settings' );\n this.$status = this.$( '.upload-status' );\n this.$text = this.$( '.upload-text' );\n this.$text_content = this.$( '.upload-text-content' );\n this.$info_text = this.$( '.upload-info-text' );\n this.$info_progress = this.$( '.upload-info-progress' );\n this.$file_name = this.$( '.upload-file-name' );\n this.$file_desc = this.$( '.upload-file-desc' );\n this.$file_size = this.$( '.upload-file-size' );\n this.$progress_bar = this.$( '.upload-progress-bar' );\n this.$percentage = this.$( '.upload-percentage' );\n\n // build upload functions\n this.uploadinput = this.$el.uploadinput({\n ondragover: function() {\n self.model.get( 'enabled' ) && self.$el.addClass( 'warning' );\n },\n ondragleave: function() {\n self.$el.removeClass( 'warning' );\n },\n onchange: function( files ) {\n if ( self.model.get( 'status' ) != 'running' && files && files.length > 0 ) {\n self.model.reset({\n 'file_data': files[ 0 ],\n 'file_name': files[ 0 ].name,\n 'file_size': files[ 0 ].size,\n 'file_mode': files[ 0 ].mode || 'local'\n });\n self._refreshReady();\n }\n }\n });\n\n // source selection popup\n this.button_menu = new Ui.ButtonMenu({\n icon : 'fa-caret-down',\n title : 'Select',\n pull : 'left'\n });\n this.$source.append( this.button_menu.$el );\n this.button_menu.addMenu({\n icon : 'fa-laptop',\n title : 'Choose local file',\n onclick : function() { self.uploadinput.dialog() }\n });\n if ( this.app.ftp_upload_site ) {\n this.button_menu.addMenu({\n icon : 'fa-folder-open-o',\n title : 'Choose FTP file',\n onclick : function() { self._showFtp() }\n });\n }\n this.button_menu.addMenu({\n icon : 'fa-edit',\n title : 'Paste/Fetch data',\n onclick : function() {\n self.model.reset( { 'file_mode': 'new', 'file_name': 'New File' } );\n }\n });\n\n // add ftp file viewer\n this.ftp = new Popover.View({\n title : 'Choose FTP file:',\n container : this.$source.find( '.ui-button-menu' ),\n placement : 'right'\n });\n\n // append popup to settings icon\n this.settings = new Popover.View({\n title : 'Upload configuration',\n container : this.$settings,\n placement : 'bottom'\n });\n\n // handle text editing event\n this.$text_content.on( 'change input', function( e ) {\n self.model.set( { 'url_paste': $( e.target ).val(),\n 'file_size': $( e.target ).val().length } );\n self._refreshReady();\n });\n\n // handle settings popover\n this.$settings.on( 'click', function( e ) { self._showSettings() } )\n .on( 'mousedown', function( e ) { e.preventDefault() } );\n\n // model events\n this.listenTo( this.model, 'change:percentage', function() { self._refreshPercentage() } );\n this.listenTo( this.model, 'change:status', function() { self._refreshStatus() } );\n this.listenTo( this.model, 'change:info', function() { self._refreshInfo() } );\n this.listenTo( this.model, 'change:file_name', function() { self._refreshFileName() } );\n this.listenTo( this.model, 'change:file_mode', function() { self._refreshMode() } );\n this.listenTo( this.model, 'change:file_size', function() { self._refreshFileSize() } );\n this.listenTo( this.model, 'remove', function() { self.remove() } );\n this.app.collection.on( 'reset', function() { self.remove() } );\n },\n\n render: function() {\n this.$el.attr( 'id', 'upload-row-' + this.model.id );\n this.$file_name.html( _.escape( this.model.get( 'file_name' ) || '-' ) );\n this.$file_desc.html( this.model.get( 'file_desc' ) || 'Unavailable' );\n this.$file_size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );\n this.$status.removeClass().addClass( this.status_classes.init );\n },\n\n /** Remove view */\n remove: function() {\n // call the base class remove method\n Backbone.View.prototype.remove.apply( this );\n },\n\n //\n // handle model events\n //\n\n /** Refresh ready or not states */\n _refreshReady: function() {\n this.app.collection.each( function( model ) {\n model.set( 'status', ( model.get( 'file_size' ) > 0 ) && 'ready' || 'init' );\n });\n },\n\n /** Refresh mode and e.g. show/hide textarea field */\n _refreshMode: function() {\n var file_mode = this.model.get( 'file_mode' );\n if ( file_mode == 'new' ) {\n this.height = this.$el.height();\n this.$text.css( { 'width' : this.$el.width() - 16 + 'px',\n 'top' : this.$el.height() - 8 + 'px' } ).show();\n this.$el.height( this.$el.height() - 8 + this.$text.height() + 16 );\n this.$text_content.val( '' ).trigger( 'keyup' );\n } else {\n this.$el.height( this.height );\n this.$text.hide();\n }\n },\n\n /** Refresh information */\n _refreshInfo: function() {\n var info = this.model.get( 'info' );\n if ( info ) {\n this.$info_text.html( 'Failed: ' + info ).show();\n } else {\n this.$info_text.hide();\n }\n },\n\n /** Refresh percentage */\n _refreshPercentage : function() {\n var percentage = parseInt( this.model.get( 'percentage' ) );\n if ( percentage != 0 ) {\n this.$progress_bar.css( { width : percentage + '%' } );\n } else {\n this.$progress_bar.addClass( 'no-transition' );\n this.$progress_bar.css( { width : '0%' } );\n this.$progress_bar[ 0 ].offsetHeight;\n this.$progress_bar.removeClass( 'no-transition' );\n }\n this.$percentage.html( percentage != 100 ? percentage + '%' : 'Adding to history...' );\n },\n\n /** Refresh status */\n _refreshStatus : function() {\n var status = this.model.get( 'status' );\n this.$status.removeClass().addClass( this.status_classes[ status ] );\n this.model.set( 'enabled', status != 'running' );\n this.$text_content.attr( 'disabled', !this.model.get( 'enabled' ) );\n this.$el.removeClass( 'success danger warning' );\n if ( status == 'running' || status == 'ready' ) {\n this.model.set( 'percentage', 0 );\n }\n this.$source.find( '.button' )[ status == 'running' ? 'addClass' : 'removeClass' ]( 'disabled' );\n if ( status == 'success' ) {\n this.$el.addClass( 'success' );\n this.model.set( 'percentage', 100 );\n this.$percentage.html( '100%' );\n }\n if ( status == 'error' ) {\n this.$el.addClass( 'danger' );\n this.model.set( 'percentage', 0 );\n this.$info_progress.hide();\n this.$info_text.show();\n } else {\n this.$info_progress.show();\n this.$info_text.hide();\n }\n },\n\n /** File name */\n _refreshFileName: function() {\n this.$file_name.html( this.model.get( 'file_name' ) || '-' );\n },\n\n /** File size */\n _refreshFileSize: function() {\n this.$file_size.html( Utils.bytesToString ( this.model.get( 'file_size' ) ) );\n },\n\n /** Show/hide ftp popup */\n _showFtp: function() {\n if ( !this.ftp.visible ) {\n var self = this;\n this.ftp.empty();\n this.ftp.append( ( new UploadFtp( {\n ftp_upload_site : this.app.ftp_upload_site,\n onchange : function( ftp_file ) {\n self.ftp.hide();\n if ( self.model.get( 'status' ) != 'running' && ftp_file ) {\n self.model.reset({\n 'file_mode': 'ftp',\n 'file_name': ftp_file.path,\n 'file_size': ftp_file.size,\n 'file_path': ftp_file.path\n });\n self._refreshReady();\n }\n }\n } ) ).$el );\n this.ftp.show();\n } else {\n this.ftp.hide();\n }\n },\n\n /** Show/hide settings popup */\n _showSettings : function() {\n if ( !this.settings.visible ) {\n this.settings.empty();\n this.settings.append( ( new UploadSettings( this ) ).$el );\n this.settings.show();\n } else {\n this.settings.hide();\n }\n },\n\n /** Template */\n _template: function() {\n return '
' +\n ' ';\n }\n });\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/upload/composite/composite-row.js\n// module id = 119\n// module chunks = 3","/** Renders contents of the composite uploader */\ndefine([ 'utils/utils', 'mvc/upload/upload-model', 'mvc/upload/composite/composite-row', 'mvc/ui/ui-popover', 'mvc/ui/ui-select', 'mvc/ui/ui-misc'],\nfunction( Utils, UploadModel, UploadRow, Popover, Select, Ui ) {\n return Backbone.View.extend({\n collection: new UploadModel.Collection(),\n initialize: function(app) {\n var self = this;\n this.app = app;\n this.options = app.options;\n this.list_extensions = app.list_extensions;\n this.list_genomes = app.list_genomes;\n this.ftp_upload_site = app.currentFtp();\n this.setElement( this._template() );\n\n // create button section\n this.btnStart = new Ui.Button( { title: 'Start', onclick: function() { self._eventStart() } } );\n this.btnClose = new Ui.Button( { title: 'Close', onclick: function() { self.app.modal.hide() } } );\n\n // append buttons to dom\n _.each( [ this.btnStart, this.btnClose ], function( button ) {\n self.$( '.upload-buttons' ).prepend( button.$el );\n });\n\n // select extension\n this.select_extension = new Select.View({\n css : 'upload-footer-selection',\n container : this.$( '.upload-footer-extension' ),\n data : _.filter( this.list_extensions, function( ext ) { return ext.composite_files } ),\n onchange : function( extension ) {\n self.collection.reset();\n var details = _.findWhere( self.list_extensions, { id : extension } );\n if ( details && details.composite_files ) {\n _.each( details.composite_files, function( item ) {\n self.collection.add( { id : self.collection.size(),\n file_desc : item.description || item.name } );\n } );\n }\n }\n });\n\n // handle extension info popover\n this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {\n self._showExtensionInfo({\n $el : $( e.target ),\n title : self.select_extension.text(),\n extension : self.select_extension.value(),\n placement : 'top'\n });\n }).on( 'mousedown', function( e ) { e.preventDefault() } );\n\n // genome extension\n this.select_genome = new Select.View({\n css : 'upload-footer-selection',\n container : this.$( '.upload-footer-genome' ),\n data : this.list_genomes,\n value : this.options.default_genome\n });\n\n // listener for collection triggers on change in composite datatype and extension selection\n this.listenTo( this.collection, 'add', function ( model ) { self._eventAnnounce( model ) } );\n this.listenTo( this.collection, 'change add', function() { self.render() } );\n this.select_extension.options.onchange( this.select_extension.value() );\n this.render();\n },\n\n render: function () {\n var model = this.collection.first();\n if ( model && model.get( 'status' ) == 'running' ) {\n this.select_genome.disable();\n this.select_extension.disable();\n } else {\n this.select_genome.enable();\n this.select_extension.enable();\n }\n if ( this.collection.where( { status : 'ready' } ).length == this.collection.length && this.collection.length > 0 ) {\n this.btnStart.enable();\n this.btnStart.$el.addClass( 'btn-primary' );\n } else {\n this.btnStart.disable();\n this.btnStart.$el.removeClass( 'btn-primary' );\n }\n this.$( '.upload-table' )[ this.collection.length > 0 ? 'show' : 'hide' ]();\n },\n\n //\n // upload events / process pipeline\n //\n\n /** Builds the basic ui with placeholder rows for each composite data type file */\n _eventAnnounce: function( model ) {\n var upload_row = new UploadRow( this, { model : model } );\n this.$( '.upload-table > tbody:first' ).append( upload_row.$el );\n this.$( '.upload-table' )[ this.collection.length > 0 ? 'show' : 'hide' ]();\n upload_row.render();\n },\n\n /** Start upload process */\n _eventStart: function() {\n var self = this;\n this.collection.each( function( model ) {\n model.set( { 'genome' : self.select_genome.value(),\n 'extension': self.select_extension.value() } );\n });\n $.uploadpost({\n url : this.app.options.nginx_upload_path,\n data : this.app.toData( this.collection.filter() ),\n success : function( message ) { self._eventSuccess( message ) },\n error : function( message ) { self._eventError( message ) },\n progress : function( percentage ) { self._eventProgress( percentage ) }\n });\n },\n\n /** Refresh progress state */\n _eventProgress: function( percentage ) {\n this.collection.each( function( it ) { it.set( 'percentage', percentage ) } );\n },\n\n /** Refresh success state */\n _eventSuccess: function( message ) {\n this.collection.each( function( it ) { it.set('status', 'success') } );\n Galaxy.currHistoryPanel.refreshContents();\n },\n\n /** Refresh error state */\n _eventError: function( message ) {\n this.collection.each( function( it ) { it.set( { 'status': 'error', 'info': message } ) } );\n },\n\n /** Display extension info popup */\n _showExtensionInfo: function(options) {\n var self = this;\n var $el = options.$el;\n var extension = options.extension;\n var title = options.title;\n var description = _.findWhere(this.list_extensions, { id : extension });\n this.extension_popup && this.extension_popup.remove();\n this.extension_popup = new Popover.View({\n placement: options.placement || 'bottom',\n container: $el,\n destroy: true\n });\n this.extension_popup.title( title );\n this.extension_popup.empty();\n this.extension_popup.append( this._templateDescription( description ) );\n this.extension_popup.show();\n },\n\n /* Template for extensions description */\n _templateDescription: function( options ) {\n if ( options.description ) {\n var tmpl = options.description;\n if ( options.description_url ) {\n tmpl += ' (read more)';\n }\n return tmpl;\n } else {\n return 'There is no description available for this file extension.';\n }\n },\n\n /** Load html template */\n _template: function() {\n return '' +\n ' ' +\n '' +\n ' ' +\n '' +\n '' +\n ' ' +\n '' +\n '' +\n ' ' +\n '' +\n '' +\n ' ' +\n '' +\n ' ' +\n ' ' +\n '' +\n '' +\n '
' +\n '' +\n ' ' +\n '' +\n '' +\n '' +\n ' ' +\n ' Description ' +\n 'Name ' +\n 'Size ' +\n 'Settings ' +\n 'Status ' +\n '' +\n ' ';\n }\n });\n});\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./galaxy/scripts/mvc/upload/default/default-row.js\n// module id = 121\n// module chunks = 3","/** Renders contents of the default uploader */\ndefine([ 'utils/utils', 'mvc/upload/upload-model', 'mvc/upload/default/default-row', 'mvc/upload/upload-ftp', 'mvc/ui/ui-popover', 'mvc/ui/ui-select', 'mvc/ui/ui-misc', 'utils/uploadbox'],\nfunction( Utils, UploadModel, UploadRow, UploadFtp, Popover, Select, Ui ) {\n return Backbone.View.extend({\n // current upload size in bytes\n upload_size: 0,\n\n // contains upload row models\n collection : new UploadModel.Collection(),\n\n // keeps track of the current uploader state\n counter : {\n announce : 0,\n success : 0,\n error : 0,\n running : 0,\n reset : function() { this.announce = this.success = this.error = this.running = 0 }\n },\n\n initialize : function( app ) {\n var self = this;\n this.app = app;\n this.options = app.options;\n this.list_extensions = app.list_extensions;\n this.list_genomes = app.list_genomes;\n this.ui_button = app.ui_button;\n this.ftp_upload_site = app.currentFtp();\n this.setElement( this._template() );\n\n // append buttons to dom\n this.btnLocal = new Ui.Button( { id: 'btn-local', title: 'Choose local file', onclick: function() { self.uploadbox.select() }, icon: 'fa fa-laptop' } );\n this.btnFtp = new Ui.Button( { id: 'btn-ftp', title: 'Choose FTP file', onclick: function() { self._eventFtp() }, icon: 'fa fa-folder-open-o' } );\n this.btnCreate = new Ui.Button( { id: 'btn-new', title: 'Paste/Fetch data', onclick: function() { self._eventCreate() }, icon: 'fa fa-edit' } );\n this.btnStart = new Ui.Button( { id: 'btn-start', title: 'Start', onclick: function() { self._eventStart() } } );\n this.btnStop = new Ui.Button( { id: 'btn-stop', title: 'Pause', onclick: function() { self._eventStop() } } );\n this.btnReset = new Ui.Button( { id: 'btn-reset', title: 'Reset', onclick: function() { self._eventReset() } } );\n this.btnClose = new Ui.Button( { id: 'btn-close', title: 'Close', onclick: function() { self.app.modal.hide() } } );\n _.each( [ this.btnLocal, this.btnFtp, this.btnCreate, this.btnStop, this.btnReset, this.btnStart, this.btnClose ], function( button ) {\n self.$( '.upload-buttons' ).prepend( button.$el );\n });\n\n // file upload\n this.uploadbox = this.$( '.upload-box' ).uploadbox({\n url : this.app.options.nginx_upload_path,\n announce : function( index, file ) { self._eventAnnounce( index, file ) },\n initialize : function( index ) { return self.app.toData( [ self.collection.get( index ) ], self.history_id ) },\n progress : function( index, percentage ) { self._eventProgress( index, percentage ) },\n success : function( index, message ) { self._eventSuccess( index, message ) },\n error : function( index, message ) { self._eventError( index, message ) },\n complete : function() { self._eventComplete() },\n ondragover : function() { self.$( '.upload-box' ).addClass( 'highlight' ) },\n ondragleave : function() { self.$( '.upload-box' ).removeClass( 'highlight' ) }\n });\n\n // add ftp file viewer\n this.ftp = new Popover.View( { title: 'FTP files', container: this.btnFtp.$el } );\n\n // select extension\n this.select_extension = new Select.View({\n css : 'upload-footer-selection',\n container : this.$( '.upload-footer-extension' ),\n data : _.filter( this.list_extensions, function( ext ) { return !ext.composite_files } ),\n value : this.options.default_extension,\n onchange : function( extension ) { self.updateExtension( extension ) }\n });\n\n // handle extension info popover\n this.$( '.upload-footer-extension-info' ).on( 'click', function( e ) {\n self.showExtensionInfo({\n $el : $( e.target ),\n title : self.select_extension.text(),\n extension : self.select_extension.value(),\n placement : 'top'\n });\n }).on( 'mousedown', function( e ) { e.preventDefault() } );\n\n // genome extension\n this.select_genome = new Select.View({\n css : 'upload-footer-selection',\n container : this.$( '.upload-footer-genome' ),\n data : this.list_genomes,\n value : this.options.default_genome,\n onchange : function( genome ) { self.updateGenome(genome) }\n });\n\n // events\n this.collection.on( 'remove', function( model ) { self._eventRemove( model ) } );\n this._updateScreen();\n },\n\n /** A new file has been dropped/selected through the uploadbox plugin */\n _eventAnnounce: function( index, file ) {\n this.counter.announce++;\n var new_model = new UploadModel.Model({\n id : index,\n file_name : file.name,\n file_size : file.size,\n file_mode : file.mode || 'local',\n file_path : file.path,\n file_data : file\n });\n this.collection.add( new_model );\n var upload_row = new UploadRow( this, { model: new_model } );\n this.$( '.upload-table > tbody:first' ).append( upload_row.$el );\n this._updateScreen();\n upload_row.render();\n },\n\n /** Progress */\n _eventProgress: function( index, percentage ) {\n var it = this.collection.get( index );\n it.set( 'percentage', percentage );\n this.ui_button.model.set( 'percentage', this._uploadPercentage( percentage, it.get( 'file_size' ) ) );\n },\n\n /** Success */\n _eventSuccess: function( index, message ) {\n var it = this.collection.get( index );\n it.set( { 'percentage': 100, 'status': 'success' } );\n this.ui_button.model.set( 'percentage', this._uploadPercentage( 100, it.get( 'file_size' ) ) );\n this.upload_completed += it.get( 'file_size' ) * 100;\n this.counter.announce--;\n this.counter.success++;\n this._updateScreen();\n Galaxy.currHistoryPanel.refreshContents();\n },\n\n /** Error */\n _eventError: function( index, message ) {\n var it = this.collection.get( index );\n it.set( { 'percentage': 100, 'status': 'error', 'info': message } );\n this.ui_button.model.set( { 'percentage': this._uploadPercentage( 100, it.get( 'file_size' ) ), 'status': 'danger' } );\n this.upload_completed += it.get( 'file_size' ) * 100;\n this.counter.announce--;\n this.counter.error++;\n this._updateScreen();\n },\n\n /** Queue is done */\n _eventComplete: function() {\n this.collection.each( function( model ) { model.get( 'status' ) == 'queued' && model.set( 'status', 'init' ) } );\n this.counter.running = 0;\n this._updateScreen();\n },\n\n /** Remove model from upload list */\n _eventRemove: function( model ) {\n var status = model.get( 'status' );\n if ( status == 'success' ) {\n this.counter.success--;\n } else if ( status == 'error' ) {\n this.counter.error--;\n } else {\n this.counter.announce--;\n }\n this.uploadbox.remove( model.id );\n this._updateScreen();\n },\n\n //\n // events triggered by this view\n //\n\n /** [public] display extension info popup */\n showExtensionInfo: function( options ) {\n var self = this;\n var $el = options.$el;\n var extension = options.extension;\n var title = options.title;\n var description = _.findWhere( self.list_extensions, { 'id': extension } );\n this.extension_popup && this.extension_popup.remove();\n this.extension_popup = new Popover.View({ placement: options.placement || 'bottom', container: $el } );\n this.extension_popup.title( title );\n this.extension_popup.empty();\n this.extension_popup.append( this._templateDescription( description ) );\n this.extension_popup.show();\n },\n\n /** Show/hide ftp popup */\n _eventFtp: function() {\n if ( !this.ftp.visible ) {\n this.ftp.empty();\n var self = this;\n this.ftp.append( ( new UploadFtp({\n collection : this.collection,\n ftp_upload_site : this.ftp_upload_site,\n onadd : function( ftp_file ) {\n self.uploadbox.add([{\n mode: 'ftp',\n name: ftp_file.path,\n size: ftp_file.size,\n path: ftp_file.path\n }]);\n },\n onremove: function( model_index ) {\n self.collection.remove( model_index );\n }\n } ) ).$el );\n this.ftp.show();\n } else {\n this.ftp.hide();\n }\n },\n\n /** Create a new file */\n _eventCreate: function (){\n this.uploadbox.add( [ { name: 'New File', size: 0, mode: 'new' } ] );\n },\n\n /** Start upload process */\n _eventStart: function() {\n if ( this.counter.announce == 0 || this.counter.running > 0 ) {\n return;\n }\n var self = this;\n this.upload_size = 0;\n this.upload_completed = 0;\n this.collection.each( function( model ) {\n if( model.get( 'status' ) == 'init' ) {\n model.set( 'status', 'queued' );\n self.upload_size += model.get( 'file_size' );\n }\n });\n this.ui_button.model.set( { 'percentage': 0, 'status': 'success' } );\n this.counter.running = this.counter.announce;\n this.history_id = this.app.currentHistory();\n this.uploadbox.start();\n this._updateScreen();\n },\n\n /** Pause upload process */\n _eventStop: function() {\n if ( this.counter.running > 0 ) {\n this.ui_button.model.set( 'status', 'info' );\n $( '.upload-top-info' ).html( 'Queue will pause after completing the current file...' );\n this.uploadbox.stop();\n }\n },\n\n /** Remove all */\n _eventReset: function() {\n if ( this.counter.running == 0 ){\n this.collection.reset();\n this.counter.reset();\n this.uploadbox.reset();\n this.select_extension.value( this.options.default_extension );\n this.select_genome.value( this.options.default_genome );\n this.ui_button.model.set( 'percentage', 0 );\n this._updateScreen();\n }\n },\n\n /** Update extension for all models */\n updateExtension: function( extension, defaults_only ) {\n var self = this;\n this.collection.each( function( model ) {\n if ( model.get( 'status' ) == 'init' && ( model.get( 'extension' ) == self.options.default_extension || !defaults_only ) ) {\n model.set( 'extension', extension );\n }\n });\n },\n\n /** Update genome for all models */\n updateGenome: function( genome, defaults_only ) {\n var self = this;\n this.collection.each( function( model ) {\n if ( model.get( 'status' ) == 'init' && ( model.get( 'genome' ) == self.options.default_genome || !defaults_only ) ) {\n model.set( 'genome', genome );\n }\n });\n },\n\n /** Set screen */\n _updateScreen: function () {\n var message = '';\n if( this.counter.announce == 0 ) {\n if (this.uploadbox.compatible()) {\n message = ' ';\n } else {\n message = 'Browser does not support Drag & Drop. Try Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.';\n }\n } else {\n if ( this.counter.running == 0 ) {\n message = 'You added ' + this.counter.announce + ' file(s) to the queue. Add more files or click \\'Start\\' to proceed.';\n } else {\n message = 'Please wait...' + this.counter.announce + ' out of ' + this.counter.running + ' remaining.';\n }\n }\n this.$( '.upload-top-info' ).html( message );\n var enable_reset = this.counter.running == 0 && this.counter.announce + this.counter.success + this.counter.error > 0;\n var enable_start = this.counter.running == 0 && this.counter.announce > 0;\n var enable_sources = this.counter.running == 0;\n var show_table = this.counter.announce + this.counter.success + this.counter.error > 0;\n this.btnReset[ enable_reset ? 'enable' : 'disable' ]();\n this.btnStart[ enable_start ? 'enable' : 'disable' ]();\n this.btnStart.$el[ enable_start ? 'addClass' : 'removeClass' ]( 'btn-primary' );\n this.btnStop[ this.counter.running > 0 ? 'enable' : 'disable' ]();\n this.btnLocal[ enable_sources ? 'enable' : 'disable' ]();\n this.btnFtp[ enable_sources ? 'enable' : 'disable' ]();\n this.btnCreate[ enable_sources ? 'enable' : 'disable' ]();\n this.btnFtp.$el[ this.ftp_upload_site ? 'show' : 'hide' ]();\n this.$( '.upload-table' )[ show_table ? 'show' : 'hide' ]();\n this.$( '.upload-helper' )[ show_table ? 'hide' : 'show' ]();\n },\n\n /** Calculate percentage of all queued uploads */\n _uploadPercentage: function( percentage, size ) {\n return ( this.upload_completed + ( percentage * size ) ) / this.upload_size;\n },\n\n /** Template for extensions description */\n _templateDescription: function( options ) {\n if ( options.description ) {\n var tmpl = options.description;\n if ( options.description_url ) {\n tmpl += ' (read more)';\n }\n return tmpl;\n } else {\n return 'There is no description available for this file extension.';\n }\n },\n\n /** Template */\n _template: function() {\n return '' +\n ' ' +\n '' +\n ' ' +\n '' +\n '  ' +\n '' +\n ' ' +\n '' +\n ' ' +\n '' +\n ' ' +\n ' ' +\n '' +\n '' +\n ' ' +\n '' +\n '
' +\n '' +\n '
' +\n '' +\n ' ' +\n '' +\n '' +\n 'Name ' +\n 'Size ' +\n 'Type ' +\n 'Genome ' +\n 'Settings ' +\n 'Status ' +\n '' +\n ' ' ).addClass( 'ui-panel-table' ) );\n if( !config.use_remote_user ) {\n self._link( self.defs.information );\n self._link( self.defs.password );\n }\n if( config.enable_communication_server ) {\n self._link( self.defs.communication );\n }\n self._link( self.defs.permissions );\n self._link( self.defs.api_key );\n if( config.has_user_tool_filters ) {\n self._link( self.defs.toolbox_filters );\n }\n if( config.enable_openid && !config.use_remote_user ) {\n self._link( self.defs.openids );\n }\n self._link( self.defs.logout );\n self.$preferences.append( self._templateFooter( data ) );\n self.$el.empty().append( self.$preferences );\n });\n },\n\n _link: function( page ) {\n var self = this;\n var $page_item = $( this._templateRow( page ) );\n this.$table.append( $page_item );\n $page_item.find( 'a' ).on( 'click', function() {\n if ( page.url ) {\n $.ajax({\n url : Galaxy.root + page.url,\n type : 'GET'\n }).done( function( response ) {\n var options = $.extend( {}, page, response );\n var form = new Form({\n title : options.title,\n icon : options.icon,\n inputs : options.inputs,\n operations: {\n 'submit': new Ui.ButtonIcon({\n tooltip : options.submit_tooltip,\n title : options.submit_title || 'Save settings',\n icon : options.submit_icon || 'fa-save',\n onclick : function() { self._submit( form, options ) }\n }),\n 'back': new Ui.ButtonIcon({\n icon : 'fa-caret-left',\n tooltip : 'Return to user preferences',\n title : 'Preferences',\n onclick : function() { form.remove(); self.$preferences.show(); }\n })\n }\n });\n self.$preferences.hide();\n self.$el.append( form.$el );\n }).fail( function( response ) {\n self.message.update( { message: 'Failed to load resource ' + page.url + '.', status: 'danger' } );\n })\n } else {\n page.onclick();\n }\n });\n },\n\n _submit: function( form, options ) {\n var self = this;\n $.ajax( {\n url : options.url,\n data : JSON.stringify(form.data.create()),\n type : 'PUT',\n contentType : 'application/json'\n }).done( function( response ) {\n var updated_values = false;\n form.data.matchModel( response, function ( input, input_id ) {\n form.field_list[ input_id ].value( input.value );\n updated_values = true;\n });\n if ( updated_values ) {\n form.message.update( { message: response.message, status: 'success' } );\n } else {\n form.remove();\n self.$preferences.show();\n self.message.update( { message: response.message, status: 'success' } );\n }\n }).fail( function( response ) {\n form.message.update( { message: response.responseJSON.err_msg, status: 'danger' } );\n });\n },\n\n _templateRow: function( options ) {\n return '
' +\n ' ';\n },\n\n _templateFooter: function( options ) {\n return '' +\n ' ' +\n '' +\n '' + options.title + '' +\n ' ' +\n 'a\";\n\t\n\t\t// IE strips leading whitespace when .innerHTML is used\n\t\tsupport.leadingWhitespace = div.firstChild.nodeType === 3;\n\t\n\t\t// Make sure that tbody elements aren't automatically inserted\n\t\t// IE will insert them into empty tables\n\t\tsupport.tbody = !div.getElementsByTagName( \"tbody\" ).length;\n\t\n\t\t// Make sure that link elements get serialized correctly by innerHTML\n\t\t// This requires a wrapper element in IE\n\t\tsupport.htmlSerialize = !!div.getElementsByTagName( \"link\" ).length;\n\t\n\t\t// Makes sure cloning an html5 element does not cause problems\n\t\t// Where outerHTML is undefined, this still works\n\t\tsupport.html5Clone =\n\t\t\tdocument.createElement( \"nav\" ).cloneNode( true ).outerHTML !== \"<:nav>\";\n\t\n\t\t// Check if a disconnected checkbox will retain its checked\n\t\t// value of true after appended to the DOM (IE6/7)\n\t\tinput.type = \"checkbox\";\n\t\tinput.checked = true;\n\t\tfragment.appendChild( input );\n\t\tsupport.appendChecked = input.checked;\n\t\n\t\t// Make sure textarea (and checkbox) defaultValue is properly cloned\n\t\t// Support: IE6-IE11+\n\t\tdiv.innerHTML = \"\";\n\t\tsupport.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;\n\t\n\t\t// #11217 - WebKit loses check when the name is after the checked attribute\n\t\tfragment.appendChild( div );\n\t\n\t\t// Support: Windows Web Apps (WWA)\n\t\t// `name` and `type` must use .setAttribute for WWA (#14901)\n\t\tinput = document.createElement( \"input\" );\n\t\tinput.setAttribute( \"type\", \"radio\" );\n\t\tinput.setAttribute( \"checked\", \"checked\" );\n\t\tinput.setAttribute( \"name\", \"t\" );\n\t\n\t\tdiv.appendChild( input );\n\t\n\t\t// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3\n\t\t// old WebKit doesn't clone checked state correctly in fragments\n\t\tsupport.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;\n\t\n\t\t// Support: IE<9\n\t\t// Cloned elements keep attachEvent handlers, we use addEventListener on IE9+\n\t\tsupport.noCloneEvent = !!div.addEventListener;\n\t\n\t\t// Support: IE<9\n\t\t// Since attributes and properties are the same in IE,\n\t\t// cleanData must set properties to undefined rather than use removeAttribute\n\t\tdiv[ jQuery.expando ] = 1;\n\t\tsupport.attributes = !div.getAttribute( jQuery.expando );\n\t} )();\n\t\n\t\n\t// We have to close these tags to support XHTML (#13200)\n\tvar wrapMap = {\n\t\toption: [ 1, \"\" ],\n\t\tlegend: [ 1, \"\" ],\n\t\tarea: [ 1, \"\" ],\n\t\n\t\t// Support: IE8\n\t\tparam: [ 1, \"\" ],\n\t\tthead: [ 1, \"
\", \"
\" ],\n\t\ttr: [ 2, \"\", \"
\" ],\n\t\tcol: [ 2, \"
\" ],\n\t\ttd: [ 3, \"
\" ],\n\t\n\t\t// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,\n\t\t// unless wrapped in a div with non-breaking characters in front of it.\n\t\t_default: support.htmlSerialize ? [ 0, \"\", \"\" ] : [ 1, \"X\", \" , *may* have spurious \n\t\t\t\t\t\telem = tag === \"table\" && !rtbody.test( elem ) ?\n\t\t\t\t\t\t\ttmp.firstChild :\n\t\n\t\t\t\t\t\t\t// String was a bare or \n\t\t\t\t\t\t\twrap[ 1 ] === \"
\" && !rtbody.test( elem ) ?\n\t\t\t\t\t\t\t\ttmp :\n\t\t\t\t\t\t\t\t0;\n\t\n\t\t\t\t\t\tj = elem && elem.childNodes.length;\n\t\t\t\t\t\twhile ( j-- ) {\n\t\t\t\t\t\t\tif ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), \"tbody\" ) &&\n\t\t\t\t\t\t\t\t!tbody.childNodes.length ) {\n\t\n\t\t\t\t\t\t\t\telem.removeChild( tbody );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\n\t\t\t\t\tjQuery.merge( nodes, tmp.childNodes );\n\t\n\t\t\t\t\t// Fix #12392 for WebKit and IE > 9\n\t\t\t\t\ttmp.textContent = \"\";\n\t\n\t\t\t\t\t// Fix #12392 for oldIE\n\t\t\t\t\twhile ( tmp.firstChild ) {\n\t\t\t\t\t\ttmp.removeChild( tmp.firstChild );\n\t\t\t\t\t}\n\t\n\t\t\t\t\t// Remember the top-level container for proper cleanup\n\t\t\t\t\ttmp = safe.lastChild;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\t// Fix #11356: Clear elements from fragment\n\t\tif ( tmp ) {\n\t\t\tsafe.removeChild( tmp );\n\t\t}\n\t\n\t\t// Reset defaultChecked for any radios and checkboxes\n\t\t// about to be appended to the DOM in IE 6/7 (#8060)\n\t\tif ( !support.appendChecked ) {\n\t\t\tjQuery.grep( getAll( nodes, \"input\" ), fixDefaultChecked );\n\t\t}\n\t\n\t\ti = 0;\n\t\twhile ( ( elem = nodes[ i++ ] ) ) {\n\t\n\t\t\t// Skip elements already in the context collection (trac-4087)\n\t\t\tif ( selection && jQuery.inArray( elem, selection ) > -1 ) {\n\t\t\t\tif ( ignored ) {\n\t\t\t\t\tignored.push( elem );\n\t\t\t\t}\n\t\n\t\t\t\tcontinue;\n\t\t\t}\n\t\n\t\t\tcontains = jQuery.contains( elem.ownerDocument, elem );\n\t\n\t\t\t// Append to fragment\n\t\t\ttmp = getAll( safe.appendChild( elem ), \"script\" );\n\t\n\t\t\t// Preserve script evaluation history\n\t\t\tif ( contains ) {\n\t\t\t\tsetGlobalEval( tmp );\n\t\t\t}\n\t\n\t\t\t// Capture executables\n\t\t\tif ( scripts ) {\n\t\t\t\tj = 0;\n\t\t\t\twhile ( ( elem = tmp[ j++ ] ) ) {\n\t\t\t\t\tif ( rscriptType.test( elem.type || \"\" ) ) {\n\t\t\t\t\t\tscripts.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\n\t\ttmp = null;\n\t\n\t\treturn safe;\n\t}\n\t\n\t\n\t( function() {\n\t\tvar i, eventName,\n\t\t\tdiv = document.createElement( \"div\" );\n\t\n\t\t// Support: IE<9 (lack submit/change bubble), Firefox (lack focus(in | out) events)\n\t\tfor ( i in { submit: true, change: true, focusin: true } ) {\n\t\t\teventName = \"on\" + i;\n\t\n\t\t\tif ( !( support[ i ] = eventName in window ) ) {\n\t\n\t\t\t\t// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)\n\t\t\t\tdiv.setAttribute( eventName, \"t\" );\n\t\t\t\tsupport[ i ] = div.attributes[ eventName ].expando === false;\n\t\t\t}\n\t\t}\n\t\n\t\t// Null elements to avoid leaks in IE.\n\t\tdiv = null;\n\t} )();\n\t\n\t\n\tvar rformElems = /^(?:input|select|textarea)$/i,\n\t\trkeyEvent = /^key/,\n\t\trmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,\n\t\trfocusMorph = /^(?:focusinfocus|focusoutblur)$/,\n\t\trtypenamespace = /^([^.]*)(?:\\.(.+)|)/;\n\t\n\tfunction returnTrue() {\n\t\treturn true;\n\t}\n\t\n\tfunction returnFalse() {\n\t\treturn false;\n\t}\n\t\n\t// Support: IE9\n\t// See #13393 for more info\n\tfunction safeActiveElement() {\n\t\ttry {\n\t\t\treturn document.activeElement;\n\t\t} catch ( err ) { }\n\t}\n\t\n\tfunction on( elem, types, selector, data, fn, one ) {\n\t\tvar origFn, type;\n\t\n\t\t// Types can be a map of types/handlers\n\t\tif ( typeof types === \"object\" ) {\n\t\n\t\t\t// ( types-Object, selector, data )\n\t\t\tif ( typeof selector !== \"string\" ) {\n\t\n\t\t\t\t// ( types-Object, data )\n\t\t\t\tdata = data || selector;\n\t\t\t\tselector = undefined;\n\t\t\t}\n\t\t\tfor ( type in types ) {\n\t\t\t\ton( elem, type, selector, data, types[ type ], one );\n\t\t\t}\n\t\t\treturn elem;\n\t\t}\n\t\n\t\tif ( data == null && fn == null ) {\n\t\n\t\t\t// ( types, fn )\n\t\t\tfn = selector;\n\t\t\tdata = selector = undefined;\n\t\t} else if ( fn == null ) {\n\t\t\tif ( typeof selector === \"string\" ) {\n\t\n\t\t\t\t// ( types, selector, fn )\n\t\t\t\tfn = data;\n\t\t\t\tdata = undefined;\n\t\t\t} else {\n\t\n\t\t\t\t// ( types, data, fn )\n\t\t\t\tfn = data;\n\t\t\t\tdata = selector;\n\t\t\t\tselector = undefined;\n\t\t\t}\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t} else if ( !fn ) {\n\t\t\treturn elem;\n\t\t}\n\t\n\t\tif ( one === 1 ) {\n\t\t\torigFn = fn;\n\t\t\tfn = function( event ) {\n\t\n\t\t\t\t// Can use an empty set, since event contains the info\n\t\t\t\tjQuery().off( event );\n\t\t\t\treturn origFn.apply( this, arguments );\n\t\t\t};\n\t\n\t\t\t// Use same guid so caller can remove using origFn\n\t\t\tfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n\t\t}\n\t\treturn elem.each( function() {\n\t\t\tjQuery.event.add( this, types, fn, data, selector );\n\t\t} );\n\t}\n\t\n\t/*\n\t * Helper functions for managing events -- not part of the public interface.\n\t * Props to Dean Edwards' addEvent library for many of the ideas.\n\t */\n\tjQuery.event = {\n\t\n\t\tglobal: {},\n\t\n\t\tadd: function( elem, types, handler, data, selector ) {\n\t\t\tvar tmp, events, t, handleObjIn,\n\t\t\t\tspecial, eventHandle, handleObj,\n\t\t\t\thandlers, type, namespaces, origType,\n\t\t\t\telemData = jQuery._data( elem );\n\t\n\t\t\t// Don't attach events to noData or text/comment nodes (but allow plain objects)\n\t\t\tif ( !elemData ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// Caller can pass in an object of custom data in lieu of the handler\n\t\t\tif ( handler.handler ) {\n\t\t\t\thandleObjIn = handler;\n\t\t\t\thandler = handleObjIn.handler;\n\t\t\t\tselector = handleObjIn.selector;\n\t\t\t}\n\t\n\t\t\t// Make sure that the handler has a unique ID, used to find/remove it later\n\t\t\tif ( !handler.guid ) {\n\t\t\t\thandler.guid = jQuery.guid++;\n\t\t\t}\n\t\n\t\t\t// Init the element's event structure and main handler, if this is the first\n\t\t\tif ( !( events = elemData.events ) ) {\n\t\t\t\tevents = elemData.events = {};\n\t\t\t}\n\t\t\tif ( !( eventHandle = elemData.handle ) ) {\n\t\t\t\teventHandle = elemData.handle = function( e ) {\n\t\n\t\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n\t\t\t\t\t// when an event is called after a page has unloaded\n\t\t\t\t\treturn typeof jQuery !== \"undefined\" &&\n\t\t\t\t\t\t( !e || jQuery.event.triggered !== e.type ) ?\n\t\t\t\t\t\tjQuery.event.dispatch.apply( eventHandle.elem, arguments ) :\n\t\t\t\t\t\tundefined;\n\t\t\t\t};\n\t\n\t\t\t\t// Add elem as a property of the handle fn to prevent a memory leak\n\t\t\t\t// with IE non-native events\n\t\t\t\teventHandle.elem = elem;\n\t\t\t}\n\t\n\t\t\t// Handle multiple events separated by a space\n\t\t\ttypes = ( types || \"\" ).match( rnotwhite ) || [ \"\" ];\n\t\t\tt = types.length;\n\t\t\twhile ( t-- ) {\n\t\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\t\ttype = origType = tmp[ 1 ];\n\t\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\t\n\t\t\t\t// There *must* be a type, no attaching namespace-only handlers\n\t\t\t\tif ( !type ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\n\t\t\t\t// If event changes its type, use the special event handlers for the changed type\n\t\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\n\t\t\t\t// If selector defined, determine special event api type, otherwise given type\n\t\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\t\n\t\t\t\t// Update special based on newly reset type\n\t\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\n\t\t\t\t// handleObj is passed to all event handlers\n\t\t\t\thandleObj = jQuery.extend( {\n\t\t\t\t\ttype: type,\n\t\t\t\t\torigType: origType,\n\t\t\t\t\tdata: data,\n\t\t\t\t\thandler: handler,\n\t\t\t\t\tguid: handler.guid,\n\t\t\t\t\tselector: selector,\n\t\t\t\t\tneedsContext: selector && jQuery.expr.match.needsContext.test( selector ),\n\t\t\t\t\tnamespace: namespaces.join( \".\" )\n\t\t\t\t}, handleObjIn );\n\t\n\t\t\t\t// Init the event handler queue if we're the first\n\t\t\t\tif ( !( handlers = events[ type ] ) ) {\n\t\t\t\t\thandlers = events[ type ] = [];\n\t\t\t\t\thandlers.delegateCount = 0;\n\t\n\t\t\t\t\t// Only use addEventListener/attachEvent if the special events handler returns false\n\t\t\t\t\tif ( !special.setup ||\n\t\t\t\t\t\tspecial.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\t\n\t\t\t\t\t\t// Bind the global event handler to the element\n\t\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\t\telem.addEventListener( type, eventHandle, false );\n\t\n\t\t\t\t\t\t} else if ( elem.attachEvent ) {\n\t\t\t\t\t\t\telem.attachEvent( \"on\" + type, eventHandle );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\tif ( special.add ) {\n\t\t\t\t\tspecial.add.call( elem, handleObj );\n\t\n\t\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t// Add to the element's handler list, delegates in front\n\t\t\t\tif ( selector ) {\n\t\t\t\t\thandlers.splice( handlers.delegateCount++, 0, handleObj );\n\t\t\t\t} else {\n\t\t\t\t\thandlers.push( handleObj );\n\t\t\t\t}\n\t\n\t\t\t\t// Keep track of which events have ever been used, for event optimization\n\t\t\t\tjQuery.event.global[ type ] = true;\n\t\t\t}\n\t\n\t\t\t// Nullify elem to prevent memory leaks in IE\n\t\t\telem = null;\n\t\t},\n\t\n\t\t// Detach an event or set of events from an element\n\t\tremove: function( elem, types, handler, selector, mappedTypes ) {\n\t\t\tvar j, handleObj, tmp,\n\t\t\t\torigCount, t, events,\n\t\t\t\tspecial, handlers, type,\n\t\t\t\tnamespaces, origType,\n\t\t\t\telemData = jQuery.hasData( elem ) && jQuery._data( elem );\n\t\n\t\t\tif ( !elemData || !( events = elemData.events ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// Once for each type.namespace in types; type may be omitted\n\t\t\ttypes = ( types || \"\" ).match( rnotwhite ) || [ \"\" ];\n\t\t\tt = types.length;\n\t\t\twhile ( t-- ) {\n\t\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\t\ttype = origType = tmp[ 1 ];\n\t\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\t\n\t\t\t\t// Unbind all events (on this namespace, if provided) for the element\n\t\t\t\tif ( !type ) {\n\t\t\t\t\tfor ( type in events ) {\n\t\t\t\t\t\tjQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\n\t\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\t\t\t\thandlers = events[ type ] || [];\n\t\t\t\ttmp = tmp[ 2 ] &&\n\t\t\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" );\n\t\n\t\t\t\t// Remove matching events\n\t\t\t\torigCount = j = handlers.length;\n\t\t\t\twhile ( j-- ) {\n\t\t\t\t\thandleObj = handlers[ j ];\n\t\n\t\t\t\t\tif ( ( mappedTypes || origType === handleObj.origType ) &&\n\t\t\t\t\t\t( !handler || handler.guid === handleObj.guid ) &&\n\t\t\t\t\t\t( !tmp || tmp.test( handleObj.namespace ) ) &&\n\t\t\t\t\t\t( !selector || selector === handleObj.selector ||\n\t\t\t\t\t\t\tselector === \"**\" && handleObj.selector ) ) {\n\t\t\t\t\t\thandlers.splice( j, 1 );\n\t\n\t\t\t\t\t\tif ( handleObj.selector ) {\n\t\t\t\t\t\t\thandlers.delegateCount--;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\n\t\t\t\t// Remove generic event handler if we removed something and no more handlers exist\n\t\t\t\t// (avoids potential for endless recursion during removal of special event handlers)\n\t\t\t\tif ( origCount && !handlers.length ) {\n\t\t\t\t\tif ( !special.teardown ||\n\t\t\t\t\t\tspecial.teardown.call( elem, namespaces, elemData.handle ) === false ) {\n\t\n\t\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t\t}\n\t\n\t\t\t\t\tdelete events[ type ];\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Remove the expando if it's no longer used\n\t\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\t\tdelete elemData.handle;\n\t\n\t\t\t\t// removeData also checks for emptiness and clears the expando if empty\n\t\t\t\t// so use it instead of delete\n\t\t\t\tjQuery._removeData( elem, \"events\" );\n\t\t\t}\n\t\t},\n\t\n\t\ttrigger: function( event, data, elem, onlyHandlers ) {\n\t\t\tvar handle, ontype, cur,\n\t\t\t\tbubbleType, special, tmp, i,\n\t\t\t\teventPath = [ elem || document ],\n\t\t\t\ttype = hasOwn.call( event, \"type\" ) ? event.type : event,\n\t\t\t\tnamespaces = hasOwn.call( event, \"namespace\" ) ? event.namespace.split( \".\" ) : [];\n\t\n\t\t\tcur = tmp = elem = elem || document;\n\t\n\t\t\t// Don't do events on text and comment nodes\n\t\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// focus/blur morphs to focusin/out; ensure we're not firing them right now\n\t\t\tif ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\tif ( type.indexOf( \".\" ) > -1 ) {\n\t\n\t\t\t\t// Namespaced trigger; create a regexp to match event type in handle()\n\t\t\t\tnamespaces = type.split( \".\" );\n\t\t\t\ttype = namespaces.shift();\n\t\t\t\tnamespaces.sort();\n\t\t\t}\n\t\t\tontype = type.indexOf( \":\" ) < 0 && \"on\" + type;\n\t\n\t\t\t// Caller can pass in a jQuery.Event object, Object, or just an event type string\n\t\t\tevent = event[ jQuery.expando ] ?\n\t\t\t\tevent :\n\t\t\t\tnew jQuery.Event( type, typeof event === \"object\" && event );\n\t\n\t\t\t// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)\n\t\t\tevent.isTrigger = onlyHandlers ? 2 : 3;\n\t\t\tevent.namespace = namespaces.join( \".\" );\n\t\t\tevent.rnamespace = event.namespace ?\n\t\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" ) :\n\t\t\t\tnull;\n\t\n\t\t\t// Clean up the event in case it is being reused\n\t\t\tevent.result = undefined;\n\t\t\tif ( !event.target ) {\n\t\t\t\tevent.target = elem;\n\t\t\t}\n\t\n\t\t\t// Clone any incoming data and prepend the event, creating the handler arg list\n\t\t\tdata = data == null ?\n\t\t\t\t[ event ] :\n\t\t\t\tjQuery.makeArray( data, [ event ] );\n\t\n\t\t\t// Allow special events to draw outside the lines\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\tif ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// Determine event propagation path in advance, per W3C events spec (#9951)\n\t\t\t// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)\n\t\t\tif ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {\n\t\n\t\t\t\tbubbleType = special.delegateType || type;\n\t\t\t\tif ( !rfocusMorph.test( bubbleType + type ) ) {\n\t\t\t\t\tcur = cur.parentNode;\n\t\t\t\t}\n\t\t\t\tfor ( ; cur; cur = cur.parentNode ) {\n\t\t\t\t\teventPath.push( cur );\n\t\t\t\t\ttmp = cur;\n\t\t\t\t}\n\t\n\t\t\t\t// Only add window if we got to document (e.g., not plain obj or detached DOM)\n\t\t\t\tif ( tmp === ( elem.ownerDocument || document ) ) {\n\t\t\t\t\teventPath.push( tmp.defaultView || tmp.parentWindow || window );\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Fire handlers on the event path\n\t\t\ti = 0;\n\t\t\twhile ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\n\t\t\t\tevent.type = i > 1 ?\n\t\t\t\t\tbubbleType :\n\t\t\t\t\tspecial.bindType || type;\n\t\n\t\t\t\t// jQuery handler\n\t\t\t\thandle = ( jQuery._data( cur, \"events\" ) || {} )[ event.type ] &&\n\t\t\t\t\tjQuery._data( cur, \"handle\" );\n\t\n\t\t\t\tif ( handle ) {\n\t\t\t\t\thandle.apply( cur, data );\n\t\t\t\t}\n\t\n\t\t\t\t// Native handler\n\t\t\t\thandle = ontype && cur[ ontype ];\n\t\t\t\tif ( handle && handle.apply && acceptData( cur ) ) {\n\t\t\t\t\tevent.result = handle.apply( cur, data );\n\t\t\t\t\tif ( event.result === false ) {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tevent.type = type;\n\t\n\t\t\t// If nobody prevented the default action, do it now\n\t\t\tif ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\t\n\t\t\t\tif (\n\t\t\t\t\t( !special._default ||\n\t\t\t\t\t special._default.apply( eventPath.pop(), data ) === false\n\t\t\t\t\t) && acceptData( elem )\n\t\t\t\t) {\n\t\n\t\t\t\t\t// Call a native DOM method on the target with the same name name as the event.\n\t\t\t\t\t// Can't use an .isFunction() check here because IE6/7 fails that test.\n\t\t\t\t\t// Don't do default actions on window, that's where global variables be (#6170)\n\t\t\t\t\tif ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {\n\t\n\t\t\t\t\t\t// Don't re-trigger an onFOO event when we call its FOO() method\n\t\t\t\t\t\ttmp = elem[ ontype ];\n\t\n\t\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\t\telem[ ontype ] = null;\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t// Prevent re-triggering of the same event, since we already bubbled it above\n\t\t\t\t\t\tjQuery.event.triggered = type;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\telem[ type ]();\n\t\t\t\t\t\t} catch ( e ) {\n\t\n\t\t\t\t\t\t\t// IE<9 dies on focus/blur to hidden element (#1486,#12518)\n\t\t\t\t\t\t\t// only reproducible on winXP IE8 native, not IE9 in IE8 mode\n\t\t\t\t\t\t}\n\t\t\t\t\t\tjQuery.event.triggered = undefined;\n\t\n\t\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\t\telem[ ontype ] = tmp;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\treturn event.result;\n\t\t},\n\t\n\t\tdispatch: function( event ) {\n\t\n\t\t\t// Make a writable jQuery.Event from the native event object\n\t\t\tevent = jQuery.event.fix( event );\n\t\n\t\t\tvar i, j, ret, matched, handleObj,\n\t\t\t\thandlerQueue = [],\n\t\t\t\targs = slice.call( arguments ),\n\t\t\t\thandlers = ( jQuery._data( this, \"events\" ) || {} )[ event.type ] || [],\n\t\t\t\tspecial = jQuery.event.special[ event.type ] || {};\n\t\n\t\t\t// Use the fix-ed jQuery.Event rather than the (read-only) native event\n\t\t\targs[ 0 ] = event;\n\t\t\tevent.delegateTarget = this;\n\t\n\t\t\t// Call the preDispatch hook for the mapped type, and let it bail if desired\n\t\t\tif ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\n\t\t\t// Determine handlers\n\t\t\thandlerQueue = jQuery.event.handlers.call( this, event, handlers );\n\t\n\t\t\t// Run delegates first; they may want to stop propagation beneath us\n\t\t\ti = 0;\n\t\t\twhile ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\t\tevent.currentTarget = matched.elem;\n\t\n\t\t\t\tj = 0;\n\t\t\t\twhile ( ( handleObj = matched.handlers[ j++ ] ) &&\n\t\t\t\t\t!event.isImmediatePropagationStopped() ) {\n\t\n\t\t\t\t\t// Triggered event must either 1) have no namespace, or 2) have namespace(s)\n\t\t\t\t\t// a subset or equal to those in the bound event (both can have no namespace).\n\t\t\t\t\tif ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {\n\t\n\t\t\t\t\t\tevent.handleObj = handleObj;\n\t\t\t\t\t\tevent.data = handleObj.data;\n\t\n\t\t\t\t\t\tret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||\n\t\t\t\t\t\t\thandleObj.handler ).apply( matched.elem, args );\n\t\n\t\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\t\tif ( ( event.result = ret ) === false ) {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\n\t\t\t// Call the postDispatch hook for the mapped type\n\t\t\tif ( special.postDispatch ) {\n\t\t\t\tspecial.postDispatch.call( this, event );\n\t\t\t}\n\t\n\t\t\treturn event.result;\n\t\t},\n\t\n\t\thandlers: function( event, handlers ) {\n\t\t\tvar i, matches, sel, handleObj,\n\t\t\t\thandlerQueue = [],\n\t\t\t\tdelegateCount = handlers.delegateCount,\n\t\t\t\tcur = event.target;\n\t\n\t\t\t// Support (at least): Chrome, IE9\n\t\t\t// Find delegate handlers\n\t\t\t// Black-hole SVG