diff --git a/client/galaxy/scripts/galaxy.workflow_editor.canvas.js b/client/galaxy/scripts/galaxy.workflow_editor.canvas.js index b22dc903c69..918ecf0d124 100644 --- a/client/galaxy/scripts/galaxy.workflow_editor.canvas.js +++ b/client/galaxy/scripts/galaxy.workflow_editor.canvas.js @@ -805,7 +805,7 @@ var Node = Backbone.Model.extend({ nodeView.addDataOutput( output ); } ); nodeView.render(); - workflow.node_changed( this ); + workflow.node_changed( this, true); }, update_field_data : function( data ) { var node = this; @@ -1094,9 +1094,9 @@ $.extend( Workflow.prototype, { this.active_node = node; } }, - node_changed : function ( node ) { + node_changed : function ( node, force ) { this.has_changes = true; - if ( this.active_node == node ) { + if ( this.active_node == node && (!parent.__NEWTOOLFORM__ || force)) { // Reactive with new form_html this.check_changes_in_active_form(); //Force changes to be saved even on new connection (previously dumped) parent.show_form_for_tool( node.form_html + node.tooltip, node ); @@ -1305,6 +1305,12 @@ function add_node( type, title_text, tool_id ) { return node; } +function update_node( data ) { + var node = workflow.active_node; + if (node) { + node.update_field_data(data, true); + } +} var ext_to_type = null; var type_to_type = null; diff --git a/client/galaxy/scripts/mvc/tools/tools-form-base.js b/client/galaxy/scripts/mvc/tools/tools-form-base.js new file mode 100644 index 00000000000..71eecb21069 --- /dev/null +++ b/client/galaxy/scripts/mvc/tools/tools-form-base.js @@ -0,0 +1,315 @@ +/** + This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules. +*/ +define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', + 'mvc/citation/citation-model', 'mvc/citation/citation-view', + 'mvc/tools', 'mvc/tools/tools-template', 'mvc/tools/tools-content', 'mvc/tools/tools-section', 'mvc/tools/tools-tree'], + function(Utils, Deferred, Portlet, Ui, CitationModel, CitationView, + Tools, ToolTemplate, ToolContent, ToolSection, ToolTree) { + + // create form view + return Backbone.View.extend({ + // initialize + initialize: function(options) { + // log options + console.debug(options); + + // link galaxy modal or create one + var galaxy = parent.Galaxy; + if (galaxy && galaxy.modal) { + this.modal = galaxy.modal; + } else { + this.modal = new Ui.Modal.View(); + } + + // check if the user is an admin + if (galaxy && galaxy.currUser) { + this.is_admin = galaxy.currUser.get('is_admin'); + } else { + this.is_admin = false; + } + + // link options + this.options = options; + + // link container + this.container = this.options.container || 'body'; + + // create deferred processing queue handler + // this handler reduces the number of requests to the api by filtering redundant requests + this.deferred = new Deferred(); + + // set element + this.setElement('
'); + + // add to main element + $(this.container).append(this.$el); + + // build this form + this._buildForm(); + }, + + /** Shows the final message (usually upon successful job submission) + */ + reciept: function($el) { + $(this.container).empty(); + $(this.container).append($el); + }, + + /** Highlight and scroll to input element (currently only used for error notifications) + */ + highlight: function (input_id, message, silent) { + // get input field + var input_element = this.element_list[input_id]; + + // check input element + if (input_element) { + // mark error + input_element.error(message || 'Please verify this parameter.'); + + // scroll to first input element + if (!silent) { + $(this.container).animate({ + scrollTop: input_element.$el.offset().top - 20 + }, 500); + } + } + }, + + /** Main tool form build function. This function is called once a new model is available. + */ + _buildForm: function() { + // link this + var self = this; + + // reset events + this.off('refresh'); + this.off('reset'); + + // reset field list, which contains the input field elements + this.field_list = {}; + + // reset sequential input definition list, which contains the input definitions as provided from the api + this.input_list = {}; + + // reset input element list, which contains the dom elements of each input element (includes also the input field) + this.element_list = {}; + + // creates a tree/json data structure from the input form + this.tree = new ToolTree(this); + + // request history content and build form + this.content = new ToolContent(this); + + // link model options + var options = this.options; + + // create ui elements + this._renderForm(options); + + // rebuild the underlying data structure + this.tree.finalize(); + + // show errors + if (!this.workflow && options.errors) { + var error_messages = this.tree.matchResponse(options.errors); + for (var input_id in error_messages) { + this.highlight(input_id, error_messages[input_id], true); + } + } + + // add refresh listener + this.on('refresh', function() { + // by using/reseting the deferred ajax queue the number of redundant calls is reduced + self.deferred.reset(); + self.deferred.execute(function(){self._updateModel()}); + }); + + // add reset listener + this.on('reset', function() { + for (var i in this.element_list) { + this.element_list[i].reset(); + } + }); + }, + + /** Renders the UI elements required for the form + */ + _renderForm: function(options) { + // link this + var self = this; + + // create message view + this.message = new Ui.Message(); + + // button for version selection + var requirements_button = new Ui.ButtonIcon({ + icon : 'fa-info-circle', + title : 'Requirements', + tooltip : 'Display tool requirements', + onclick : function() { + if (!this.visible) { + this.visible = true; + self.message.update({ + persistent : true, + message : ToolTemplate.requirements(options), + status : 'info' + }); + } else { + this.visible = false; + self.message.update({ + message : '' + }); + } + } + }); + if (!options.requirements || options.requirements.length == 0) { + requirements_button.$el.hide(); + } + + // button for version selection + var versions_button = new Ui.ButtonMenu({ + icon : 'fa-cubes', + title : 'Versions', + tooltip : 'Select another tool version' + }); + if (options.versions && options.versions.length > 1) { + for (var i in options.versions) { + var version = options.versions[i]; + if (version != options.version) { + versions_button.addMenu({ + title : 'Switch to ' + version, + version : version, + icon : 'fa-cube', + onclick : function() { + // here we update the tool version (some tools encode the version also in the id) + options.id = options.id.replace(options.version, this.version); + options.version = this.version; + + // rebuild the model and form + self.deferred.reset(); + self.deferred.execute(function(){self._buildModel()}); + } + }); + } + } + } else { + versions_button.$el.hide(); + } + + // button menu + var menu_button = new Ui.ButtonMenu({ + icon : 'fa-caret-down', + title : 'Options', + tooltip : 'View available options' + }); + + // configure button selection + if(options.biostar_url) { + // add question option + menu_button.addMenu({ + icon : 'fa-question-circle', + title : 'Question?', + tooltip : 'Ask a question about this tool (Biostar)', + onclick : function() { + window.open(options.biostar_url + '/p/new/post/'); + } + }); + + // create search button + menu_button.addMenu({ + icon : 'fa-search', + title : 'Search', + tooltip : 'Search help for this tool (Biostar)', + onclick : function() { + window.open(options.biostar_url + '/t/' + options.id + '/'); + } + }); + }; + + // create share button + menu_button.addMenu({ + icon : 'fa-share', + title : 'Share', + tooltip : 'Share this tool', + onclick : function() { + prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id); + } + }); + + // add admin operations + if (this.is_admin) { + // create download button + menu_button.addMenu({ + icon : 'fa-download', + title : 'Download', + tooltip : 'Download this tool', + onclick : function() { + window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download'; + } + }); + } + + // create tool form section + this.section = new ToolSection.View(self, { + inputs : options.inputs, + cls : 'ui-table-plain' + }); + + // switch to classic tool form mako if the form definition is incompatible + if (this.incompatible) { + this.$el.hide(); + $('#tool-form-classic').show(); + return; + } + + // create portlet + this.portlet = new Portlet.View({ + icon : 'fa-wrench', + title : '' + options.name + ' ' + options.description + ' (Galaxy Tool Version ' + options.version + ')', + cls : 'ui-portlet-slim', + operations: { + requirements : requirements_button, + menu : menu_button, + versions : versions_button + }, + buttons : this.buttons + }); + + // append message + this.portlet.append(this.message.$el, true); + + // append tool section + this.portlet.append(this.section.$el); + + // start form + this.$el.empty(); + this.$el.append(this.portlet.$el); + + // append help + if (options.help != '') { + this.$el.append(ToolTemplate.help(options.help)); + } + + // append citations + if (options.citations) { + var $citations = $('
'); + var citations = new CitationModel.ToolCitationCollection(); + citations.tool_id = options.id; + var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations } ); + citation_list_view.render(); + citations.fetch(); + this.$el.append($citations); + } + + // show message if available in model + if (options.message) { + this.message.update({ + persistent : true, + status : 'warning', + message : options.message + }); + } + } + }); +}); diff --git a/client/galaxy/scripts/mvc/tools/tools-form-workflow.js b/client/galaxy/scripts/mvc/tools/tools-form-workflow.js new file mode 100644 index 00000000000..c9a45ee795e --- /dev/null +++ b/client/galaxy/scripts/mvc/tools/tools-form-workflow.js @@ -0,0 +1,66 @@ +/** + This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules. +*/ +define(['utils/utils', 'mvc/tools/tools-form-base'], + function(Utils, ToolFormBase) { + + // create form view + var View = ToolFormBase.extend({ + initialize: function(options) { + this.workflow = true; + ToolFormBase.prototype.initialize.call(this, options); + }, + + /** Builds a new model through api call and recreates the entire form + */ + _buildModel: function() { + }, + + /** Request a new model for an already created tool form and updates the form inputs + */ + _updateModel: function() { + // create the request dictionary + var self = this; + var current_state = this.tree.finalize(); + + // log tool state + console.debug('tools-form-workflow::_refreshForm() - Refreshing states.'); + console.debug(current_state); + + // register process + var process_id = this.deferred.register(); + + // build model url for request + var model_url = galaxy_config.root + 'workflow/editor_form_post?tool_id=' + this.options.id; + + // post job + Utils.request({ + type : 'GET', + url : model_url, + data : current_state, + success : function(node) { + parent.update_node(node); + + // process completed + self.deferred.done(process_id); + + // log success + console.debug('tools-form::_refreshForm() - States refreshed.'); + console.debug(node); + }, + error : function(response) { + // process completed + self.deferred.done(process_id); + + // log error + console.debug('tools-form::_refreshForm() - Refresh request failed.'); + console.debug(response); + } + }); + } + }); + + return { + View: View + }; +}); diff --git a/client/galaxy/scripts/mvc/tools/tools-form.js b/client/galaxy/scripts/mvc/tools/tools-form.js index a92abe0ad34..94a0af0a7f4 100644 --- a/client/galaxy/scripts/mvc/tools/tools-form.js +++ b/client/galaxy/scripts/mvc/tools/tools-form.js @@ -1,79 +1,28 @@ /** This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules. */ -define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', - 'mvc/citation/citation-model', 'mvc/citation/citation-view', - 'mvc/tools', 'mvc/tools/tools-template', 'mvc/tools/tools-content', 'mvc/tools/tools-section', 'mvc/tools/tools-tree', 'mvc/tools/tools-jobs'], - function(Utils, Deferred, Portlet, Ui, CitationModel, CitationView, - Tools, ToolTemplate, ToolContent, ToolSection, ToolTree, ToolJobs) { +define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/tools/tools-form-base', 'mvc/tools/tools-jobs'], + function(Utils, Ui, ToolFormBase, ToolJobs) { // create form view - var View = Backbone.View.extend({ - // base element - container: 'body', - + var View = ToolFormBase.extend({ // initialize initialize: function(options) { - // log options - console.debug(options); - - // link galaxy modal or create one - var galaxy = parent.Galaxy; - if (galaxy && galaxy.modal) { - this.modal = galaxy.modal; - } else { - this.modal = new Ui.Modal.View(); - } - - // check if the user is an admin - if (galaxy && galaxy.currUser) { - this.is_admin = galaxy.currUser.get('is_admin') - } else { - this.is_admin = false; - } - - // link options - this.options = options; - - // create deferred processing queue handler - // this handler reduces the number of requests to the api by filtering redundant requests - this.deferred = new Deferred(); - - // set element - this.setElement('
'); - - // add to main element - $(this.container).append(this.$el); - - // build this form - this._buildForm(); - }, - - /** Shows the final message (usually upon successful job submission) - */ - reciept: function($el) { - $(this.container).empty(); - $(this.container).append($el); - }, - - /** Highlight and scroll to input element (currently only used for error notifications) - */ - highlight: function (input_id, message, silent) { - // get input field - var input_element = this.element_list[input_id]; - - // check input element - if (input_element) { - // mark error - input_element.error(message || 'Please verify this parameter.'); - - // scroll to first input element - if (!silent) { - $(this.container).animate({ - scrollTop: input_element.$el.offset().top - 20 - }, 500); - } + var self = this; + this.job_handler = new ToolJobs(this); + this.buttons = { + execute : new Ui.Button({ + icon : 'fa-check', + tooltip : 'Execute: ' + options.name, + title : 'Execute', + cls : 'btn btn-primary', + floating : 'clear', + onclick : function() { + self.job_handler.submit(); + } + }) } + ToolFormBase.prototype.initialize.call(this, options); }, /** Builds a new model through api call and recreates the entire form @@ -250,256 +199,6 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', console.debug(response); } }); - }, - - /** Main tool form build function. This function is called once a new model is available. - */ - _buildForm: function() { - // link this - var self = this; - - // reset events - this.off('refresh'); - this.off('reset'); - - // reset field list, which contains the input field elements - this.field_list = {}; - - // reset sequential input definition list, which contains the input definitions as provided from the api - this.input_list = {}; - - // reset input element list, which contains the dom elements of each input element (includes also the input field) - this.element_list = {}; - - // creates a tree/json data structure from the input form - this.tree = new ToolTree(this); - - // creates the job handler - this.job_handler = new ToolJobs(this); - - // request history content and build form - this.content = new ToolContent(this); - - // link model options - var options = this.options; - - // create ui elements - this._renderForm(options); - - // rebuild the underlying data structure - this.tree.finalize(); - - // show errors - if (options.errors) { - var error_messages = this.tree.matchResponse(options.errors); - for (var input_id in error_messages) { - this.highlight(input_id, error_messages[input_id], true); - } - } - - // add refresh listener - this.on('refresh', function() { - // by using/reseting the deferred ajax queue the number of redundant calls is reduced - self.deferred.reset(); - self.deferred.execute(function(){self._updateModel()}); - }); - - // add reset listener - this.on('reset', function() { - for (var i in this.element_list) { - this.element_list[i].reset(); - } - }); - }, - - /** Renders the UI elements required for the form - */ - _renderForm: function(options) { - // link this - var self = this; - - // create message view - this.message = new Ui.Message(); - - // button for version selection - var requirements_button = new Ui.ButtonIcon({ - icon : 'fa-info-circle', - title : 'Requirements', - tooltip : 'Display tool requirements', - onclick : function() { - if (!this.visible) { - this.visible = true; - self.message.update({ - persistent : true, - message : ToolTemplate.requirements(options), - status : 'info' - }); - } else { - this.visible = false; - self.message.update({ - message : '' - }); - } - } - }); - if (!options.requirements || options.requirements.length == 0) { - requirements_button.$el.hide(); - } - - // button for version selection - var versions_button = new Ui.ButtonMenu({ - icon : 'fa-cubes', - title : 'Versions', - tooltip : 'Select another tool version' - }); - if (options.versions && options.versions.length > 1) { - for (var i in options.versions) { - var version = options.versions[i]; - if (version != options.version) { - versions_button.addMenu({ - title : 'Switch to ' + version, - version : version, - icon : 'fa-cube', - onclick : function() { - // here we update the tool version (some tools encode the version also in the id) - options.id = options.id.replace(options.version, this.version); - options.version = this.version; - - // rebuild the model and form - self.deferred.reset(); - self.deferred.execute(function(){self._buildModel()}); - } - }); - } - } - } else { - versions_button.$el.hide(); - } - - // button menu - var menu_button = new Ui.ButtonMenu({ - icon : 'fa-caret-down', - title : 'Options', - tooltip : 'View available options' - }); - - // configure button selection - if(options.biostar_url) { - // add question option - menu_button.addMenu({ - icon : 'fa-question-circle', - title : 'Question?', - tooltip : 'Ask a question about this tool (Biostar)', - onclick : function() { - window.open(options.biostar_url + '/p/new/post/'); - } - }); - - // create search button - menu_button.addMenu({ - icon : 'fa-search', - title : 'Search', - tooltip : 'Search help for this tool (Biostar)', - onclick : function() { - window.open(options.biostar_url + '/t/' + options.id + '/'); - } - }); - }; - - // create share button - menu_button.addMenu({ - icon : 'fa-share', - title : 'Share', - tooltip : 'Share this tool', - onclick : function() { - prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id); - } - }); - - // add admin operations - if (this.is_admin) { - // create download button - menu_button.addMenu({ - icon : 'fa-download', - title : 'Download', - tooltip : 'Download this tool', - onclick : function() { - window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download'; - } - }); - } - - // create tool form section - this.section = new ToolSection.View(self, { - inputs : options.inputs, - cls : 'ui-table-plain' - }); - - // switch to classic tool form mako if the form definition is incompatible - if (this.incompatible) { - this.$el.hide(); - $('#tool-form-classic').show(); - return; - } - - // create portlet - this.portlet = new Portlet.View({ - icon : 'fa-wrench', - title : '' + options.name + ' ' + options.description + ' (Galaxy Tool Version ' + options.version + ')', - cls : 'ui-portlet-slim', - operations: { - requirements : requirements_button, - menu : menu_button, - versions : versions_button - }, - buttons: { - execute : new Ui.Button({ - icon : 'fa-check', - tooltip : 'Execute: ' + options.name, - title : 'Execute', - cls : 'btn btn-primary', - floating : 'clear', - onclick : function() { - self.job_handler.submit(); - } - }) - } - }); - - // append message - this.portlet.append(this.message.$el, true); - - // append tool section - this.portlet.append(this.section.$el); - - // start form - this.$el.empty(); - this.$el.append(this.portlet.$el); - - // append help - if (options.help != '') { - this.$el.append(ToolTemplate.help(options.help)); - } - - // append citations - if (options.citations) { - var $citations = $('
'); - var citations = new CitationModel.ToolCitationCollection(); - citations.tool_id = options.id; - var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations } ); - citation_list_view.render(); - citations.fetch(); - this.$el.append($citations); - } - - // show message if available in model - if (options.message) { - this.message.update({ - persistent : true, - status : 'warning', - message : options.message - }); - } } }); diff --git a/client/galaxy/scripts/mvc/tools/tools-jobs.js b/client/galaxy/scripts/mvc/tools/tools-jobs.js index 469d9b67daf..790b44dfa0c 100644 --- a/client/galaxy/scripts/mvc/tools/tools-jobs.js +++ b/client/galaxy/scripts/mvc/tools/tools-jobs.js @@ -98,7 +98,7 @@ return Backbone.Model.extend({ } // validate non-optional fields - if (!input_def.optional && input_field.validate && !input_field.validate()) { + if (!input_def.optional && input_value == null) { this.app.highlight(input_id); return false; } diff --git a/client/galaxy/scripts/mvc/tools/tools-section.js b/client/galaxy/scripts/mvc/tools/tools-section.js index fab21d2a8f7..8038a403079 100644 --- a/client/galaxy/scripts/mvc/tools/tools-section.js +++ b/client/galaxy/scripts/mvc/tools/tools-section.js @@ -382,6 +382,11 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep /** Data input field */ _fieldData : function(input_def) { + if (this.app.workflow) { + var extensions = Utils.textify(input_def.extensions.toString()); + input_def.info = 'Data input \'' + input_def.name + '\' (' + extensions + ')'; + return this._fieldHidden(input_def); + } var self = this; return new SelectContent.View(this.app, { id : 'field-' + input_def.id, @@ -398,6 +403,14 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep /** Select/Checkbox/Radio options field */ _fieldSelect : function (input_def) { + // show text field in workflow + if (this.app.workflow && input_def.is_dynamic) { + if (!Utils.validate(input_def.value)) { + input_def.value = ''; + } + return this._fieldText(input_def); + } + // configure options fields var options = []; for (var i in input_def.options) { @@ -475,7 +488,8 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep */ _fieldHidden : function(input_def) { return new Ui.Hidden({ - id : 'field-' + input_def.id + id : 'field-' + input_def.id, + info : input_def.info }); }, diff --git a/client/galaxy/scripts/mvc/tools/tools-select-content.js b/client/galaxy/scripts/mvc/tools/tools-select-content.js index 7453963d69d..fa7e64d922c 100644 --- a/client/galaxy/scripts/mvc/tools/tools-select-content.js +++ b/client/galaxy/scripts/mvc/tools/tools-select-content.js @@ -36,14 +36,7 @@ var View = Backbone.View.extend({ this.list = {}; // error messages - var extensions = options.extensions.toString(); - if (extensions) { - extensions = extensions.replace(/,/g, ', '); - var pos = extensions.lastIndexOf(', '); - if (pos != -1) { - extensions = extensions.substr(0, pos) + ' or ' + extensions.substr(pos+1); - } - } + var extensions = Utils.textify(options.extensions); var hda_error = 'No dataset available.'; if (extensions) { hda_error = 'No ' + extensions + ' dataset available.'; diff --git a/client/galaxy/scripts/mvc/tools/tools-tree.js b/client/galaxy/scripts/mvc/tools/tools-tree.js index af08497a719..3a8cde64e78 100644 --- a/client/galaxy/scripts/mvc/tools/tools-tree.js +++ b/client/galaxy/scripts/mvc/tools/tools-tree.js @@ -2,7 +2,7 @@ This class maps the tool form dom to an api compatible javascript dictionary. */ // dependencies -define([], function() { +define(['utils/utils'], function(Utils) { // tool form tree return Backbone.Model.extend({ @@ -120,7 +120,7 @@ return Backbone.Model.extend({ // handle default value if (!field.skip) { - if (input.optional && field.validate && !field.validate()) { + if (field.validate && !field.validate(value)) { value = null; } add (job_input_id, input.id, value); diff --git a/client/galaxy/scripts/mvc/ui/ui-misc.js b/client/galaxy/scripts/mvc/ui/ui-misc.js index 666fd78882c..efedef2c0c3 100644 --- a/client/galaxy/scripts/mvc/ui/ui-misc.js +++ b/client/galaxy/scripts/mvc/ui/ui-misc.js @@ -412,7 +412,7 @@ var Hidden = Backbone.View.extend({ initialize : function(options) { // configure options this.options = options; - + // create new element this.setElement(this._template(this.options)); @@ -425,14 +425,20 @@ var Hidden = Backbone.View.extend({ // value value : function (new_val) { if (new_val !== undefined) { - this.$el.val(new_val); + this.$('hidden').val(new_val); } - return this.$el.val(); + return this.$('hidden').val(); }, // element _template: function(options) { - return ''; + var tmpl = '
'; + if (options.info) { + tmpl += ''; + } + tmpl += '' + + '
'; + return tmpl; } }); diff --git a/client/galaxy/scripts/mvc/ui/ui-options.js b/client/galaxy/scripts/mvc/ui/ui-options.js index 8d47dfe0330..21e0911747f 100644 --- a/client/galaxy/scripts/mvc/ui/ui-options.js +++ b/client/galaxy/scripts/mvc/ui/ui-options.js @@ -137,16 +137,7 @@ var Base = Backbone.View.extend({ /** Validate the selected option/options */ validate: function() { - var current = this.value(); - if (!(current instanceof Array)) { - current = [current]; - } - for (var i in current) { - if ([null, 'null', undefined].indexOf(current[i]) > -1) { - return false; - } - } - return true; + return Utils.validate(this.value()); }, /** Wait message during request processing diff --git a/client/galaxy/scripts/mvc/ui/ui-select-default.js b/client/galaxy/scripts/mvc/ui/ui-select-default.js index 6171ca3043d..99b63065e92 100644 --- a/client/galaxy/scripts/mvc/ui/ui-select-default.js +++ b/client/galaxy/scripts/mvc/ui/ui-select-default.js @@ -96,16 +96,7 @@ var View = Backbone.View.extend({ /** Validate the current selection */ validate: function() { - var current = this.value(); - if (!(current instanceof Array)) { - current = [current]; - } - for (var i in current) { - if ([null, 'null', undefined].indexOf(current[i]) > -1) { - return false; - } - } - return true; + return Utils.validate(this.value()); }, /** Return the label/text of the current selection diff --git a/client/galaxy/scripts/mvc/upload/upload-view.js b/client/galaxy/scripts/mvc/upload/upload-view.js index 064139f2caa..db33657cf6d 100644 --- a/client/galaxy/scripts/mvc/upload/upload-view.js +++ b/client/galaxy/scripts/mvc/upload/upload-view.js @@ -104,7 +104,7 @@ return Backbone.View.extend({ }); // define location - $('#left .unified-panel-header-inner').append((new UploadButton.View(this.ui_button)).$el); + $('.with-upload-button').append((new UploadButton.View(this.ui_button)).$el); // load extension var self = this; diff --git a/client/galaxy/scripts/utils/utils.js b/client/galaxy/scripts/utils/utils.js index 86432291d25..be978959a7d 100644 --- a/client/galaxy/scripts/utils/utils.js +++ b/client/galaxy/scripts/utils/utils.js @@ -14,6 +14,40 @@ function sanitize(content) { return $('
').text(content).html(); }; +/** + * Validate atomic values or list of values + * usually used for selectable options + * @param{String} value - Value or list to be validated + */ +function validate (value) { + if (!(value instanceof Array)) { + value = [value]; + } + for (var i in value) { + if (['None', null, 'null', undefined, 'undefined'].indexOf(value[i]) > -1) { + return false; + } + } + return true; +}; + +/** + * Convert list to pretty string + * @param{String} lst - List of strings to be converted in human readable list sentence + */ +function textify(lst) { + var lst = lst.toString(); + if (lst) { + lst = lst.replace(/,/g, ', '); + var pos = lst.lastIndexOf(', '); + if (pos != -1) { + lst = lst.substr(0, pos) + ' or ' + lst.substr(pos+1); + } + return lst; + } + return ''; +}; + /** * Request handler for GET * @param{String} url - Url request is made to @@ -212,7 +246,9 @@ return { time: time, wrap: wrap, request: request, - sanitize: sanitize + sanitize: sanitize, + textify: textify, + validate: validate }; }); diff --git a/config/datatypes_conf.xml.sample b/config/datatypes_conf.xml.sample index 4f9aca67bd4..50b55b28cca 100644 --- a/config/datatypes_conf.xml.sample +++ b/config/datatypes_conf.xml.sample @@ -181,7 +181,8 @@ - + + @@ -271,6 +272,7 @@ --> + diff --git a/lib/galaxy/datatypes/binary.py b/lib/galaxy/datatypes/binary.py index 8b45b610d53..19669f6dd02 100644 --- a/lib/galaxy/datatypes/binary.py +++ b/lib/galaxy/datatypes/binary.py @@ -20,7 +20,7 @@ eggs.require( "bx-python" ) from bx.seq.twobit import TWOBIT_MAGIC_NUMBER, TWOBIT_MAGIC_NUMBER_SWAP, TWOBIT_MAGIC_SIZE from galaxy.util import sqlite -from galaxy.datatypes.metadata import MetadataElement,ListParameter,DictParameter +from galaxy.datatypes.metadata import MetadataElement, MetadataParameter, ListParameter, DictParameter from galaxy.datatypes import metadata import dataproviders @@ -640,8 +640,66 @@ class SQlite ( Binary ): return dataproviders.dataset.SQliteDataDictProvider( dataset_source, **settings ) +#Binary.register_sniffable_binary_format("sqlite", "sqlite", SQlite) + + +class GeminiSQLite( SQlite ): + """Class describing a Gemini Sqlite database """ + MetadataElement( name="gemini_version", default='0.10.0' , param=MetadataParameter, desc="Gemini Version", + readonly=True, visible=True, no_value='0.10.0' ) + file_ext = "gemini.sqlite" + + def set_meta( self, dataset, overwrite = True, **kwd ): + super( GeminiSQLite, self ).set_meta( dataset, overwrite = overwrite, **kwd ) + try: + conn = sqlite.connect( dataset.file_name ) + c = conn.cursor() + tables_query = "SELECT version FROM version" + result = c.execute( tables_query ).fetchall() + for version, in result: + dataset.metadata.gemini_version = version + # TODO: Can/should we detect even more attributes, such as use of PED file, what was input annotation type, etc. + except Exception, e: + log.warn( '%s, set_meta Exception: %s', self, e ) + + def sniff( self, filename ): + if super( GeminiSQLite, self ).sniff( filename ): + gemini_table_names = [ "gene_detailed", "gene_summary", "resources", "sample_genotype_counts", "sample_genotypes", "samples", + "variant_impacts", "variants", "version" ] + try: + conn = sqlite.connect( filename ) + c = conn.cursor() + tables_query = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" + result = c.execute( tables_query ).fetchall() + result = map( lambda x: x[0], result ) + for table_name in gemini_table_names: + if table_name not in result: + return False + return True + except Exception, e: + log.warn( '%s, sniff Exception: %s', self, e ) + return False + + def set_peek( self, dataset, is_multi_byte=False ): + if not dataset.dataset.purged: + dataset.peek = "Gemini SQLite Database, version %s" % ( dataset.metadata.gemini_version or 'unknown' ) + dataset.blurb = data.nice_size( dataset.get_size() ) + else: + dataset.peek = 'file does not exist' + dataset.blurb = 'file purged from disk' + + def display_peek( self, dataset ): + try: + return dataset.peek + except: + return "Gemini SQLite Database, version %s" % ( dataset.metadata.gemini_version or 'unknown' ) + +Binary.register_sniffable_binary_format( "gemini.sqlite", "gemini.sqlite", GeminiSQLite ) +# FIXME: We need to register gemini.sqlite before sqlite, since register_sniffable_binary_format and is_sniffable_binary called in upload.py +# ignores sniff order declared in datatypes_conf.xml Binary.register_sniffable_binary_format("sqlite", "sqlite", SQlite) + class Xlsx(Binary): """Class for Excel 2007 (xlsx) files""" file_ext="xlsx" diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 63409c2191b..9d0231ddc1c 100755 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -33,7 +33,7 @@ from galaxy.tools.actions.upload import UploadToolAction from galaxy.tools.actions.data_source import DataSourceToolAction from galaxy.tools.actions.data_manager import DataManagerToolAction from galaxy.tools.deps import build_dependency_manager -from galaxy.tools.parameters import check_param, params_from_strings, params_to_strings +from galaxy.tools.parameters import params_to_incoming, check_param, params_from_strings, params_to_strings from galaxy.tools.parameters import output_collect from galaxy.tools.parameters.basic import (BaseURLToolParameter, DataToolParameter, HiddenToolParameter, LibraryDatasetToolParameter, @@ -1843,7 +1843,8 @@ class Tool( object, Dictifiable ): """Return a list of commands to be run to populate the current environment to include this tools requirements.""" return self.app.toolbox.dependency_manager.dependency_shell_commands( self.requirements, - installed_tool_dependencies=self.installed_tool_dependencies + installed_tool_dependencies=self.installed_tool_dependencies, + tool_dir=self.tool_dir, ) @property @@ -2072,6 +2073,336 @@ class Tool( object, Dictifiable ): return tool_dict + def to_json (self, trans, **kwd): + """ + Recursively creates a tool dictionary containing repeats, dynamic options and updated states. + """ + job_id = kwd.get('job_id', None) + dataset_id = kwd.get('dataset_id', None) + + # load job details if provided + job = None + if job_id: + try: + job_id = trans.security.decode_id( job_id ) + job = trans.sa_session.query( trans.app.model.Job ).get( job_id ) + except Exception, exception: + trans.response.status = 500 + log.error('Failed to retrieve job.') + return { 'error': 'Failed to retrieve job.' } + elif dataset_id: + try: + dataset_id = trans.security.decode_id( dataset_id ) + data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id ) + if not ( trans.user_is_admin() or trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), data.dataset ) ): + trans.response.status = 500 + log.error('User has no access to dataset.') + return { 'error': 'User has no access to dataset.' } + job = data.creating_job + if not job: + trans.response.status = 500 + log.error('Creating job not found.') + return { 'error': 'Creating job not found.' } + except Exception, exception: + trans.response.status = 500 + log.error('Failed to get job information.') + return { 'error': 'Failed to get job information.' } + + # load job parameters into incoming + tool_message = '' + if job: + try: + job_params = job.get_param_values( trans.app, ignore_errors = True ) + job_messages = self.check_and_update_param_values( job_params, trans, update_values=False ) + tool_message = self._compare_tool_version(trans, job) + params_to_incoming( kwd, self.inputs, job_params, trans.app ) + except Exception, exception: + trans.response.status = 500 + return { 'error': str( exception ) } + + # create parameter object + params = galaxy.util.Params( kwd, sanitize = False ) + + # convert value to jsonifiable value + def jsonify(v): + # check if value is numeric + isnumber = False + try: + float(v) + isnumber = True + except Exception: + pass + + # fix hda parsing + if isinstance(v, trans.app.model.HistoryDatasetAssociation): + return { + 'id' : trans.security.encode_id(v.id), + 'src' : 'hda' + } + elif isinstance(v, bool): + if v is True: + return 'true' + else: + return 'false' + elif isinstance(v, basestring) or isnumber: + return v + else: + return None + + # ensures that input dictionary is jsonifiable + def sanitize(dict): + # get current value + value = dict['value'] if 'value' in dict else None + + # jsonify by type + if dict['type'] in ['data']: + if isinstance(value, list): + value = [ jsonify(v) for v in value ] + else: + value = [ jsonify(value) ] + value = { 'values': value } + elif isinstance(value, list): + value = [ jsonify(v) for v in value ] + else: + value = jsonify(value) + + # update and return + dict['value'] = value + return dict + + # initialize state using default parameters + def initialize_state(trans, inputs, state, context=None): + context = ExpressionContext(state, context) + for input in inputs.itervalues(): + state[input.name] = input.get_initial_value(trans, context) + + # check the current state of a value and update it if necessary + def check_state(trans, input, default_value, context): + value = default_value + error = 'State validation failed.' + try: + # resolves the inconsistent definition of boolean parameters (see base.py) without modifying shared code + if input.type == 'boolean' and isinstance(default_value, basestring): + value, error = [util.string_as_bool(default_value), None] + else: + value, error = check_param(trans, input, default_value, context) + except Exception, err: + log.error('Checking parameter failed. %s', str(err)) + pass + return [value, error] + + # populates state with incoming url parameters + def populate_state(trans, inputs, state, errors, incoming, prefix="", context=None ): + context = ExpressionContext(state, context) + for input in inputs.itervalues(): + key = prefix + input.name + if input.type == 'repeat': + group_state = state[input.name] + rep_index = 0 + del group_state[:] + while True: + rep_name = "%s_%d" % (key, rep_index) + if not any([incoming_key.startswith(rep_name) for incoming_key in incoming.keys()]): + break + if rep_index < input.max: + new_state = {} + new_state['__index__'] = rep_index + initialize_state(trans, input.inputs, new_state, context) + group_state.append(new_state) + populate_state(trans, input.inputs, new_state, errors, incoming, prefix=rep_name + "|", context=context) + rep_index += 1 + elif input.type == 'conditional': + group_state = state[input.name] + group_prefix = "%s|" % ( key ) + test_param_key = group_prefix + input.test_param.name + default_value = incoming.get(test_param_key, group_state.get(input.test_param.name, None)) + value, error = check_state(trans, input.test_param, default_value, context) + if error: + errors[test_param_key] = error + else: + try: + current_case = input.get_current_case(value, trans) + group_state = state[input.name] = {} + initialize_state(trans, input.cases[current_case].inputs, group_state, context) + populate_state( trans, input.cases[current_case].inputs, group_state, errors, incoming, prefix=group_prefix, context=context) + group_state['__current_case__'] = current_case + except Exception, e: + errors[test_param_key] = 'The selected case is unavailable/invalid.' + pass + group_state[input.test_param.name] = value + else: + default_value = incoming.get(key, state.get(input.name, None)) + value, error = check_state(trans, input, default_value, context) + if error: + errors[key] = error + state[input.name] = value + + # builds tool model including all attributes + def iterate(group_inputs, inputs, state_inputs, other_values=None): + other_values = ExpressionContext( state_inputs, other_values ) + for input_index, input in enumerate( inputs.itervalues() ): + # create model dictionary + tool_dict = input.to_dict(trans) + if tool_dict is None: + continue + + # state for subsection/group + group_state = state_inputs[input.name] + + # iterate and update values + if input.type == 'repeat': + group_cache = tool_dict['cache'] = {} + for i in range( len( group_state ) ): + group_cache[i] = {} + iterate( group_cache[i], input.inputs, group_state[i], other_values ) + elif input.type == 'conditional': + if 'test_param' in tool_dict: + test_param = tool_dict['test_param'] + test_param['value'] = jsonify(group_state[test_param['name']]) + if '__current_case__' in group_state: + i = group_state['__current_case__'] + iterate(tool_dict['cases'][i]['inputs'], input.cases[i].inputs, group_state, other_values) + else: + # create input dictionary, try to pass other_values if to_dict function supports it e.g. dynamic options + try: + tool_dict = input.to_dict(trans, other_values=other_values) + except Exception: + pass + + # identify name + input_name = tool_dict.get('name') + if input_name: + # update input value from tool state + if input_name in state_inputs: + tool_dict['value'] = state_inputs[input_name] + + # sanitize if value exists + tool_dict = sanitize(tool_dict) + + # backup final input dictionary + group_inputs[input_index] = tool_dict + + # sanatization for the final tool state + def sanitize_state(state): + keys = None + if isinstance(state, dict): + keys = state + elif isinstance(state, list): + keys = range( len(state) ) + if keys: + for k in keys: + if isinstance(state[k], dict) or isinstance(state[k], list): + sanitize_state(state[k]) + else: + state[k] = jsonify(state[k]) + + # do param translation here, used by datasource tools + if self.input_translator: + self.input_translator.translate( params ) + + # initialize and populate tool state + state_inputs = {} + state_errors = {} + initialize_state(trans, self.inputs, state_inputs) + populate_state(trans, self.inputs, state_inputs, state_errors, params.__dict__) + + # create basic tool model + tool_model = self.to_dict(trans) + tool_model['inputs'] = {} + + # build tool model and tool state + iterate(tool_model['inputs'], self.inputs, state_inputs, '') + + # sanitize tool state + sanitize_state(state_inputs) + + # load tool help + tool_help = '' + if self.help: + tool_help = self.help + tool_help = tool_help.render( static_path=url_for( '/static' ), host_url=url_for('/', qualified=True) ) + if type( tool_help ) is not unicode: + tool_help = unicode( tool_help, 'utf-8') + + # check if citations exist + tool_citations = False + if self.citations: + tool_citations = True + + # get tool versions + tool_versions = [] + tools = self.app.toolbox.get_loaded_tools_by_lineage(self.id) + for t in tools: + tool_versions.append(t.version) + + ## add information with underlying requirements and their versions + tool_requirements = [] + if self.requirements: + for requirement in self.requirements: + tool_requirements.append({ + 'name' : requirement.name, + 'version' : requirement.version + }) + + # add additional properties + tool_model.update({ + 'help' : tool_help, + 'citations' : tool_citations, + 'biostar_url' : trans.app.config.biostar_url, + 'message' : tool_message, + 'versions' : tool_versions, + 'requirements' : tool_requirements, + 'errors' : state_errors, + 'state_inputs' : state_inputs + }) + + # check for errors + if 'error' in tool_message: + return tool_message + + # return enriched tool model + return tool_model + + def _compare_tool_version( self, trans, job ): + """ + Compares a tool version with the tool version from a job (from ToolRunner). + """ + tool_id = job.tool_id + tool_version = job.tool_version + message = '' + try: + select_field, tools, tool = self.app.toolbox.get_tool_components( tool_id, tool_version=tool_version, get_loaded_tools_by_lineage=False, set_selected=True ) + if tool is None: + trans.response.status = 500 + return { 'error': 'This dataset was created by an obsolete tool (%s). Can\'t re-run.' % tool_id } + if ( self.id != tool_id and self.old_id != tool_id ) or self.version != tool_version: + if self.id == tool_id: + if tool_version == None: + # for some reason jobs don't always keep track of the tool version. + message = '' + else: + message = 'This job was initially run with tool version "%s", which is currently not available. ' % tool_version + if len( tools ) > 1: + message += 'You can re-run the job with the selected tool or choose another derivation of the tool.' + else: + message += 'You can re-run the job with this tool version, which is a derivation of the original tool.' + else: + if len( tools ) > 1: + message = 'This job was initially run with tool version "%s", which is currently not available. ' % tool_version + message += 'You can re-run the job with the selected tool or choose another derivation of the tool.' + else: + message = 'This job was initially run with tool id "%s", version "%s", which is ' % ( tool_id, tool_version ) + message += 'currently not available. You can re-run the job with this tool, which is a derivation of the original tool.' + except Exception, error: + trans.response.status = 500 + return { 'error': str (error) } + + # can't rerun upload, external data sources, et cetera. workflow compatible will proxy this for now + #if not self.is_workflow_compatible: + # trans.response.status = 500 + # return { 'error': 'The \'%s\' tool does currently not support re-running.' % self.name } + return message + def get_default_history_by_trans( self, trans, create=False ): return trans.get_history( create=create ) diff --git a/lib/galaxy/tools/deps/brew_exts.py b/lib/galaxy/tools/deps/brew_exts.py index fdf543b964a..763bac60697 100755 --- a/lib/galaxy/tools/deps/brew_exts.py +++ b/lib/galaxy/tools/deps/brew_exts.py @@ -28,6 +28,7 @@ import glob import os import re import sys +import string import subprocess WHITESPACE_PATTERN = re.compile("[\s]+") @@ -43,6 +44,7 @@ NO_BREW_ERROR_MESSAGE = "Could not find brew on PATH, please place on path or pa CANNOT_DETERMINE_TAP_ERROR_MESSAGE = "Cannot determine tap of specified recipe - please use fully qualified recipe (e.g. homebrew/science/samtools)." VERBOSE = False RELAXED = False +BREW_ARGS = [] class BrewContext(object): @@ -104,6 +106,7 @@ class RecipeContext(object): def main(): global VERBOSE global RELAXED + global BREW_ARGS parser = argparse.ArgumentParser(description=DESCRIPTION) parser.add_argument("--brew", help="Path to linuxbrew 'brew' executable to target") actions = ["vinstall", "vuninstall", "vdeps", "vinfo", "env"] @@ -114,11 +117,13 @@ def main(): parser.add_argument('version', metavar='version', help="Version for action (e.g. 0.1.19).") parser.add_argument('--relaxed', action='store_true', help="Relaxed processing - for instance allow use of env on non-vinstall-ed recipes.") parser.add_argument('--verbose', action='store_true', help="Verbose output") + parser.add_argument('restargs', nargs=argparse.REMAINDER) args = parser.parse_args() if args.verbose: VERBOSE = True if args.relaxed: RELAXED = True + BREW_ARGS = args.restargs if not action: action = args.action brew_context = BrewContext(args) @@ -159,7 +164,7 @@ class CommandLineException(Exception): return self.message -def versioned_install(recipe_context, package=None, version=None): +def versioned_install(recipe_context, package=None, version=None, installed_deps=[]): if package is None: package = recipe_context.recipe version = recipe_context.version @@ -176,10 +181,15 @@ def versioned_install(recipe_context, package=None, version=None): versioned = version_info[2] if versioned: dep_to_version[dep] = dep_version + if dep in installed_deps: + continue versioned_install(recipe_context, dep, dep_version) + installed_deps.append(dep) else: # Install latest. dep_to_version[dep] = None + if dep in installed_deps: + continue unversioned_install(dep) try: for dep in deps: @@ -198,7 +208,16 @@ def versioned_install(recipe_context, package=None, version=None): } deps_metadata.append(dep_metadata) - brew_execute(["install", package]) + cellar_root = recipe_context.brew_context.homebrew_cellar + cellar_path = recipe_context.cellar_path + env_actions = build_env_actions(deps_metadata, cellar_root, cellar_path, custom_only=True) + env = EnvAction.build_env(env_actions) + args = ["install"] + if VERBOSE: + args.append("--verbose") + args.extend(BREW_ARGS) + args.append(package) + brew_execute(args, env=env) deps = brew_execute(["deps", package]) deps = [d.strip() for d in deps.split("\n") if d] metadata = { @@ -278,10 +297,10 @@ def attempt_unlink(package): pass -def brew_execute(args): +def brew_execute(args, env=None): os.environ["HOMEBREW_NO_EMOJI"] = "1" # simplify brew parsing. cmds = ["brew"] + args - return execute(cmds) + return execute(cmds, env=env) def build_env_statements_from_recipe_context(recipe_context, **kwds): @@ -290,11 +309,20 @@ def build_env_statements_from_recipe_context(recipe_context, **kwds): return env_statements -def build_env_statements(cellar_root, cellar_path, relaxed=None): +def build_env_statements(cellar_root, cellar_path, relaxed=None, custom_only=False): deps = load_versioned_deps(cellar_path, relaxed=relaxed) + actions = build_env_actions(deps, cellar_root, cellar_path, relaxed, custom_only) + env_statements = [] + for action in actions: + env_statements.extend(action.to_statements()) + return "\n".join(env_statements) + + +def build_env_actions(deps, cellar_root, cellar_path, relaxed=None, custom_only=False): path_appends = [] ld_path_appends = [] + actions = [] def handle_keg(cellar_path): bin_path = os.path.join(cellar_path, "bin") @@ -303,6 +331,14 @@ def build_env_statements(cellar_root, cellar_path, relaxed=None): lib_path = os.path.join(cellar_path, "lib") if os.path.isdir(lib_path): ld_path_appends.append(lib_path) + env_path = os.path.join(cellar_path, "platform_environment.json") + if os.path.exists(env_path): + with open(env_path, "r") as f: + env_metadata = json.load(f) + if "actions" in env_metadata: + def to_action(desc): + return EnvAction(cellar_path, desc) + actions.extend(map(to_action, env_metadata["actions"])) for dep in deps: package = dep['name'] @@ -311,14 +347,54 @@ def build_env_statements(cellar_root, cellar_path, relaxed=None): handle_keg( dep_cellar_path ) handle_keg( cellar_path ) - env_statements = [] - if path_appends: - env_statements.append("PATH=" + ":".join(path_appends) + ":$PATH") - env_statements.append("export PATH") - if ld_path_appends: - env_statements.append("LD_LIBRARY_PATH=" + ":".join(ld_path_appends) + ":$LD_LIBRARY_PATH") - env_statements.append("export LD_LIBRARY_PATH") - return "\n".join(env_statements) + if not custom_only: + if path_appends: + actions.append(EnvAction(cellar_path, {"action": "prepend", "variable": "PATH", "value": ":".join(path_appends)})) + if ld_path_appends: + actions.append(EnvAction(cellar_path, {"action": "prepend", "variable": "LD_LIBRARY_PATH", "value": ":".join(path_appends)})) + return actions + + +class EnvAction(object): + + def __init__(self, keg_root, action_description): + self.variable = action_description["variable"] + self.action = action_description["action"] + self.value = string.Template(action_description["value"]).safe_substitute({ + 'KEG_ROOT': keg_root, + }) + + @staticmethod + def build_env(env_actions): + new_env = os.environ.copy() + map(lambda env_action: env_action.modify_environ(new_env), env_actions) + return new_env + + def modify_environ(self, environ): + if self.action == "set" or not environ.get(self.variable, ""): + environ[self.variable] = self.__eval("${value}") + elif self.action == "prepend": + environ[self.variable] = self.__eval("${value}:%s" % environ[self.variable]) + else: + environ[self.variable] = self.__eval("%s:${value}" % environ[self.variable]) + + def __eval(self, template): + return string.Template(template).safe_substitute( + variable=self.variable, + value=self.value, + ) + + def to_statements(self): + if self.action == "set": + template = '''${variable}="${value}"''' + elif self.action == "prepend": + template = '''${variable}="${value}:$$${variable}"''' + else: + template = '''${variable}="$$${variable}:${value}"''' + return [ + self.__eval(template), + "export %s" % self.variable + ] @contextlib.contextmanager @@ -350,8 +426,15 @@ def git_execute(args): return execute(cmds) -def execute(cmds): - p = subprocess.Popen(cmds, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +def execute(cmds, env=None): + subprocess_kwds = dict( + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + if env: + subprocess_kwds["env"] = env + p = subprocess.Popen(cmds, **subprocess_kwds) #log = p.stdout.read() global VERBOSE stdout, stderr = p.communicate() @@ -363,7 +446,10 @@ def execute(cmds): def brew_deps(package): - stdout = brew_execute(["deps", package]) + args = ["deps"] + args.extend(BREW_ARGS) + args.append(package) + stdout = brew_execute(args) return [p.strip() for p in stdout.split("\n") if p] diff --git a/lib/galaxy/tools/deps/resolvers/brewed_tool_shed_packages.py b/lib/galaxy/tools/deps/resolvers/brewed_tool_shed_packages.py new file mode 100644 index 00000000000..6a4cb989809 --- /dev/null +++ b/lib/galaxy/tools/deps/resolvers/brewed_tool_shed_packages.py @@ -0,0 +1,150 @@ +""" +This dependency resolver resolves tool shed dependencies (those defined +tool_dependencies.xml) installed using Platform Homebrew and converted +via shed2tap (e.g. https://github.com/jmchilton/homebrew-toolshed). +""" +import logging +import os +from xml.etree import ElementTree as ET + +from .resolver_mixins import ( + UsesHomebrewMixin, + UsesToolDependencyDirMixin, + UsesInstalledRepositoriesMixin, +) +from ..resolvers import DependencyResolver, INDETERMINATE_DEPENDENCY + +log = logging.getLogger(__name__) + + +class HomebrewToolShedDependencyResolver( + DependencyResolver, + UsesHomebrewMixin, + UsesToolDependencyDirMixin, + UsesInstalledRepositoriesMixin, +): + resolver_type = "tool_shed_tap" + + def __init__(self, dependency_manager, **kwds): + self._init_homebrew(**kwds) + self._init_base_path(dependency_manager, **kwds) + + def resolve(self, name, version, type, **kwds): + if type != "package": + return INDETERMINATE_DEPENDENCY + + if version is None: + return INDETERMINATE_DEPENDENCY + + return self._find_tool_dependencies(name, version, type, **kwds) + + def _find_tool_dependencies(self, name, version, type, **kwds): + installed_tool_dependency = self._get_installed_dependency(name, type, version=version, **kwds) + if installed_tool_dependency: + return self._resolve_from_installed_tool_dependency(name, version, installed_tool_dependency) + + if "tool_dir" in kwds: + tool_directory = os.path.abspath(kwds["tool_dir"]) + tool_depenedencies_path = os.path.join(tool_directory, "tool_dependencies.xml") + if os.path.exists(tool_depenedencies_path): + return self._resolve_from_tool_dependencies_path(name, version, tool_depenedencies_path) + + return INDETERMINATE_DEPENDENCY + + def _resolve_from_installed_tool_dependency(self, name, version, installed_tool_dependency): + tool_shed_repository = installed_tool_dependency.tool_shed_repository + recipe_name = build_recipe_name( + package_name=name, + package_version=version, + repository_owner=tool_shed_repository.owner, + repository_name=tool_shed_repository.name, + ) + return self._find_dep_default(recipe_name, None) + + def _resolve_from_tool_dependencies_path(self, name, version, tool_dependencies_path): + try: + raw_dependencies = RawDependencies(tool_dependencies_path) + except Exception: + log.debug("Failed to parse dependencies in file %s" % tool_dependencies_path) + return INDETERMINATE_DEPENDENCY + + raw_dependency = raw_dependencies.find(name, version) + if not raw_dependency: + return INDETERMINATE_DEPENDENCY + + recipe_name = build_recipe_name( + package_name=name, + package_version=version, + repository_owner=raw_dependency.repository_owner, + repository_name=raw_dependency.repository_name + ) + dep = self._find_dep_default(recipe_name, None) + return dep + + +class RawDependencies(object): + + def __init__(self, dependencies_file): + self.root = ET.parse(dependencies_file).getroot() + dependencies = [] + package_els = self.root.findall("package") or [] + for package_el in package_els: + repository_el = package_el.find("repository") + if repository_el is None: + continue + dependency = RawDependency(self, package_el, repository_el) + dependencies.append(dependency) + self.dependencies = dependencies + + def find(self, package_name, package_version): + target_dependency = None + + for dependency in self.dependencies: + if dependency.package_name == package_name and dependency.package_version == package_version: + target_dependency = dependency + break + return target_dependency + + +class RawDependency(object): + + def __init__(self, dependencies, package_el, repository_el): + self.dependencies = dependencies + self.package_el = package_el + self.repository_el = repository_el + + def __repr__(self): + temp = "Dependency[package_name=%s,version=%s,dependent_package=%s]" + return temp % ( + self.package_el.attrib["name"], + self.package_el.attrib["version"], + self.repository_el.attrib["name"] + ) + + @property + def repository_owner(self): + return self.repository_el.attrib["owner"] + + @property + def repository_name(self): + return self.repository_el.attrib["name"] + + @property + def package_name(self): + return self.package_el.attrib["name"] + + @property + def package_version(self): + return self.package_el.attrib["version"] + + +def build_recipe_name(package_name, package_version, repository_owner, repository_name): + # TODO: Consider baking package_name and package_version into name? (would be more "correct") + owner = repository_owner.replace("-", "") + name = repository_name + name = name.replace("_", "").replace("-", "") + base = "%s_%s" % (owner, name) + return base + + +__all__ = [HomebrewToolShedDependencyResolver] diff --git a/lib/galaxy/tools/deps/resolvers/galaxy_packages.py b/lib/galaxy/tools/deps/resolvers/galaxy_packages.py index 7f250479315..99eab3a6939 100644 --- a/lib/galaxy/tools/deps/resolvers/galaxy_packages.py +++ b/lib/galaxy/tools/deps/resolvers/galaxy_packages.py @@ -1,12 +1,13 @@ -from os.path import join, islink, realpath, basename, exists, abspath +from os.path import join, islink, realpath, basename, exists from ..resolvers import DependencyResolver, INDETERMINATE_DEPENDENCY, Dependency +from .resolver_mixins import UsesToolDependencyDirMixin import logging log = logging.getLogger( __name__ ) -class GalaxyPackageDependencyResolver(DependencyResolver): +class GalaxyPackageDependencyResolver(DependencyResolver, UsesToolDependencyDirMixin): resolver_type = "galaxy_packages" def __init__(self, dependency_manager, **kwds): @@ -16,7 +17,7 @@ class GalaxyPackageDependencyResolver(DependencyResolver): ## resolver that will just grab 'default' version of exact version ## unavailable. self.versionless = str(kwds.get('versionless', "false")).lower() == "true" - self.base_path = abspath( kwds.get('base_path', dependency_manager.default_base_path) ) + self._init_base_path( dependency_manager, **kwds ) def resolve( self, name, version, type, **kwds ): """ diff --git a/lib/galaxy/tools/deps/resolvers/homebrew.py b/lib/galaxy/tools/deps/resolvers/homebrew.py index 20f180a2928..53885041ebd 100644 --- a/lib/galaxy/tools/deps/resolvers/homebrew.py +++ b/lib/galaxy/tools/deps/resolvers/homebrew.py @@ -12,20 +12,19 @@ This is still an experimental module and there will almost certainly be backward incompatible changes coming. """ -import os -from ..brew_exts import DEFAULT_HOMEBREW_ROOT, recipe_cellar_path, build_env_statements -from ..resolvers import DependencyResolver, INDETERMINATE_DEPENDENCY, Dependency +from .resolver_mixins import UsesHomebrewMixin +from ..resolvers import DependencyResolver, INDETERMINATE_DEPENDENCY # TODO: Implement prefer version linked... PREFER_VERSION_LINKED = 'linked' PREFER_VERSION_LATEST = 'latest' -UNKNOWN_PREFER_VERSION_MESSAGE_TEMPLATE = "HomebrewDependencyResolver prefer_version must be latest %s" +UNKNOWN_PREFER_VERSION_MESSAGE_TEMPLATE = "HomebrewDependencyResolver prefer_version must be %s" UNKNOWN_PREFER_VERSION_MESSAGE = UNKNOWN_PREFER_VERSION_MESSAGE_TEMPLATE % (PREFER_VERSION_LATEST) DEFAULT_PREFER_VERSION = PREFER_VERSION_LATEST -class HomebrewDependencyResolver(DependencyResolver): +class HomebrewDependencyResolver(DependencyResolver, UsesHomebrewMixin): resolver_type = "homebrew" def __init__(self, dependency_manager, **kwds): @@ -38,11 +37,7 @@ class HomebrewDependencyResolver(DependencyResolver): if self.versionless and self.prefer_version not in [PREFER_VERSION_LATEST]: raise Exception(UNKNOWN_PREFER_VERSION_MESSAGE) - cellar_root = kwds.get('cellar', None) - if cellar_root is None: - cellar_root = os.path.join(DEFAULT_HOMEBREW_ROOT, "Cellar") - - self.cellar_root = cellar_root + self._init_homebrew(**kwds) def resolve(self, name, version, type, **kwds): if type != "package": @@ -53,41 +48,6 @@ class HomebrewDependencyResolver(DependencyResolver): else: return self._find_dep_versioned(name, version) - def _find_dep_versioned(self, name, version): - recipe_path = recipe_cellar_path(self.cellar_root, name, version) - if not os.path.exists(recipe_path) or not os.path.isdir(recipe_path): - return INDETERMINATE_DEPENDENCY - - commands = build_env_statements(self.cellar_root, recipe_path, relaxed=True) - return HomebrewDependency(commands) - - def _find_dep_default(self, name, version): - installed_versions = self._installed_versions(name) - if not installed_versions: - return INDETERMINATE_DEPENDENCY - - # Just grab newest installed version - may make sense some day to find - # the linked version instead. - default_version = sorted(installed_versions, reverse=True)[0] - return self._find_dep_versioned(name, default_version) - - def _installed_versions(self, recipe): - recipe_base_path = os.path.join(self.cellar_root, recipe) - if not os.path.exists(recipe_base_path): - return [] - - names = os.listdir(recipe_base_path) - return filter(lambda n: os.path.isdir(os.path.join(recipe_base_path, n)), names) - - -class HomebrewDependency(Dependency): - - def __init__(self, commands): - self.commands = commands - - def shell_commands(self, requirement): - return self.commands.replace("\n", ";") + "\n" - def _string_as_bool( value ): return str( value ).lower() == "true" diff --git a/lib/galaxy/tools/deps/resolvers/resolver_mixins.py b/lib/galaxy/tools/deps/resolvers/resolver_mixins.py new file mode 100644 index 00000000000..661d87ecaee --- /dev/null +++ b/lib/galaxy/tools/deps/resolvers/resolver_mixins.py @@ -0,0 +1,73 @@ +import os +from ..brew_exts import DEFAULT_HOMEBREW_ROOT, recipe_cellar_path, build_env_statements +from ..resolvers import INDETERMINATE_DEPENDENCY, Dependency + + +class UsesHomebrewMixin: + + def _init_homebrew(self, **kwds): + cellar_root = kwds.get('cellar', None) + if cellar_root is None: + cellar_root = os.path.join(DEFAULT_HOMEBREW_ROOT, "Cellar") + + self.cellar_root = cellar_root + + def _find_dep_versioned(self, name, version): + recipe_path = recipe_cellar_path(self.cellar_root, name, version) + if not os.path.exists(recipe_path) or not os.path.isdir(recipe_path): + return INDETERMINATE_DEPENDENCY + + commands = build_env_statements(self.cellar_root, recipe_path, relaxed=True) + return HomebrewDependency(commands) + + def _find_dep_default(self, name, version): + installed_versions = self._installed_versions(name) + if not installed_versions: + return INDETERMINATE_DEPENDENCY + + # Just grab newest installed version - may make sense some day to find + # the linked version instead. + default_version = sorted(installed_versions, reverse=True)[0] + return self._find_dep_versioned(name, default_version) + + def _installed_versions(self, recipe): + recipe_base_path = os.path.join(self.cellar_root, recipe) + if not os.path.exists(recipe_base_path): + return [] + + names = os.listdir(recipe_base_path) + return filter(lambda n: os.path.isdir(os.path.join(recipe_base_path, n)), names) + + +class UsesToolDependencyDirMixin: + + def _init_base_path(self, dependency_manager, **kwds): + self.base_path = os.path.abspath( kwds.get('base_path', dependency_manager.default_base_path) ) + + +class UsesInstalledRepositoriesMixin: + + def _get_installed_dependency( self, name, type, version=None, **kwds ): + installed_tool_dependencies = kwds.get("installed_tool_dependencies", []) + for installed_tool_dependency in (installed_tool_dependencies or []): + name_and_type_equal = installed_tool_dependency.name == name and installed_tool_dependency.type == type + if version: + if name_and_type_equal and installed_tool_dependency.version == version: + return installed_tool_dependency + else: + if name_and_type_equal: + return installed_tool_dependency + return None + + +class HomebrewDependency(Dependency): + + def __init__(self, commands): + self.commands = commands + + def shell_commands(self, requirement): + raw_commands = self.commands.replace("\n", ";") + return raw_commands + + def __repr__(self): + return "PlatformBrewDependency[commands=%s]" % self.commands diff --git a/lib/galaxy/tools/deps/resolvers/tool_shed_packages.py b/lib/galaxy/tools/deps/resolvers/tool_shed_packages.py index d72b4d6a20b..a58703bd6fb 100644 --- a/lib/galaxy/tools/deps/resolvers/tool_shed_packages.py +++ b/lib/galaxy/tools/deps/resolvers/tool_shed_packages.py @@ -1,10 +1,11 @@ from os.path import abspath, join, exists +from .resolver_mixins import UsesInstalledRepositoriesMixin from .galaxy_packages import GalaxyPackageDependencyResolver, GalaxyPackageDependency from ..resolvers import INDETERMINATE_DEPENDENCY -class ToolShedPackageDependencyResolver(GalaxyPackageDependencyResolver): +class ToolShedPackageDependencyResolver(GalaxyPackageDependencyResolver, UsesInstalledRepositoriesMixin): resolver_type = "tool_shed_packages" def __init__(self, dependency_manager, **kwds): @@ -12,9 +13,8 @@ class ToolShedPackageDependencyResolver(GalaxyPackageDependencyResolver): def _find_dep_versioned( self, name, version, type='package', **kwds ): installed_tool_dependency = self._get_installed_dependency( name, type, version=version, **kwds ) - base_path = self.base_path if installed_tool_dependency: - path = self._get_package_installed_dependency_path( installed_tool_dependency, base_path, name, version ) + path = self._get_package_installed_dependency_path( installed_tool_dependency, name, version ) return self._galaxy_package_dep(path, version) else: return INDETERMINATE_DEPENDENCY @@ -29,26 +29,17 @@ class ToolShedPackageDependencyResolver(GalaxyPackageDependencyResolver): return GalaxyPackageDependency(dependency.script, dependency.path, None) return INDETERMINATE_DEPENDENCY - def _get_installed_dependency( self, name, type, version=None, **kwds ): - installed_tool_dependencies = kwds.get("installed_tool_dependencies", []) - for installed_tool_dependency in (installed_tool_dependencies or []): - name_and_type_equal = installed_tool_dependency.name == name and installed_tool_dependency.type == type - if version: - if name_and_type_equal and installed_tool_dependency.version == version: - return installed_tool_dependency - else: - if name_and_type_equal: - return installed_tool_dependency - return None - - def _get_package_installed_dependency_path( self, installed_tool_dependency, base_path, name, version ): + def _get_package_installed_dependency_path( self, installed_tool_dependency, name, version ): tool_shed_repository = installed_tool_dependency.tool_shed_repository - return join( base_path, - name, - version, - tool_shed_repository.owner, - tool_shed_repository.name, - tool_shed_repository.installed_changeset_revision ) + base_path = self.base_path + return join( + base_path, + name, + version, + tool_shed_repository.owner, + tool_shed_repository.name, + tool_shed_repository.installed_changeset_revision + ) def _get_set_environment_installed_dependency_script_path( self, installed_tool_dependency, name ): tool_shed_repository = installed_tool_dependency.tool_shed_repository @@ -64,4 +55,5 @@ class ToolShedPackageDependencyResolver(GalaxyPackageDependencyResolver): return GalaxyPackageDependency(script, path, None) return INDETERMINATE_DEPENDENCY + __all__ = [ToolShedPackageDependencyResolver] diff --git a/lib/galaxy/util/plugin_config.py b/lib/galaxy/util/plugin_config.py index 8cff759d534..a3b6b9fb78b 100644 --- a/lib/galaxy/util/plugin_config.py +++ b/lib/galaxy/util/plugin_config.py @@ -24,7 +24,7 @@ def plugins_dict(module, plugin_type_identifier): for plugin_module in submodules( module ): # FIXME: this is not how one is suppose to use __all__ why did you do # this past John? - for clazz in plugin_module.__all__: + for clazz in getattr( plugin_module, "__all__", [] ): plugin_type = getattr( clazz, plugin_type_identifier, None ) if plugin_type: plugin_dict[ plugin_type ] = clazz diff --git a/lib/galaxy/webapps/galaxy/api/libraries.py b/lib/galaxy/webapps/galaxy/api/libraries.py index 97f1a41545c..aaf777707a1 100644 --- a/lib/galaxy/webapps/galaxy/api/libraries.py +++ b/lib/galaxy/webapps/galaxy/api/libraries.py @@ -6,10 +6,7 @@ from galaxy import exceptions from galaxy.managers import libraries, folders, roles from galaxy.web import _future_expose_api as expose_api from galaxy.web import _future_expose_api_anonymous as expose_api_anonymous -from galaxy.model.orm import and_, not_, or_ from galaxy.web.base.controller import BaseAPIController -from sqlalchemy.orm.exc import MultipleResultsFound -from sqlalchemy.orm.exc import NoResultFound import logging log = logging.getLogger( __name__ ) @@ -48,7 +45,7 @@ class LibrariesController( BaseAPIController ): def __decode_id( self, trans, encoded_id, object_name=None ): """ - Try to decode the id. + Try to decode the id. :param object_name: Name of the object the id belongs to. (optional) :type object_name: str @@ -149,7 +146,7 @@ class LibrariesController( BaseAPIController ): synopsis = payload.get( 'synopsis', None ) else: raise exceptions.RequestParameterMissingException( "You did not specify any payload." ) - updated_library = self.library_manager.update( trans, library, name, description, synopsis ) + updated_library = self.library_manager.update( trans, library, name, description, synopsis ) library_dict = self.library_manager.get_library_dict( trans, updated_library ) return library_dict @@ -248,20 +245,20 @@ class LibrariesController( BaseAPIController ): *POST /api/libraries/{encoded_library_id}/permissions :param encoded_library_id: the encoded id of the library to set the permissions of - :type encoded_library_id: an encoded id string + :type encoded_library_id: an encoded id string :param action: (required) describes what action should be performed available actions: remove_restrictions, set_permissions - :type action: string + :type action: string :param access_ids[]: list of Role.id defining roles that should have access permission on the library - :type access_ids[]: string or list + :type access_ids[]: string or list :param add_ids[]: list of Role.id defining roles that should have add item permission on the library - :type add_ids[]: string or list + :type add_ids[]: string or list :param manage_ids[]: list of Role.id defining roles that should have manage permission on the library - :type manage_ids[]: string or list + :type manage_ids[]: string or list :param modify_ids[]: list of Role.id defining roles that should have modify permission on the library - :type modify_ids[]: string or list + :type modify_ids[]: string or list :rtype: dictionary :returns: dict of current roles for all available permission types @@ -306,7 +303,7 @@ class LibrariesController( BaseAPIController ): else: invalid_access_roles_names.append( role_id ) if len( invalid_access_roles_names ) > 0: - log.warning( "The following roles could not be added to the library access permission: " + str( invalid_access_roles_names ) ) + log.warning( "The following roles could not be added to the library access permission: " + str( invalid_access_roles_names ) ) # ADD TO LIBRARY ROLES valid_add_roles = [] @@ -319,8 +316,8 @@ class LibrariesController( BaseAPIController ): else: invalid_add_roles_names.append( role_id ) if len( invalid_add_roles_names ) > 0: - log.warning( "The following roles could not be added to the add library item permission: " + str( invalid_add_roles_names ) ) - + log.warning( "The following roles could not be added to the add library item permission: " + str( invalid_add_roles_names ) ) + # MANAGE LIBRARY ROLES valid_manage_roles = [] invalid_manage_roles_names = [] @@ -332,8 +329,8 @@ class LibrariesController( BaseAPIController ): else: invalid_manage_roles_names.append( role_id ) if len( invalid_manage_roles_names ) > 0: - log.warning( "The following roles could not be added to the manage library permission: " + str( invalid_manage_roles_names ) ) - + log.warning( "The following roles could not be added to the manage library permission: " + str( invalid_manage_roles_names ) ) + # MODIFY LIBRARY ROLES valid_modify_roles = [] invalid_modify_roles_names = [] @@ -345,7 +342,7 @@ class LibrariesController( BaseAPIController ): else: invalid_modify_roles_names.append( role_id ) if len( invalid_modify_roles_names ) > 0: - log.warning( "The following roles could not be added to the modify library permission: " + str( invalid_modify_roles_names ) ) + log.warning( "The following roles could not be added to the modify library permission: " + str( invalid_modify_roles_names ) ) permissions = { trans.app.security_agent.permitted_actions.LIBRARY_ACCESS : valid_access_roles } permissions.update( { trans.app.security_agent.permitted_actions.LIBRARY_ADD : valid_add_roles } ) @@ -357,7 +354,7 @@ class LibrariesController( BaseAPIController ): # Copy the permissions to the root folder trans.app.security_agent.copy_library_permissions( trans, library, library.root_folder ) else: - raise exceptions.RequestParameterInvalidException( 'The mandatory parameter "action" has an invalid value.' + raise exceptions.RequestParameterInvalidException( 'The mandatory parameter "action" has an invalid value.' 'Allowed values are: "remove_restrictions", set_permissions"' ) roles = self.library_manager.get_current_roles( trans, library ) return roles @@ -369,18 +366,16 @@ class LibrariesController( BaseAPIController ): POST /api/libraries/{encoded_library_id}/permissions Updates the library permissions. """ - import galaxy.util - params = galaxy.util.Params( payload ) + params = util.Params( payload ) permissions = {} for k, v in trans.app.model.Library.permitted_actions.items(): role_params = params.get( k + '_in', [] ) - in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( trans.security.decode_id( x ) ) for x in galaxy.util.listify( role_params ) ] + in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( trans.security.decode_id( x ) ) for x in util.listify( role_params ) ] permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles trans.app.security_agent.set_all_library_permissions( trans, library, permissions ) trans.sa_session.refresh( library ) # Copy the permissions to the root folder trans.app.security_agent.copy_library_permissions( trans, library, library.root_folder ) - message = "Permissions updated for library '%s'." % library.name item = library.to_dict( view='element', value_mapper={ 'id' : trans.security.encode_id , 'root_folder_id' : trans.security.encode_id } ) return item diff --git a/lib/galaxy/webapps/galaxy/api/library_contents.py b/lib/galaxy/webapps/galaxy/api/library_contents.py index 1c01fbefaa0..33b8f32201a 100644 --- a/lib/galaxy/webapps/galaxy/api/library_contents.py +++ b/lib/galaxy/webapps/galaxy/api/library_contents.py @@ -29,12 +29,12 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar .. note:: May be slow! Returns all content traversing recursively through all folders. .. seealso:: :class:`galaxy.webapps.galaxy.api.FolderContentsController.index` for a non-recursive solution - :param library_id: encoded id string of the library - :type library_id: string + :param library_id: the encoded id of the library + :type library_id: str :returns: list of dictionaries of the form: * id: the encoded id of the library item - * name: the 'libary path' + * name: the 'library path' or relationship of the library item to the root * type: 'file' or 'folder' * url: the url to get detailed information on the library item @@ -109,7 +109,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar :param id: the encoded id of the library item to return :type id: str - :param library_id: encoded id string of the library that contains this item + :param library_id: the encoded id of the library that contains this item :type library_id: str :returns: detailed library item information @@ -119,7 +119,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar :func:`galaxy.model.LibraryDataset.to_dict` and :attr:`galaxy.model.LibraryFolder.dict_element_visible_keys` """ - class_name, content_id = self.__decode_library_content_id( trans, id ) + class_name, content_id = self.__decode_library_content_id( id ) if class_name == 'LibraryFolder': content = self.get_library_folder( trans, content_id, check_ownership=False, check_accessible=True ) rval = content.to_dict( view='element', value_mapper={ 'id': trans.security.encode_id } ) @@ -146,27 +146,39 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar the HDA's encoded id in ``from_hda_id`` (and optionally ``ldda_message``). :type library_id: str - :param library_id: encoded id string of the library where to create the new item + :param library_id: the encoded id of the library where to create the new item :type payload: dict :param payload: dictionary structure containing: - * folder_id: the parent folder of the new item + * folder_id: the encoded id of the parent folder of the new item * create_type: the type of item to create ('file', 'folder' or 'collection') - * from_hda_id: (optional) the id of an accessible HDA to copy into the - library + * from_hda_id: (optional, only if create_type is 'file') the + encoded id of an accessible HDA to copy into the library * ldda_message: (optional) the new message attribute of the LDDA created * extended_metadata: (optional) dub-dictionary containing any extended metadata to associate with the item - * link_data_only: (optional) either 'copy_files' (default) or 'link_to_files' * upload_option: (optional) one of 'upload_file' (default), 'upload_directory' or 'upload_paths' - * server_dir: (optional) only if upload_option is 'upload_directory' - * filesystem_paths: (optional) only if upload_option is 'upload_paths' and the user is an admin + * server_dir: (optional, only if upload_option is + 'upload_directory') relative path of the subdirectory of Galaxy + ``library_import_dir`` to upload. All and only the files (i.e. + no subdirectories) contained in the specified directory will be + uploaded. + * filesystem_paths: (optional, only if upload_option is + 'upload_paths' and the user is an admin) file paths on the + Galaxy server to upload to the library, one file per line + * link_data_only: (optional, only when upload_option is + 'upload_directory' or 'upload_paths') either 'copy_files' + (default) or 'link_to_files'. Setting to 'link_to_files' + symlinks instead of copying the files + * name: (optional, only if create_type is 'folder') name of the + folder to create + * description: (optional, only if create_type is 'folder') + description of the folder to create :rtype: dict :returns: a dictionary containing the id, name, and 'show' url of the new item """ - create_type = None if 'create_type' not in payload: trans.response.status = 400 return "Missing required 'create_type' parameter." @@ -178,10 +190,10 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar if 'folder_id' not in payload: trans.response.status = 400 - return "Missing requred 'folder_id' parameter." + return "Missing required 'folder_id' parameter." else: folder_id = payload.pop( 'folder_id' ) - class_name, folder_id = self.__decode_library_content_id( trans, folder_id ) + class_name, folder_id = self.__decode_library_content_id( folder_id ) try: # security is checked in the downstream controller parent = self.get_library_folder( trans, folder_id, check_ownership=False, check_accessible=False ) @@ -198,9 +210,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar #check for extended metadata, store it and pop it out of the param #otherwise sanitize_param will have a fit - ex_meta_payload = None - if 'extended_metadata' in payload: - ex_meta_payload = payload.pop('extended_metadata') + ex_meta_payload = payload.pop('extended_metadata', None) # Now create the desired content object, either file or folder. if create_type == 'file': @@ -272,7 +282,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar def _copy_hda_to_library_folder( self, trans, from_hda_id, library_id, folder_id, ldda_message='' ): """ - Copies hda ``from_hda_id`` to library folder ``library_folder_id`` optionally + Copies hda ``from_hda_id`` to library folder ``folder_id``, optionally adding ``ldda_message`` to the new ldda's ``message``. ``library_contents.create`` will branch to this if called with 'from_hda_id' @@ -325,7 +335,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar :type id: str :param id: the encoded id of the library item to return :type library_id: str - :param library_id: encoded id string of the library that contains this item + :param library_id: the encoded id of the library that contains this item :type payload: dict :param payload: dictionary structure containing:: 'converted_dataset_id': @@ -344,7 +354,7 @@ class LibraryContentsController( BaseAPIController, UsesLibraryMixin, UsesLibrar trans.sa_session.add( assoc ) trans.sa_session.flush() - def __decode_library_content_id( self, trans, content_id ): + def __decode_library_content_id( self, content_id ): if len( content_id ) % 16 == 0: return 'LibraryDataset', content_id elif content_id.startswith( 'F' ): diff --git a/lib/galaxy/webapps/galaxy/api/tools.py b/lib/galaxy/webapps/galaxy/api/tools.py index 07ad81d0580..2de5e84932c 100644 --- a/lib/galaxy/webapps/galaxy/api/tools.py +++ b/lib/galaxy/webapps/galaxy/api/tools.py @@ -11,7 +11,7 @@ from galaxy.web.base.controller import UsesHistoryMixin from galaxy.visualization.genomes import GenomeRegion from galaxy.util.json import dumps from galaxy.visualization.data_providers.genome import * -from galaxy.tools.parameters import params_to_incoming, check_param +from galaxy.tools.parameters import check_param from galaxy.tools.parameters import visit_input_values from galaxy.tools.parameters.meta import expand_meta_parameters from galaxy.managers.collections_util import dictify_dataset_collection_instance @@ -85,7 +85,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin, UsesHistoryMix if not tool: trans.response.status = 500 return { 'error': 'Could not find tool with id \'%s\'' % tool_id } - return self._build_dict(trans, tool, kwd) + return tool.to_json(trans, **kwd) @_future_expose_api @web.require_admin @@ -491,308 +491,3 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin, UsesHistoryMix dataset_dict[ 'id' ] = trans.security.encode_id( dataset_dict[ 'id' ] ) dataset_dict[ 'track_config' ] = self.get_new_track_config( trans, output_dataset ) return dataset_dict - - def _build_dict(self, trans, tool, kwd={}): - """ - Recursively creates a tool dictionary containing repeats, dynamic options and updated states. - """ - job_id = kwd.get('job_id', None) - dataset_id = kwd.get('dataset_id', None) - - # load job details if provided - job = None - if job_id: - try: - job_id = trans.security.decode_id( job_id ) - job = trans.sa_session.query( trans.app.model.Job ).get( job_id ) - except Exception, exception: - trans.response.status = 500 - return { 'error': 'Failed to retrieve job.' } - elif dataset_id: - try: - dataset_id = trans.security.decode_id( dataset_id ) - data = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id ) - if not ( trans.user_is_admin() or trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), data.dataset ) ): - trans.response.status = 500 - return { 'error': 'User has no access to dataset.' } - job = data.creating_job - if not job: - trans.response.status = 500 - return { 'error': 'Creating job not found.' } - except Exception, exception: - trans.response.status = 500 - return { 'error': 'Failed to get job information.' } - - # load job parameters into incoming - tool_message = '' - if job: - try: - job_params = job.get_param_values( trans.app, ignore_errors = True ) - job_messages = tool.check_and_update_param_values( job_params, trans, update_values=False ) - tool_message = self._compare_tool_version(trans, tool, job) - params_to_incoming( kwd, tool.inputs, job_params, trans.app ) - except Exception, exception: - trans.response.status = 500 - return { 'error': str( exception ) } - - # create parameter object - params = galaxy.util.Params( kwd, sanitize = False ) - - # convert value to jsonifiable value - def convert(v): - # check if value is numeric - isnumber = False - try: - float(v) - isnumber = True - except Exception: - pass - - # fix hda parsing - if isinstance(v, HistoryDatasetAssociation): - return { - 'id' : trans.security.encode_id(v.id), - 'src' : 'hda' - } - elif isinstance(v, bool): - if v is True: - return 'true' - else: - return 'false' - elif isinstance(v, basestring) or isnumber: - return v - else: - return None - - # ensures that input dictionary is jsonifiable - def sanitize(dict): - # get current value - value = dict['value'] if 'value' in dict else None - - # identify lists - if dict['type'] in ['data']: - if isinstance(value, list): - value = [ convert(v) for v in value ] - else: - value = [ convert(value) ] - value = { - 'batch' : dict['multiple'], - 'values' : value - } - elif isinstance(value, list): - value = [ convert(v) for v in value ] - else: - value = convert(value) - - # update and return - dict['value'] = value - return dict - - # initialize state using default parameters - def initialize_state(trans, inputs, state, context=None): - context = ExpressionContext(state, context) - for input in inputs.itervalues(): - state[input.name] = input.get_initial_value(trans, context) - - # check the current state of a value and update it if necessary - def check_state(trans, input, default_value, context): - value = default_value - error = 'State validation failed.' - try: - # resolves the inconsistent definition of boolean parameters (see base.py) without modifying shared code - if input.type == 'boolean' and isinstance(default_value, basestring): - value, error = [util.string_as_bool(default_value), None] - else: - value, error = check_param(trans, input, default_value, context) - except Exception: - log.error('Checking parameter %s failed.', input.name) - pass - return [value, error] - - # populates state with incoming url parameters - def populate_state(trans, inputs, state, errors, incoming, prefix="", context=None ): - context = ExpressionContext(state, context) - for input in inputs.itervalues(): - key = prefix + input.name - if input.type == 'repeat': - group_state = state[input.name] - rep_index = 0 - del group_state[:] - while True: - rep_name = "%s_%d" % (key, rep_index) - if not any([incoming_key.startswith(rep_name) for incoming_key in incoming.keys()]): - break - if rep_index < input.max: - new_state = {} - new_state['__index__'] = rep_index - initialize_state(trans, input.inputs, new_state, context) - group_state.append(new_state) - populate_state(trans, input.inputs, new_state, errors, incoming, prefix=rep_name + "|", context=context) - rep_index += 1 - elif input.type == 'conditional': - group_state = state[input.name] - group_prefix = "%s|" % ( key ) - test_param_key = group_prefix + input.test_param.name - default_value = incoming.get(test_param_key, group_state.get(input.test_param.name, None)) - value, error = check_state(trans, input.test_param, default_value, context) - if error: - errors[test_param_key] = error - else: - current_case = input.get_current_case(value, trans) - group_state = state[input.name] = {} - initialize_state(trans, input.cases[current_case].inputs, group_state, context) - populate_state( trans, input.cases[current_case].inputs, group_state, errors, incoming, prefix=group_prefix, context=context) - group_state['__current_case__'] = current_case - group_state[input.test_param.name] = value - else: - default_value = incoming.get(key, state.get(input.name, None)) - value, error = check_state(trans, input, default_value, context) - if error: - errors[key] = error - state[input.name] = value - - # builds tool model including all attributes - def iterate(group_inputs, inputs, tool_state, other_values=None): - other_values = ExpressionContext( tool_state, other_values ) - for input_index, input in enumerate( inputs.itervalues() ): - # create model dictionary - group_inputs[input_index] = input.to_dict(trans) - if group_inputs[input_index] is None: - continue - - # identify stat for subsection/group - group_state = tool_state[input.name] - - # iterate and update values - if input.type == 'repeat': - group_cache = group_inputs[input_index]['cache'] = {} - for i in range( len( group_state ) ): - group_cache[i] = {} - iterate( group_cache[i], input.inputs, group_state[i], other_values ) - elif input.type == 'conditional': - try: - test_param = group_inputs[input_index]['test_param'] - test_param['value'] = convert(group_state[test_param['name']]) - except Exception: - pass - i = group_state['__current_case__'] - iterate(group_inputs[input_index]['cases'][i]['inputs'], input.cases[i].inputs, group_state, other_values) - else: - # create input dictionary, try to pass other_values if to_dict function supports it e.g. dynamic options - try: - group_inputs[input_index] = input.to_dict(trans, other_values=other_values) - except Exception: - pass - - # update input value from tool state - try: - group_inputs[input_index]['value'] = tool_state[group_inputs[input_index]['name']] - except Exception: - pass - - # sanitize if value exists - if group_inputs[input_index]['value']: - group_inputs[input_index] = sanitize(group_inputs[input_index]) - - # do param translation here, used by datasource tools - if tool.input_translator: - tool.input_translator.translate( params ) - - # create tool state - state_inputs = {} - state_errors = {} - initialize_state(trans, tool.inputs, state_inputs) - populate_state(trans, tool.inputs, state_inputs, state_errors, params.__dict__) - - # create basic tool model - tool_model = tool.to_dict(trans) - tool_model['inputs'] = {} - - # build tool model - iterate(tool_model['inputs'], tool.inputs, state_inputs, '') - - # load tool help - tool_help = '' - if tool.help: - tool_help = tool.help - tool_help = tool_help.render( static_path=web.url_for( '/static' ), host_url=web.url_for('/', qualified=True) ) - if type( tool_help ) is not unicode: - tool_help = unicode( tool_help, 'utf-8') - - # check if citations exist - tool_citations = False - if tool.citations: - tool_citations = True - - # get tool versions - tool_versions = [] - tools = self.app.toolbox.get_loaded_tools_by_lineage(tool.id) - for t in tools: - tool_versions.append(t.version) - - ## add information with underlying requirements and their versions - tool_requirements = [] - if tool.requirements: - for requirement in tool.requirements: - tool_requirements.append({ - 'name' : requirement.name, - 'version' : requirement.version - }) - - # add additional properties - tool_model.update({ - 'help' : tool_help, - 'citations' : tool_citations, - 'biostar_url' : trans.app.config.biostar_url, - 'message' : tool_message, - 'versions' : tool_versions, - 'requirements' : tool_requirements, - 'errors' : state_errors - }) - - # check for errors - if 'error' in tool_message: - return tool_message - - # return enriched tool model - return tool_model - - def _compare_tool_version( self, trans, tool, job ): - """ - Compares a tool version with the tool version from a job (from ToolRunner). - """ - tool_id = job.tool_id - tool_version = job.tool_version - message = '' - try: - select_field, tools, tool = self.app.toolbox.get_tool_components( tool_id, tool_version=tool_version, get_loaded_tools_by_lineage=False, set_selected=True ) - if tool is None: - trans.response.status = 500 - return { 'error': 'This dataset was created by an obsolete tool (%s). Can\'t re-run.' % tool_id } - if ( tool.id != tool_id and tool.old_id != tool_id ) or tool.version != tool_version: - if tool.id == tool_id: - if tool_version == None: - # for some reason jobs don't always keep track of the tool version. - message = '' - else: - message = 'This job was initially run with tool version "%s", which is currently not available. ' % tool_version - if len( tools ) > 1: - message += 'You can re-run the job with the selected tool or choose another derivation of the tool.' - else: - message += 'You can re-run the job with this tool version, which is a derivation of the original tool.' - else: - if len( tools ) > 1: - message = 'This job was initially run with tool version "%s", which is currently not available. ' % tool_version - message += 'You can re-run the job with the selected tool or choose another derivation of the tool.' - else: - message = 'This job was initially run with tool id "%s", version "%s", which is ' % ( tool_id, tool_version ) - message += 'currently not available. You can re-run the job with this tool, which is a derivation of the original tool.' - except Exception, error: - trans.response.status = 500 - return { 'error': str (error) } - - # can't rerun upload, external data sources, et cetera. workflow compatible will proxy this for now - if not tool.is_workflow_compatible: - trans.response.status = 500 - return { 'error': 'The \'%s\' tool does currently not support re-running.' % tool.name } - return message diff --git a/lib/galaxy/webapps/galaxy/controllers/workflow.py b/lib/galaxy/webapps/galaxy/controllers/workflow.py index 11baa0ef945..5bbda0cff46 100644 --- a/lib/galaxy/webapps/galaxy/controllers/workflow.py +++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py @@ -4,6 +4,7 @@ import json import os import sgmllib import urllib2 +import copy from sqlalchemy import and_ from tool_shed.util import common_util @@ -609,16 +610,26 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix is selected. """ + tool_state = incoming.pop('tool_state', None) trans.workflow_building_mode = True module = module_factory.from_dict( trans, { 'type': type, 'tool_id': tool_id, - 'tool_state': incoming.pop("tool_state") + 'tool_state': tool_state } ) + + # create tool model and default tool state (if missing) + tool_model = None + if not tool_state: + tool_model = module.tool.to_json(trans, **incoming) + module.state.inputs = copy.deepcopy(tool_model['state_inputs']) + + # update module state module.update_state( incoming ) - + if type == 'tool': return { + 'tool_model': tool_model, 'tool_state': module.get_state(), 'data_inputs': module.get_data_inputs(), 'data_outputs': module.get_data_outputs(), @@ -653,6 +664,7 @@ class WorkflowController( BaseUIController, SharableMixin, UsesStoredWorkflowMix 'name': module.get_name(), 'tool_id': module.get_tool_id(), 'tool_state': module.get_state(), + 'tool_model': module.tool.to_json(trans), 'tooltip': module.get_tooltip( static_path=url_for( '/static' ) ), 'data_inputs': module.get_data_inputs(), 'data_outputs': module.get_data_outputs(), diff --git a/lib/galaxy/workflow/modules.py b/lib/galaxy/workflow/modules.py index adf8db4f6ac..13644176ebd 100644 --- a/lib/galaxy/workflow/modules.py +++ b/lib/galaxy/workflow/modules.py @@ -18,7 +18,7 @@ from galaxy.dataset_collections import matching from galaxy.web.framework import formbuilder from galaxy.jobs.actions.post import ActionBox from galaxy.model import PostJobAction -from galaxy.tools.parameters import check_param, DataToolParameter, DummyDataset, RuntimeValue, visit_input_values +from galaxy.tools.parameters import params_to_incoming, check_param, DataToolParameter, DummyDataset, RuntimeValue, visit_input_values from galaxy.tools.parameters import DataCollectionToolParameter from galaxy.tools.parameters.wrapped import make_dict_copy from galaxy.tools.execute import execute @@ -509,7 +509,8 @@ class ToolModule( WorkflowModule ): message = "%s: using version '%s' instead of version '%s' indicated in this workflow." % ( tool_id, d.get( 'tool_version', 'Unspecified' ), module.get_tool_version() ) log.debug(message) module.version_changes.append(message) - module.state.decode( d[ "tool_state" ], module.tool, module.trans.app, secure=secure ) + if d[ "tool_state" ]: + module.state.decode( d[ "tool_state" ], module.tool, module.trans.app, secure=secure ) module.errors = d.get( "tool_errors", None ) module.post_job_actions = d.get( "post_job_actions", {} ) module.workflow_outputs = d.get( "workflow_outputs", [] ) @@ -687,7 +688,7 @@ class ToolModule( WorkflowModule ): def get_config_form( self ): self.add_dummy_datasets() - return self.trans.fill_template( "workflow/editor_tool_form.mako", + return self.trans.fill_template( "workflow/editor_tool_form.mako", module=self, tool=self.tool, values=self.state.inputs, errors=( self.errors or {} ) ) def encode_runtime_state( self, trans, state ): diff --git a/run_tests.sh b/run_tests.sh index 5f5924736e4..91bf74ae532 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,5 +1,7 @@ #!/bin/sh +./scripts/common_startup.sh + # A good place to look for nose info: http://somethingaboutorange.com/mrl/projects/nose/ rm -f run_functional_tests.log diff --git a/scripts/functional_tests.py b/scripts/functional_tests.py index 8196013af9c..a1aff16f495 100644 --- a/scripts/functional_tests.py +++ b/scripts/functional_tests.py @@ -225,7 +225,11 @@ def main(): if not os.path.isabs( galaxy_test_file_dir ): galaxy_test_file_dir = os.path.join( os.getcwd(), galaxy_test_file_dir ) library_import_dir = galaxy_test_file_dir - user_library_import_dir = os.path.join( galaxy_test_file_dir, 'users' ) + import_dir = os.path.join( galaxy_test_file_dir, 'users' ) + if os.path.exists(import_dir): + user_library_import_dir = import_dir + else: + user_library_import_dir = None ignore_files = () start_server = 'GALAXY_TEST_EXTERNAL' not in os.environ diff --git a/static/scripts/galaxy.workflow_editor.canvas.js b/static/scripts/galaxy.workflow_editor.canvas.js index b22dc903c69..918ecf0d124 100644 --- a/static/scripts/galaxy.workflow_editor.canvas.js +++ b/static/scripts/galaxy.workflow_editor.canvas.js @@ -805,7 +805,7 @@ var Node = Backbone.Model.extend({ nodeView.addDataOutput( output ); } ); nodeView.render(); - workflow.node_changed( this ); + workflow.node_changed( this, true); }, update_field_data : function( data ) { var node = this; @@ -1094,9 +1094,9 @@ $.extend( Workflow.prototype, { this.active_node = node; } }, - node_changed : function ( node ) { + node_changed : function ( node, force ) { this.has_changes = true; - if ( this.active_node == node ) { + if ( this.active_node == node && (!parent.__NEWTOOLFORM__ || force)) { // Reactive with new form_html this.check_changes_in_active_form(); //Force changes to be saved even on new connection (previously dumped) parent.show_form_for_tool( node.form_html + node.tooltip, node ); @@ -1305,6 +1305,12 @@ function add_node( type, title_text, tool_id ) { return node; } +function update_node( data ) { + var node = workflow.active_node; + if (node) { + node.update_field_data(data, true); + } +} var ext_to_type = null; var type_to_type = null; diff --git a/static/scripts/mvc/tools/tools-form-base.js b/static/scripts/mvc/tools/tools-form-base.js new file mode 100644 index 00000000000..71eecb21069 --- /dev/null +++ b/static/scripts/mvc/tools/tools-form-base.js @@ -0,0 +1,315 @@ +/** + This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules. +*/ +define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', + 'mvc/citation/citation-model', 'mvc/citation/citation-view', + 'mvc/tools', 'mvc/tools/tools-template', 'mvc/tools/tools-content', 'mvc/tools/tools-section', 'mvc/tools/tools-tree'], + function(Utils, Deferred, Portlet, Ui, CitationModel, CitationView, + Tools, ToolTemplate, ToolContent, ToolSection, ToolTree) { + + // create form view + return Backbone.View.extend({ + // initialize + initialize: function(options) { + // log options + console.debug(options); + + // link galaxy modal or create one + var galaxy = parent.Galaxy; + if (galaxy && galaxy.modal) { + this.modal = galaxy.modal; + } else { + this.modal = new Ui.Modal.View(); + } + + // check if the user is an admin + if (galaxy && galaxy.currUser) { + this.is_admin = galaxy.currUser.get('is_admin'); + } else { + this.is_admin = false; + } + + // link options + this.options = options; + + // link container + this.container = this.options.container || 'body'; + + // create deferred processing queue handler + // this handler reduces the number of requests to the api by filtering redundant requests + this.deferred = new Deferred(); + + // set element + this.setElement('
'); + + // add to main element + $(this.container).append(this.$el); + + // build this form + this._buildForm(); + }, + + /** Shows the final message (usually upon successful job submission) + */ + reciept: function($el) { + $(this.container).empty(); + $(this.container).append($el); + }, + + /** Highlight and scroll to input element (currently only used for error notifications) + */ + highlight: function (input_id, message, silent) { + // get input field + var input_element = this.element_list[input_id]; + + // check input element + if (input_element) { + // mark error + input_element.error(message || 'Please verify this parameter.'); + + // scroll to first input element + if (!silent) { + $(this.container).animate({ + scrollTop: input_element.$el.offset().top - 20 + }, 500); + } + } + }, + + /** Main tool form build function. This function is called once a new model is available. + */ + _buildForm: function() { + // link this + var self = this; + + // reset events + this.off('refresh'); + this.off('reset'); + + // reset field list, which contains the input field elements + this.field_list = {}; + + // reset sequential input definition list, which contains the input definitions as provided from the api + this.input_list = {}; + + // reset input element list, which contains the dom elements of each input element (includes also the input field) + this.element_list = {}; + + // creates a tree/json data structure from the input form + this.tree = new ToolTree(this); + + // request history content and build form + this.content = new ToolContent(this); + + // link model options + var options = this.options; + + // create ui elements + this._renderForm(options); + + // rebuild the underlying data structure + this.tree.finalize(); + + // show errors + if (!this.workflow && options.errors) { + var error_messages = this.tree.matchResponse(options.errors); + for (var input_id in error_messages) { + this.highlight(input_id, error_messages[input_id], true); + } + } + + // add refresh listener + this.on('refresh', function() { + // by using/reseting the deferred ajax queue the number of redundant calls is reduced + self.deferred.reset(); + self.deferred.execute(function(){self._updateModel()}); + }); + + // add reset listener + this.on('reset', function() { + for (var i in this.element_list) { + this.element_list[i].reset(); + } + }); + }, + + /** Renders the UI elements required for the form + */ + _renderForm: function(options) { + // link this + var self = this; + + // create message view + this.message = new Ui.Message(); + + // button for version selection + var requirements_button = new Ui.ButtonIcon({ + icon : 'fa-info-circle', + title : 'Requirements', + tooltip : 'Display tool requirements', + onclick : function() { + if (!this.visible) { + this.visible = true; + self.message.update({ + persistent : true, + message : ToolTemplate.requirements(options), + status : 'info' + }); + } else { + this.visible = false; + self.message.update({ + message : '' + }); + } + } + }); + if (!options.requirements || options.requirements.length == 0) { + requirements_button.$el.hide(); + } + + // button for version selection + var versions_button = new Ui.ButtonMenu({ + icon : 'fa-cubes', + title : 'Versions', + tooltip : 'Select another tool version' + }); + if (options.versions && options.versions.length > 1) { + for (var i in options.versions) { + var version = options.versions[i]; + if (version != options.version) { + versions_button.addMenu({ + title : 'Switch to ' + version, + version : version, + icon : 'fa-cube', + onclick : function() { + // here we update the tool version (some tools encode the version also in the id) + options.id = options.id.replace(options.version, this.version); + options.version = this.version; + + // rebuild the model and form + self.deferred.reset(); + self.deferred.execute(function(){self._buildModel()}); + } + }); + } + } + } else { + versions_button.$el.hide(); + } + + // button menu + var menu_button = new Ui.ButtonMenu({ + icon : 'fa-caret-down', + title : 'Options', + tooltip : 'View available options' + }); + + // configure button selection + if(options.biostar_url) { + // add question option + menu_button.addMenu({ + icon : 'fa-question-circle', + title : 'Question?', + tooltip : 'Ask a question about this tool (Biostar)', + onclick : function() { + window.open(options.biostar_url + '/p/new/post/'); + } + }); + + // create search button + menu_button.addMenu({ + icon : 'fa-search', + title : 'Search', + tooltip : 'Search help for this tool (Biostar)', + onclick : function() { + window.open(options.biostar_url + '/t/' + options.id + '/'); + } + }); + }; + + // create share button + menu_button.addMenu({ + icon : 'fa-share', + title : 'Share', + tooltip : 'Share this tool', + onclick : function() { + prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id); + } + }); + + // add admin operations + if (this.is_admin) { + // create download button + menu_button.addMenu({ + icon : 'fa-download', + title : 'Download', + tooltip : 'Download this tool', + onclick : function() { + window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download'; + } + }); + } + + // create tool form section + this.section = new ToolSection.View(self, { + inputs : options.inputs, + cls : 'ui-table-plain' + }); + + // switch to classic tool form mako if the form definition is incompatible + if (this.incompatible) { + this.$el.hide(); + $('#tool-form-classic').show(); + return; + } + + // create portlet + this.portlet = new Portlet.View({ + icon : 'fa-wrench', + title : '' + options.name + ' ' + options.description + ' (Galaxy Tool Version ' + options.version + ')', + cls : 'ui-portlet-slim', + operations: { + requirements : requirements_button, + menu : menu_button, + versions : versions_button + }, + buttons : this.buttons + }); + + // append message + this.portlet.append(this.message.$el, true); + + // append tool section + this.portlet.append(this.section.$el); + + // start form + this.$el.empty(); + this.$el.append(this.portlet.$el); + + // append help + if (options.help != '') { + this.$el.append(ToolTemplate.help(options.help)); + } + + // append citations + if (options.citations) { + var $citations = $('
'); + var citations = new CitationModel.ToolCitationCollection(); + citations.tool_id = options.id; + var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations } ); + citation_list_view.render(); + citations.fetch(); + this.$el.append($citations); + } + + // show message if available in model + if (options.message) { + this.message.update({ + persistent : true, + status : 'warning', + message : options.message + }); + } + } + }); +}); diff --git a/static/scripts/mvc/tools/tools-form-workflow.js b/static/scripts/mvc/tools/tools-form-workflow.js new file mode 100644 index 00000000000..c9a45ee795e --- /dev/null +++ b/static/scripts/mvc/tools/tools-form-workflow.js @@ -0,0 +1,66 @@ +/** + This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules. +*/ +define(['utils/utils', 'mvc/tools/tools-form-base'], + function(Utils, ToolFormBase) { + + // create form view + var View = ToolFormBase.extend({ + initialize: function(options) { + this.workflow = true; + ToolFormBase.prototype.initialize.call(this, options); + }, + + /** Builds a new model through api call and recreates the entire form + */ + _buildModel: function() { + }, + + /** Request a new model for an already created tool form and updates the form inputs + */ + _updateModel: function() { + // create the request dictionary + var self = this; + var current_state = this.tree.finalize(); + + // log tool state + console.debug('tools-form-workflow::_refreshForm() - Refreshing states.'); + console.debug(current_state); + + // register process + var process_id = this.deferred.register(); + + // build model url for request + var model_url = galaxy_config.root + 'workflow/editor_form_post?tool_id=' + this.options.id; + + // post job + Utils.request({ + type : 'GET', + url : model_url, + data : current_state, + success : function(node) { + parent.update_node(node); + + // process completed + self.deferred.done(process_id); + + // log success + console.debug('tools-form::_refreshForm() - States refreshed.'); + console.debug(node); + }, + error : function(response) { + // process completed + self.deferred.done(process_id); + + // log error + console.debug('tools-form::_refreshForm() - Refresh request failed.'); + console.debug(response); + } + }); + } + }); + + return { + View: View + }; +}); diff --git a/static/scripts/mvc/tools/tools-form.js b/static/scripts/mvc/tools/tools-form.js index a92abe0ad34..94a0af0a7f4 100644 --- a/static/scripts/mvc/tools/tools-form.js +++ b/static/scripts/mvc/tools/tools-form.js @@ -1,79 +1,28 @@ /** This is the main class of the tool form plugin. It is referenced as 'app' in all lower level modules. */ -define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', - 'mvc/citation/citation-model', 'mvc/citation/citation-view', - 'mvc/tools', 'mvc/tools/tools-template', 'mvc/tools/tools-content', 'mvc/tools/tools-section', 'mvc/tools/tools-tree', 'mvc/tools/tools-jobs'], - function(Utils, Deferred, Portlet, Ui, CitationModel, CitationView, - Tools, ToolTemplate, ToolContent, ToolSection, ToolTree, ToolJobs) { +define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/tools/tools-form-base', 'mvc/tools/tools-jobs'], + function(Utils, Ui, ToolFormBase, ToolJobs) { // create form view - var View = Backbone.View.extend({ - // base element - container: 'body', - + var View = ToolFormBase.extend({ // initialize initialize: function(options) { - // log options - console.debug(options); - - // link galaxy modal or create one - var galaxy = parent.Galaxy; - if (galaxy && galaxy.modal) { - this.modal = galaxy.modal; - } else { - this.modal = new Ui.Modal.View(); - } - - // check if the user is an admin - if (galaxy && galaxy.currUser) { - this.is_admin = galaxy.currUser.get('is_admin') - } else { - this.is_admin = false; - } - - // link options - this.options = options; - - // create deferred processing queue handler - // this handler reduces the number of requests to the api by filtering redundant requests - this.deferred = new Deferred(); - - // set element - this.setElement('
'); - - // add to main element - $(this.container).append(this.$el); - - // build this form - this._buildForm(); - }, - - /** Shows the final message (usually upon successful job submission) - */ - reciept: function($el) { - $(this.container).empty(); - $(this.container).append($el); - }, - - /** Highlight and scroll to input element (currently only used for error notifications) - */ - highlight: function (input_id, message, silent) { - // get input field - var input_element = this.element_list[input_id]; - - // check input element - if (input_element) { - // mark error - input_element.error(message || 'Please verify this parameter.'); - - // scroll to first input element - if (!silent) { - $(this.container).animate({ - scrollTop: input_element.$el.offset().top - 20 - }, 500); - } + var self = this; + this.job_handler = new ToolJobs(this); + this.buttons = { + execute : new Ui.Button({ + icon : 'fa-check', + tooltip : 'Execute: ' + options.name, + title : 'Execute', + cls : 'btn btn-primary', + floating : 'clear', + onclick : function() { + self.job_handler.submit(); + } + }) } + ToolFormBase.prototype.initialize.call(this, options); }, /** Builds a new model through api call and recreates the entire form @@ -250,256 +199,6 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', console.debug(response); } }); - }, - - /** Main tool form build function. This function is called once a new model is available. - */ - _buildForm: function() { - // link this - var self = this; - - // reset events - this.off('refresh'); - this.off('reset'); - - // reset field list, which contains the input field elements - this.field_list = {}; - - // reset sequential input definition list, which contains the input definitions as provided from the api - this.input_list = {}; - - // reset input element list, which contains the dom elements of each input element (includes also the input field) - this.element_list = {}; - - // creates a tree/json data structure from the input form - this.tree = new ToolTree(this); - - // creates the job handler - this.job_handler = new ToolJobs(this); - - // request history content and build form - this.content = new ToolContent(this); - - // link model options - var options = this.options; - - // create ui elements - this._renderForm(options); - - // rebuild the underlying data structure - this.tree.finalize(); - - // show errors - if (options.errors) { - var error_messages = this.tree.matchResponse(options.errors); - for (var input_id in error_messages) { - this.highlight(input_id, error_messages[input_id], true); - } - } - - // add refresh listener - this.on('refresh', function() { - // by using/reseting the deferred ajax queue the number of redundant calls is reduced - self.deferred.reset(); - self.deferred.execute(function(){self._updateModel()}); - }); - - // add reset listener - this.on('reset', function() { - for (var i in this.element_list) { - this.element_list[i].reset(); - } - }); - }, - - /** Renders the UI elements required for the form - */ - _renderForm: function(options) { - // link this - var self = this; - - // create message view - this.message = new Ui.Message(); - - // button for version selection - var requirements_button = new Ui.ButtonIcon({ - icon : 'fa-info-circle', - title : 'Requirements', - tooltip : 'Display tool requirements', - onclick : function() { - if (!this.visible) { - this.visible = true; - self.message.update({ - persistent : true, - message : ToolTemplate.requirements(options), - status : 'info' - }); - } else { - this.visible = false; - self.message.update({ - message : '' - }); - } - } - }); - if (!options.requirements || options.requirements.length == 0) { - requirements_button.$el.hide(); - } - - // button for version selection - var versions_button = new Ui.ButtonMenu({ - icon : 'fa-cubes', - title : 'Versions', - tooltip : 'Select another tool version' - }); - if (options.versions && options.versions.length > 1) { - for (var i in options.versions) { - var version = options.versions[i]; - if (version != options.version) { - versions_button.addMenu({ - title : 'Switch to ' + version, - version : version, - icon : 'fa-cube', - onclick : function() { - // here we update the tool version (some tools encode the version also in the id) - options.id = options.id.replace(options.version, this.version); - options.version = this.version; - - // rebuild the model and form - self.deferred.reset(); - self.deferred.execute(function(){self._buildModel()}); - } - }); - } - } - } else { - versions_button.$el.hide(); - } - - // button menu - var menu_button = new Ui.ButtonMenu({ - icon : 'fa-caret-down', - title : 'Options', - tooltip : 'View available options' - }); - - // configure button selection - if(options.biostar_url) { - // add question option - menu_button.addMenu({ - icon : 'fa-question-circle', - title : 'Question?', - tooltip : 'Ask a question about this tool (Biostar)', - onclick : function() { - window.open(options.biostar_url + '/p/new/post/'); - } - }); - - // create search button - menu_button.addMenu({ - icon : 'fa-search', - title : 'Search', - tooltip : 'Search help for this tool (Biostar)', - onclick : function() { - window.open(options.biostar_url + '/t/' + options.id + '/'); - } - }); - }; - - // create share button - menu_button.addMenu({ - icon : 'fa-share', - title : 'Share', - tooltip : 'Share this tool', - onclick : function() { - prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id); - } - }); - - // add admin operations - if (this.is_admin) { - // create download button - menu_button.addMenu({ - icon : 'fa-download', - title : 'Download', - tooltip : 'Download this tool', - onclick : function() { - window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download'; - } - }); - } - - // create tool form section - this.section = new ToolSection.View(self, { - inputs : options.inputs, - cls : 'ui-table-plain' - }); - - // switch to classic tool form mako if the form definition is incompatible - if (this.incompatible) { - this.$el.hide(); - $('#tool-form-classic').show(); - return; - } - - // create portlet - this.portlet = new Portlet.View({ - icon : 'fa-wrench', - title : '' + options.name + ' ' + options.description + ' (Galaxy Tool Version ' + options.version + ')', - cls : 'ui-portlet-slim', - operations: { - requirements : requirements_button, - menu : menu_button, - versions : versions_button - }, - buttons: { - execute : new Ui.Button({ - icon : 'fa-check', - tooltip : 'Execute: ' + options.name, - title : 'Execute', - cls : 'btn btn-primary', - floating : 'clear', - onclick : function() { - self.job_handler.submit(); - } - }) - } - }); - - // append message - this.portlet.append(this.message.$el, true); - - // append tool section - this.portlet.append(this.section.$el); - - // start form - this.$el.empty(); - this.$el.append(this.portlet.$el); - - // append help - if (options.help != '') { - this.$el.append(ToolTemplate.help(options.help)); - } - - // append citations - if (options.citations) { - var $citations = $('
'); - var citations = new CitationModel.ToolCitationCollection(); - citations.tool_id = options.id; - var citation_list_view = new CitationView.CitationListView({ el: $citations, collection: citations } ); - citation_list_view.render(); - citations.fetch(); - this.$el.append($citations); - } - - // show message if available in model - if (options.message) { - this.message.update({ - persistent : true, - status : 'warning', - message : options.message - }); - } } }); diff --git a/static/scripts/mvc/tools/tools-jobs.js b/static/scripts/mvc/tools/tools-jobs.js index 469d9b67daf..790b44dfa0c 100644 --- a/static/scripts/mvc/tools/tools-jobs.js +++ b/static/scripts/mvc/tools/tools-jobs.js @@ -98,7 +98,7 @@ return Backbone.Model.extend({ } // validate non-optional fields - if (!input_def.optional && input_field.validate && !input_field.validate()) { + if (!input_def.optional && input_value == null) { this.app.highlight(input_id); return false; } diff --git a/static/scripts/mvc/tools/tools-section.js b/static/scripts/mvc/tools/tools-section.js index fab21d2a8f7..8038a403079 100644 --- a/static/scripts/mvc/tools/tools-section.js +++ b/static/scripts/mvc/tools/tools-section.js @@ -382,6 +382,11 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep /** Data input field */ _fieldData : function(input_def) { + if (this.app.workflow) { + var extensions = Utils.textify(input_def.extensions.toString()); + input_def.info = 'Data input \'' + input_def.name + '\' (' + extensions + ')'; + return this._fieldHidden(input_def); + } var self = this; return new SelectContent.View(this.app, { id : 'field-' + input_def.id, @@ -398,6 +403,14 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep /** Select/Checkbox/Radio options field */ _fieldSelect : function (input_def) { + // show text field in workflow + if (this.app.workflow && input_def.is_dynamic) { + if (!Utils.validate(input_def.value)) { + input_def.value = ''; + } + return this._fieldText(input_def); + } + // configure options fields var options = []; for (var i in input_def.options) { @@ -475,7 +488,8 @@ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-misc', 'mvc/tools/tools-rep */ _fieldHidden : function(input_def) { return new Ui.Hidden({ - id : 'field-' + input_def.id + id : 'field-' + input_def.id, + info : input_def.info }); }, diff --git a/static/scripts/mvc/tools/tools-select-content.js b/static/scripts/mvc/tools/tools-select-content.js index 7453963d69d..fa7e64d922c 100644 --- a/static/scripts/mvc/tools/tools-select-content.js +++ b/static/scripts/mvc/tools/tools-select-content.js @@ -36,14 +36,7 @@ var View = Backbone.View.extend({ this.list = {}; // error messages - var extensions = options.extensions.toString(); - if (extensions) { - extensions = extensions.replace(/,/g, ', '); - var pos = extensions.lastIndexOf(', '); - if (pos != -1) { - extensions = extensions.substr(0, pos) + ' or ' + extensions.substr(pos+1); - } - } + var extensions = Utils.textify(options.extensions); var hda_error = 'No dataset available.'; if (extensions) { hda_error = 'No ' + extensions + ' dataset available.'; diff --git a/static/scripts/mvc/tools/tools-tree.js b/static/scripts/mvc/tools/tools-tree.js index af08497a719..3a8cde64e78 100644 --- a/static/scripts/mvc/tools/tools-tree.js +++ b/static/scripts/mvc/tools/tools-tree.js @@ -2,7 +2,7 @@ This class maps the tool form dom to an api compatible javascript dictionary. */ // dependencies -define([], function() { +define(['utils/utils'], function(Utils) { // tool form tree return Backbone.Model.extend({ @@ -120,7 +120,7 @@ return Backbone.Model.extend({ // handle default value if (!field.skip) { - if (input.optional && field.validate && !field.validate()) { + if (field.validate && !field.validate(value)) { value = null; } add (job_input_id, input.id, value); diff --git a/static/scripts/mvc/ui/ui-misc.js b/static/scripts/mvc/ui/ui-misc.js index 666fd78882c..efedef2c0c3 100644 --- a/static/scripts/mvc/ui/ui-misc.js +++ b/static/scripts/mvc/ui/ui-misc.js @@ -412,7 +412,7 @@ var Hidden = Backbone.View.extend({ initialize : function(options) { // configure options this.options = options; - + // create new element this.setElement(this._template(this.options)); @@ -425,14 +425,20 @@ var Hidden = Backbone.View.extend({ // value value : function (new_val) { if (new_val !== undefined) { - this.$el.val(new_val); + this.$('hidden').val(new_val); } - return this.$el.val(); + return this.$('hidden').val(); }, // element _template: function(options) { - return ''; + var tmpl = '
'; + if (options.info) { + tmpl += ''; + } + tmpl += '' + + '
'; + return tmpl; } }); diff --git a/static/scripts/mvc/ui/ui-options.js b/static/scripts/mvc/ui/ui-options.js index 8d47dfe0330..21e0911747f 100644 --- a/static/scripts/mvc/ui/ui-options.js +++ b/static/scripts/mvc/ui/ui-options.js @@ -137,16 +137,7 @@ var Base = Backbone.View.extend({ /** Validate the selected option/options */ validate: function() { - var current = this.value(); - if (!(current instanceof Array)) { - current = [current]; - } - for (var i in current) { - if ([null, 'null', undefined].indexOf(current[i]) > -1) { - return false; - } - } - return true; + return Utils.validate(this.value()); }, /** Wait message during request processing diff --git a/static/scripts/mvc/ui/ui-select-default.js b/static/scripts/mvc/ui/ui-select-default.js index 6171ca3043d..99b63065e92 100644 --- a/static/scripts/mvc/ui/ui-select-default.js +++ b/static/scripts/mvc/ui/ui-select-default.js @@ -96,16 +96,7 @@ var View = Backbone.View.extend({ /** Validate the current selection */ validate: function() { - var current = this.value(); - if (!(current instanceof Array)) { - current = [current]; - } - for (var i in current) { - if ([null, 'null', undefined].indexOf(current[i]) > -1) { - return false; - } - } - return true; + return Utils.validate(this.value()); }, /** Return the label/text of the current selection diff --git a/static/scripts/mvc/upload/upload-view.js b/static/scripts/mvc/upload/upload-view.js index 064139f2caa..db33657cf6d 100644 --- a/static/scripts/mvc/upload/upload-view.js +++ b/static/scripts/mvc/upload/upload-view.js @@ -104,7 +104,7 @@ return Backbone.View.extend({ }); // define location - $('#left .unified-panel-header-inner').append((new UploadButton.View(this.ui_button)).$el); + $('.with-upload-button').append((new UploadButton.View(this.ui_button)).$el); // load extension var self = this; diff --git a/static/scripts/packed/galaxy.workflow_editor.canvas.js b/static/scripts/packed/galaxy.workflow_editor.canvas.js index 9f7fff67a09..f3de71974e1 100644 --- a/static/scripts/packed/galaxy.workflow_editor.canvas.js +++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js @@ -1 +1 @@ -function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.css("color","red")}},onMouseLeave:function(a){this.$el.css("color","black")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasConnectedOutputTerminals()&&!this.node.hasConnectedMappedInputTerminals()){_.each(this.node.mappedInputTerminals(),function(b){b.resetMappingIfNeeded()})}var a=!this.node.hasMappedOverInputTerminals();if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(this._collectionAttached()){inputsFilled=true}else{a=false}}else{a=true}}return a},_collectionAttached:function(){if(!this.connected()){return false}else{var a=this.connectors[0].handle1;if(!a){return false}else{if(a.isDataCollectionInput||a.isMappedOver()||a.datatypes.indexOf("input_collection")>0){return true}else{return false}}}},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(this.connected()&&!this._collectionAttached()){return false}if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(b){var a=[];$.each(b,function(c,d){if(d.connectors.length>0){a.push(d)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){a.push(d)}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.label=b.label;this.uuid=b.uuid;this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="
"+d+"
";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,uuid:f.uuid,label:f.label,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("
No node selected
")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("
");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("
"+j+"
");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("
");var h="
loading tool info...
";m.append(h);g.form_html=h;i.append(m);var k=$("
");k.append($("
").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("
")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("
"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("
").append($("").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("
").css({display:"none"}).appendTo("body").append($("
").append($("
").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('
').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(b,c){var a=c.proxy.terminal.connectors[0];if(a){a.destroy()}$(c.proxy).remove();$(c.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-qA){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-qu){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}}); \ No newline at end of file +function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.css("color","red")}},onMouseLeave:function(a){this.$el.css("color","black")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasConnectedOutputTerminals()&&!this.node.hasConnectedMappedInputTerminals()){_.each(this.node.mappedInputTerminals(),function(b){b.resetMappingIfNeeded()})}var a=!this.node.hasMappedOverInputTerminals();if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(this._collectionAttached()){inputsFilled=true}else{a=false}}else{a=true}}return a},_collectionAttached:function(){if(!this.connected()){return false}else{var a=this.connectors[0].handle1;if(!a){return false}else{if(a.isDataCollectionInput||a.isMappedOver()||a.datatypes.indexOf("input_collection")>0){return true}else{return false}}}},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(this.connected()&&!this._collectionAttached()){return false}if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(b){var a=[];$.each(b,function(c,d){if(d.connectors.length>0){a.push(d)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){a.push(d)}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.label=b.label;this.uuid=b.uuid;this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this,true)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="
"+d+"
";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,uuid:f.uuid,label:f.label,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("
No node selected
")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a,b){this.has_changes=true;if(this.active_node==a&&(!parent.__NEWTOOLFORM__||b)){this.check_changes_in_active_form();parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("
");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("
"+j+"
");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("
");var h="
loading tool info...
";m.append(h);g.form_html=h;i.append(m);var k=$("
");k.append($("
").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}function update_node(b){var a=workflow.active_node;if(a){a.update_field_data(b,true)}}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("
")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("
"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("
").append($("").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("
").css({display:"none"}).appendTo("body").append($("
").append($("
").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('
').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(b,c){var a=c.proxy.terminal.connectors[0];if(a){a.destroy()}$(c.proxy).remove();$(c.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-qA){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-qu){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-form-base.js b/static/scripts/packed/mvc/tools/tools-form-base.js new file mode 100644 index 00000000000..d3905253c58 --- /dev/null +++ b/static/scripts/packed/mvc/tools/tools-form-base.js @@ -0,0 +1 @@ +define(["utils/utils","utils/deferred","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-content","mvc/tools/tools-section","mvc/tools/tools-tree"],function(g,h,f,k,i,a,d,c,e,j,b){return Backbone.View.extend({initialize:function(l){console.debug(l);var m=parent.Galaxy;if(m&&m.modal){this.modal=m.modal}else{this.modal=new k.Modal.View()}if(m&&m.currUser){this.is_admin=m.currUser.get("is_admin")}else{this.is_admin=false}this.options=l;this.container=this.options.container||"body";this.deferred=new h();this.setElement("
");$(this.container).append(this.$el);this._buildForm()},reciept:function(l){$(this.container).empty();$(this.container).append(l)},highlight:function(m,n,l){var o=this.element_list[m];if(o){o.error(n||"Please verify this parameter.");if(!l){$(this.container).animate({scrollTop:o.$el.offset().top-20},500)}}},_buildForm:function(){var l=this;this.off("refresh");this.off("reset");this.field_list={};this.input_list={};this.element_list={};this.tree=new b(this);this.content=new e(this);var n=this.options;this._renderForm(n);this.tree.finalize();if(!this.workflow&&n.errors){var o=this.tree.matchResponse(n.errors);for(var m in o){this.highlight(m,o[m],true)}}this.on("refresh",function(){l.deferred.reset();l.deferred.execute(function(){l._updateModel()})});this.on("reset",function(){for(var p in this.element_list){this.element_list[p].reset()}})},_renderForm:function(u){var t=this;this.message=new k.Message();var q=new k.ButtonIcon({icon:"fa-info-circle",title:"Requirements",tooltip:"Display tool requirements",onclick:function(){if(!this.visible){this.visible=true;t.message.update({persistent:true,message:c.requirements(u),status:"info"})}else{this.visible=false;t.message.update({message:""})}}});if(!u.requirements||u.requirements.length==0){q.$el.hide()}var m=new k.ButtonMenu({icon:"fa-cubes",title:"Versions",tooltip:"Select another tool version"});if(u.versions&&u.versions.length>1){for(var o in u.versions){var r=u.versions[o];if(r!=u.version){m.addMenu({title:"Switch to "+r,version:r,icon:"fa-cube",onclick:function(){u.id=u.id.replace(u.version,this.version);u.version=this.version;t.deferred.reset();t.deferred.execute(function(){t._buildModel()})}})}}}else{m.$el.hide()}var p=new k.ButtonMenu({icon:"fa-caret-down",title:"Options",tooltip:"View available options"});if(u.biostar_url){p.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(u.biostar_url+"/p/new/post/")}});p.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(u.biostar_url+"/t/"+u.id+"/")}})}p.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+galaxy_config.root+"root?tool_id="+u.id)}});if(this.is_admin){p.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=galaxy_config.root+"api/tools/"+u.id+"/download"}})}this.section=new j.View(t,{inputs:u.inputs,cls:"ui-table-plain"});if(this.incompatible){this.$el.hide();$("#tool-form-classic").show();return}this.portlet=new f.View({icon:"fa-wrench",title:""+u.name+" "+u.description+" (Galaxy Tool Version "+u.version+")",cls:"ui-portlet-slim",operations:{requirements:q,menu:p,versions:m},buttons:this.buttons});this.portlet.append(this.message.$el,true);this.portlet.append(this.section.$el);this.$el.empty();this.$el.append(this.portlet.$el);if(u.help!=""){this.$el.append(c.help(u.help))}if(u.citations){var s=$("
");var l=new i.ToolCitationCollection();l.tool_id=u.id;var n=new a.CitationListView({el:s,collection:l});n.render();l.fetch();this.$el.append(s)}if(u.message){this.message.update({persistent:true,status:"warning",message:u.message})}}})}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-form-workflow.js b/static/scripts/packed/mvc/tools/tools-form-workflow.js new file mode 100644 index 00000000000..4bf9dfdf6a0 --- /dev/null +++ b/static/scripts/packed/mvc/tools/tools-form-workflow.js @@ -0,0 +1 @@ +define(["utils/utils","mvc/tools/tools-form-base"],function(b,a){var c=a.extend({initialize:function(d){this.workflow=true;a.prototype.initialize.call(this,d)},_buildModel:function(){},_updateModel:function(){var d=this;var e=this.tree.finalize();console.debug("tools-form-workflow::_refreshForm() - Refreshing states.");console.debug(e);var g=this.deferred.register();var f=galaxy_config.root+"workflow/editor_form_post?tool_id="+this.options.id;b.request({type:"GET",url:f,data:e,success:function(h){parent.update_node(h);d.deferred.done(g);console.debug("tools-form::_refreshForm() - States refreshed.");console.debug(h)},error:function(h){d.deferred.done(g);console.debug("tools-form::_refreshForm() - Refresh request failed.");console.debug(h)}})}});return{View:c}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-form.js b/static/scripts/packed/mvc/tools/tools-form.js index 6e8b6b289bf..e66145336be 100644 --- a/static/scripts/packed/mvc/tools/tools-form.js +++ b/static/scripts/packed/mvc/tools/tools-form.js @@ -1 +1 @@ -define(["utils/utils","utils/deferred","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-content","mvc/tools/tools-section","mvc/tools/tools-tree","mvc/tools/tools-jobs"],function(i,j,h,m,k,a,e,d,f,l,c,g){var b=Backbone.View.extend({container:"body",initialize:function(n){console.debug(n);var o=parent.Galaxy;if(o&&o.modal){this.modal=o.modal}else{this.modal=new m.Modal.View()}if(o&&o.currUser){this.is_admin=o.currUser.get("is_admin")}else{this.is_admin=false}this.options=n;this.deferred=new j();this.setElement("
");$(this.container).append(this.$el);this._buildForm()},reciept:function(n){$(this.container).empty();$(this.container).append(n)},highlight:function(o,p,n){var q=this.element_list[o];if(q){q.error(p||"Please verify this parameter.");if(!n){$(this.container).animate({scrollTop:q.$el.offset().top-20},500)}}},_buildModel:function(){var n=this;var o=galaxy_config.root+"api/tools/"+this.options.id+"/build?";if(this.options.job_id){o+="job_id="+this.options.job_id}else{if(this.options.dataset_id){o+="dataset_id="+this.options.dataset_id}else{o+="tool_version="+this.options.version+"&";var q=top.location.href;var r=q.indexOf("?");if(q.indexOf("tool_id=")!=-1&&r!==-1){o+=q.slice(r+1)}}}var p=this.deferred.register();i.request({type:"GET",url:o,success:function(s){n.options=s;n._buildForm();n.message.update({status:"success",message:"Now you are using '"+n.options.name+"' version "+n.options.version+".",persistent:false});n.deferred.done(p);console.debug("tools-form::initialize() - Initial tool model ready.");console.debug(s)},error:function(s){n.deferred.done(p);console.debug("tools-form::initialize() - Initial tool model request failed.");console.debug(s);var t=s.error||"Uncaught error.";n.modal.show({title:"Tool cannot be executed",body:t,buttons:{Close:function(){n.modal.hide()}}})}})},_updateModel:function(){var n=this;var o=this.tree.finalize({data:function(s){if(s.values.length>0&&s.values[0]&&s.values[0].src==="hda"){return n.content.get({id:s.values[0].id,src:"hda"}).id_uncoded}return null}});console.debug("tools-form::_refreshForm() - Refreshing states.");console.debug(o);function r(v){for(var t in n.input_list){var u=n.field_list[t];var s=n.input_list[t];if(s.is_dynamic&&u.wait&&u.unwait){if(v){u.wait()}else{u.unwait()}}}}r(true);var q=this.deferred.register();var p=galaxy_config.root+"api/tools/"+this.options.id+"/build?tool_version="+this.options.version;i.request({type:"GET",url:p,data:o,success:function(s){n.tree.matchModel(s,function(u,y){var t=n.input_list[u];if(t&&t.options){if(!_.isEqual(t.options,y.options)){t.options=y.options;var z=n.field_list[u];if(z.update){var x=[];if((["data","data_collection","drill_down"]).indexOf(t.type)!=-1){x=t.options}else{for(var w in y.options){var v=y.options[w];if(v.length>2){x.push({label:v[0],value:v[1]})}}}z.update(x);z.trigger("change");console.debug("Updating options for "+u)}}}});r(false);n.deferred.done(q);console.debug("tools-form::_refreshForm() - States refreshed.");console.debug(s)},error:function(s){n.deferred.done(q);console.debug("tools-form::_refreshForm() - Refresh request failed.");console.debug(s)}})},_buildForm:function(){var n=this;this.off("refresh");this.off("reset");this.field_list={};this.input_list={};this.element_list={};this.tree=new c(this);this.job_handler=new g(this);this.content=new f(this);var p=this.options;this._renderForm(p);this.tree.finalize();if(p.errors){var q=this.tree.matchResponse(p.errors);for(var o in q){this.highlight(o,q[o],true)}}this.on("refresh",function(){n.deferred.reset();n.deferred.execute(function(){n._updateModel()})});this.on("reset",function(){for(var r in this.element_list){this.element_list[r].reset()}})},_renderForm:function(w){var v=this;this.message=new m.Message();var s=new m.ButtonIcon({icon:"fa-info-circle",title:"Requirements",tooltip:"Display tool requirements",onclick:function(){if(!this.visible){this.visible=true;v.message.update({persistent:true,message:d.requirements(w),status:"info"})}else{this.visible=false;v.message.update({message:""})}}});if(!w.requirements||w.requirements.length==0){s.$el.hide()}var o=new m.ButtonMenu({icon:"fa-cubes",title:"Versions",tooltip:"Select another tool version"});if(w.versions&&w.versions.length>1){for(var q in w.versions){var t=w.versions[q];if(t!=w.version){o.addMenu({title:"Switch to "+t,version:t,icon:"fa-cube",onclick:function(){w.id=w.id.replace(w.version,this.version);w.version=this.version;v.deferred.reset();v.deferred.execute(function(){v._buildModel()})}})}}}else{o.$el.hide()}var r=new m.ButtonMenu({icon:"fa-caret-down",title:"Options",tooltip:"View available options"});if(w.biostar_url){r.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(w.biostar_url+"/p/new/post/")}});r.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(w.biostar_url+"/t/"+w.id+"/")}})}r.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+galaxy_config.root+"root?tool_id="+w.id)}});if(this.is_admin){r.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=galaxy_config.root+"api/tools/"+w.id+"/download"}})}this.section=new l.View(v,{inputs:w.inputs,cls:"ui-table-plain"});if(this.incompatible){this.$el.hide();$("#tool-form-classic").show();return}this.portlet=new h.View({icon:"fa-wrench",title:""+w.name+" "+w.description+" (Galaxy Tool Version "+w.version+")",cls:"ui-portlet-slim",operations:{requirements:s,menu:r,versions:o},buttons:{execute:new m.Button({icon:"fa-check",tooltip:"Execute: "+w.name,title:"Execute",cls:"btn btn-primary",floating:"clear",onclick:function(){v.job_handler.submit()}})}});this.portlet.append(this.message.$el,true);this.portlet.append(this.section.$el);this.$el.empty();this.$el.append(this.portlet.$el);if(w.help!=""){this.$el.append(d.help(w.help))}if(w.citations){var u=$("
");var n=new k.ToolCitationCollection();n.tool_id=w.id;var p=new a.CitationListView({el:u,collection:n});p.render();n.fetch();this.$el.append(u)}if(w.message){this.message.update({persistent:true,status:"warning",message:w.message})}}});return{View:b}}); \ No newline at end of file +define(["utils/utils","mvc/ui/ui-misc","mvc/tools/tools-form-base","mvc/tools/tools-jobs"],function(c,e,b,a){var d=b.extend({initialize:function(g){var f=this;this.job_handler=new a(this);this.buttons={execute:new e.Button({icon:"fa-check",tooltip:"Execute: "+g.name,title:"Execute",cls:"btn btn-primary",floating:"clear",onclick:function(){f.job_handler.submit()}})};b.prototype.initialize.call(this,g)},_buildModel:function(){var f=this;var g=galaxy_config.root+"api/tools/"+this.options.id+"/build?";if(this.options.job_id){g+="job_id="+this.options.job_id}else{if(this.options.dataset_id){g+="dataset_id="+this.options.dataset_id}else{g+="tool_version="+this.options.version+"&";var i=top.location.href;var j=i.indexOf("?");if(i.indexOf("tool_id=")!=-1&&j!==-1){g+=i.slice(j+1)}}}var h=this.deferred.register();c.request({type:"GET",url:g,success:function(k){f.options=k;f._buildForm();f.message.update({status:"success",message:"Now you are using '"+f.options.name+"' version "+f.options.version+".",persistent:false});f.deferred.done(h);console.debug("tools-form::initialize() - Initial tool model ready.");console.debug(k)},error:function(k){f.deferred.done(h);console.debug("tools-form::initialize() - Initial tool model request failed.");console.debug(k);var l=k.error||"Uncaught error.";f.modal.show({title:"Tool cannot be executed",body:l,buttons:{Close:function(){f.modal.hide()}}})}})},_updateModel:function(){var f=this;var g=this.tree.finalize({data:function(k){if(k.values.length>0&&k.values[0]&&k.values[0].src==="hda"){return f.content.get({id:k.values[0].id,src:"hda"}).id_uncoded}return null}});console.debug("tools-form::_refreshForm() - Refreshing states.");console.debug(g);function j(n){for(var l in f.input_list){var m=f.field_list[l];var k=f.input_list[l];if(k.is_dynamic&&m.wait&&m.unwait){if(n){m.wait()}else{m.unwait()}}}}j(true);var i=this.deferred.register();var h=galaxy_config.root+"api/tools/"+this.options.id+"/build?tool_version="+this.options.version;c.request({type:"GET",url:h,data:g,success:function(k){f.tree.matchModel(k,function(m,q){var l=f.input_list[m];if(l&&l.options){if(!_.isEqual(l.options,q.options)){l.options=q.options;var r=f.field_list[m];if(r.update){var p=[];if((["data","data_collection","drill_down"]).indexOf(l.type)!=-1){p=l.options}else{for(var o in q.options){var n=q.options[o];if(n.length>2){p.push({label:n[0],value:n[1]})}}}r.update(p);r.trigger("change");console.debug("Updating options for "+m)}}}});j(false);f.deferred.done(i);console.debug("tools-form::_refreshForm() - States refreshed.");console.debug(k)},error:function(k){f.deferred.done(i);console.debug("tools-form::_refreshForm() - Refresh request failed.");console.debug(k)}})}});return{View:d}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-jobs.js b/static/scripts/packed/mvc/tools/tools-jobs.js index 514aee410f5..62ee1607d73 100644 --- a/static/scripts/packed/mvc/tools/tools-jobs.js +++ b/static/scripts/packed/mvc/tools/tools-jobs.js @@ -1 +1 @@ -define(["utils/utils","mvc/tools/tools-template"],function(b,a){return Backbone.Model.extend({initialize:function(c){this.app=c},submit:function(){var c=this;var d={tool_id:this.app.options.id,tool_version:this.app.options.version,inputs:this.app.tree.finalize()};this.app.trigger("reset");if(!this._validation(d)){console.debug("tools-jobs::submit - Submission canceled. Validation failed.");return}console.debug(d);this.app.modal.show({title:"Please wait...",body:"progress",closing_events:true,buttons:{Close:function(){c.app.modal.hide()}}});b.request({type:"POST",url:galaxy_config.root+"api/tools",data:d,success:function(e){c.app.modal.hide();c.app.reciept(a.success(e));c._refreshHdas()},error:function(e,g){c.app.modal.hide();if(e&&e.message&&e.message.data){var h=c.app.tree.matchResponse(e.message.data);for(var f in h){c.app.highlight(f,h[f]);break}}else{console.debug(e);c.app.modal.show({title:"Job submission failed",body:a.error(d),buttons:{Close:function(){c.app.modal.hide()}}})}}})},_validation:function(h){var d=h.inputs;var m=-1;var i=null;for(var k in d){var f=d[k];var l=this.app.tree.match(k);var e=this.app.field_list[l];var j=this.app.input_list[l];if(!l||!j||!e){console.debug("tools-jobs::_validation - Retrieving input objects failed.");continue}if(!j.optional&&e.validate&&!e.validate()){this.app.highlight(l);return false}if(f&&f.batch){var g=f.values.length;var c=null;if(g>0){c=f.values[0]&&f.values[0].src}if(c){if(i===null){i=c}else{if(i!==c){this.app.highlight(l,"Please select either dataset or dataset list fields for all batch mode fields.");return false}}}if(m===-1){m=g}else{if(m!==g){this.app.highlight(l,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains "+g+" selection(s) while a previous field contains "+m+".");return false}}}}return true},_refreshHdas:function(){if(parent.Galaxy&&parent.Galaxy.currHistoryPanel){parent.Galaxy.currHistoryPanel.refreshContents()}}})}); \ No newline at end of file +define(["utils/utils","mvc/tools/tools-template"],function(b,a){return Backbone.Model.extend({initialize:function(c){this.app=c},submit:function(){var c=this;var d={tool_id:this.app.options.id,tool_version:this.app.options.version,inputs:this.app.tree.finalize()};this.app.trigger("reset");if(!this._validation(d)){console.debug("tools-jobs::submit - Submission canceled. Validation failed.");return}console.debug(d);this.app.modal.show({title:"Please wait...",body:"progress",closing_events:true,buttons:{Close:function(){c.app.modal.hide()}}});b.request({type:"POST",url:galaxy_config.root+"api/tools",data:d,success:function(e){c.app.modal.hide();c.app.reciept(a.success(e));c._refreshHdas()},error:function(e,g){c.app.modal.hide();if(e&&e.message&&e.message.data){var h=c.app.tree.matchResponse(e.message.data);for(var f in h){c.app.highlight(f,h[f]);break}}else{console.debug(e);c.app.modal.show({title:"Job submission failed",body:a.error(d),buttons:{Close:function(){c.app.modal.hide()}}})}}})},_validation:function(h){var d=h.inputs;var m=-1;var i=null;for(var k in d){var f=d[k];var l=this.app.tree.match(k);var e=this.app.field_list[l];var j=this.app.input_list[l];if(!l||!j||!e){console.debug("tools-jobs::_validation - Retrieving input objects failed.");continue}if(!j.optional&&f==null){this.app.highlight(l);return false}if(f&&f.batch){var g=f.values.length;var c=null;if(g>0){c=f.values[0]&&f.values[0].src}if(c){if(i===null){i=c}else{if(i!==c){this.app.highlight(l,"Please select either dataset or dataset list fields for all batch mode fields.");return false}}}if(m===-1){m=g}else{if(m!==g){this.app.highlight(l,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains "+g+" selection(s) while a previous field contains "+m+".");return false}}}}return true},_refreshHdas:function(){if(parent.Galaxy&&parent.Galaxy.currHistoryPanel){parent.Galaxy.currHistoryPanel.refreshContents()}}})}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-section.js b/static/scripts/packed/mvc/tools/tools-section.js index e168486d9ff..a9ead10010d 100644 --- a/static/scripts/packed/mvc/tools/tools-section.js +++ b/static/scripts/packed/mvc/tools/tools-section.js @@ -1 +1 @@ -define(["utils/utils","mvc/ui/ui-table","mvc/ui/ui-misc","mvc/tools/tools-repeat","mvc/tools/tools-select-content","mvc/tools/tools-input"],function(d,b,g,c,a,e){var f=Backbone.View.extend({initialize:function(i,h){this.app=i;this.inputs=h.inputs;h.cls_tr="section-row";this.table=new b.View(h);this.setElement(this.table.$el);this.render()},render:function(){this.table.delAll();for(var h in this.inputs){this._add(this.inputs[h])}},_add:function(j){var i=this;var h=jQuery.extend(true,{},j);h.id=j.id=d.uuid();this.app.input_list[h.id]=h;var k=h.type;switch(k){case"conditional":this._addConditional(h);break;case"repeat":this._addRepeat(h);break;default:this._addRow(h)}},_addConditional:function(h){var j=this;h.test_param.id=h.id;var m=this._addRow(h.test_param);m.options.onchange=function(t){var p=j.app.tree.matchCase(h,t);for(var r in h.cases){var w=h.cases[r];var u=h.id+"-section-"+r;var o=j.table.get(u);var v=false;for(var q in w.inputs){var s=w.inputs[q].type;if(s&&s!=="hidden"){v=true;break}}if(r==p&&v){o.fadeIn("fast")}else{o.hide()}}j.app.trigger("refresh")};for(var l in h.cases){var k=h.id+"-section-"+l;var n=new f(this.app,{inputs:h.cases[l].inputs,cls:"ui-table-plain"});n.$el.addClass("ui-table-form-section");this.table.add(n.$el);this.table.append(k)}m.trigger("change")},_addRepeat:function(o){var r=this;var p=0;function m(i,t){var s=o.id+"-section-"+(p++);var u=null;if(t){u=function(){k.del(s);k.retitle(o.title);r.app.trigger("refresh")}}var v=new f(r.app,{inputs:i,cls:"ui-table-plain"});k.add({id:s,title:o.title,$el:v.$el,ondel:u});k.retitle(o.title)}var k=new c.View({title_new:o.title,max:o.max,onnew:function(){m(o.inputs,true);r.app.trigger("refresh")}});var h=o.min;var q=_.size(o.cache);for(var l=0;l=h)}var j=new e(this.app,{label:o.title,help:o.help,field:k});j.$el.addClass("ui-table-form-section");this.table.add(j.$el);this.table.append(o.id)},_addRow:function(h){var k=h.id;var i=this._createField(h);this.app.field_list[k]=i;var j=new e(this.app,{label:h.label,optional:h.optional,help:h.help,field:i});this.app.element_list[k]=j;this.table.add(j.$el);this.table.append(k);return i},_createField:function(h){var i=null;switch(h.type){case"text":i=this._fieldText(h);break;case"select":i=this._fieldSelect(h);break;case"data":i=this._fieldData(h);break;case"data_collection":i=this._fieldData(h);break;case"data_column":h.error_text="Missing columns in referenced dataset.";i=this._fieldSelect(h);break;case"hidden":i=this._fieldHidden(h);break;case"hidden_data":i=this._fieldHidden(h);break;case"integer":i=this._fieldSlider(h);break;case"float":i=this._fieldSlider(h);break;case"boolean":i=this._fieldBoolean(h);break;case"genomebuild":h.searchable=true;i=this._fieldSelect(h);break;case"drill_down":i=this._fieldDrilldown(h);break;case"baseurl":i=this._fieldHidden(h);break;default:this.app.incompatible=true;if(h.options){i=this._fieldSelect(h)}else{i=this._fieldText(h)}console.debug("tools-form::_addRow() : Auto matched field type ("+h.type+").")}if(h.value!==undefined){i.value(h.value)}return i},_fieldData:function(h){var i=this;return new a.View(this.app,{id:"field-"+h.id,extensions:h.extensions,multiple:h.multiple,type:h.type,data:h.options,onchange:function(){i.app.trigger("refresh")}})},_fieldSelect:function(h){var k=[];for(var l in h.options){var m=h.options[l];k.push({label:m[0],value:m[1]})}var n=g.Select;switch(h.display){case"checkboxes":n=g.Checkbox;break;case"radio":n=g.Radio;break}var j=this;return new n.View({id:"field-"+h.id,data:k,error_text:h.error_text||"No options available",multiple:h.multiple,searchable:h.searchable,onchange:function(){j.app.trigger("refresh")}})},_fieldDrilldown:function(h){var i=this;return new g.Drilldown.View({id:"field-"+h.id,data:h.options,display:h.display,onchange:function(){i.app.trigger("refresh")}})},_fieldText:function(h){var i=this;return new g.Input({id:"field-"+h.id,area:h.area,onchange:function(){i.app.trigger("refresh")}})},_fieldSlider:function(h){return new g.Slider.View({id:"field-"+h.id,precise:h.type=="float",min:h.min,max:h.max})},_fieldHidden:function(h){return new g.Hidden({id:"field-"+h.id})},_fieldBoolean:function(h){return new g.RadioButton.View({id:"field-"+h.id,data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]})}});return{View:f}}); \ No newline at end of file +define(["utils/utils","mvc/ui/ui-table","mvc/ui/ui-misc","mvc/tools/tools-repeat","mvc/tools/tools-select-content","mvc/tools/tools-input"],function(d,b,g,c,a,e){var f=Backbone.View.extend({initialize:function(i,h){this.app=i;this.inputs=h.inputs;h.cls_tr="section-row";this.table=new b.View(h);this.setElement(this.table.$el);this.render()},render:function(){this.table.delAll();for(var h in this.inputs){this._add(this.inputs[h])}},_add:function(j){var i=this;var h=jQuery.extend(true,{},j);h.id=j.id=d.uuid();this.app.input_list[h.id]=h;var k=h.type;switch(k){case"conditional":this._addConditional(h);break;case"repeat":this._addRepeat(h);break;default:this._addRow(h)}},_addConditional:function(h){var j=this;h.test_param.id=h.id;var m=this._addRow(h.test_param);m.options.onchange=function(t){var p=j.app.tree.matchCase(h,t);for(var r in h.cases){var w=h.cases[r];var u=h.id+"-section-"+r;var o=j.table.get(u);var v=false;for(var q in w.inputs){var s=w.inputs[q].type;if(s&&s!=="hidden"){v=true;break}}if(r==p&&v){o.fadeIn("fast")}else{o.hide()}}j.app.trigger("refresh")};for(var l in h.cases){var k=h.id+"-section-"+l;var n=new f(this.app,{inputs:h.cases[l].inputs,cls:"ui-table-plain"});n.$el.addClass("ui-table-form-section");this.table.add(n.$el);this.table.append(k)}m.trigger("change")},_addRepeat:function(o){var r=this;var p=0;function m(i,t){var s=o.id+"-section-"+(p++);var u=null;if(t){u=function(){k.del(s);k.retitle(o.title);r.app.trigger("refresh")}}var v=new f(r.app,{inputs:i,cls:"ui-table-plain"});k.add({id:s,title:o.title,$el:v.$el,ondel:u});k.retitle(o.title)}var k=new c.View({title_new:o.title,max:o.max,onnew:function(){m(o.inputs,true);r.app.trigger("refresh")}});var h=o.min;var q=_.size(o.cache);for(var l=0;l=h)}var j=new e(this.app,{label:o.title,help:o.help,field:k});j.$el.addClass("ui-table-form-section");this.table.add(j.$el);this.table.append(o.id)},_addRow:function(h){var k=h.id;var i=this._createField(h);this.app.field_list[k]=i;var j=new e(this.app,{label:h.label,optional:h.optional,help:h.help,field:i});this.app.element_list[k]=j;this.table.add(j.$el);this.table.append(k);return i},_createField:function(h){var i=null;switch(h.type){case"text":i=this._fieldText(h);break;case"select":i=this._fieldSelect(h);break;case"data":i=this._fieldData(h);break;case"data_collection":i=this._fieldData(h);break;case"data_column":h.error_text="Missing columns in referenced dataset.";i=this._fieldSelect(h);break;case"hidden":i=this._fieldHidden(h);break;case"hidden_data":i=this._fieldHidden(h);break;case"integer":i=this._fieldSlider(h);break;case"float":i=this._fieldSlider(h);break;case"boolean":i=this._fieldBoolean(h);break;case"genomebuild":h.searchable=true;i=this._fieldSelect(h);break;case"drill_down":i=this._fieldDrilldown(h);break;case"baseurl":i=this._fieldHidden(h);break;default:this.app.incompatible=true;if(h.options){i=this._fieldSelect(h)}else{i=this._fieldText(h)}console.debug("tools-form::_addRow() : Auto matched field type ("+h.type+").")}if(h.value!==undefined){i.value(h.value)}return i},_fieldData:function(h){if(this.app.workflow){var j=d.textify(h.extensions.toString());h.info="Data input '"+h.name+"' ("+j+")";return this._fieldHidden(h)}var i=this;return new a.View(this.app,{id:"field-"+h.id,extensions:h.extensions,multiple:h.multiple,type:h.type,data:h.options,onchange:function(){i.app.trigger("refresh")}})},_fieldSelect:function(h){if(this.app.workflow&&h.is_dynamic){if(!d.validate(h.value)){h.value=""}return this._fieldText(h)}var k=[];for(var l in h.options){var m=h.options[l];k.push({label:m[0],value:m[1]})}var n=g.Select;switch(h.display){case"checkboxes":n=g.Checkbox;break;case"radio":n=g.Radio;break}var j=this;return new n.View({id:"field-"+h.id,data:k,error_text:h.error_text||"No options available",multiple:h.multiple,searchable:h.searchable,onchange:function(){j.app.trigger("refresh")}})},_fieldDrilldown:function(h){var i=this;return new g.Drilldown.View({id:"field-"+h.id,data:h.options,display:h.display,onchange:function(){i.app.trigger("refresh")}})},_fieldText:function(h){var i=this;return new g.Input({id:"field-"+h.id,area:h.area,onchange:function(){i.app.trigger("refresh")}})},_fieldSlider:function(h){return new g.Slider.View({id:"field-"+h.id,precise:h.type=="float",min:h.min,max:h.max})},_fieldHidden:function(h){return new g.Hidden({id:"field-"+h.id,info:h.info})},_fieldBoolean:function(h){return new g.RadioButton.View({id:"field-"+h.id,data:[{label:"Yes",value:"true"},{label:"No",value:"false"}]})}});return{View:f}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-select-content.js b/static/scripts/packed/mvc/tools/tools-select-content.js index dbe99f86fa9..37a7a79faea 100644 --- a/static/scripts/packed/mvc/tools/tools-select-content.js +++ b/static/scripts/packed/mvc/tools/tools-select-content.js @@ -1 +1 @@ -define(["utils/utils","mvc/ui/ui-misc","mvc/ui/ui-tabs","mvc/tools/tools-template"],function(c,e,b,a){var d=Backbone.View.extend({initialize:function(f,o){this.app=f;this.options=o;var n=this;this.setElement("
");this.list={};var m=[];if(o.type=="data_collection"){this.mode="collection"}else{if(o.multiple){this.mode="multiple"}else{this.mode="single"}}this.current=this.mode;this.list={};var j=o.extensions.toString();if(j){j=j.replace(/,/g,", ");var k=j.lastIndexOf(", ");if(k!=-1){j=j.substr(0,k)+" or "+j.substr(k+1)}}var h="No dataset available.";if(j){h="No "+j+" dataset available."}var l="No dataset list available.";if(j){l="No "+j+" dataset collection available."}if(this.mode=="single"){m.push({icon:"fa-file-o",label:"Single dataset",value:"single"});this.select_single=new e.Select.View({error_text:h,onchange:function(){n.trigger("change")}});this.list.single={field:this.select_single,type:"hda"}}if(this.mode=="single"||this.mode=="multiple"){m.push({icon:"fa-files-o",label:"Multiple datasets",value:"multiple"});this.select_multiple=new e.Select.View({multiple:true,error_text:h,onchange:function(){n.trigger("change")}});this.list.multiple={field:this.select_multiple,type:"hda"}}if(this.mode=="single"||this.mode=="collection"){m.push({icon:"fa-folder-o",label:"Dataset collection",value:"collection"});this.select_collection=new e.Select.View({error_text:l,onchange:function(){n.trigger("change")}});this.list.collection={field:this.select_collection,type:"hdca"}}this.button_type=new e.RadioButton.View({value:this.current,data:m,onchange:function(i){n.current=i;n.refresh();n.trigger("change")}});this.$batch=$(a.batchMode());if(_.size(this.list)>1){this.$el.append(c.wrap(this.button_type.$el))}for(var g in this.list){this.$el.append(this.list[g].field.$el)}this.$el.append(this.$batch);this.update(o.data);if(this.options.value!==undefined){this.value(this.options.value)}this.refresh();this.on("change",function(){if(o.onchange){o.onchange(n.value())}})},wait:function(){for(var f in this.list){this.list[f].field.wait()}},unwait:function(){for(var f in this.list){this.list[f].field.unwait()}},update:function(g){var l=[];for(var j in g.hda){var k=g.hda[j];l.push({label:k.hid+": "+k.name,value:k.id})}var f=[];for(var j in g.hdca){var h=g.hdca[j];f.push({label:h.hid+": "+h.name,value:h.id})}this.select_single&&this.select_single.update(l);this.select_multiple&&this.select_multiple.update(l);this.select_collection&&this.select_collection.update(f);this.app.content.add(g)},value:function(m){if(m&&m.values){try{var l=[];for(var h in m.values){l.push(m.values[h].id)}if(m&&m.values.length>0&&m.values[0].src=="hcda"){this.current="collection";this.select_collection.value(l[0])}else{if(this.mode=="multiple"){this.current="multiple";this.select_multiple.value(l)}else{this.current="single";this.select_single.value(l[0])}}this.refresh();var g=this._select();if(!g.validate()){g.value(g.first())}}catch(k){console.debug("tools-select-content::value() - Skipped.")}}var j=this._select().value();if(!(j instanceof Array)){j=[j]}var f={batch:this.mode=="single"&&this.current!="single",values:[]};for(var h in j){f.values.push({id:j[h],src:this.list[this.current].type})}return f},validate:function(){return this._select().validate()},refresh:function(){for(var g in this.list){var f=this.list[g].field.$el;if(this.current==g){f.show()}else{f.hide()}}if(this.mode=="single"&&this.current!="single"){this.$batch.show()}else{this.$batch.hide()}},_select:function(){return this.list[this.current].field}});return{View:d}}); \ No newline at end of file +define(["utils/utils","mvc/ui/ui-misc","mvc/ui/ui-tabs","mvc/tools/tools-template"],function(c,e,b,a){var d=Backbone.View.extend({initialize:function(l,g){this.app=l;this.options=g;var f=this;this.setElement("
");this.list={};var j=[];if(g.type=="data_collection"){this.mode="collection"}else{if(g.multiple){this.mode="multiple"}else{this.mode="single"}}this.current=this.mode;this.list={};var k=c.textify(g.extensions);var n="No dataset available.";if(k){n="No "+k+" dataset available."}var m="No dataset list available.";if(k){m="No "+k+" dataset collection available."}if(this.mode=="single"){j.push({icon:"fa-file-o",label:"Single dataset",value:"single"});this.select_single=new e.Select.View({error_text:n,onchange:function(){f.trigger("change")}});this.list.single={field:this.select_single,type:"hda"}}if(this.mode=="single"||this.mode=="multiple"){j.push({icon:"fa-files-o",label:"Multiple datasets",value:"multiple"});this.select_multiple=new e.Select.View({multiple:true,error_text:n,onchange:function(){f.trigger("change")}});this.list.multiple={field:this.select_multiple,type:"hda"}}if(this.mode=="single"||this.mode=="collection"){j.push({icon:"fa-folder-o",label:"Dataset collection",value:"collection"});this.select_collection=new e.Select.View({error_text:m,onchange:function(){f.trigger("change")}});this.list.collection={field:this.select_collection,type:"hdca"}}this.button_type=new e.RadioButton.View({value:this.current,data:j,onchange:function(i){f.current=i;f.refresh();f.trigger("change")}});this.$batch=$(a.batchMode());if(_.size(this.list)>1){this.$el.append(c.wrap(this.button_type.$el))}for(var h in this.list){this.$el.append(this.list[h].field.$el)}this.$el.append(this.$batch);this.update(g.data);if(this.options.value!==undefined){this.value(this.options.value)}this.refresh();this.on("change",function(){if(g.onchange){g.onchange(f.value())}})},wait:function(){for(var f in this.list){this.list[f].field.wait()}},unwait:function(){for(var f in this.list){this.list[f].field.unwait()}},update:function(g){var l=[];for(var j in g.hda){var k=g.hda[j];l.push({label:k.hid+": "+k.name,value:k.id})}var f=[];for(var j in g.hdca){var h=g.hdca[j];f.push({label:h.hid+": "+h.name,value:h.id})}this.select_single&&this.select_single.update(l);this.select_multiple&&this.select_multiple.update(l);this.select_collection&&this.select_collection.update(f);this.app.content.add(g)},value:function(m){if(m&&m.values){try{var l=[];for(var h in m.values){l.push(m.values[h].id)}if(m&&m.values.length>0&&m.values[0].src=="hcda"){this.current="collection";this.select_collection.value(l[0])}else{if(this.mode=="multiple"){this.current="multiple";this.select_multiple.value(l)}else{this.current="single";this.select_single.value(l[0])}}this.refresh();var g=this._select();if(!g.validate()){g.value(g.first())}}catch(k){console.debug("tools-select-content::value() - Skipped.")}}var j=this._select().value();if(!(j instanceof Array)){j=[j]}var f={batch:this.mode=="single"&&this.current!="single",values:[]};for(var h in j){f.values.push({id:j[h],src:this.list[this.current].type})}return f},validate:function(){return this._select().validate()},refresh:function(){for(var g in this.list){var f=this.list[g].field.$el;if(this.current==g){f.show()}else{f.hide()}}if(this.mode=="single"&&this.current!="single"){this.$batch.show()}else{this.$batch.hide()}},_select:function(){return this.list[this.current].field}});return{View:d}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-tree.js b/static/scripts/packed/mvc/tools/tools-tree.js index ba3f5091ac1..299d40365de 100644 --- a/static/scripts/packed/mvc/tools/tools-tree.js +++ b/static/scripts/packed/mvc/tools/tools-tree.js @@ -1 +1 @@ -define([],function(){return Backbone.Model.extend({initialize:function(a){this.app=a},finalize:function(f){var a=this;this.map_dict={};if(!this.app.section){return{}}f=f||{};var e={};var d={};this._iterate(this.app.section.$el,d);function c(i,h,g){e[i]=g;a.map_dict[i]=h}function b(n,q){for(var l in q){var j=q[l];if(j.input){var s=j.input;var m=n;if(n!=""){m+="|"}m+=s.name;switch(s.type){case"repeat":var h="section-";var v=[];var p=null;for(var u in j){var o=u.indexOf(h);if(o!=-1){o+=h.length;v.push(parseInt(u.substr(o)));if(!p){p=u.substr(0,o)}}}v.sort(function(w,i){return w-i});var l=0;for(var k in v){b(m+"_"+l++,j[p+v[k]])}break;case"conditional":var t=a.app.field_list[s.id].value();if(f[s.test_param.type]){t=f[s.test_param.type](t)}c(m+"|"+s.test_param.name,s.id,t);var g=a.matchCase(s,t);if(g!=-1){b(m,q[s.id+"-section-"+g])}break;default:var r=a.app.field_list[s.id];var t=r.value();if(f[s.type]){t=f[s.type](t)}if(!r.skip){if(s.optional&&r.validate&&!r.validate()){t=null}c(m,s.id,t)}}}}}b("",d);return e},match:function(a){return this.map_dict&&this.map_dict[a]},matchCase:function(a,c){if(a.test_param.type=="boolean"){if(c=="true"){c=a.test_param.truevalue||"true"}else{c=a.test_param.falsevalue||"false"}}for(var b in a.cases){if(a.cases[b].value==c){return b}}return -1},matchModel:function(c,e){var a={};var b=this;function d(f,o){for(var l in o){var h=o[l];var m=h.name;if(f!=""){m=f+"|"+m}switch(h.type){case"repeat":for(var k in h.cache){d(m+"_"+k,h.cache[k])}break;case"conditional":var p=h.test_param&&h.test_param.value;var g=b.matchCase(h,p);if(g!=-1){d(m,h.cases[g].inputs)}break;default:var n=b.map_dict[m];if(n){e(n,h)}}}}d("",c.inputs);return a},matchResponse:function(c){var a={};var b=this;function d(k,h){if(typeof h==="string"){var f=b.map_dict[k];if(f){a[f]=h}}else{for(var g in h){var e=g;if(k!==""){var j="|";if(h instanceof Array){j="_"}e=k+j+e}d(e,h[g])}}}d("",c);return a},_iterate:function(c,d){var a=this;var b=$(c).children();b.each(function(){var g=this;var f=$(g).attr("id");if($(g).hasClass("section-row")){d[f]={};var e=a.app.input_list[f];if(e){d[f]={input:e}}a._iterate(g,d[f])}else{a._iterate(g,d)}})}})}); \ No newline at end of file +define(["utils/utils"],function(a){return Backbone.Model.extend({initialize:function(b){this.app=b},finalize:function(g){var b=this;this.map_dict={};if(!this.app.section){return{}}g=g||{};var f={};var e={};this._iterate(this.app.section.$el,e);function d(j,i,h){f[j]=h;b.map_dict[j]=i}function c(o,r){for(var m in r){var k=r[m];if(k.input){var t=k.input;var n=o;if(o!=""){n+="|"}n+=t.name;switch(t.type){case"repeat":var j="section-";var w=[];var q=null;for(var v in k){var p=v.indexOf(j);if(p!=-1){p+=j.length;w.push(parseInt(v.substr(p)));if(!q){q=v.substr(0,p)}}}w.sort(function(x,i){return x-i});var m=0;for(var l in w){c(n+"_"+m++,k[q+w[l]])}break;case"conditional":var u=b.app.field_list[t.id].value();if(g[t.test_param.type]){u=g[t.test_param.type](u)}d(n+"|"+t.test_param.name,t.id,u);var h=b.matchCase(t,u);if(h!=-1){c(n,r[t.id+"-section-"+h])}break;default:var s=b.app.field_list[t.id];var u=s.value();if(g[t.type]){u=g[t.type](u)}if(!s.skip){if(s.validate&&!s.validate(u)){u=null}d(n,t.id,u)}}}}}c("",e);return f},match:function(b){return this.map_dict&&this.map_dict[b]},matchCase:function(b,d){if(b.test_param.type=="boolean"){if(d=="true"){d=b.test_param.truevalue||"true"}else{d=b.test_param.falsevalue||"false"}}for(var c in b.cases){if(b.cases[c].value==d){return c}}return -1},matchModel:function(d,f){var b={};var c=this;function e(g,p){for(var m in p){var k=p[m];var n=k.name;if(g!=""){n=g+"|"+n}switch(k.type){case"repeat":for(var l in k.cache){e(n+"_"+l,k.cache[l])}break;case"conditional":var q=k.test_param&&k.test_param.value;var h=c.matchCase(k,q);if(h!=-1){e(n,k.cases[h].inputs)}break;default:var o=c.map_dict[n];if(o){f(o,k)}}}}e("",d.inputs);return b},matchResponse:function(d){var b={};var c=this;function e(l,j){if(typeof j==="string"){var g=c.map_dict[l];if(g){b[g]=j}}else{for(var h in j){var f=h;if(l!==""){var k="|";if(j instanceof Array){k="_"}f=l+k+f}e(f,j[h])}}}e("",d);return b},_iterate:function(d,e){var b=this;var c=$(d).children();c.each(function(){var h=this;var g=$(h).attr("id");if($(h).hasClass("section-row")){e[g]={};var f=b.app.input_list[g];if(f){e[g]={input:f}}b._iterate(h,e[g])}else{b._iterate(h,e)}})}})}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/ui/ui-misc.js b/static/scripts/packed/mvc/ui/ui-misc.js index e8022656191..c0ad97f0a5e 100644 --- a/static/scripts/packed/mvc/ui/ui-misc.js +++ b/static/scripts/packed/mvc/ui/ui-misc.js @@ -1 +1 @@ -define(["utils/utils","mvc/ui/ui-select-default","mvc/ui/ui-slider","mvc/ui/ui-options","mvc/ui/ui-drilldown","mvc/ui/ui-button-menu","mvc/ui/ui-modal"],function(k,b,e,o,l,q,m){var d=Backbone.View.extend({optionsDefault:{url:"",cls:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options))},_template:function(r){return''}});var j=Backbone.View.extend({optionsDefault:{title:"",cls:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options))},title:function(r){this.$el.html(r)},_template:function(r){return'"},value:function(){return options.title}});var c=Backbone.View.extend({optionsDefault:{floating:"right",icon:"",tooltip:"",placement:"bottom",title:"",cls:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options));$(this.el).tooltip({title:r.tooltip,placement:"bottom"})},_template:function(r){return'
 '+r.title+"
"}});var g=Backbone.View.extend({optionsDefault:{id:null,title:"",floating:"right",cls:"ui-button btn btn-default",icon:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options));$(this.el).on("click",function(){$(".tooltip").hide();if(r.onclick){r.onclick()}});$(this.el).tooltip({title:r.tooltip,placement:"bottom"})},_template:function(r){var s='";return s}});var h=Backbone.View.extend({optionsDefault:{id:null,title:"",floating:"right",cls:"ui-button-icon",icon:"",tooltip:"",onclick:null},initialize:function(s){this.options=k.merge(s,this.optionsDefault);this.setElement(this._template(this.options));this.$button=this.$el.find(".button");var r=this;$(this.el).on("click",function(){$(".tooltip").hide();if(s.onclick&&!r.disabled){s.onclick()}});$(this.el).tooltip({title:s.tooltip,placement:"bottom"})},disable:function(){this.$button.addClass("disabled");this.disabled=true},enable:function(){this.$button.removeClass("disabled");this.disabled=false},_template:function(r){var s="";if(r.title){s="width: auto;"}var t='
';if(r.title){t+='
 '+r.title+"
"}else{t+=''}t+="
";return t}});var f=Backbone.View.extend({optionsDefault:{title:"",cls:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options));$(this.el).on("click",r.onclick)},_template:function(r){return'"}});var n=Backbone.View.extend({optionsDefault:{message:null,status:"info",persistent:false},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement("
");if(this.options.message){this.update(this.options)}},update:function(s){this.options=k.merge(s,this.optionsDefault);if(s.message!=""){this.$el.html(this._template(this.options));this.$el.find(".alert").append(s.message);this.$el.fadeIn();if(this.timeout){window.clearTimeout(this.timeout)}if(!s.persistent){var r=this;this.timeout=window.setTimeout(function(){if(r.$el.is(":visible")){r.$el.fadeOut()}else{r.$el.hide()}},3000)}}else{this.$el.fadeOut()}},_template:function(r){return'
'}});var a=Backbone.View.extend({optionsDefault:{onclick:null,searchword:""},initialize:function(s){this.options=k.merge(s,this.optionsDefault);this.setElement(this._template(this.options));var r=this;if(this.options.onclick){this.$el.on("submit",function(u){var t=r.$el.find("#search");r.options.onclick(t.val())})}},_template:function(r){return''}});var i=Backbone.View.extend({optionsDefault:{type:"text",placeholder:"",disabled:false,visible:true,cls:"",area:false},initialize:function(s){this.options=k.merge(s,this.optionsDefault);this.setElement(this._template(this.options));if(this.options.value!==undefined){this.value(this.options.value)}if(this.options.disabled){this.$el.prop("disabled",true)}if(!this.options.visible){this.$el.hide()}var r=this;this.$el.on("input",function(){if(r.options.onchange){r.options.onchange(r.$el.val())}})},value:function(r){if(r!==undefined){this.$el.val(r)}return this.$el.val()},_template:function(r){if(r.area){return''}else{return''}}});var p=Backbone.View.extend({initialize:function(r){this.options=r;this.setElement(this._template(this.options));if(this.options.value!==undefined){this.value(this.options.value)}},value:function(r){if(r!==undefined){this.$el.val(r)}return this.$el.val()},_template:function(r){return''}});return{Anchor:f,Button:g,ButtonIcon:h,ButtonMenu:q,Icon:c,Image:d,Input:i,Label:j,Message:n,Modal:m,RadioButton:o.RadioButton,Checkbox:o.Checkbox,Radio:o.Radio,Searchbox:a,Select:b,Hidden:p,Slider:e,Drilldown:l}}); \ No newline at end of file +define(["utils/utils","mvc/ui/ui-select-default","mvc/ui/ui-slider","mvc/ui/ui-options","mvc/ui/ui-drilldown","mvc/ui/ui-button-menu","mvc/ui/ui-modal"],function(k,b,e,o,l,q,m){var d=Backbone.View.extend({optionsDefault:{url:"",cls:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options))},_template:function(r){return''}});var j=Backbone.View.extend({optionsDefault:{title:"",cls:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options))},title:function(r){this.$el.html(r)},_template:function(r){return'"},value:function(){return options.title}});var c=Backbone.View.extend({optionsDefault:{floating:"right",icon:"",tooltip:"",placement:"bottom",title:"",cls:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options));$(this.el).tooltip({title:r.tooltip,placement:"bottom"})},_template:function(r){return'
 '+r.title+"
"}});var g=Backbone.View.extend({optionsDefault:{id:null,title:"",floating:"right",cls:"ui-button btn btn-default",icon:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options));$(this.el).on("click",function(){$(".tooltip").hide();if(r.onclick){r.onclick()}});$(this.el).tooltip({title:r.tooltip,placement:"bottom"})},_template:function(r){var s='";return s}});var h=Backbone.View.extend({optionsDefault:{id:null,title:"",floating:"right",cls:"ui-button-icon",icon:"",tooltip:"",onclick:null},initialize:function(s){this.options=k.merge(s,this.optionsDefault);this.setElement(this._template(this.options));this.$button=this.$el.find(".button");var r=this;$(this.el).on("click",function(){$(".tooltip").hide();if(s.onclick&&!r.disabled){s.onclick()}});$(this.el).tooltip({title:s.tooltip,placement:"bottom"})},disable:function(){this.$button.addClass("disabled");this.disabled=true},enable:function(){this.$button.removeClass("disabled");this.disabled=false},_template:function(r){var s="";if(r.title){s="width: auto;"}var t='
';if(r.title){t+='
 '+r.title+"
"}else{t+=''}t+="
";return t}});var f=Backbone.View.extend({optionsDefault:{title:"",cls:""},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement(this._template(this.options));$(this.el).on("click",r.onclick)},_template:function(r){return'"}});var n=Backbone.View.extend({optionsDefault:{message:null,status:"info",persistent:false},initialize:function(r){this.options=k.merge(r,this.optionsDefault);this.setElement("
");if(this.options.message){this.update(this.options)}},update:function(s){this.options=k.merge(s,this.optionsDefault);if(s.message!=""){this.$el.html(this._template(this.options));this.$el.find(".alert").append(s.message);this.$el.fadeIn();if(this.timeout){window.clearTimeout(this.timeout)}if(!s.persistent){var r=this;this.timeout=window.setTimeout(function(){if(r.$el.is(":visible")){r.$el.fadeOut()}else{r.$el.hide()}},3000)}}else{this.$el.fadeOut()}},_template:function(r){return'
'}});var a=Backbone.View.extend({optionsDefault:{onclick:null,searchword:""},initialize:function(s){this.options=k.merge(s,this.optionsDefault);this.setElement(this._template(this.options));var r=this;if(this.options.onclick){this.$el.on("submit",function(u){var t=r.$el.find("#search");r.options.onclick(t.val())})}},_template:function(r){return''}});var i=Backbone.View.extend({optionsDefault:{type:"text",placeholder:"",disabled:false,visible:true,cls:"",area:false},initialize:function(s){this.options=k.merge(s,this.optionsDefault);this.setElement(this._template(this.options));if(this.options.value!==undefined){this.value(this.options.value)}if(this.options.disabled){this.$el.prop("disabled",true)}if(!this.options.visible){this.$el.hide()}var r=this;this.$el.on("input",function(){if(r.options.onchange){r.options.onchange(r.$el.val())}})},value:function(r){if(r!==undefined){this.$el.val(r)}return this.$el.val()},_template:function(r){if(r.area){return''}else{return''}}});var p=Backbone.View.extend({initialize:function(r){this.options=r;this.setElement(this._template(this.options));if(this.options.value!==undefined){this.value(this.options.value)}},value:function(r){if(r!==undefined){this.$("hidden").val(r)}return this.$("hidden").val()},_template:function(s){var r='
';if(s.info){r+=""}r+='
';return r}});return{Anchor:f,Button:g,ButtonIcon:h,ButtonMenu:q,Icon:c,Image:d,Input:i,Label:j,Message:n,Modal:m,RadioButton:o.RadioButton,Checkbox:o.Checkbox,Radio:o.Radio,Searchbox:a,Select:b,Hidden:p,Slider:e,Drilldown:l}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/ui/ui-options.js b/static/scripts/packed/mvc/ui/ui-options.js index 53a6e1537ea..61cdb01934b 100644 --- a/static/scripts/packed/mvc/ui/ui-options.js +++ b/static/scripts/packed/mvc/ui/ui-options.js @@ -1 +1 @@ -define(["utils/utils"],function(b){var a=Backbone.View.extend({initialize:function(g){this.optionsDefault={visible:true,data:[],id:b.uuid(),error_text:"No data available.",wait_text:"Please wait..."};this.options=b.merge(g,this.optionsDefault);this.setElement("
");this.$message=$("
");this.$options=$(this._template(g));this.$el.append(this.$message);this.$el.append(this.$options);if(!this.options.visible){this.$el.hide()}this.update(this.options.data);if(this.options.value!==undefined){this.value(this.options.value)}var f=this;this.on("change",function(){f._change()})},update:function(g){var j=this._getValue();this.$options.empty();if(this._templateOptions){this.$options.append(this._templateOptions(g))}else{for(var h in g){var i=$(this._templateOption(g[h]));i.addClass("ui-option");this.$options.append(i)}}var f=this;this.$el.find("input").on("change",function(){f.value(f._getValue());f._change()});this._refresh();this.value(j)},value:function(g){if(g!==undefined){if(!(g instanceof Array)){g=[g]}this.$el.find("input").prop("checked",false);for(var f in g){this.$el.find('input[value="'+g[f]+'"]').first().prop("checked",true)}}return this._getValue()},exists:function(g){if(g!==undefined){if(!(g instanceof Array)){g=[g]}for(var f in g){if(this.$el.find('input[value="'+g[f]+'"]').length>0){return true}}}return false},first:function(){var f=this.$el.find("input");if(f.length>0){return f.val()}else{return undefined}},validate:function(){var g=this.value();if(!(g instanceof Array)){g=[g]}for(var f in g){if([null,"null",undefined].indexOf(g[f])>-1){return false}}return true},wait:function(){if(this._size()==0){this._messageShow(this.options.wait_text,"info");this.$options.hide()}},unwait:function(){this._messageHide();this._refresh()},_change:function(){if(this.options.onchange){this.options.onchange(this._getValue())}},_refresh:function(){if(this._size()==0){this._messageShow(this.options.error_text,"danger");this.$options.hide()}else{this._messageHide();this.$options.css("display","inline-block")}},_getValue:function(){var g=this.$el.find(":checked");if(g.length==0){return"null"}if(this.options.multiple){var f=[];g.each(function(){f.push($(this).val())});return f}else{return g.val()}},_size:function(){return this.$el.find(".ui-option").length},_messageShow:function(g,f){this.$message.show();this.$message.removeClass();this.$message.addClass("ui-message alert alert-"+f);this.$message.html(g)},_messageHide:function(){this.$message.hide()},_template:function(){return'
'}});var d={};d.View=a.extend({initialize:function(f){a.prototype.initialize.call(this,f)},_templateOption:function(f){return'
'+f.label+"
"}});var c={};c.View=a.extend({initialize:function(f){f.multiple=true;a.prototype.initialize.call(this,f)},_templateOption:function(f){return'
'+f.label+"
"}});var e={};e.View=a.extend({initialize:function(f){a.prototype.initialize.call(this,f)},value:function(f){if(f!==undefined){this.$el.find("input").prop("checked",false);this.$el.find("label").removeClass("active");this.$el.find('[value="'+f+'"]').prop("checked",true).closest("label").addClass("active")}return this._getValue()},_templateOption:function(g){var f='";return f},_template:function(){return'
'}});return{Base:a,Radio:d,RadioButton:e,Checkbox:c}}); \ No newline at end of file +define(["utils/utils"],function(b){var a=Backbone.View.extend({initialize:function(g){this.optionsDefault={visible:true,data:[],id:b.uuid(),error_text:"No data available.",wait_text:"Please wait..."};this.options=b.merge(g,this.optionsDefault);this.setElement("
");this.$message=$("
");this.$options=$(this._template(g));this.$el.append(this.$message);this.$el.append(this.$options);if(!this.options.visible){this.$el.hide()}this.update(this.options.data);if(this.options.value!==undefined){this.value(this.options.value)}var f=this;this.on("change",function(){f._change()})},update:function(g){var j=this._getValue();this.$options.empty();if(this._templateOptions){this.$options.append(this._templateOptions(g))}else{for(var h in g){var i=$(this._templateOption(g[h]));i.addClass("ui-option");this.$options.append(i)}}var f=this;this.$el.find("input").on("change",function(){f.value(f._getValue());f._change()});this._refresh();this.value(j)},value:function(g){if(g!==undefined){if(!(g instanceof Array)){g=[g]}this.$el.find("input").prop("checked",false);for(var f in g){this.$el.find('input[value="'+g[f]+'"]').first().prop("checked",true)}}return this._getValue()},exists:function(g){if(g!==undefined){if(!(g instanceof Array)){g=[g]}for(var f in g){if(this.$el.find('input[value="'+g[f]+'"]').length>0){return true}}}return false},first:function(){var f=this.$el.find("input");if(f.length>0){return f.val()}else{return undefined}},validate:function(){return b.validate(this.value())},wait:function(){if(this._size()==0){this._messageShow(this.options.wait_text,"info");this.$options.hide()}},unwait:function(){this._messageHide();this._refresh()},_change:function(){if(this.options.onchange){this.options.onchange(this._getValue())}},_refresh:function(){if(this._size()==0){this._messageShow(this.options.error_text,"danger");this.$options.hide()}else{this._messageHide();this.$options.css("display","inline-block")}},_getValue:function(){var g=this.$el.find(":checked");if(g.length==0){return"null"}if(this.options.multiple){var f=[];g.each(function(){f.push($(this).val())});return f}else{return g.val()}},_size:function(){return this.$el.find(".ui-option").length},_messageShow:function(g,f){this.$message.show();this.$message.removeClass();this.$message.addClass("ui-message alert alert-"+f);this.$message.html(g)},_messageHide:function(){this.$message.hide()},_template:function(){return'
'}});var d={};d.View=a.extend({initialize:function(f){a.prototype.initialize.call(this,f)},_templateOption:function(f){return'
'+f.label+"
"}});var c={};c.View=a.extend({initialize:function(f){f.multiple=true;a.prototype.initialize.call(this,f)},_templateOption:function(f){return'
'+f.label+"
"}});var e={};e.View=a.extend({initialize:function(f){a.prototype.initialize.call(this,f)},value:function(f){if(f!==undefined){this.$el.find("input").prop("checked",false);this.$el.find("label").removeClass("active");this.$el.find('[value="'+f+'"]').prop("checked",true).closest("label").addClass("active")}return this._getValue()},_templateOption:function(g){var f='";return f},_template:function(){return'
'}});return{Base:a,Radio:d,RadioButton:e,Checkbox:c}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/ui/ui-select-default.js b/static/scripts/packed/mvc/ui/ui-select-default.js index 4255908df03..1a69cef9950 100644 --- a/static/scripts/packed/mvc/ui/ui-select-default.js +++ b/static/scripts/packed/mvc/ui/ui-select-default.js @@ -1 +1 @@ -define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{id:"",cls:"",error_text:"No data available",visible:true,wait:false,multiple:false,searchable:false},initialize:function(d){this.options=a.merge(d,this.optionsDefault);this.setElement(this._template(this.options));this.$select=this.$el.find(".select");this.$icon=this.$el.find(".icon");this.$button=this.$el.find(".button");if(this.options.multiple){this.$select.prop("multiple",true);this.$select.addClass("ui-select-multiple");this.$icon.remove()}else{this.$el.addClass("ui-select")}this.update(this.options.data);if(this.options.value!==undefined){this.value(this.options.value)}if(!this.options.visible){this.hide()}if(this.options.wait){this.wait()}else{this.show()}var c=this;this.$select.on("change",function(){c._change()});this.on("change",function(){c._change()})},value:function(c){if(c!==undefined){this.$select.val(c);if(this.$select.select2){this.$select.select2("val",c)}}return this.$select.val()},first:function(){var c=this.$select.find("option");if(c.length>0){return c.val()}else{return undefined}},validate:function(){var d=this.value();if(!(d instanceof Array)){d=[d]}for(var c in d){if([null,"null",undefined].indexOf(d[c])>-1){return false}}return true},text:function(){return this.$select.find("option:selected").text()},show:function(){this.unwait();this.$select.show();this.$el.show()},hide:function(){this.$el.hide()},wait:function(){this.$icon.removeClass();this.$icon.addClass("fa fa-spinner fa-spin")},unwait:function(){this.$icon.removeClass();this.$icon.addClass("fa fa-caret-down")},disabled:function(){return this.$select.is(":disabled")},enable:function(){this.$select.prop("disabled",false)},disable:function(){this.$select.prop("disabled",true)},add:function(c){this.$select.append(this._templateOption(c));this._refresh()},del:function(c){this.$select.find("option[value="+c+"]").remove();this.$select.trigger("change");this._refresh()},update:function(c){var e=this.$select.val();this.$select.find("option").remove();for(var d in c){this.$select.append(this._templateOption(c[d]))}this._refresh();this.$select.val(e);if(!this.$select.val()){this.$select.val(this.first())}if(this.options.searchable){this.$button.hide();this.$select.select2("destroy");this.$select.select2()}},setOnChange:function(c){this.options.onchange=c},exists:function(c){return this.$select.find('option[value="'+c+'"]').length>0},_change:function(){if(this.options.onchange){this.options.onchange(this.$select.val())}},_refresh:function(){this.$select.find("option[value=null]").remove();var c=this.$select.find("option").length;if(c==0){this.disable();this.$select.append(this._templateOption({value:"null",label:this.options.error_text}))}else{this.enable()}},_templateOption:function(c){return'"},_template:function(c){return'
'}});return{View:b}}); \ No newline at end of file +define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{id:"",cls:"",error_text:"No data available",visible:true,wait:false,multiple:false,searchable:false},initialize:function(d){this.options=a.merge(d,this.optionsDefault);this.setElement(this._template(this.options));this.$select=this.$el.find(".select");this.$icon=this.$el.find(".icon");this.$button=this.$el.find(".button");if(this.options.multiple){this.$select.prop("multiple",true);this.$select.addClass("ui-select-multiple");this.$icon.remove()}else{this.$el.addClass("ui-select")}this.update(this.options.data);if(this.options.value!==undefined){this.value(this.options.value)}if(!this.options.visible){this.hide()}if(this.options.wait){this.wait()}else{this.show()}var c=this;this.$select.on("change",function(){c._change()});this.on("change",function(){c._change()})},value:function(c){if(c!==undefined){this.$select.val(c);if(this.$select.select2){this.$select.select2("val",c)}}return this.$select.val()},first:function(){var c=this.$select.find("option");if(c.length>0){return c.val()}else{return undefined}},validate:function(){return a.validate(this.value())},text:function(){return this.$select.find("option:selected").text()},show:function(){this.unwait();this.$select.show();this.$el.show()},hide:function(){this.$el.hide()},wait:function(){this.$icon.removeClass();this.$icon.addClass("fa fa-spinner fa-spin")},unwait:function(){this.$icon.removeClass();this.$icon.addClass("fa fa-caret-down")},disabled:function(){return this.$select.is(":disabled")},enable:function(){this.$select.prop("disabled",false)},disable:function(){this.$select.prop("disabled",true)},add:function(c){this.$select.append(this._templateOption(c));this._refresh()},del:function(c){this.$select.find("option[value="+c+"]").remove();this.$select.trigger("change");this._refresh()},update:function(c){var e=this.$select.val();this.$select.find("option").remove();for(var d in c){this.$select.append(this._templateOption(c[d]))}this._refresh();this.$select.val(e);if(!this.$select.val()){this.$select.val(this.first())}if(this.options.searchable){this.$button.hide();this.$select.select2("destroy");this.$select.select2()}},setOnChange:function(c){this.options.onchange=c},exists:function(c){return this.$select.find('option[value="'+c+'"]').length>0},_change:function(){if(this.options.onchange){this.options.onchange(this.$select.val())}},_refresh:function(){this.$select.find("option[value=null]").remove();var c=this.$select.find("option").length;if(c==0){this.disable();this.$select.append(this._templateOption({value:"null",label:this.options.error_text}))}else{this.enable()}},_templateOption:function(c){return'"},_template:function(c){return'
'}});return{View:b}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/upload/upload-view.js b/static/scripts/packed/mvc/upload/upload-view.js index 20670410b1b..0fe65750254 100644 --- a/static/scripts/packed/mvc/upload/upload-view.js +++ b/static/scripts/packed/mvc/upload/upload-view.js @@ -1 +1 @@ -define(["utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui/ui-popover","mvc/ui/ui-modal","utils/uploadbox"],function(f,e,c,b,g,d,a){return Backbone.View.extend({options:{nginx_upload_path:""},modal:null,ui_button:null,uploadbox:null,current_history:null,upload_size:0,list_extensions:[],list_genomes:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(i){var h=this;if(i){this.options=_.defaults(i,this.options)}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(j){if(j){j.preventDefault();h.show()}},onunload:function(){if(h.counter.running>0){return"Several uploads are still processing."}}});$("#left .unified-panel-header-inner").append((new e.View(this.ui_button)).$el);var h=this;f.get({url:galaxy_config.root+"api/datatypes?extension_only=False",success:function(j){for(key in j){h.list_extensions.push({id:j[key].extension,text:j[key].extension,description:j[key].description,description_url:j[key].description_url})}h.list_extensions.sort(function(l,k){return l.id>k.id?1:l.idk.id?1:l.id0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(h=="local"){this.uploadbox.configure({paramname:"files_0|file_data"})}else{this.uploadbox.configure({paramname:null})}tool_input={};if(h=="new"){tool_input["files_0|url_paste"]=q}if(h=="ftp"){tool_input["files_0|ftp_files"]=n}tool_input.dbkey=r;tool_input.file_type=p;tool_input["files_0|type"]="upload_dataset";tool_input.space_to_tabs=l;tool_input.to_posix_lines=i;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(i,j,h){var k=this.collection.get(i);k.set("percentage",h);this.ui_button.set("percentage",this._upload_percentage(h,j.size))},_eventSuccess:function(i,j,l){var k=this.collection.get(i);k.set("percentage",100);k.set("status","success");var h=k.get("file_size");this.ui_button.set("percentage",this._upload_percentage(100,h));this.upload_completed+=h*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshContents()},_eventError:function(h,i,k){var j=this.collection.get(h);j.set("percentage",100);j.set("status","error");j.set("info",k);this.ui_button.set("percentage",this._upload_percentage(100,i.size));this.ui_button.set("status","danger");this.upload_completed+=i.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(h){if(h.get("status")=="queued"){h.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var h=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(i){if(i.get("status")=="init"){i.set("status","queued");h.upload_size+=i.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.ui_button.set("percentage",0)}},_updateUser:function(){this.current_user=Galaxy.currUser.get("id");this.current_history=null;if(this.current_user){this.current_history=Galaxy.currHistoryPanel.model.get("id")}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.
Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Paste/Fetch data")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Paste/Fetch data")}if(this.current_user&&this.options.ftp_upload_dir&&this.options.ftp_upload_site){this.modal.showButton("Choose FTP file")}else{this.modal.hideButton("Choose FTP file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_upload_percentage:function(h,i){return(this.upload_completed+(h*i))/this.upload_size},_template:function(i,h){return'
'}})}); \ No newline at end of file +define(["utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui/ui-popover","mvc/ui/ui-modal","utils/uploadbox"],function(f,e,c,b,g,d,a){return Backbone.View.extend({options:{nginx_upload_path:""},modal:null,ui_button:null,uploadbox:null,current_history:null,upload_size:0,list_extensions:[],list_genomes:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(i){var h=this;if(i){this.options=_.defaults(i,this.options)}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(j){if(j){j.preventDefault();h.show()}},onunload:function(){if(h.counter.running>0){return"Several uploads are still processing."}}});$(".with-upload-button").append((new e.View(this.ui_button)).$el);var h=this;f.get({url:galaxy_config.root+"api/datatypes?extension_only=False",success:function(j){for(key in j){h.list_extensions.push({id:j[key].extension,text:j[key].extension,description:j[key].description,description_url:j[key].description_url})}h.list_extensions.sort(function(l,k){return l.id>k.id?1:l.idk.id?1:l.id0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(h=="local"){this.uploadbox.configure({paramname:"files_0|file_data"})}else{this.uploadbox.configure({paramname:null})}tool_input={};if(h=="new"){tool_input["files_0|url_paste"]=q}if(h=="ftp"){tool_input["files_0|ftp_files"]=n}tool_input.dbkey=r;tool_input.file_type=p;tool_input["files_0|type"]="upload_dataset";tool_input.space_to_tabs=l;tool_input.to_posix_lines=i;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(i,j,h){var k=this.collection.get(i);k.set("percentage",h);this.ui_button.set("percentage",this._upload_percentage(h,j.size))},_eventSuccess:function(i,j,l){var k=this.collection.get(i);k.set("percentage",100);k.set("status","success");var h=k.get("file_size");this.ui_button.set("percentage",this._upload_percentage(100,h));this.upload_completed+=h*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshContents()},_eventError:function(h,i,k){var j=this.collection.get(h);j.set("percentage",100);j.set("status","error");j.set("info",k);this.ui_button.set("percentage",this._upload_percentage(100,i.size));this.ui_button.set("status","danger");this.upload_completed+=i.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(h){if(h.get("status")=="queued"){h.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var h=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(i){if(i.get("status")=="init"){i.set("status","queued");h.upload_size+=i.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.ui_button.set("percentage",0)}},_updateUser:function(){this.current_user=Galaxy.currUser.get("id");this.current_history=null;if(this.current_user){this.current_history=Galaxy.currHistoryPanel.model.get("id")}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.
Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Paste/Fetch data")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Paste/Fetch data")}if(this.current_user&&this.options.ftp_upload_dir&&this.options.ftp_upload_site){this.modal.showButton("Choose FTP file")}else{this.modal.hideButton("Choose FTP file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_upload_percentage:function(h,i){return(this.upload_completed+(h*i))/this.upload_size},_template:function(i,h){return'
'}})}); \ No newline at end of file diff --git a/static/scripts/packed/utils/utils.js b/static/scripts/packed/utils/utils.js index 0a05fe61c62..f0a3c4cd314 100644 --- a/static/scripts/packed/utils/utils.js +++ b/static/scripts/packed/utils/utils.js @@ -1 +1 @@ -define(["libs/underscore"],function(k){function d(l){return $("
").text(l).html()}function e(l){top.__utils__get__=top.__utils__get__||{};if(l.cache&&top.__utils__get__[l.url]){l.success&&l.success(top.__utils__get__[l.url]);console.debug("utils.js::get() - Fetching from cache ["+l.url+"].")}else{h({url:l.url,data:l.data,success:function(m){top.__utils__get__[l.url]=m;l.success&&l.success(m)},error:function(m){l.error&&l.error(m)}})}}function h(m){var l={contentType:"application/json",type:m.type||"GET",data:m.data||{},url:m.url};if(l.type=="GET"||l.type=="DELETE"){if(l.url.indexOf("?")==-1){l.url+="?"}else{l.url+="&"}l.url=l.url+$.param(l.data);l.data=null}else{l.dataType="json";l.url=l.url;l.data=JSON.stringify(l.data)}$.ajax(l).done(function(n){if(typeof n==="string"){try{n=n.replace("Infinity,",'"Infinity",');n=jQuery.parseJSON(n)}catch(o){console.debug(o)}}m.success&&m.success(n)}).fail(function(o){var n=null;try{n=jQuery.parseJSON(o.responseText)}catch(p){n=o.responseText}m.error&&m.error(n,o)})}function i(o,l){var m=$('
');m.appendTo(":eq(0)");var n=m.css(l);m.remove();return n}function g(l){if(!$('link[href^="'+l+'"]').length){$('').appendTo("head")}}function j(l,m){if(l){return k.defaults(l,m)}else{return m}}function b(m,o){var n="";if(m>=100000000000){m=m/100000000000;n="TB"}else{if(m>=100000000){m=m/100000000;n="GB"}else{if(m>=100000){m=m/100000;n="MB"}else{if(m>=100){m=m/100;n="KB"}else{if(m>0){m=m*10;n="b"}else{return"-"}}}}}var l=(Math.round(m)/10);if(o){return l+" "+n}else{return""+l+" "+n}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(l){var m=$("

");m.append(l);return m}function f(){var n=new Date();var l=(n.getHours()<10?"0":"")+n.getHours();var m=(n.getMinutes()<10?"0":"")+n.getMinutes();var o=n.getDate()+"/"+(n.getMonth()+1)+"/"+n.getFullYear()+", "+l+":"+m;return o}return{cssLoadFile:g,cssGetAttribute:i,get:e,merge:j,bytesToString:b,uuid:a,time:f,wrap:c,request:h,sanitize:d}}); \ No newline at end of file +define(["libs/underscore"],function(m){function d(n){return $("
").text(n).html()}function l(o){if(!(o instanceof Array)){o=[o]}for(var n in o){if(["None",null,"null",undefined,"undefined"].indexOf(o[n])>-1){return false}}return true}function h(n){var n=n.toString();if(n){n=n.replace(/,/g,", ");var o=n.lastIndexOf(", ");if(o!=-1){n=n.substr(0,o)+" or "+n.substr(o+1)}return n}return""}function e(n){top.__utils__get__=top.__utils__get__||{};if(n.cache&&top.__utils__get__[n.url]){n.success&&n.success(top.__utils__get__[n.url]);console.debug("utils.js::get() - Fetching from cache ["+n.url+"].")}else{i({url:n.url,data:n.data,success:function(o){top.__utils__get__[n.url]=o;n.success&&n.success(o)},error:function(o){n.error&&n.error(o)}})}}function i(o){var n={contentType:"application/json",type:o.type||"GET",data:o.data||{},url:o.url};if(n.type=="GET"||n.type=="DELETE"){if(n.url.indexOf("?")==-1){n.url+="?"}else{n.url+="&"}n.url=n.url+$.param(n.data);n.data=null}else{n.dataType="json";n.url=n.url;n.data=JSON.stringify(n.data)}$.ajax(n).done(function(p){if(typeof p==="string"){try{p=p.replace("Infinity,",'"Infinity",');p=jQuery.parseJSON(p)}catch(q){console.debug(q)}}o.success&&o.success(p)}).fail(function(q){var p=null;try{p=jQuery.parseJSON(q.responseText)}catch(r){p=q.responseText}o.error&&o.error(p,q)})}function j(q,n){var o=$('
');o.appendTo(":eq(0)");var p=o.css(n);o.remove();return p}function g(n){if(!$('link[href^="'+n+'"]').length){$('').appendTo("head")}}function k(n,o){if(n){return m.defaults(n,o)}else{return o}}function b(o,q){var p="";if(o>=100000000000){o=o/100000000000;p="TB"}else{if(o>=100000000){o=o/100000000;p="GB"}else{if(o>=100000){o=o/100000;p="MB"}else{if(o>=100){o=o/100;p="KB"}else{if(o>0){o=o*10;p="b"}else{return"-"}}}}}var n=(Math.round(o)/10);if(q){return n+" "+p}else{return""+n+" "+p}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(n){var o=$("

");o.append(n);return o}function f(){var p=new Date();var n=(p.getHours()<10?"0":"")+p.getHours();var o=(p.getMinutes()<10?"0":"")+p.getMinutes();var q=p.getDate()+"/"+(p.getMonth()+1)+"/"+p.getFullYear()+", "+n+":"+o;return q}return{cssLoadFile:g,cssGetAttribute:j,get:e,merge:k,bytesToString:b,uuid:a,time:f,wrap:c,request:i,sanitize:d,textify:h,validate:l}}); \ No newline at end of file diff --git a/static/scripts/utils/utils.js b/static/scripts/utils/utils.js index 86432291d25..be978959a7d 100644 --- a/static/scripts/utils/utils.js +++ b/static/scripts/utils/utils.js @@ -14,6 +14,40 @@ function sanitize(content) { return $('
').text(content).html(); }; +/** + * Validate atomic values or list of values + * usually used for selectable options + * @param{String} value - Value or list to be validated + */ +function validate (value) { + if (!(value instanceof Array)) { + value = [value]; + } + for (var i in value) { + if (['None', null, 'null', undefined, 'undefined'].indexOf(value[i]) > -1) { + return false; + } + } + return true; +}; + +/** + * Convert list to pretty string + * @param{String} lst - List of strings to be converted in human readable list sentence + */ +function textify(lst) { + var lst = lst.toString(); + if (lst) { + lst = lst.replace(/,/g, ', '); + var pos = lst.lastIndexOf(', '); + if (pos != -1) { + lst = lst.substr(0, pos) + ' or ' + lst.substr(pos+1); + } + return lst; + } + return ''; +}; + /** * Request handler for GET * @param{String} url - Url request is made to @@ -212,7 +246,9 @@ return { time: time, wrap: wrap, request: request, - sanitize: sanitize + sanitize: sanitize, + textify: textify, + validate: validate }; }); diff --git a/static/style/blue/base.css b/static/style/blue/base.css index 5b94e50ff7c..cd41ac92d68 100644 --- a/static/style/blue/base.css +++ b/static/style/blue/base.css @@ -1742,7 +1742,7 @@ div.unified-panel-body{position:absolute;top:30px;bottom:0;width:100%} #masthead li.dropdown>a:hover .caret{border-top-color:gold;border-bottom-color:gold} #masthead .navbar-brand{position:absolute;left:0;top:0;font-family:verdana;font-weight:bold;font-size:20px;line-height:1;color:white;padding:5px 20px 12px;margin-left:-15px;z-index:2000}#masthead .navbar-brand img{display:inline;width:26px;vertical-align:top} #masthead .navbar-brand a{color:white;text-decoration:none} -#masthead .iconbar{position:absolute;top:6px;right:110px;cursor:pointer;color:#999;overflow:hidden}#masthead .iconbar .symbol{float:left;margin:0px 10px;height:26px;font-size:11px} +#masthead .iconbar{position:absolute;top:7px;right:110px;cursor:pointer;color:#999;overflow:hidden}#masthead .iconbar .symbol{float:left;margin:0px 10px;height:26px;font-size:11px} #masthead .iconbar .symbol .number{font-weight:bold;font-size:10px;font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;position:relative;left:23px;top:-12px} #masthead .iconbar .toggle{color:gold} .quota-meter-container{position:absolute;top:0;right:0;height:32px} diff --git a/static/style/src/less/base.less b/static/style/src/less/base.less index a1f9b4b3095..076aca221fd 100644 --- a/static/style/src/less/base.less +++ b/static/style/src/less/base.less @@ -425,7 +425,7 @@ div.unified-panel-body { .iconbar { position : absolute; - top : 6px; + top : 7px; right : 110px; cursor : pointer; color : @gray-light; diff --git a/templates/webapps/galaxy/root/index.mako b/templates/webapps/galaxy/root/index.mako index 4e36be60ca3..d4514e7c125 100644 --- a/templates/webapps/galaxy/root/index.mako +++ b/templates/webapps/galaxy/root/index.mako @@ -58,7 +58,7 @@ <%def name="left_panel()">
-
+
${n_('Tools')}
diff --git a/templates/webapps/galaxy/tool_form.mako b/templates/webapps/galaxy/tool_form.mako index 20ead434dc5..50834049a28 100644 --- a/templates/webapps/galaxy/tool_form.mako +++ b/templates/webapps/galaxy/tool_form.mako @@ -8,12 +8,10 @@ ${h.css('base', 'jquery-ui/smoothness/jquery-ui')} <% ## TEMPORARY: create tool dictionary in mako while both tool forms are in use. ## This avoids making two separate requests since the classic form requires the mako anyway. - from galaxy.webapps.galaxy.api.tools import ToolsController - controller = ToolsController(trans.app) params = dict(trans.request.params) if 'id' in params: params['dataset_id'] = params['id'] - self.form_config = controller._build_dict(trans, tool, params) + self.form_config = tool.to_json(trans, **params) self.form_config.update({ 'id' : tool.id, 'job_id' : trans.security.encode_id( job.id ) if job else None, diff --git a/templates/webapps/galaxy/workflow/editor_tool_form.mako b/templates/webapps/galaxy/workflow/editor_tool_form.mako index 9b47b8e5295..0b94fe270f3 100644 --- a/templates/webapps/galaxy/workflow/editor_tool_form.mako +++ b/templates/webapps/galaxy/workflow/editor_tool_form.mako @@ -1,127 +1,161 @@ -<% -from galaxy.tools.parameters import DataToolParameter, RuntimeValue -from galaxy.tools.parameters import DataCollectionToolParameter -from galaxy.util.expressions import ExpressionContext -%> +## TEMPORARY SWITCH FOR THE NEW TOOL FORM +%if False: + ${h.js("libs/bibtex", "libs/jquery/jquery-ui")} + ${h.css('jquery-ui/smoothness/jquery-ui')} + <% + ## TEMPORARY: create tool dictionary in mako while both tool forms are in use. + ## This avoids making two separate requests since the classic form requires the mako anyway. + from galaxy.tools.parameters import params_to_incoming + incoming = {} + params_to_incoming( incoming, tool.inputs, module.state.inputs, trans.app ) + self.form_config = tool.to_json(trans, **incoming) + self.form_config.update({ + 'id' : tool.id, + 'job_id' : trans.security.encode_id( job.id ) if job else None, + 'history_id' : trans.security.encode_id( trans.history.id ), + 'workflow' : True, + 'container' : '#right-content' + }) + %> + +
+
+ +
+
+%else: + <% + from galaxy.tools.parameters import DataToolParameter, RuntimeValue + from galaxy.tools.parameters import DataCollectionToolParameter + from galaxy.util.expressions import ExpressionContext + %> -<%def name="do_inputs( inputs, values, errors, prefix, ctx=None )"> - <% ctx = ExpressionContext( values, ctx ) %> - %for input_index, input in enumerate( inputs.itervalues() ): - %if input.type == "repeat": -
- - <% repeat_values = values[input.name] %> - %for i in range( len( repeat_values ) ): - <% - if input.name in errors: - rep_errors = errors[input.name][i] - else: - rep_errors = dict() - index = repeat_values[i]['__index__'] - %> -
-
- ${do_inputs( input.inputs, repeat_values[ i ], rep_errors, prefix + input.name + "_" + str(index) + "|", ctx )} -
-
- %endfor -
-
- %elif input.type == "conditional": - %if input.is_job_resource_conditional: - <% continue %> - %endif - <% group_values = values[input.name] %> - <% current_case = group_values['__current_case__'] %> - <% group_prefix = prefix + input.name + "|" %> - <% group_errors = errors.get( input.name, {} ) %> - ${row_for_param( input.test_param, group_values[ input.test_param.name ], group_errors, group_prefix, ctx, allow_runtime=False )} - ${do_inputs( input.cases[ current_case ].inputs, group_values, group_errors, group_prefix, ctx )} - %else: - %if input.name in values: - ${row_for_param( input, values[ input.name ], errors, prefix, ctx )} - %else: - <% errors[ input.name ] = 'Value not stored, displaying default' %> - ${row_for_param( input, input.get_initial_value( trans, values ), errors, prefix, ctx )} - %endif - %endif - %endfor - - -<%def name="row_for_param( param, value, error_dict, prefix, ctx, allow_runtime=True )"> - %if error_dict.has_key( param.name ): - <% cls = "form-row form-row-error" %> - %else: - <% cls = "form-row" %> - %endif -
- ## Data parameters are very special since their value / runtime state - ## comes from connectors - %if type( param ) is DataToolParameter: - -
- Data input '${param.name}' (${" or ".join( param.extensions )}) -
- %elif type( param ) is DataCollectionToolParameter: - -
- Data collection input '${param.name}' -
+ <%def name="do_inputs( inputs, values, errors, prefix, ctx=None )"> + <% ctx = ExpressionContext( values, ctx ) %> + %for input_index, input in enumerate( inputs.itervalues() ): + %if input.type == "repeat": +
+ + <% repeat_values = values[input.name] %> + %for i in range( len( repeat_values ) ): + <% + if input.name in errors: + rep_errors = errors[input.name][i] + else: + rep_errors = dict() + index = repeat_values[i]['__index__'] + %> +
+
+ ${do_inputs( input.inputs, repeat_values[ i ], rep_errors, prefix + input.name + "_" + str(index) + "|", ctx )} +
+
+ %endfor +
+
+ %elif input.type == "conditional": + %if input.is_job_resource_conditional: + <% continue %> + %endif + <% group_values = values[input.name] %> + <% current_case = group_values['__current_case__'] %> + <% group_prefix = prefix + input.name + "|" %> + <% group_errors = errors.get( input.name, {} ) %> + ${row_for_param( input.test_param, group_values[ input.test_param.name ], group_errors, group_prefix, ctx, allow_runtime=False )} + ${do_inputs( input.cases[ current_case ].inputs, group_values, group_errors, group_prefix, ctx )} %else: - %if isinstance( value, RuntimeValue ): + %if input.name in values: + ${row_for_param( input, values[ input.name ], errors, prefix, ctx )} + %else: + <% errors[ input.name ] = 'Value not stored, displaying default' %> + ${row_for_param( input, input.get_initial_value( trans, values ), errors, prefix, ctx )} + %endif + %endif + %endfor + + + <%def name="row_for_param( param, value, error_dict, prefix, ctx, allow_runtime=True )"> + %if error_dict.has_key( param.name ): + <% cls = "form-row form-row-error" %> + %else: + <% cls = "form-row" %> + %endif +
+ ## Data parameters are very special since their value / runtime state + ## comes from connectors + %if type( param ) is DataToolParameter:
- To be set at runtime + Data input '${param.name}' (${" or ".join( param.extensions )}) +
+ %elif type( param ) is DataCollectionToolParameter: + +
+ Data collection input '${param.name}'
%else: -
- - -
- -
-
Tool: ${tool.name}
- %if tool.version: -
Version: ${tool.version}
- %endif -
- - %for i, inputs in enumerate( tool.inputs_by_page ): - %if tool.has_multiple_pages: -
Page ${i+1}
+ +
+ To be set at runtime +
+ %else: + +
+ ${param.get_html_field( trans, value, ctx ).get_html( prefix )} +
%endif - ${do_inputs( inputs, values, errors, "" )} - %endfor + %if error_dict.has_key( param.name ): +
+
 ${error_dict[param.name]}
+
+ %endif + %endif +
-
- + -
+
+ +
+
Tool: ${tool.name}
+ %if tool.version: +
Version: ${tool.version}
+ %endif +
+ + %for i, inputs in enumerate( tool.inputs_by_page ): + %if tool.has_multiple_pages: +
Page ${i+1}
+ %endif + ${do_inputs( inputs, values, errors, "" )} + %endfor +
+
+ + +
+%endif \ No newline at end of file diff --git a/tools/data_source/upload.py b/tools/data_source/upload.py index 42fd67f5fc1..0c968f1e5ba 100644 --- a/tools/data_source/upload.py +++ b/tools/data_source/upload.py @@ -113,6 +113,8 @@ def add_file( dataset, registry, json_file, output_path ): ext = sniff.guess_ext( dataset.path, is_multi_byte=True ) # Is dataset content supported sniffable binary? else: + # FIXME: This ignores the declared sniff order in datatype_conf.xml + # resulting in improper behavior type_info = Binary.is_sniffable_binary( dataset.path ) if type_info: data_type = type_info[0]