diff --git a/.ci/py3_sources.txt b/.ci/py3_sources.txt index 325fa99e160..035b842699f 100644 --- a/.ci/py3_sources.txt +++ b/.ci/py3_sources.txt @@ -3,29 +3,40 @@ cron/ lib/galaxy/actions/ lib/galaxy/auth/ lib/galaxy/config.py +lib/galaxy/dataset_collections/ +lib/galaxy/datatypes/binary.py +lib/galaxy/datatypes/converters/ +lib/galaxy/datatypes/dataproviders/ +lib/galaxy/datatypes/sequence.py +lib/galaxy/datatypes/sniff.py +lib/galaxy/datatypes/tabular.py lib/galaxy/dependencies/ lib/galaxy/eggs/ lib/galaxy/exceptions/ lib/galaxy/external_services/ lib/galaxy/forms/ lib/galaxy/jobs/ +lib/galaxy/managers/ +lib/galaxy/model/__init__.py +lib/galaxy/model/migrate/ lib/galaxy/objectstore/ lib/galaxy/openid/ lib/galaxy/quota/ lib/galaxy/sample_tracking/ +lib/galaxy/security/ lib/galaxy/tags/ lib/galaxy/tools/cwl/ -lib/galaxy/tools/parser/ +lib/galaxy/tools/deps/ lib/galaxy/tools/lint.py lib/galaxy/tools/lint_util.py +lib/galaxy/tools/linters/ lib/galaxy/tools/loader.py lib/galaxy/tools/loader_directory.py -lib/galaxy/tools/linters/ -lib/galaxy/tools/deps/ -lib/galaxy/tools/toolbox/ lib/galaxy/tools/parser/ +lib/galaxy/tools/toolbox/ lib/galaxy/tours/ lib/galaxy/util/ +lib/galaxy/visualization/ lib/galaxy/work/ lib/galaxy_ext/ lib/galaxy_utils/ @@ -40,22 +51,28 @@ lib/tool_shed/repository_types/ lib/tool_shed/tools/ lib/tool_shed/util/ lib/tool_shed/utility_containers/ -scripts/api/common.py -scripts/api/display.py -scripts/api/workflow_execute_parameters.py +scripts/api/ scripts/auth/ +scripts/bootstrap_history.py +scripts/build_toolbox.py +scripts/check_eggs.py +scripts/check_galaxy.py +scripts/check_python.py scripts/cleanup_datasets/admin_cleanup_datasets.py scripts/cleanup_datasets/cleanup_datasets.py -test/api/test_workflows_from_yaml.py -test/base/ -test/casperjs/ -test/functional/ -test/integration/ -test/manual/ -test/unit/tools/test_actions.py -test/unit/workflows/test_run_parameters.py +scripts/cleanup_datasets/pgcleanup.py +scripts/cleanup_datasets/populate_uuid.py +scripts/cleanup_datasets/remove_renamed_datasets_from_disk.py +scripts/cleanup_datasets/rename_purged_datasets.py +scripts/cleanup_datasets/update_dataset_size.py +scripts/cleanup_datasets/update_metadata.py +scripts/data_libraries/build_whoosh_index.py +scripts/db_shell.py +scripts/drmaa_external_runner.py +test/ tool_list.py tools/data_source/ tools/evolution/ tools/sr_mapping/ +tools/stats/aggregate_scores_in_intervals.py tools/visualization/ diff --git a/Makefile b/Makefile index e1c0b3d3e25..cea7c06f367 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ VENV?=.venv IN_VENV=if [ -f $(VENV)/bin/activate ]; then . $(VENV)/bin/activate; fi; PROJECT_URL?=https://github.com/galaxyproject/galaxy GRUNT_DOCKER_NAME:=galaxy/client-builder:16.01 +GRUNT_EXEC?=node_modules/grunt-cli/bin/grunt all: help @echo "This makefile is primarily used for building Galaxy's JS client. A sensible all target is not yet implemented." @@ -59,10 +60,13 @@ npm-deps: ## Install NodeJS dependencies. cd client && npm install grunt: npm-deps ## Calls out to Grunt to build client - cd client && node_modules/grunt-cli/bin/grunt + cd client && $(GRUNT_EXEC) style: npm-deps ## Calls the style task of Grunt - cd client && node_modules/grunt-cli/bin/grunt style + cd client && $(GRUNT_EXEC) style + +client-install-libs: npm-deps ## Fetch updated client dependencies using bower. + cd client && $(GRUNT_EXEC) install-libs client: grunt style ## Rebuild all client-side artifacts diff --git a/client/galaxy/scripts/mvc/collection/collection-li.js b/client/galaxy/scripts/mvc/collection/collection-li.js index 9fea9eb7dfa..1c679f486d5 100644 --- a/client/galaxy/scripts/mvc/collection/collection-li.js +++ b/client/galaxy/scripts/mvc/collection/collection-li.js @@ -125,6 +125,8 @@ DCListItemView.prototype.templates = (function(){ _l( 'a pair of datasets' ), '<% } else if( collection.collection_type === "list:paired" ){ %>', _l( 'a list of <%- countText %>dataset pairs' ), + '<% } else if( collection.collection_type === "list:list" ){ %>', + _l( 'a list of <%- countText %>dataset lists' ), '<% } %>', '' ], 'collection' ); diff --git a/client/galaxy/scripts/mvc/collection/collection-model.js b/client/galaxy/scripts/mvc/collection/collection-model.js index 4fa44c3ff45..27b89690cef 100644 --- a/client/galaxy/scripts/mvc/collection/collection-model.js +++ b/client/galaxy/scripts/mvc/collection/collection-model.js @@ -504,10 +504,58 @@ var ListPairedDatasetCollection = DatasetCollection.extend( }); +//============================================================================== +/** @class Backbone model for a list dataset collection within a list:list dataset collection. */ +var NestedListDCDCE = ListDatasetCollection.extend( BASE_MVC.mixin( DatasetCollectionElementMixin, +/** @lends NestedListDCDCE.prototype */{ + + /** This is both a collection and a collection element - call the constructor */ + constructor : function( attributes, options ){ + this.debug( '\t NestedListDCDCE.constructor:', attributes, options ); + DatasetCollectionElementMixin.constructor.call( this, attributes, options ); + }, + + /** String representation. */ + toString : function(){ + var objStr = ( this.object )?( '' + this.object ):( this.get( 'element_identifier' ) ); + return ([ 'NestedListDCDCE(', objStr, ')' ].join( '' )); + } +})); + + +//============================================================================== +/** @class Backbone collection containing list dataset collections. */ +var NestedListDCDCECollection = NestedDCDCECollection.extend({ + + /** We know this collection is composed of only nested pair collections */ + model: NestedListDCDCE, + + /** String representation. */ + toString : function(){ + return ([ 'NestedListDCDCECollection(', this.length, ')' ].join( '' )); + } +}); + + +//============================================================================== +/** @class Backbone Model for a DatasetCollection (list) that contains other lists. */ +var ListOfListsDatasetCollection = DatasetCollection.extend({ + + /** list:paired is the only collection that itself contains collections */ + collectionClass : NestedListDCDCECollection, + + /** String representation. */ + toString : function(){ + return ([ 'ListOfListsDatasetCollection(', this.get( 'name' ), ')' ].join( '' )); + } +}); + + //============================================================================== return { - ListDatasetCollection : ListDatasetCollection, - PairDatasetCollection : PairDatasetCollection, - ListPairedDatasetCollection : ListPairedDatasetCollection + ListDatasetCollection : ListDatasetCollection, + PairDatasetCollection : PairDatasetCollection, + ListPairedDatasetCollection : ListPairedDatasetCollection, + ListOfListsDatasetCollection: ListOfListsDatasetCollection }; }); diff --git a/client/galaxy/scripts/mvc/collection/collection-view-edit.js b/client/galaxy/scripts/mvc/collection/collection-view-edit.js index d07e25037bd..d2e7fdc5dc2 100644 --- a/client/galaxy/scripts/mvc/collection/collection-view-edit.js +++ b/client/galaxy/scripts/mvc/collection/collection-view-edit.js @@ -128,7 +128,7 @@ var NestedPairCollectionViewEdit = PairCollectionViewEdit.extend( // ============================================================================= -/** @class non-editable, read-only View/Controller for a dataset collection. */ +/** @class editable, View/Controller for a list of pairs dataset collection. */ var ListOfPairsCollectionViewEdit = CollectionViewEdit.extend( /** @lends ListOfPairsCollectionView.prototype */{ @@ -146,11 +146,31 @@ var ListOfPairsCollectionViewEdit = CollectionViewEdit.extend( }); +// ============================================================================= +/** @class View/Controller for a list of lists dataset collection. */ +var ListOfListsCollectionViewEdit = CollectionViewEdit.extend( +/** @lends ListOfListsCollectionView.prototype */{ + + //TODO: not strictly needed - due to switch in CollectionView._getContentClass + /** sub view class used for nested collections */ + NestedDCDCEViewClass : DC_EDIT.NestedDCDCEListItemEdit.extend({ + foldoutPanelClass : NestedPairCollectionViewEdit + }), + + // ........................................................................ misc + /** string rep */ + toString : function(){ + return 'ListOfListsCollectionViewEdit(' + (( this.model )?( this.model.get( 'name' )):( '' )) + ')'; + } +}); + + //============================================================================== return { CollectionViewEdit : CollectionViewEdit, ListCollectionViewEdit : ListCollectionViewEdit, PairCollectionViewEdit : PairCollectionViewEdit, - ListOfPairsCollectionViewEdit : ListOfPairsCollectionViewEdit + ListOfPairsCollectionViewEdit : ListOfPairsCollectionViewEdit, + ListOfListsCollectionViewEdit : ListOfListsCollectionViewEdit }; }); diff --git a/client/galaxy/scripts/mvc/collection/collection-view.js b/client/galaxy/scripts/mvc/collection/collection-view.js index 8178ea17105..99993810e6f 100644 --- a/client/galaxy/scripts/mvc/collection/collection-view.js +++ b/client/galaxy/scripts/mvc/collection/collection-view.js @@ -153,6 +153,8 @@ CollectionView.prototype.templates = (function(){ _l( 'a pair of datasets' ), '<% } else if( collection.collection_type === "list:paired" ){ %>', _l( 'a list of paired datasets' ), + '<% } else if( collection.collection_type === "list:list" ){ %>', + _l( 'a list of dataset lists' ), '<% } %>', '', '', @@ -215,11 +217,28 @@ var ListOfPairsCollectionView = CollectionView.extend( }); +// ============================================================================= +/** @class non-editable, read-only View/Controller for a list of lists dataset collection. */ +var ListOfListsCollectionView = CollectionView.extend({ + + /** sub view class used for nested collections */ + NestedDCDCEViewClass : DC_LI.NestedDCDCEListItemView.extend({ + foldoutPanelClass : PairCollectionView + }), + + /** string rep */ + toString : function(){ + return 'ListOfListsCollectionView(' + (( this.model )?( this.model.get( 'name' )):( '' )) + ')'; + } +}); + + //============================================================================== return { CollectionView : CollectionView, ListCollectionView : ListCollectionView, PairCollectionView : PairCollectionView, - ListOfPairsCollectionView : ListOfPairsCollectionView + ListOfPairsCollectionView : ListOfPairsCollectionView, + ListOfListsCollectionView : ListOfListsCollectionView }; }); diff --git a/client/galaxy/scripts/mvc/dataset/data.js b/client/galaxy/scripts/mvc/dataset/data.js index 3a392b1702a..309dd20f92a 100644 --- a/client/galaxy/scripts/mvc/dataset/data.js +++ b/client/galaxy/scripts/mvc/dataset/data.js @@ -64,7 +64,7 @@ var TabularDataset = Dataset.extend({ defaults: _.extend({}, Dataset.prototype.defaults, { chunk_url: null, first_data_chunk: null, - chunk_index: -1, + offset: 0, at_eof: false }), @@ -72,7 +72,9 @@ var TabularDataset = Dataset.extend({ Dataset.prototype.initialize.call(this); // If first data chunk is available, next chunk is 1. - this.attributes.chunk_index = (this.attributes.first_data_chunk ? 1 : 0); + if (this.attributes.first_data_chunk){ + this.attributes.offset = this.attributes.first_data_chunk.offset; + } this.attributes.chunk_url = Galaxy.root + 'dataset/display?dataset_id=' + this.id; this.attributes.url_viz = Galaxy.root + 'visualization'; }, @@ -90,12 +92,13 @@ var TabularDataset = Dataset.extend({ var self = this, next_chunk = $.Deferred(); $.getJSON(this.attributes.chunk_url, { - chunk: self.attributes.chunk_index++ + offset: self.attributes.offset }).success(function(chunk) { var rval; if (chunk.ck_data !== '') { // Found chunk. rval = chunk; + self.attributes.offset = chunk.offset; } else { // At EOF. diff --git a/client/galaxy/scripts/mvc/form/form-data.js b/client/galaxy/scripts/mvc/form/form-data.js index 98338d73512..9a0981e47c3 100644 --- a/client/galaxy/scripts/mvc/form/form-data.js +++ b/client/galaxy/scripts/mvc/form/form-data.js @@ -7,8 +7,7 @@ define([ 'utils/utils' ], function( Utils ) { this.app = app; }, - /** Creates a checksum. - */ + /** Creates a checksum. */ checksum: function() { var sum = ''; var self = this; @@ -22,8 +21,15 @@ define([ 'utils/utils' ], function( Utils ) { return sum; }, - /** Convert dom into a dictionary of flat id/value pairs used e.g. on job submission. - */ + /** Set parameter values from model */ + set: function( model ) { + for ( var attr in model.attributes ) { + var index = this.match( attr ); + index && this.app.field_list[ index ].value( model.get( attr ) ); + } + }, + + /** Convert dom into a dictionary of flat id/value pairs used e.g. on job submission. */ create: function() { var self = this; diff --git a/client/galaxy/scripts/mvc/form/form-parameters.js b/client/galaxy/scripts/mvc/form/form-parameters.js index ecbb0059e17..b1eeedcbf00 100644 --- a/client/galaxy/scripts/mvc/form/form-parameters.js +++ b/client/galaxy/scripts/mvc/form/form-parameters.js @@ -31,17 +31,11 @@ define(['utils/utils', 'ftpfile' : '_fieldFtp' }, - initialize: function( app, options ) { - this.app = app; - }, - - /** Returns an input field for a given field type - */ + /** Returns an input field for a given field type */ create: function( input_def ) { var fieldClass = this.types[ input_def.type ]; var field = typeof( this[ fieldClass ] ) === 'function' ? this[ fieldClass ].call( this, input_def ) : null; if ( !field ) { - this.app.incompatible = true; field = input_def.options ? this._fieldSelect( input_def ) : this._fieldText( input_def ); Galaxy.emit.debug('form-parameters::_addRow()', 'Auto matched field type (' + input_def.type + ').'); } @@ -50,10 +44,8 @@ define(['utils/utils', return field; }, - /** Data input field - */ + /** Data input field */ _fieldData: function( input_def ) { - var self = this; return new SelectContent.View({ id : 'field-' + input_def.id, extensions : input_def.extensions, @@ -62,14 +54,11 @@ define(['utils/utils', type : input_def.type, flavor : input_def.flavor, data : input_def.options, - onchange : function() { - self.app.trigger( 'change' ); - } + onchange : input_def.onchange }); }, - /** Select/Checkbox/Radio options field - */ + /** Select/Checkbox/Radio options field */ _fieldSelect: function ( input_def ) { // show text field e.g. in workflow editor if( input_def.is_workflow ) { @@ -81,11 +70,14 @@ define(['utils/utils', input_def.error_text = 'Missing columns in referenced dataset.' } - // configure options fields - var options = []; - _.each( input_def.options, function( option ) { - options.push( { label: option[ 0 ], value: option[ 1 ] } ); - }); + // identify available options + var data = input_def.data; + if( !data ) { + data = []; + _.each( input_def.options, function( option ) { + data.push( { label: option[ 0 ], value: option[ 1 ] } ); + }); + } // identify display type var SelectClass = Ui.Select; @@ -96,24 +88,23 @@ define(['utils/utils', case 'radio': SelectClass = Ui.Radio; break; + case 'radiobutton': + SelectClass = Ui.RadioButton; + break; } // create select field - var self = this; return new SelectClass.View({ id : 'field-' + input_def.id, - data : options, + data : data, error_text : input_def.error_text || 'No options available', multiple : input_def.multiple, optional : input_def.optional, - onchange : function() { - self.app.trigger( 'change' ); - } + onchange : input_def.onchange }); }, - /** Drill down options field - */ + /** Drill down options field */ _fieldDrilldown: function ( input_def ) { // show text field e.g. in workflow editor if( input_def.is_workflow ) { @@ -121,23 +112,19 @@ define(['utils/utils', } // create drill down field - var self = this; return new Ui.Drilldown.View({ id : 'field-' + input_def.id, data : input_def.options, display : input_def.display, optional : input_def.optional, - onchange : function() { - self.app.trigger( 'change' ); - } + onchange : input_def.onchange }); }, - /** Text input field - */ + /** Text input field */ _fieldText: function( input_def ) { // field replaces e.g. a select field - if ( input_def.options ) { + if ( input_def.options && input_def.data ) { input_def.area = input_def.multiple; if ( Utils.isEmpty( input_def.value ) ) { input_def.value = null; @@ -156,34 +143,27 @@ define(['utils/utils', } } // create input element - var self = this; return new Ui.Input({ id : 'field-' + input_def.id, area : input_def.area, - onchange : function( new_value ) { - input_def.onchange ? input_def.onchange( new_value ) : self.app.trigger( 'change' ); - } + placeholder : input_def.placeholder, + onchange : input_def.onchange }); }, - /** Slider field - */ + /** Slider field */ _fieldSlider: function( input_def ) { - var self = this; return new Ui.Slider.View({ id : 'field-' + input_def.id, precise : input_def.type == 'float', is_workflow : input_def.is_workflow, min : input_def.min, max : input_def.max, - onchange : function() { - self.app.trigger( 'change' ); - } + onchange : input_def.onchange }); }, - /** Hidden field - */ + /** Hidden field */ _fieldHidden: function( input_def ) { return new Ui.Hidden({ id : 'field-' + input_def.id, @@ -191,57 +171,42 @@ define(['utils/utils', }); }, - /** Boolean field - */ + /** Boolean field */ _fieldBoolean: function( input_def ) { - var self = this; return new Ui.RadioButton.View({ id : 'field-' + input_def.id, data : [ { label : 'Yes', value : 'true' }, { label : 'No', value : 'false' }], - onchange : function() { - self.app.trigger( 'change' ); - } + onchange : input_def.onchange }); }, - /** Color picker field - */ + /** Color picker field */ _fieldColor: function( input_def ) { - var self = this; return new ColorPicker({ id : 'field-' + input_def.id, - onchange : function() { - self.app.trigger( 'change' ); - } + onchange : input_def.onchange }); }, - /** Library dataset field - */ + /** Library dataset field */ _fieldLibrary: function( input_def ) { - var self = this; return new SelectLibrary.View({ id : 'field-' + input_def.id, optional : input_def.optional, multiple : input_def.multiple, - onchange : function() { - self.app.trigger( 'change' ); - } + onchange : input_def.onchange }); }, /** FTP file field */ _fieldFtp: function( input_def ) { - var self = this; return new SelectFtp.View({ id : 'field-' + input_def.id, optional : input_def.optional, multiple : input_def.multiple, - onchange : function() { - self.app.trigger( 'change' ); - } + onchange : input_def.onchange }); } }); diff --git a/client/galaxy/scripts/mvc/form/form-repeat.js b/client/galaxy/scripts/mvc/form/form-repeat.js index 2c91c2b0a30..fcfcbbef679 100644 --- a/client/galaxy/scripts/mvc/form/form-repeat.js +++ b/client/galaxy/scripts/mvc/form/form-repeat.js @@ -1,9 +1,6 @@ -// dependencies +/** This class creates a ui component which enables the dynamic creation of portlets */ define(['utils/utils', 'mvc/ui/ui-table', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc'], function(Utils, Table, Portlet, Ui) { - -/** This class creates a ui component which enables the dynamic creation of portlets -*/ var View = Backbone.View.extend({ initialize : function(options) { var self = this; @@ -41,14 +38,12 @@ var View = Backbone.View.extend({ this.n = 0; }, - /** Number of repeat blocks - */ + /** Number of repeat blocks */ size: function() { return this.n; }, - /** Add new repeat block - */ + /** Add new repeat block */ add: function(options) { if (!options.id || this.list[options.id]) { Galaxy.emit.debug('form-repeat::add()', 'Duplicate repeat block id.'); @@ -68,7 +63,7 @@ var View = Backbone.View.extend({ var portlet = new Portlet.View({ id : options.id, title : 'placeholder', - cls : 'ui-portlet-repeat', + cls : options.cls || 'ui-portlet-repeat', operations : { button_delete : button_delete } @@ -84,8 +79,7 @@ var View = Backbone.View.extend({ this._refresh(); }, - /** Delete repeat block - */ + /** Delete repeat block */ del: function(id) { if (!this.list[id]) { Galaxy.emit.debug('form-repeat::del()', 'Invalid repeat block id.'); @@ -99,8 +93,14 @@ var View = Backbone.View.extend({ this._refresh(); }, - /** Hides add/del options - */ + /** Remove all */ + delAll: function() { + for( var id in this.list ) { + this.del( id ); + } + }, + + /** Hides add/del options */ hideOptions: function() { this.button_new.$el.hide(); _.each( this.list, function( portlet ) { @@ -111,8 +111,7 @@ var View = Backbone.View.extend({ } }, - /** Refresh view - */ + /** Refresh view */ _refresh: function() { var index = 0; for (var id in this.list) { diff --git a/client/galaxy/scripts/mvc/form/form-section.js b/client/galaxy/scripts/mvc/form/form-section.js index d7b32b3d191..f4ef044d2eb 100644 --- a/client/galaxy/scripts/mvc/form/form-section.js +++ b/client/galaxy/scripts/mvc/form/form-section.js @@ -23,7 +23,7 @@ define(['utils/utils', // create/render views this.table = new Table.View(options); - this.parameters = new Parameters(app, options); + this.parameters = new Parameters(); this.setElement(this.table.$el); this.render(); }, @@ -187,7 +187,9 @@ define(['utils/utils', /** Add a single input field element */ _addRow: function(input_def) { + var self = this; var id = input_def.id; + input_def.onchange = function() { self.app.trigger( 'change' ) }; var field = this.parameters.create(input_def); this.app.field_list[id] = field; var input_element = new InputElement(this.app, { diff --git a/client/galaxy/scripts/mvc/form/form-view.js b/client/galaxy/scripts/mvc/form/form-view.js index e207fafffaf..04402b04465 100644 --- a/client/galaxy/scripts/mvc/form/form-view.js +++ b/client/galaxy/scripts/mvc/form/form-view.js @@ -86,8 +86,7 @@ define(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', 'mvc/form/form-sec } }, - /** Highlights errors - */ + /** Highlights errors */ errors: function(options) { this.trigger('reset'); if (options && options.errors) { @@ -101,8 +100,12 @@ define(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', 'mvc/form/form-sec } }, - /** Render tool form - */ + /** Modify onchange event handler */ + setOnChange: function( callback ) { + this.options.onchange = callback; + }, + + /** Render tool form */ render: function() { // link this var self = this; @@ -153,8 +156,7 @@ define(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', 'mvc/form/form-sec return this; }, - /** Renders the UI elements required for the form - */ + /** Renders the UI elements required for the form */ _renderForm: function() { // create message view this.message = new Ui.Message(); diff --git a/client/galaxy/scripts/mvc/history/hdca-li-edit.js b/client/galaxy/scripts/mvc/history/hdca-li-edit.js index 0cc3f4623ba..0960b94ad7f 100644 --- a/client/galaxy/scripts/mvc/history/hdca-li-edit.js +++ b/client/galaxy/scripts/mvc/history/hdca-li-edit.js @@ -26,6 +26,8 @@ var HDCAListItemEdit = _super.extend( return DC_VIEW_EDIT.PairCollectionViewEdit; case 'list:paired': return DC_VIEW_EDIT.ListOfPairsCollectionViewEdit; + case 'list:list': + return DC_VIEW_EDIT.ListOfListsCollectionViewEdit; } throw new TypeError( 'Uknown collection_type: ' + this.model.get( 'collection_type' ) ); }, diff --git a/client/galaxy/scripts/mvc/history/hdca-li.js b/client/galaxy/scripts/mvc/history/hdca-li.js index 3d6289f38cd..9959ad66c56 100644 --- a/client/galaxy/scripts/mvc/history/hdca-li.js +++ b/client/galaxy/scripts/mvc/history/hdca-li.js @@ -38,6 +38,8 @@ var HDCAListItemView = _super.extend( return DC_VIEW.PairCollectionView; case 'list:paired': return DC_VIEW.ListOfPairsCollectionView; + case 'list:list': + return DC_VIEW.ListOfListsCollectionView; } throw new TypeError( 'Uknown collection_type: ' + this.model.get( 'collection_type' ) ); }, diff --git a/client/galaxy/scripts/mvc/history/hdca-model.js b/client/galaxy/scripts/mvc/history/hdca-model.js index 96576d1cb63..93ffaca7253 100644 --- a/client/galaxy/scripts/mvc/history/hdca-model.js +++ b/client/galaxy/scripts/mvc/history/hdca-model.js @@ -18,7 +18,8 @@ TODO: var hcontentMixin = HISTORY_CONTENT.HistoryContentMixin, ListDC = DC_MODEL.ListDatasetCollection, PairDC = DC_MODEL.PairDatasetCollection, - ListPairedDC = DC_MODEL.ListPairedDatasetCollection; + ListPairedDC = DC_MODEL.ListPairedDatasetCollection, + ListOfListsDC = DC_MODEL.ListOfListsDatasetCollection; //============================================================================== /** Override to post to contents route w/o id. */ @@ -91,11 +92,8 @@ var HistoryPairDatasetCollection = PairDC.extend( hcontentMixin ).extend( //============================================================================== -/** @class Backbone model for List of Pairs Dataset Collection within a History. - * @constructs - */ -var HistoryListPairedDatasetCollection = ListPairedDC.extend( hcontentMixin ).extend( -/** @lends HistoryListPairedDatasetCollection.prototype */{ +/** @class Backbone model for List of Pairs Dataset Collection within a History. */ +var HistoryListPairedDatasetCollection = ListPairedDC.extend( hcontentMixin ).extend({ defaults : _.extend( _.clone( ListPairedDC.prototype.defaults ), { history_content_type: 'dataset_collection', @@ -118,10 +116,36 @@ var HistoryListPairedDatasetCollection = ListPairedDC.extend( hcontentMixin ).ex }); +//============================================================================== +/** @class Backbone model for List of Lists Dataset Collection within a History. */ +var HistoryListOfListsDatasetCollection = ListOfListsDC.extend( hcontentMixin ).extend({ + + defaults : _.extend( _.clone( ListOfListsDC.prototype.defaults ), { + history_content_type: 'dataset_collection', + collection_type : 'list:list', + model_class : 'HistoryDatasetCollectionAssociation' + }), + + initialize : function( model, options ){ + ListOfListsDC.prototype.initialize.call( this, model, options ); + hcontentMixin.initialize.call( this, model, options ); + }, + + /** Override to post to contents route w/o id. */ + save : buildHDCASave( ListOfListsDC.prototype.save ), + + /** String representation. */ + toString : function(){ + return ([ 'HistoryListOfListsDatasetCollection(', this.get( 'name' ), ')' ].join( '' )); + } +}); + + //============================================================================== return { HistoryListDatasetCollection : HistoryListDatasetCollection, HistoryPairDatasetCollection : HistoryPairDatasetCollection, - HistoryListPairedDatasetCollection : HistoryListPairedDatasetCollection + HistoryListPairedDatasetCollection : HistoryListPairedDatasetCollection, + HistoryListOfListsDatasetCollection : HistoryListOfListsDatasetCollection }; }); diff --git a/client/galaxy/scripts/mvc/history/history-contents.js b/client/galaxy/scripts/mvc/history/history-contents.js index a08eec492e3..62943e56bf9 100644 --- a/client/galaxy/scripts/mvc/history/history-contents.js +++ b/client/galaxy/scripts/mvc/history/history-contents.js @@ -44,12 +44,16 @@ var HistoryContents = Backbone.Collection return new HDCA_MODEL.HistoryPairDatasetCollection( attrs, options ); case 'list:paired': return new HDCA_MODEL.HistoryListPairedDatasetCollection( attrs, options ); + case 'list:list': + return new HDCA_MODEL.HistoryListOfListsDatasetCollection( attrs, options ); } // This is a hack inside a hack: // Raise a plain object with validationError to fake a model.validationError // (since we don't have a model to use validate with) // (the outer hack being the mixed content/model function in this collection) - return { validationError : 'Unknown collection_type: ' + attrs.history_content_type }; + var msg = 'Unknown collection_type: ' + attrs.collection_type; + console.warn( msg, attrs ); + return { validationError : msg }; } return { validationError : 'Unknown history_content_type: ' + attrs.history_content_type }; }, @@ -281,10 +285,14 @@ var HistoryContents = Backbone.Collection type = ( contentType === 'hdca'? 'dataset_collection' : 'dataset' ); } var collection = this, - xhr = jQuery.post( this.url(), { - content : id, - source : contentType, - type : type + xhr = jQuery.ajax( this.url(), { + method: 'POST', + contentType: 'application/json', + data: JSON.stringify({ + content : id, + source : contentType, + type : type + }) }) .done( function( response ){ collection.add([ response ]); diff --git a/client/galaxy/scripts/mvc/tool/tool-form-composite.js b/client/galaxy/scripts/mvc/tool/tool-form-composite.js index fbb88f5494c..59723b36536 100644 --- a/client/galaxy/scripts/mvc/tool/tool-form-composite.js +++ b/client/galaxy/scripts/mvc/tool/tool-form-composite.js @@ -1,8 +1,9 @@ /** This is the run workflow tool form view. */ -define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view', 'mvc/form/form-data', 'mvc/tool/tool-form-base' ], - function( Utils, Deferred, Ui, Form, FormData, ToolFormBase ) { +define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view', 'mvc/form/form-data', 'mvc/tool/tool-form-base', 'mvc/ui/ui-modal' ], + function( Utils, Deferred, Ui, Form, FormData, ToolFormBase, Modal ) { var View = Backbone.View.extend({ initialize: function( options ) { + this.modal = parent.Galaxy.modal || new Modal.View(); this.model = options && options.model || new Backbone.Model( options ); this.deferred = new Deferred(); this.setElement( $( '
' ).addClass( 'ui-form-composite' ) @@ -330,7 +331,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' var self = this; var job_def = { new_history_name : this.history_form.data.create()[ 'new_history|name' ], - wf_parm : this.wp_form ? this.wp_form.data.create() : {}, + replacement_params : this.wp_form ? this.wp_form.data.create() : {}, inputs : {} }; var validated = true; @@ -371,7 +372,7 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' Galaxy.emit.debug( 'tool-form-composite::submit()', 'Validation complete.', job_def ); Utils.request({ type : 'POST', - url : Galaxy.root + 'api/workflows/' + this.workflow_id + '/invocations', + url : Galaxy.root + 'api_internal/workflows/' + this.model.id + '/run', data : job_def, success : function( response ) { Galaxy.emit.debug( 'tool-form-composite::submit', 'Submission successful.', response ); @@ -393,13 +394,12 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' } } } else { - var modal = parent.Galaxy.modal; - modal && modal.show({ + self.modal.show({ title : 'Job submission failed', body : self._templateError( response && response.err_msg || job_def ), buttons : { 'Close' : function() { - modal.hide(); + self.modal.hide(); } } }); @@ -477,4 +477,4 @@ define([ 'utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view' return { View: View }; -}); \ No newline at end of file +}); diff --git a/client/galaxy/scripts/mvc/ui/ui-misc.js b/client/galaxy/scripts/mvc/ui/ui-misc.js index 42c2fa5da88..4ecc4308e65 100644 --- a/client/galaxy/scripts/mvc/ui/ui-misc.js +++ b/client/galaxy/scripts/mvc/ui/ui-misc.js @@ -67,7 +67,7 @@ define(['utils/utils', if ( !this.model.get( 'persistent' ) ) { var self = this; this.timeout = window.setTimeout( function() { - self.$el.fadeOut(); + self.model.set( 'message', '' ); }, 3000 ); } } else { diff --git a/client/galaxy/scripts/mvc/ui/ui-select-default.js b/client/galaxy/scripts/mvc/ui/ui-select-default.js index 617dcb2b42a..f6d54a850ab 100644 --- a/client/galaxy/scripts/mvc/ui/ui-select-default.js +++ b/client/galaxy/scripts/mvc/ui/ui-select-default.js @@ -18,7 +18,7 @@ var View = Backbone.View.extend({ disabled : false, onchange : function(){} }).set( options ); - this.on( 'change', function() { self.model.get( 'onchange' )( self.value() ) } ); + this.on( 'change', function() { self.model.get( 'onchange' ) && self.model.get( 'onchange' )( self.value() ) } ); this.listenTo( this.model, 'change:data', this._changeData, this ); this.listenTo( this.model, 'change:disabled', this._changeDisabled, this ); this.listenTo( this.model, 'change:wait', this._changeWait, this ); diff --git a/client/galaxy/scripts/mvc/ui/ui-tabs.js b/client/galaxy/scripts/mvc/ui/ui-tabs.js index 3cf6e56f160..1b5f82f1bda 100644 --- a/client/galaxy/scripts/mvc/ui/ui-tabs.js +++ b/client/galaxy/scripts/mvc/ui/ui-tabs.js @@ -1,288 +1,198 @@ -// dependencies -define(['utils/utils'], function(Utils) { - -// return +/** + * Renders tabs e.g. used in the Charts editor, behaves similar to repeat and section rendering + */ +define( [ 'utils/utils' ], function( Utils ) { var View = Backbone.View.extend({ - // defaults options - optionsDefault: { - title_new : '', - operations : null, - onnew : null, - max : null, - onchange : null - }, - - // initialize - initialize : function(options) { - // configure + initialize : function( options ) { + var self = this; this.visible = false; this.$nav = null; this.$content = null; this.first_tab = null; this.current_id = null; - - // configure options - this.options = Utils.merge(options, this.optionsDefault); - - // create tabs - var $tabs = $(this._template(this.options)); - - // link elements - this.$nav = $tabs.find('.tab-navigation'); - this.$content = $tabs.find('.tab-content'); - - // create new element - this.setElement($tabs); - - // clear list - this.list = {}; - - // link this - var self = this; - - // append operations - if (this.options.operations) { - $.each(this.options.operations, function(name, item) { - item.$el.prop('id', name); - self.$nav.find('.operations').append(item.$el); - }); - } - - // add built-in add-new-tab tab - if (this.options.onnew) { - // create tab object - var $tab_new = $(this._template_tab_new(this.options)); - - // append to navbar - this.$nav.append($tab_new); - - // add tooltip - $tab_new.tooltip({title: 'Add a new tab', placement: 'bottom', container: self.$el}); - - // link click event - $tab_new.on('click', function(e) { - $tab_new.tooltip('hide'); - self.options.onnew(); - }); - } - }, - - // size - size: function() { - return _.size(this.list); - }, - - // front - current: function() { - return this.$el.find('.tab-pane.active').attr('id'); - }, - - // append - add: function(options) { - // self - var self = this; - - // get tab id - var id = options.id; + this.list = {}; + this.options = Utils.merge( options, { + title_new : '', + operations : null, + onnew : null, + max : null, + onchange : null + }); + this.setElement( $( this._template( this.options ) ) ); + this.$nav = this.$( '.tab-navigation' ); + this.$content = this.$( '.tab-content' ); + this.$operations = this.$nav.find( '.tab-operations' ); - // create tab object - var $tab_title = $(this._template_tab(options)); - var $tab_content = $(this._template_tab_content(options)); - - // add to list - this.list[id] = options.ondel ? true : false; - - // add a new tab either before the add-new-tab tab or behind the last tab - if (this.options.onnew) { - this.$nav.find('#new-tab').before($tab_title); - } else { - this.$nav.append($tab_title); + // Renders tab operations + if ( this.options.operations ) { + $.each( this.options.operations, function( name, item ) { + item.$el.prop( 'id', name ); + self.$operations.append( item.$el ); + }); } - - // add content - $tab_content.append(options.$el); - this.$content.append($tab_content); - - // activate this tab if this is the first tab - if (this.size() == 1) { - $tab_title.addClass('active'); - $tab_content.addClass('active'); + + // Allows user to add new tabs + this.options.onnew && this.$nav.append( $( this._template_tab_new( this.options ) ) + .tooltip( { title: 'Add a new tab', placement: 'bottom', container: self.$el } ) + .on( 'click', function( e ) { self.options.onnew() } ) + ); + this.$tabnew = this.$nav.find( '.tab-new' ); + + // Remove all tooltips on click + this.$el.on( 'click', function() { $( '.tooltip' ).hide() } ); + }, + + /** Returns current number of tabs */ + size: function() { + return _.size( this.list ); + }, + + /** Returns tab id for currently shown tab */ + current: function() { + return this.$el.find( '.tab-pane.active' ).attr( 'id' ); + }, + + /** Adds a new tab */ + add: function( options ) { + var self = this; + var id = options.id; + var $tab_title = $( this._template_tab( options ) ); + var $tab_content = $( '' ).attr( 'id', options.id ).addClass( 'tab-pane' ); + + // hide new tab if maximum number of tabs has been reached + this.list[ id ] = true; + if ( this.options.max && this.size() >= this.options.max ) { + this.$tabnew.hide(); + } + + // insert tab before new tab or as last tab + if ( this.options.onnew ) { + this.$tabnew.before( $tab_title ); + } else { + this.$nav.append( $tab_title ); + } + + // assing delete callback if provided + if ( options.ondel ) { + $tab_title.find( '.tab-delete' ).tooltip( { title: 'Delete this tab', placement: 'bottom', container: self.$el } ) + .on( 'click', function() { options.ondel() } ); + } else { + $tab_title.tooltip( { title: options.tooltip, placement: 'bottom', container: self.$el } ); + } + $tab_title.on( 'click', function( e ) { + e.preventDefault(); + options.onclick ? options.onclick() : self.show( id ); + }); + this.$content.append( $tab_content.append( options.$el ) ); + + // assign current/first tab + if ( this.size() == 1 ) { + $tab_title.addClass( 'active' ); + $tab_content.addClass( 'active' ); this.first_tab = id; } - - // hide add tab - if (this.options.max && this.size() >= this.options.max) { - this.$el.find('#new-tab').hide(); - } - - // add click event to remove tab - if (options.ondel) { - var $del_icon = $tab_title.find('#delete'); - $del_icon.tooltip({title: 'Delete this tab', placement: 'bottom', container: self.$el}); - $del_icon.on('click', function() { - $del_icon.tooltip('destroy'); - self.$el.find('.tooltip').remove(); - options.ondel(); - return false; - }); - } - - // add custom click event handler - $tab_title.on('click', function(e) { - // prevent default - e.preventDefault(); - - // click - if (options.onclick) { - options.onclick(); - } else { - self.show(id); - } - }); - - // initialize current id - if (!this.current_id) { + if ( !this.current_id ) { this.current_id = id; } }, - - // delete tab - del: function(id) { - // delete tab from dom - this.$el.find('#tab-' + id).remove(); - this.$el.find('#' + id).remove(); - - // check if first tab has been deleted - if (this.first_tab == id) { - this.first_tab = null; - } - - // show first tab - if (this.first_tab != null) { - this.show(this.first_tab); - } - - // delete from list - if (this.list[id]) { - delete this.list[id]; - } - - // show add tab - if (this.size() < this.options.max) { - this.$el.find('#new-tab').show(); + + /** Delete tab */ + del: function( id ) { + this.$( '#tab-' + id ).remove(); + this.$( '#' + id ).remove(); + this.first_tab = this.first_tab == id ? null : this.first_tab; + this.first_tab != null && this.show( this.first_tab ); + this.list[ id ] && delete this.list[ id ]; + if ( this.size() < this.options.max ) { + this.$el.find( '.ui-tabs-new' ).show(); } }, - - // delete tab + + /** Delete all tabs */ delRemovable: function() { - for (var id in this.list) { - this.del(id); + for ( var id in this.list ) { + this.del( id ); } }, - - // show - show: function(id){ - // show tab view - this.$el.fadeIn('fast'); + + /** Show tab view and highlight a tab by id */ + show: function( id ){ + this.$el.fadeIn( 'fast' ); this.visible = true; - - // show selected tab - if (id) { - // reassign active class - this.$el.find('#tab-' + this.current_id).removeClass('active'); - this.$el.find('#' + this.current_id).removeClass('active'); - this.$el.find('#tab-' + id).addClass('active'); - this.$el.find('#' + id).addClass('active'); - - // update current id + if ( id ) { + this.$( '#tab-' + this.current_id ).removeClass('active' ); + this.$( '#' + this.current_id ).removeClass('active' ); + this.$( '#tab-' + id ).addClass( 'active' ); + this.$( '#' + id ).addClass( 'active' ); this.current_id = id; } - - // change - if (this.options.onchange) { - this.options.onchange(id); - } + this.options.onchange && this.options.onchange( id ); }, - // hide + /** Hide tab view */ hide: function(){ - this.$el.fadeOut('fast'); + this.$el.fadeOut( 'fast' ); this.visible = false; }, - // hide operation - hideOperation: function(id) { - this.$nav.find('#' + id).hide(); + /** Hide operation by id */ + hideOperation: function( id ) { + this.$nav.find( '#' + id ).hide(); }, - // show operation - showOperation: function(id) { - this.$nav.find('#' + id).show(); + /** Show operation by id */ + showOperation: function( id ) { + this.$nav.find( '#' + id ).show(); }, - - // set operation - setOperation: function(id, callback) { - var $el = this.$nav.find('#' + id); - $el.off('click'); - $el.on('click', callback); + + /** Reassign an operation to a new callback */ + setOperation: function( id, callback ) { + this.$nav.find( '#' + id ).off('click').on( 'click', callback ); }, - - // title - title: function(id, new_title) { - var $el = this.$el.find('#tab-title-text-' + id); - if (new_title) { - $el.html(new_title); - } + + /** Set/Get title */ + title: function( id, new_title ) { + var $el = this.$( '#tab-title-text-' + id ); + new_title && $el.html( new_title ); return $el.html(); }, - - // retitle - retitle: function(new_title) { + + /** Enumerate titles */ + retitle: function( new_title ) { var index = 0; - for (var id in this.list) { - this.title(id, ++index + ': ' + new_title); + for ( var id in this.list ) { + this.title( id, ++index + ': ' + new_title ); } }, - - // fill template - _template: function(options) { - return 'Node Description
" + table); - for (var i = 0; i < fieldName.length; i++) { - if (isValidUrl(values[i])) { + for (var strIndex = 0; strIndex < fieldName.length; strIndex++) { + if (isValidUrl(values[strIndex])) { var e = document.createElement("div"); e.id = "linkDiv"; $('#nodeInfoDiv').append(e); - + var url = values[i]; - document.getElementById("linkDiv").innerHTML += "| " + fieldName[i] + " | " + "" + values[i] + " | " + "
| " + "degree" + " | " + "" + degree + " | " + "
| " + "number of child nodes" + " | " + "" + childNum + " | " + "
| " + "number of parent nodes" + " | " + "" + parentNum + " | " + "