9 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/4d2168785082/ Changeset: 4d2168785082 User: erasche2 Date: 2014-11-22 19:18:08+00:00 Summary: Enable user/email list for sharing of datasets Affected #: 4 files diff -r 75261cf45d583f8a48172b5209acb24d07fe7b21 -r 4d216878508215489b7434703336f6ad0b3e3e1e config/galaxy.ini.sample --- a/config/galaxy.ini.sample +++ b/config/galaxy.ini.sample @@ -740,6 +740,14 @@ # public. #new_user_dataset_access_role_default_private = False +# Expose user list. Setting this to true will expose the user list to authenticated users. This +# makes sharing datasets in smaller galaxy instances much easier as they can type a name/email and +# have the correct user show up. This makes less sense on large public galaxy instances where +# that data shouldn't be exposed. For semi-public galaxies, it may make sense to expose just the +# username and not email, or vice versa. +#expose_user_name = False +#expose_user_email = False + # -- Beta features # Use new tool form diff -r 75261cf45d583f8a48172b5209acb24d07fe7b21 -r 4d216878508215489b7434703336f6ad0b3e3e1e lib/galaxy/config.py --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -92,6 +92,9 @@ self.user_label_filters = listify( kwargs.get( "user_tool_label_filters", [] ), do_strip=True ) self.user_section_filters = listify( kwargs.get( "user_tool_section_filters", [] ), do_strip=True ) + self.expose_user_name = kwargs.get( "expose_user_name", False ) + self.expose_user_email = kwargs.get( "expose_user_email", False ) + # Check for tools defined in the above non-shed tool configs (i.e., tool_conf.xml) tht have # been migrated from the Galaxy code distribution to the Tool Shed. self.check_migrate_tools = string_as_bool( kwargs.get( 'check_migrate_tools', True ) ) diff -r 75261cf45d583f8a48172b5209acb24d07fe7b21 -r 4d216878508215489b7434703336f6ad0b3e3e1e lib/galaxy/webapps/galaxy/api/users.py --- a/lib/galaxy/webapps/galaxy/api/users.py +++ b/lib/galaxy/webapps/galaxy/api/users.py @@ -37,11 +37,19 @@ else: query = query.filter( trans.app.model.User.table.c.deleted == False ) # noqa # special case: user can see only their own user - if not trans.user_is_admin(): + # special case2: if the galaxy admin has specified that other user email/names are + # exposed, we don't want special case #1 + if not trans.user_is_admin() and not trans.app.config.expose_user_name and not trans.app.config.expose_user_email: item = trans.user.to_dict( value_mapper={ 'id': trans.security.encode_id } ) return [item] for user in query: item = user.to_dict( value_mapper={ 'id': trans.security.encode_id } ) + # If NOT configured to expose_email, do not expose email UNLESS the user is self, or + # the user is an admin + if not trans.app.config.expose_user_name and user is not trans.user and not trans.user_is_admin(): + del item['username'] + if not trans.app.config.expose_user_email and user is not trans.user and not trans.user_is_admin(): + del item['email'] # TODO: move into api_values rval.append( item ) return rval diff -r 75261cf45d583f8a48172b5209acb24d07fe7b21 -r 4d216878508215489b7434703336f6ad0b3e3e1e templates/webapps/galaxy/history/share.mako --- a/templates/webapps/galaxy/history/share.mako +++ b/templates/webapps/galaxy/history/share.mako @@ -37,9 +37,8 @@ <div style="clear: both"></div><div class="form-row"><label>Galaxy user emails with which to share histories</label> - <div style="float: left; width: 250px; margin-right: 10px;"> - <input type="text" name="email" value="${email}" size="40"> - </div> + <input type="hidden" id="email_select" name="email" style="float: left; width: 250px; margin-right: 10px;"> + </input><div class="toolParamHelp" style="clear: both;"> Enter a Galaxy user email address or a comma-separated list of addresses if sharing with multiple users </div> @@ -55,6 +54,47 @@ <input type="submit" name="share_button" value="Submit"></div></form> + <script type="text/javascript"> + // stolen from templates/admin/impersonate.mako + /* This should be ripped out and made generic at some point for the + * various API bindings available, and once the API can filter list + * queries (term, below) */ + $("#email_select").select2({ + placeholder: "Select a user", + ajax: { + url: "${h.url_for(controller="/api/users", action="index")}", + dataType: 'json', + quietMillis: 250, + matcher: function(term, text) { return text.toUpperCase().indexOf(term.toUpperCase())>=0; }, + data: function (term) { + return { + f_email: term + }; + }, + results: function (data) { + var results = []; + $.each(data, function(index, item){ + var text = ""; + if(typeof(item.username) === "string" && typeof(item.email) === "string"){ + text = item.username + " <" + item.email + ">"; + }else if(typeof(item.username) === "string"){ + text = item.username; + }else{ + text = item.email; + } + results.push({ + id: item.email, + name: item.username, + text: text + }); + }); + return { + results: results + }; + } + } + }); + </script> %else: ## We are sharing restricted histories %if no_change_needed or can_change: https://bitbucket.org/galaxy/galaxy-central/commits/ec3301ee4783/ Changeset: ec3301ee4783 User: erasche2 Date: 2014-11-25 19:19:28+00:00 Summary: Must expose username in controller as well Affected #: 1 file diff -r 4d216878508215489b7434703336f6ad0b3e3e1e -r ec3301ee47836c121425f21b9f54ab828774c756 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -130,7 +130,7 @@ histories, credentials, and roles. """ # attributes that will be accessed and returned when calling to_dict( view='collection' ) - dict_collection_visible_keys = ( 'id', 'email' ) + dict_collection_visible_keys = ( 'id', 'email', 'username' ) # attributes that will be accessed and returned when calling to_dict( view='element' ) dict_element_visible_keys = ( 'id', 'email', 'username', 'total_disk_usage', 'nice_total_disk_usage' ) https://bitbucket.org/galaxy/galaxy-central/commits/bf8dcef796da/ Changeset: bf8dcef796da User: erasche2 Date: 2014-12-17 20:15:23+00:00 Summary: Merged galaxy/galaxy-central into default Affected #: 452 files diff -r ec3301ee47836c121425f21b9f54ab828774c756 -r bf8dcef796dafaec6462f89624ee1adb92e5a45d .hgtags --- a/.hgtags +++ b/.hgtags @@ -20,4 +20,4 @@ ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11 548ab24667d6206780237bd807f7d857a484c461 latest_2014.08.11 2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06 -acc8d1e2bc88530aa8d8651cf5f88649f6769304 latest_2014.10.06 +7086b87d83a9092cbece0fec6f3e3ed03266be0c latest_2014.10.06 diff -r ec3301ee47836c121425f21b9f54ab828774c756 -r bf8dcef796dafaec6462f89624ee1adb92e5a45d client/galaxy/scripts/galaxy.library.js --- a/client/galaxy/scripts/galaxy.library.js +++ b/client/galaxy/scripts/galaxy.library.js @@ -36,7 +36,7 @@ initialize: function() { this.routesHit = 0; //keep count of number of routes handled by the application - Backbone.history.on('route', function() { this.routesHit++; }, this); + Backbone.history.on( 'route', function() { this.routesHit++; }, this ); }, routes: { @@ -45,6 +45,7 @@ "library/:library_id/permissions" : "library_permissions", "folders/:folder_id/permissions" : "folder_permissions", "folders/:id" : "folder_content", + "folders/:id/page/:show_page" : "folder_page", "folders/:folder_id/datasets/:dataset_id" : "dataset_detail", "folders/:folder_id/datasets/:dataset_id/permissions" : "dataset_permissions", "folders/:folder_id/datasets/:dataset_id/versions/:ldda_id" : "dataset_version", @@ -53,13 +54,13 @@ }, back: function() { - if(this.routesHit > 1) { + if( this.routesHit > 1 ) { //more than one route hit -> user did not land to current page directly window.history.back(); } else { //otherwise go to the home page. Use replaceState if available so //the navigation doesn't create an extra history entry - this.navigate('#', {trigger:true, replace:true}); + this.navigate( '#', { trigger:true, replace:true } ); } } }); @@ -71,7 +72,8 @@ with_deleted : false, sort_order : 'asc', sort_by : 'name', - library_page_size : 20 + library_page_size : 20, + folder_page_size : 15 } }); @@ -94,10 +96,10 @@ this.library_router = new LibraryRouter(); - this.library_router.on('route:libraries', function() { - Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView(); - Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView(); - }); + this.library_router.on( 'route:libraries', function() { + Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView(); + Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView(); + }); this.library_router.on('route:libraries_page', function( show_page ) { if ( Galaxy.libraries.libraryToolbarView === null ){ @@ -108,66 +110,77 @@ } }); - this.library_router.on('route:folder_content', function(id) { + this.library_router.on( 'route:folder_content', function( id ) { if (Galaxy.libraries.folderToolbarView){ - Galaxy.libraries.folderToolbarView.$el.unbind('click'); - } - Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView({id: id}); - Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView({id: id}); - }); + Galaxy.libraries.folderToolbarView.$el.unbind( 'click' ); + } + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( { id: id } ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id } ); + }); - this.library_router.on('route:download', function(folder_id, format) { - if ($('#folder_list_body').find(':checked').length === 0) { - mod_toastr.info( 'You must select at least one dataset to download' ); - Galaxy.libraries.library_router.navigate('folders/' + folder_id, {trigger: true, replace: true}); - } else { - Galaxy.libraries.folderToolbarView.download(folder_id, format); - Galaxy.libraries.library_router.navigate('folders/' + folder_id, {trigger: false, replace: true}); - } - }); + this.library_router.on( 'route:folder_page', function( id, show_page ) { + if ( Galaxy.libraries.folderToolbarView === null ){ + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( {id: id} ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id, show_page: show_page } ); + } else { + Galaxy.libraries.folderListView.render( { id: id, show_page: parseInt( show_page ) } ) + } + }); - this.library_router.on('route:dataset_detail', function(folder_id, dataset_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id}); - }); - this.library_router.on('route:dataset_version', function(folder_id, dataset_id, ldda_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, ldda_id: ldda_id, show_version: true}); - }); + this.library_router.on( 'route:download', function( folder_id, format ) { + if ( $( '#folder_list_body' ).find( ':checked' ).length === 0 ) { + mod_toastr.info( 'You must select at least one dataset to download' ); + Galaxy.libraries.library_router.navigate( 'folders/' + folder_id, { trigger: true, replace: true } ); + } else { + Galaxy.libraries.folderToolbarView.download( folder_id, format ); + Galaxy.libraries.library_router.navigate( 'folders/' + folder_id, { trigger: false, replace: true } ); + } + }); - this.library_router.on('route:dataset_permissions', function(folder_id, dataset_id){ - if (Galaxy.libraries.datasetView){ - Galaxy.libraries.datasetView.$el.unbind('click'); - } - Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, show_permissions: true}); - }); + this.library_router.on( 'route:dataset_detail', function(folder_id, dataset_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id}); + }); - this.library_router.on('route:library_permissions', function(library_id){ - if (Galaxy.libraries.libraryView){ - Galaxy.libraries.libraryView.$el.unbind('click'); - } - Galaxy.libraries.libraryView = new mod_library_library_view.LibraryView({id: library_id, show_permissions: true}); - }); + this.library_router.on( 'route:dataset_version', function(folder_id, dataset_id, ldda_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, ldda_id: ldda_id, show_version: true}); + }); - this.library_router.on('route:folder_permissions', function(folder_id){ - if (Galaxy.libraries.folderView){ - Galaxy.libraries.folderView.$el.unbind('click'); - } - Galaxy.libraries.folderView = new mod_library_folder_view.FolderView({id: folder_id, show_permissions: true}); - }); - this.library_router.on('route:import_datasets', function(folder_id, source){ - if (Galaxy.libraries.folderToolbarView && Galaxy.libraries.folderListView){ - Galaxy.libraries.folderToolbarView.showImportModal({source:source}); - } else { - Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView({id: folder_id}); - Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView({id: folder_id}); - Galaxy.libraries.folderToolbarView.showImportModal({source: source}); - } - }); + this.library_router.on( 'route:dataset_permissions', function(folder_id, dataset_id){ + if (Galaxy.libraries.datasetView){ + Galaxy.libraries.datasetView.$el.unbind('click'); + } + Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, show_permissions: true}); + }); + + this.library_router.on( 'route:library_permissions', function(library_id){ + if (Galaxy.libraries.libraryView){ + Galaxy.libraries.libraryView.$el.unbind('click'); + } + Galaxy.libraries.libraryView = new mod_library_library_view.LibraryView({id: library_id, show_permissions: true}); + }); + + this.library_router.on( 'route:folder_permissions', function(folder_id){ + if (Galaxy.libraries.folderView){ + Galaxy.libraries.folderView.$el.unbind('click'); + } + Galaxy.libraries.folderView = new mod_library_folder_view.FolderView({id: folder_id, show_permissions: true}); + }); + + this.library_router.on( 'route:import_datasets', function( folder_id, source ){ + if ( Galaxy.libraries.folderToolbarView && Galaxy.libraries.folderListView ){ + Galaxy.libraries.folderToolbarView.showImportModal( { source:source } ); + } else { + Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( { id: folder_id } ); + Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: folder_id } ); + Galaxy.libraries.folderToolbarView.showImportModal( { source: source } ); + } + }); Backbone.history.start({pushState: false}); } diff -r ec3301ee47836c121425f21b9f54ab828774c756 -r bf8dcef796dafaec6462f89624ee1adb92e5a45d client/galaxy/scripts/galaxy.masthead.js --- a/client/galaxy/scripts/galaxy.masthead.js +++ b/client/galaxy/scripts/galaxy.masthead.js @@ -251,28 +251,31 @@ }, // initialize - initialize: function (options){ + initialize: function ( options ){ // read in defaults - if (options) - this.options = _.defaults(options, this.options); - + if ( options ){ + this.options = _.defaults( options, this.options ); + } + // update url - if (this.options.content && this.options.content.indexOf('//') === -1) + if ( this.options.content !== undefined && this.options.content.indexOf( '//' ) === -1 ){ this.options.content = galaxy_config.root + this.options.content; + } // add template for tab - this.setElement($(this._template(this.options))); + this.setElement( $( this._template( this.options ) ) ); // disable menu items that are not available to anonymous user // also show title to explain why they are disabled - if (this.options.disabled){ - $(this.el).find('.root').addClass('disabled'); + if ( this.options.disabled ){ + $( this.el ).find( '.root' ).addClass( 'disabled' ); this._attachPopover(); } // visiblity - if (!this.options.visible) + if ( !this.options.visible ){ this.hide(); + } }, // show diff -r ec3301ee47836c121425f21b9f54ab828774c756 -r bf8dcef796dafaec6462f89624ee1adb92e5a45d client/galaxy/scripts/galaxy.menu.js --- a/client/galaxy/scripts/galaxy.menu.js +++ b/client/galaxy/scripts/galaxy.menu.js @@ -24,7 +24,7 @@ var tab_analysis = new mod_masthead.GalaxyMastheadTab({ id : "analysis", title : "Analyze Data", - content : "root/index", + content : "", title_attribute : 'Analysis home view' }); this.masthead.append(tab_analysis); @@ -167,7 +167,7 @@ var tab_admin = new mod_masthead.GalaxyMastheadTab({ id : "admin", title : "Admin", - content : "admin/index", + content : "admin", extra_class : "admin-only", title_attribute : 'Administer this Galaxy' }); diff -r ec3301ee47836c121425f21b9f54ab828774c756 -r bf8dcef796dafaec6462f89624ee1adb92e5a45d client/galaxy/scripts/galaxy.workflow_editor.canvas.js --- a/client/galaxy/scripts/galaxy.workflow_editor.canvas.js +++ b/client/galaxy/scripts/galaxy.workflow_editor.canvas.js @@ -784,6 +784,8 @@ this.tooltip = data.tooltip ? data.tooltip : ""; this.annotation = data.annotation; this.post_job_actions = data.post_job_actions ? data.post_job_actions : {}; + this.label = data.label; + this.uuid = data.uuid; this.workflow_outputs = data.workflow_outputs ? data.workflow_outputs : []; var node = this; @@ -991,6 +993,8 @@ position : $(node.element).position(), annotation: node.annotation, post_job_actions: node.post_job_actions, + uuid: node.uuid, + label: node.label, workflow_outputs: node.workflow_outputs }; nodes[ node.id ] = node_data; This diff is so big that we needed to truncate the remainder. https://bitbucket.org/galaxy/galaxy-central/commits/bee9255a428c/ Changeset: bee9255a428c User: erasche2 Date: 2014-12-17 20:54:12+00:00 Summary: Merged galaxy/galaxy-central into default Affected #: 10 files diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff client/galaxy/scripts/mvc/data.js --- a/client/galaxy/scripts/mvc/data.js +++ b/client/galaxy/scripts/mvc/data.js @@ -169,7 +169,8 @@ }); this.$el.append(data_table); var column_names = this.model.get_metadata('column_names'), - header_row = $('<tr/>').appendTo(data_table); + header_container = $('<thead/>').appendTo(data_table), + header_row = $('<tr/>').appendTo(header_container); if (column_names) { header_row.append('<th>' + column_names.join('</th><th>') + '</th>'); } else { diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff lib/galaxy/tools/parser/xml.py --- a/lib/galaxy/tools/parser/xml.py +++ b/lib/galaxy/tools/parser/xml.py @@ -186,6 +186,11 @@ rval = dict( outputs=__parse_output_elems(test_elem), inputs=__parse_input_elems(test_elem, i), + command=__parse_assert_list_from_elem( test_elem.find("assert_command") ), + stdout=__parse_assert_list_from_elem( test_elem.find("assert_stdout") ), + stderr=__parse_assert_list_from_elem( test_elem.find("assert_stderr") ), + expect_exit_code=test_elem.get("expect_exit_code"), + expect_failure=string_as_bool(test_elem.get("expect_failure", False)), ) _copy_to_dict_if_present(test_elem, rval, ["interactor", "num_outputs"]) return rval @@ -222,6 +227,11 @@ return name, file, attributes +def __parse_command_elem( test_elem ): + assert_elem = test_elem.find("command") + return __parse_assert_list_from_elem( assert_elem ) + + def __parse_test_attributes( output_elem, attrib ): assert_list = __parse_assert_list( output_elem ) file = attrib.pop( 'file', None ) @@ -252,6 +262,10 @@ def __parse_assert_list( output_elem ): assert_elem = output_elem.find("assert_contents") + return __parse_assert_list_from_elem( assert_elem ) + + +def __parse_assert_list_from_elem( assert_elem ): assert_list = None def convert_elem(elem): diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff lib/galaxy/tools/parser/yaml.py --- a/lib/galaxy/tools/parser/yaml.py +++ b/lib/galaxy/tools/parser/yaml.py @@ -152,27 +152,47 @@ attributes["metadata"] = {} # TODO assert_list = [] - for key, assertion in attributes.get("asserts", {}).iteritems(): - # TODO: not handling nested assertions correctly, - # not sure these are used though. - children = [] - if "children" in assertion: - children = assertion["children"] - del assertion["children"] - assert_dict = dict( - tag=key, - attributes=assertion, - children=children, - ) - assert_list.append(assert_dict) + assert_list = __to_test_assert_list( attributes.get("asserts", [] ) ) attributes["assert_list"] = assert_list - _ensure_has(attributes, defaults) test_dict["outputs"] = new_outputs + test_dict["command"] = __to_test_assert_list( test_dict.get( "command", [] ) ) + test_dict["stdout"] = __to_test_assert_list( test_dict.get( "stdout", [] ) ) + test_dict["stderr"] = __to_test_assert_list( test_dict.get( "stderr", [] ) ) + test_dict["expect_exit_code"] = test_dict.get( "expect_exit_code", None ) + test_dict["expect_failure"] = test_dict.get( "expect_exit_code", False ) return test_dict +def __to_test_assert_list(assertions): + def expand_dict_form(item): + key, value = item + new_value = value.copy() + new_value["that"] = key + return new_value + + if isinstance( assertions, dict ): + assertions = map(expand_dict_form, assertions.items() ) + + assert_list = [] + for assertion in assertions: + # TODO: not handling nested assertions correctly, + # not sure these are used though. + children = [] + if "children" in assertion: + children = assertion["children"] + del assertion["children"] + assert_dict = dict( + tag=assertion["that"], + attributes=assertion, + children=children, + ) + assert_list.append(assert_dict) + + return assert_list or None # XML variant is None if no assertions made + + class YamlPageSource(PageSource): def __init__(self, inputs_list): diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff lib/galaxy/tools/test.py --- a/lib/galaxy/tools/test.py +++ b/lib/galaxy/tools/test.py @@ -127,6 +127,11 @@ if num_outputs: num_outputs = int( num_outputs ) self.num_outputs = num_outputs + self.command_line = test_dict.get("command", None) + self.stdout = test_dict.get("stdout", None) + self.stderr = test_dict.get("stderr", None) + self.expect_exit_code = test_dict.get("expect_exit_code", None) + self.expect_failure = test_dict.get("expect_failure", False) except Exception, e: self.error = True self.exception = e diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff lib/galaxy/web/framework/middleware/remoteuser.py --- a/lib/galaxy/web/framework/middleware/remoteuser.py +++ b/lib/galaxy/web/framework/middleware/remoteuser.py @@ -59,7 +59,7 @@ # Rewrite* method for passing REMOTE_USER and a user is # un-authenticated. Any other possible values need to go here as well. path_info = environ.get('PATH_INFO', '') - if environ.get(self.remote_user_header, '(null)') != '(null)': + if not environ.get(self.remote_user_header, '(null)').startswith('(null)'): if not environ[ self.remote_user_header ].count( '@' ): if self.maildomain is not None: environ[ self.remote_user_header ] += '@' + self.maildomain diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff static/scripts/mvc/data.js --- a/static/scripts/mvc/data.js +++ b/static/scripts/mvc/data.js @@ -169,7 +169,8 @@ }); this.$el.append(data_table); var column_names = this.model.get_metadata('column_names'), - header_row = $('<tr/>').appendTo(data_table); + header_container = $('<thead/>').appendTo(data_table), + header_row = $('<tr/>').appendTo(header_container); if (column_names) { header_row.append('<th>' + column_names.join('</th><th>') + '</th>'); } else { diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff static/scripts/packed/mvc/data.js --- a/static/scripts/packed/mvc/data.js +++ b/static/scripts/packed/mvc/data.js @@ -1,1 +1,1 @@ -define(["mvc/ui/ui-modal","mvc/ui/ui-frames","mvc/ui/icon-button"],function(k,j,f){var h=Backbone.Model.extend({});var b=Backbone.Model.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda",metadata:null},initialize:function(){if(!this.get("metadata")){this._set_metadata()}this.on("change",this._set_metadata,this)},_set_metadata:function(){var n=new h();_.each(_.keys(this.attributes),function(o){if(o.indexOf("metadata_")===0){var p=o.split("metadata_")[1];n.set(p,this.attributes[o]);delete this.attributes[o]}},this);this.set("metadata",n,{silent:true})},get_metadata:function(n){return this.attributes.metadata.get(n)},urlRoot:galaxy_config.root+"api/datasets"});var i=b.extend({defaults:_.extend({},b.prototype.defaults,{chunk_url:null,first_data_chunk:null,chunk_index:-1,at_eof:false}),initialize:function(n){b.prototype.initialize.call(this);this.attributes.chunk_index=(this.attributes.first_data_chunk?1:0);this.attributes.chunk_url=galaxy_config.root+"dataset/display?dataset_id="+this.id;this.attributes.url_viz=galaxy_config.root+"visualization"},get_next_chunk:function(){if(this.attributes.at_eof){return null}var n=this,o=$.Deferred();$.getJSON(this.attributes.chunk_url,{chunk:n.attributes.chunk_index++}).success(function(p){var q;if(p.ck_data!==""){q=p}else{n.attributes.at_eof=true;q=null}o.resolve(q)});return o}});var e=Backbone.Collection.extend({model:b});var a=Backbone.View.extend({initialize:function(n){this.row_count=0;this.loading_chunk=false;new d({model:n.model,$el:this.$el})},expand_to_container:function(){if(this.$el.height()<this.scroll_elt.height()){this.attempt_to_fetch()}},attempt_to_fetch:function(o){var n=this;if(!this.loading_chunk&&this.scrolled_to_bottom()){this.loading_chunk=true;this.loading_indicator.show();$.when(n.model.get_next_chunk()).then(function(p){if(p){n._renderChunk(p);n.loading_chunk=false}n.loading_indicator.hide();n.expand_to_container()})}},render:function(){this.loading_indicator=$("<div/>").attr("id","loading_indicator");this.$el.append(this.loading_indicator);var r=$("<table/>").attr({id:"content_table",cellpadding:0});this.$el.append(r);var n=this.model.get_metadata("column_names"),s=$("<tr/>").appendTo(r);if(n){s.append("<th>"+n.join("</th><th>")+"</th>")}else{for(var q=1;q<=this.model.get_metadata("columns");q++){s.append("<th>"+q+"</th>")}}var p=this,o=this.model.get("first_data_chunk");if(o){this._renderChunk(o)}else{$.when(p.model.get_next_chunk()).then(function(t){p._renderChunk(t)})}this.scroll_elt.scroll(function(){p.attempt_to_fetch()})},scrolled_to_bottom:function(){return false},_renderCell:function(q,n,r){var o=$("<td>").text(q);var p=this.model.get_metadata("column_types");if(r!==undefined){o.attr("colspan",r).addClass("stringalign")}else{if(p){if(n<p.length){if(p[n]==="str"||p[n]==="list"){o.addClass("stringalign")}}}}return o},_renderRow:function(n){var o=n.split("\t"),q=$("<tr>"),p=this.model.get_metadata("columns");if(this.row_count%2!==0){q.addClass("dark_row")}if(o.length===p){_.each(o,function(s,r){q.append(this._renderCell(s,r))},this)}else{if(o.length>p){_.each(o.slice(0,p-1),function(s,r){q.append(this._renderCell(s,r))},this);q.append(this._renderCell(o.slice(p-1).join("\t"),p-1))}else{if(p>5&&o.length===p-1){_.each(o,function(s,r){q.append(this._renderCell(s,r))},this);q.append($("<td>"))}else{q.append(this._renderCell(n,0,p))}}}this.row_count++;return q},_renderChunk:function(n){var o=this.$el.find("table");_.each(n.ck_data.split("\n"),function(p,q){if(p!==""){o.append(this._renderRow(p))}},this)}});var g=a.extend({initialize:function(n){a.prototype.initialize.call(this,n);scroll_elt=_.find(this.$el.parents(),function(o){return $(o).css("overflow")==="auto"});if(!scroll_elt){scroll_elt=window}this.scroll_elt=$(scroll_elt)},scrolled_to_bottom:function(){return(this.$el.height()-this.scroll_elt.scrollTop()-this.scroll_elt.height()<=0)}});var m=a.extend({initialize:function(n){a.prototype.initialize.call(this,n);this.scroll_elt=this.$el.css({position:"relative",overflow:"scroll",height:this.options.height||"500px"})},scrolled_to_bottom:function(){return this.$el.scrollTop()+this.$el.innerHeight()>=this.el.scrollHeight}});var d=Backbone.View.extend({col:{chrom:null,start:null,end:null},url_viz:null,dataset_id:null,genome_build:null,file_ext:null,initialize:function(p){var s=parent.Galaxy;if(s&&s.modal){this.modal=s.modal}if(s&&s.frame){this.frame=s.frame}if(!this.modal||!this.frame){return}var o=p.model;var r=o.get("metadata");if(!o.get("file_ext")){return}this.file_ext=o.get("file_ext");if(this.file_ext=="bed"){if(r.get("chromCol")&&r.get("startCol")&&r.get("endCol")){this.col.chrom=r.get("chromCol")-1;this.col.start=r.get("startCol")-1;this.col.end=r.get("endCol")-1}else{console.log("TabularButtonTrackster : Bed-file metadata incomplete.");return}}if(this.file_ext=="vcf"){function q(u,v){for(var t=0;t<v.length;t++){if(v[t].match(u)){return t}}return -1}this.col.chrom=q("Chrom",r.get("column_names"));this.col.start=q("Pos",r.get("column_names"));this.col.end=null;if(this.col.chrom==-1||this.col.start==-1){console.log("TabularButtonTrackster : VCF-file metadata incomplete.");return}}if(this.col.chrom===undefined){return}if(o.id){this.dataset_id=o.id}else{console.log("TabularButtonTrackster : Dataset identification is missing.");return}if(o.get("url_viz")){this.url_viz=o.get("url_viz")}else{console.log("TabularButtonTrackster : Url for visualization controller is missing.");return}if(o.get("genome_build")){this.genome_build=o.get("genome_build")}var n=new f.IconButtonView({model:new f.IconButton({title:"Visualize",icon_class:"chart_curve",id:"btn_viz"})});this.setElement(p.$el);this.$el.append(n.render().$el);this.hide()},events:{"mouseover tr":"show",mouseleave:"hide"},show:function(s){function r(x){return !isNaN(parseFloat(x))&&isFinite(x)}if(this.col.chrom===null){return}var w=$(s.target).parent();var t=w.children().eq(this.col.chrom).html();var n=w.children().eq(this.col.start).html();var p=this.col.end?w.children().eq(this.col.end).html():n;if(!t.match("^#")&&t!==""&&r(n)){var v={dataset_id:this.dataset_id,gene_region:t+":"+n+"-"+p};var q=w.offset();var o=q.left-10;var u=q.top-$(window).scrollTop()+3;$("#btn_viz").css({position:"fixed",top:u+"px",left:o+"px"});$("#btn_viz").off("click");$("#btn_viz").click(this.create_trackster_action(this.url_viz,v,this.genome_build));$("#btn_viz").show()}else{$("#btn_viz").hide()}},hide:function(){this.$el.find("#btn_viz").hide()},create_trackster_action:function(n,q,p){var o=this;return function(){var r={};if(p){r["f-dbkey"]=p}$.ajax({url:n+"/list_tracks?"+$.param(r),dataType:"html",error:function(){o.modal.show({title:"Something went wrong!",body:"Unfortunately we could not add this dataset to the track browser. Please try again or contact us.",buttons:{Cancel:function(){o.modal.hide()}}})},success:function(s){o.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){o.modal.hide()},"View in saved visualization":function(){o.modal.show({title:"Add Data to Saved Visualization",body:s,buttons:{Cancel:function(){o.modal.hide()},"Add to visualization":function(){o.modal.hide();o.modal.$el.find("input[name=id]:checked").each(function(){var t=$(this).val();q.id=t;o.frame.add({title:"Trackster",type:"url",content:n+"/trackster?"+$.param(q)})})}}})},"View in new visualization":function(){o.modal.hide();o.frame.add({title:"Trackster",type:"url",content:n+"/trackster?"+$.param(q)})}}})}});return false}}});var l=function(q,o,r,n){var p=new o({model:new q(r)});p.render();if(n){n.append(p.$el)}return p};var c=function(p){if(!p.model){p.model=new i(p.dataset_config)}var o=p.parent_elt;var q=p.embedded;delete p.embedded;delete p.parent_elt;delete p.dataset_config;var n=(q?new m(p):new g(p));n.render();if(o){o.append(n.$el);n.expand_to_container()}return n};return{Dataset:b,TabularDataset:i,DatasetCollection:e,TabularDatasetChunkedView:a,createTabularDatasetChunkedView:c}}); \ No newline at end of file +define(["mvc/ui/ui-modal","mvc/ui/ui-frames","mvc/ui/icon-button"],function(k,j,f){var h=Backbone.Model.extend({});var b=Backbone.Model.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda",metadata:null},initialize:function(){if(!this.get("metadata")){this._set_metadata()}this.on("change",this._set_metadata,this)},_set_metadata:function(){var n=new h();_.each(_.keys(this.attributes),function(o){if(o.indexOf("metadata_")===0){var p=o.split("metadata_")[1];n.set(p,this.attributes[o]);delete this.attributes[o]}},this);this.set("metadata",n,{silent:true})},get_metadata:function(n){return this.attributes.metadata.get(n)},urlRoot:galaxy_config.root+"api/datasets"});var i=b.extend({defaults:_.extend({},b.prototype.defaults,{chunk_url:null,first_data_chunk:null,chunk_index:-1,at_eof:false}),initialize:function(n){b.prototype.initialize.call(this);this.attributes.chunk_index=(this.attributes.first_data_chunk?1:0);this.attributes.chunk_url=galaxy_config.root+"dataset/display?dataset_id="+this.id;this.attributes.url_viz=galaxy_config.root+"visualization"},get_next_chunk:function(){if(this.attributes.at_eof){return null}var n=this,o=$.Deferred();$.getJSON(this.attributes.chunk_url,{chunk:n.attributes.chunk_index++}).success(function(p){var q;if(p.ck_data!==""){q=p}else{n.attributes.at_eof=true;q=null}o.resolve(q)});return o}});var e=Backbone.Collection.extend({model:b});var a=Backbone.View.extend({initialize:function(n){this.row_count=0;this.loading_chunk=false;new d({model:n.model,$el:this.$el})},expand_to_container:function(){if(this.$el.height()<this.scroll_elt.height()){this.attempt_to_fetch()}},attempt_to_fetch:function(o){var n=this;if(!this.loading_chunk&&this.scrolled_to_bottom()){this.loading_chunk=true;this.loading_indicator.show();$.when(n.model.get_next_chunk()).then(function(p){if(p){n._renderChunk(p);n.loading_chunk=false}n.loading_indicator.hide();n.expand_to_container()})}},render:function(){this.loading_indicator=$("<div/>").attr("id","loading_indicator");this.$el.append(this.loading_indicator);var r=$("<table/>").attr({id:"content_table",cellpadding:0});this.$el.append(r);var n=this.model.get_metadata("column_names"),s=$("<thead/>").appendTo(r),t=$("<tr/>").appendTo(s);if(n){t.append("<th>"+n.join("</th><th>")+"</th>")}else{for(var q=1;q<=this.model.get_metadata("columns");q++){t.append("<th>"+q+"</th>")}}var p=this,o=this.model.get("first_data_chunk");if(o){this._renderChunk(o)}else{$.when(p.model.get_next_chunk()).then(function(u){p._renderChunk(u)})}this.scroll_elt.scroll(function(){p.attempt_to_fetch()})},scrolled_to_bottom:function(){return false},_renderCell:function(q,n,r){var o=$("<td>").text(q);var p=this.model.get_metadata("column_types");if(r!==undefined){o.attr("colspan",r).addClass("stringalign")}else{if(p){if(n<p.length){if(p[n]==="str"||p[n]==="list"){o.addClass("stringalign")}}}}return o},_renderRow:function(n){var o=n.split("\t"),q=$("<tr>"),p=this.model.get_metadata("columns");if(this.row_count%2!==0){q.addClass("dark_row")}if(o.length===p){_.each(o,function(s,r){q.append(this._renderCell(s,r))},this)}else{if(o.length>p){_.each(o.slice(0,p-1),function(s,r){q.append(this._renderCell(s,r))},this);q.append(this._renderCell(o.slice(p-1).join("\t"),p-1))}else{if(p>5&&o.length===p-1){_.each(o,function(s,r){q.append(this._renderCell(s,r))},this);q.append($("<td>"))}else{q.append(this._renderCell(n,0,p))}}}this.row_count++;return q},_renderChunk:function(n){var o=this.$el.find("table");_.each(n.ck_data.split("\n"),function(p,q){if(p!==""){o.append(this._renderRow(p))}},this)}});var g=a.extend({initialize:function(n){a.prototype.initialize.call(this,n);scroll_elt=_.find(this.$el.parents(),function(o){return $(o).css("overflow")==="auto"});if(!scroll_elt){scroll_elt=window}this.scroll_elt=$(scroll_elt)},scrolled_to_bottom:function(){return(this.$el.height()-this.scroll_elt.scrollTop()-this.scroll_elt.height()<=0)}});var m=a.extend({initialize:function(n){a.prototype.initialize.call(this,n);this.scroll_elt=this.$el.css({position:"relative",overflow:"scroll",height:this.options.height||"500px"})},scrolled_to_bottom:function(){return this.$el.scrollTop()+this.$el.innerHeight()>=this.el.scrollHeight}});var d=Backbone.View.extend({col:{chrom:null,start:null,end:null},url_viz:null,dataset_id:null,genome_build:null,file_ext:null,initialize:function(p){var s=parent.Galaxy;if(s&&s.modal){this.modal=s.modal}if(s&&s.frame){this.frame=s.frame}if(!this.modal||!this.frame){return}var o=p.model;var r=o.get("metadata");if(!o.get("file_ext")){return}this.file_ext=o.get("file_ext");if(this.file_ext=="bed"){if(r.get("chromCol")&&r.get("startCol")&&r.get("endCol")){this.col.chrom=r.get("chromCol")-1;this.col.start=r.get("startCol")-1;this.col.end=r.get("endCol")-1}else{console.log("TabularButtonTrackster : Bed-file metadata incomplete.");return}}if(this.file_ext=="vcf"){function q(u,v){for(var t=0;t<v.length;t++){if(v[t].match(u)){return t}}return -1}this.col.chrom=q("Chrom",r.get("column_names"));this.col.start=q("Pos",r.get("column_names"));this.col.end=null;if(this.col.chrom==-1||this.col.start==-1){console.log("TabularButtonTrackster : VCF-file metadata incomplete.");return}}if(this.col.chrom===undefined){return}if(o.id){this.dataset_id=o.id}else{console.log("TabularButtonTrackster : Dataset identification is missing.");return}if(o.get("url_viz")){this.url_viz=o.get("url_viz")}else{console.log("TabularButtonTrackster : Url for visualization controller is missing.");return}if(o.get("genome_build")){this.genome_build=o.get("genome_build")}var n=new f.IconButtonView({model:new f.IconButton({title:"Visualize",icon_class:"chart_curve",id:"btn_viz"})});this.setElement(p.$el);this.$el.append(n.render().$el);this.hide()},events:{"mouseover tr":"show",mouseleave:"hide"},show:function(s){function r(x){return !isNaN(parseFloat(x))&&isFinite(x)}if(this.col.chrom===null){return}var w=$(s.target).parent();var t=w.children().eq(this.col.chrom).html();var n=w.children().eq(this.col.start).html();var p=this.col.end?w.children().eq(this.col.end).html():n;if(!t.match("^#")&&t!==""&&r(n)){var v={dataset_id:this.dataset_id,gene_region:t+":"+n+"-"+p};var q=w.offset();var o=q.left-10;var u=q.top-$(window).scrollTop()+3;$("#btn_viz").css({position:"fixed",top:u+"px",left:o+"px"});$("#btn_viz").off("click");$("#btn_viz").click(this.create_trackster_action(this.url_viz,v,this.genome_build));$("#btn_viz").show()}else{$("#btn_viz").hide()}},hide:function(){this.$el.find("#btn_viz").hide()},create_trackster_action:function(n,q,p){var o=this;return function(){var r={};if(p){r["f-dbkey"]=p}$.ajax({url:n+"/list_tracks?"+$.param(r),dataType:"html",error:function(){o.modal.show({title:"Something went wrong!",body:"Unfortunately we could not add this dataset to the track browser. Please try again or contact us.",buttons:{Cancel:function(){o.modal.hide()}}})},success:function(s){o.modal.show({title:"View Data in a New or Saved Visualization",buttons:{Cancel:function(){o.modal.hide()},"View in saved visualization":function(){o.modal.show({title:"Add Data to Saved Visualization",body:s,buttons:{Cancel:function(){o.modal.hide()},"Add to visualization":function(){o.modal.hide();o.modal.$el.find("input[name=id]:checked").each(function(){var t=$(this).val();q.id=t;o.frame.add({title:"Trackster",type:"url",content:n+"/trackster?"+$.param(q)})})}}})},"View in new visualization":function(){o.modal.hide();o.frame.add({title:"Trackster",type:"url",content:n+"/trackster?"+$.param(q)})}}})}});return false}}});var l=function(q,o,r,n){var p=new o({model:new q(r)});p.render();if(n){n.append(p.$el)}return p};var c=function(p){if(!p.model){p.model=new i(p.dataset_config)}var o=p.parent_elt;var q=p.embedded;delete p.embedded;delete p.parent_elt;delete p.dataset_config;var n=(q?new m(p):new g(p));n.render();if(o){o.append(n.$el);n.expand_to_container()}return n};return{Dataset:b,TabularDataset:i,DatasetCollection:e,TabularDatasetChunkedView:a,createTabularDatasetChunkedView:c}}); \ No newline at end of file diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff test/functional/test_toolbox.py --- a/test/functional/test_toolbox.py +++ b/test/functional/test_toolbox.py @@ -1,6 +1,7 @@ import new import sys from base.twilltestcase import TwillTestCase +from base.asserts import verify_assertions from base.interactor import build_interactor, stage_data_in_history, RunToolException from base.instrument import register_job_data from galaxy.tools import DataManagerTool @@ -92,6 +93,9 @@ raise Exception( "Test parse failure" ) def _verify_outputs( self, testdef, history, jobs, shed_tool_id, data_list, galaxy_interactor ): + assert len(jobs) == 1, "Test framework logic error, somehow tool test resulted in more than one job." + job = jobs[ 0 ] + maxseconds = testdef.maxseconds if testdef.num_outputs is not None: expected = testdef.num_outputs @@ -102,7 +106,33 @@ raise Exception( message ) found_exceptions = [] - job_stdio = None + if testdef.expect_failure: + if testdef.outputs: + raise Exception("Cannot specify outputs in a test expecting failure.") + + # Wait for the job to complete and register expections if the final + # status was not what test was expecting. + job_failed = False + try: + galaxy_interactor.wait_for_job( job[ 'id' ], history, maxseconds ) + except Exception as e: + job_failed = True + if not testdef.expect_failure: + found_exceptions.append(e) + + if not job_failed and testdef.expect_failure: + error = AssertionError("Expected job to fail but Galaxy indicated the job successfully completed.") + found_exceptions.append(error) + + job_stdio = galaxy_interactor.get_job_stdio( job[ 'id' ] ) + + expect_exit_code = testdef.expect_exit_code + if expect_exit_code is not None: + exit_code = job_stdio["exit_code"] + if str(expect_exit_code) != str(exit_code): + error = AssertionError("Expected job to complete with exit code %s, found %s" % (expect_exit_code, exit_code)) + found_exceptions.append(error) + for output_index, output_tuple in enumerate(testdef.outputs): # Get the correct hid name, outfile, attributes = output_tuple @@ -123,14 +153,25 @@ except Exception as e: if not found_exceptions: # Only print this stuff out once. - for job in jobs: - job_stdio = galaxy_interactor.get_job_stdio( job[ 'id' ] ) - for stream in ['stdout', 'stderr']: - if stream in job_stdio: - print >>sys.stderr, self._format_stream( job_stdio[ stream ], stream=stream, format=True ) + for stream in ['stdout', 'stderr']: + if stream in job_stdio: + print >>sys.stderr, self._format_stream( job_stdio[ stream ], stream=stream, format=True ) found_exceptions.append(e) - if job_stdio is None: - job_stdio = galaxy_interactor.get_job_stdio( jobs[0][ 'id' ] ) + + other_checks = { + "command_line": "Command produced by the job", + "stdout": "Standard output of the job", + "stderr": "Standard error of the job", + } + for what, description in other_checks.items(): + if getattr( testdef, what, None ) is not None: + try: + data = job_stdio[what] + verify_assertions( data, getattr( testdef, what ) ) + except AssertionError, err: + errmsg = '%s different than expected\n' % description + errmsg += str( err ) + found_exceptions.append( AssertionError( errmsg ) ) if found_exceptions: raise JobOutputsError(found_exceptions, job_stdio) diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff test/functional/tools/job_properties.xml --- /dev/null +++ b/test/functional/tools/job_properties.xml @@ -0,0 +1,62 @@ +<tool id="job_properties" name="Test Job Properties"> + <command> + #if $thebool + echo "The bool is true"; + echo "The bool is really true" 1>&2; + echo "This is a line of text." > $out_file1 + #else + echo "The bool is not true"; + echo "The bool is very not true" 1>&2; + echo "This is a different line of text." > $out_file1; + sh -c "exit 2" + #end if + #if $failbool + ; sh -c "exit 127" + #end if + + </command> + <inputs> + <param name="thebool" type="boolean" label="The boolean property" /> + <param name="failbool" type="boolean" label="The failure property" checked="false" /> + </inputs> + <outputs> + <data name="out_file1" /> + </outputs> + <stdio> + <exit_code range="127" level="fatal" description="Failing exit code." /> + </stdio> + <tests> + <test expect_exit_code="0"> + <param name="thebool" value="true" /> + <output name="out_file1" file="simple_line.txt" /> + <assert_command> + <has_text text="really" /> + </assert_command> + <assert_stdout> + <has_line line="The bool is true" /> + </assert_stdout> + <assert_stderr> + <has_line line="The bool is really true" /> + </assert_stderr> + </test> + <test expect_exit_code="2"> + <param name="thebool" value="false" /> + <output name="out_file1" file="simple_line_alternative.txt" /> + <assert_command> + <has_text text="very not" /> + </assert_command> + <assert_stdout> + <has_line line="The bool is not true" /> + </assert_stdout> + <assert_stderr> + <has_line line="The bool is very not true" /> + </assert_stderr> + </test> + <test expect_exit_coded="127" expect_failure="true"> + <param name="thebool" value="true" /> + <param name="failbool" value="true" /> + </test> + </tests> + <help> + </help> +</tool> diff -r bf8dcef796dafaec6462f89624ee1adb92e5a45d -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff test/functional/tools/samples_tool_conf.xml --- a/test/functional/tools/samples_tool_conf.xml +++ b/test/functional/tools/samples_tool_conf.xml @@ -13,6 +13,7 @@ <tool file="multi_output_assign_primary.xml" /><tool file="composite_output.xml" /><tool file="metadata.xml" /> + <tool file="job_properties.xml" /><tool file="gzipped_inputs.xml" /><tool file="output_order.xml" /><tool file="output_format.xml" /> https://bitbucket.org/galaxy/galaxy-central/commits/cf2636a3d9c3/ Changeset: cf2636a3d9c3 User: erasche2 Date: 2015-02-05 20:43:26+00:00 Summary: Merged galaxy/galaxy-central into default Affected #: 449 files diff -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d .hgignore --- a/.hgignore +++ b/.hgignore @@ -30,9 +30,9 @@ *.pyc # Galaxy Runtime Files -paster.lock -paster.log -paster.pid +*.lock +*.log +*.pid # Tool Shed Runtime Files tool_shed_webapp.lock @@ -108,12 +108,9 @@ # CSS build artifacts. sprite-*.less -# Local node_modules directories +# Local node_modules and bower_components directories node_modules - -# Testing -selenium-server.jar -selenium_results.html +bower_components # Documentation build files. doc/build diff -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d .hgtags --- a/.hgtags +++ b/.hgtags @@ -8,16 +8,18 @@ 5e605ed6069fe4c5ca9875e95e91b2713499e8ca release_2014.02.10 9e53251b0b7e93b9563008a2b112f2e815a04bbc release_2014.04.14 7e257c7b10badb65772b1528cb61d58175a42e47 release_2014.06.02 -7a4d321c0e38fa263ea83d29a35a608c3181fcba latest_2014.06.02 -9661b9d5d5b330483ae3ad2236410e0efaa7c500 latest_2014.04.14 -6b0bd93038a843b1585155f0d63f0eea2459c70b latest_2013.01.13 -3e62060b14b9afc46f8e0ec02e1a4500d77db9e1 latest_2013.02.08 -425009b3ff4d8b67d2812253b221f3c4f4a8d1e3 latest_2013.04.01 -9713d86392ef985ffcdc39ff0c8ddf51a1f9ce47 latest_2013.06.03 -9ed84cd208e07e8985ec917cb025fcbbb09edcfb latest_2013.08.12 -81fbe25bd02edcd53065e8e4476dd1dfb5a72cf2 latest_2013.11.04 -2a756ca2cb1826db7796018e77d12e2dd7b67603 latest_2014.02.10 +9bce3f426863f8ba88062f67c7efc1836e82ee7c latest_2014.06.02 +8f9dcac033694e4cabcf5daae5cca1cfefbe967f latest_2014.04.14 +9c323aad4ffdd65a3deb06a4a36f6b2c5115a60f latest_2013.01.13 +b986c184be88947b5d1d90be7f36cfd2627dd938 latest_2013.02.08 +dec9431d66b837a208e2f060d90afd913c721227 latest_2013.04.01 +19e56e66b0b344c6e2afa4541f6988e4fdb9af29 latest_2013.06.03 +cee903b8b3eee9145627ee89742555dac581791e latest_2013.08.12 +7d5aa19a166cba9039e15f338a1e3fc924c43d3a latest_2013.11.04 +0c000cc2f9c05bf4c1c2bc3a10215014fd64e696 latest_2014.02.10 ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11 -548ab24667d6206780237bd807f7d857a484c461 latest_2014.08.11 +f3fc4602e22b39468d780b955a1a05caf867a7e9 latest_2014.08.11 2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06 -7086b87d83a9092cbece0fec6f3e3ed03266be0c latest_2014.10.06 +9bd6f8b5b8153db752f4e61ed62f2b6c01ae4a11 latest_2014.10.06 +2e8dd2949dd3eee0f56f9a3a5ebf1b2baca24aee release_2015.01.13 +4039bfd5584aac053f686197a76ac176253e6f3d latest_2015.01.13 diff -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d client/GruntFile.js --- a/client/GruntFile.js +++ b/client/GruntFile.js @@ -47,15 +47,77 @@ options: { spawn: false } + }, + + // call bower to install libraries and other external resources + "bower-install-simple" : { + options: { + color: true + }, + "prod": { + options: { + production: true + } + }, + "dev": { + options: { + production: false + } + } + }, + // where to move fetched bower components into the build structure (libName: [ bower-location, libs-location ]) + libraryLocations : { + "jquery": [ "dist/jquery.js", "jquery/jquery.js" ], + "jquery-migrate": [ "jquery-migrate.js", "jquery/jquery.migrate.js" ], + "traceKit": [ "tracekit.js", "tracekit.js" ], + "ravenjs": [ "dist/raven.js", "raven.js" ], + "underscore": [ "underscore.js", "underscore.js" ], + "handlebars": [ "handlebars.runtime.js", "handlebars.runtime.js" ], + "lunr.js": [ "lunr.js", "lunr.js" ] + //"backbone": [ "backbone.js", "backbone/backbone.js" ], + + // these need to be updated and tested + //"require": [ "build/require.js", "require.js" ], + //"d3": [ "d3.js", "d3.js" ], + //"farbtastic": [ "src/farbtastic.js", "farbtastic.js" ], + //"jQTouch": [ "src/reference/jqtouch.js", "jquery/jqtouch.js" ], + //"bib2json": [ "Parser.js", "bibtex.js" ], + //"jquery-form": [ "jquery.form.js", "jquery/jquery.form.js" ], + //"jquery-autocomplete": [ "src/jquery.autocomplete.js", "jquery/jquery.autocomplete.js" ], + //"select2": [ "select2.js", "jquery/select2.js" ], + //"jStorage": [ "jstorage.js", "jquery/jstorage.js" ], + //"jquery.cookie": [ "", "jquery/jquery.cookie.js" ], + //"dynatree": [ "dist/jquery.dynatree.js", "jquery/jquery.dynatree.js" ], + //"jquery-mousewheel": [ "jquery.mousewheel.js", "jquery/jquery.mousewheel.js" ], + //"jquery.event.drag-drop": [ + // [ "event.drag/jquery.event.drag.js", "jquery/jquery.event.drag.js" ], + // [ "event.drag/jquery.event.drop.js", "jquery/jquery.event.drop.js" ] + //], + + // these are complicated by additional css/less + //"toastr": [ "toastr.js", "toastr.js" ], + //"wymeditor": [ "dist/wymeditor/jquery.wymeditor.js", "jquery/jquery.wymeditor.js" ], + //"jstree": [ "jstree.js", "jquery/jstree.js" ], + + // these have been customized by Galaxy + //"bootstrap": [ "dist/js/bootstrap.js", "bootstrap.js" ], + //"jquery-ui": [ + // // multiple components now + // [ "", "jquery/jquery-ui.js" ] + //], + } + }); grunt.loadNpmTasks( 'grunt-contrib-watch' ); grunt.loadNpmTasks( 'grunt-contrib-copy'); - grunt.loadNpmTasks('grunt-exec'); + grunt.loadNpmTasks( 'grunt-bower-install-simple'); + grunt.loadNpmTasks( 'grunt-exec' ); grunt.registerTask( 'pack', [ 'exec' ] ); - grunt.registerTask( 'default', [ 'copy', 'pack' ] ); + grunt.registerTask( 'default', [ 'copy:main', 'pack' ] ); + // -------------------------------------------------------------------------- copy,pack only those changed // adapted from grunt-contrib-watch jslint example @@ -85,4 +147,25 @@ onChange(); }); + // -------------------------------------------------------------------------- fetch/update external libraries + /** copy external libraries from bower components to scripts/libs */ + function copyLibs(){ + var libraryLocations = grunt.config( 'libraryLocations' ); + for( var libName in libraryLocations ){ + if( libraryLocations.hasOwnProperty( libName ) ){ + + var BOWER_DIR = 'bower_components', + location = libraryLocations[ libName ], + source = [ BOWER_DIR, libName, location[0] ].join( '/' ), + destination = 'galaxy/scripts/libs/' + location[1]; + grunt.log.writeln( source + ' -> ' + destination ); + grunt.file.copy( source, destination ); + } + } + } + + grunt.registerTask( 'copy-libs', 'copy external libraries to src', copyLibs ); + grunt.registerTask( 'install-libs', 'fetch external libraries and copy to src', + [ 'bower-install-simple:prod', 'copy-libs' ] ); + }; diff -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d client/bower.json --- /dev/null +++ b/client/bower.json @@ -0,0 +1,41 @@ +{ + "name": "galaxy-client-libs", + "version": "0.0.0", + "description": "External client-side libraries used by Galaxy", + "keywords": [ + "galaxy", + "galaxyproject" + ], + "homepage": "usegalaxy.org", + "dependencies": { + "jquery": "~1.11.1", + "traceKit": "*", + "ravenjs": "~1.1.16", + "require": "*", + "underscore": "~1.7.0", + "backbone": "~1.1.2", + "bootstrap": "~3.3.2", + "d3": "~3.5.3", + "farbtastic": "~2.0.0-alpha.1", + "toastr": "~2.1.0", + "jQTouch": "git://github.com/senchalabs/jQTouch#~1.0.0", + "bib2json": "git://github.com/mayanklahiri/bib2json", + "jquery-form": "~3.46.0", + "jquery-autocomplete": "git://github.com/dyve/jquery-autocomplete", + "select2": "~3.5.2", + "jStorage": "~0.4.12", + "jquery.cookie": "~1.4.1", + "dynatree": "~1.2.5", + "jquery-mousewheel": "~3.1.12", + "wymeditor": "~1.0.0-rc.1", + "jstree": "~3.0.9", + "jquery-ui": "git://github.com/jquery/jquery-ui.git#~1.11.2", + "jquery.event.drag-drop": "~2.2.1", + "handlebars": "~2.0.0", + "jquery-migrate": "~1.2.1", + "lunr.js": "git://github.com/olivernn/lunr.js.git#~0.5.7" + }, + "resolutions": { + "jquery": "~1.11.1" + } +} diff -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d client/galaxy/scripts/galaxy.masthead.js --- a/client/galaxy/scripts/galaxy.masthead.js +++ b/client/galaxy/scripts/galaxy.masthead.js @@ -364,7 +364,7 @@ var $popover_element = $(this.el).find('.head'); $popover_element.popover({ html: true, - content: 'Please <a href="' + galaxy_config.root + '/user/login">log in</a> or <a href="' + galaxy_config.root + '/user/create">register</a> to use this feature.', + content: 'Please <a href="' + galaxy_config.root + 'user/login?use_panels=True">log in</a> or <a href="' + galaxy_config.root + 'user/create?use_panels=True">register</a> to use this feature.', placement: 'bottom' }).on('shown.bs.popover', function() { // hooking on bootstrap event to automatically hide popovers after delay setTimeout(function() { diff -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d client/galaxy/scripts/galaxy.workflow_editor.canvas.js --- a/client/galaxy/scripts/galaxy.workflow_editor.canvas.js +++ b/client/galaxy/scripts/galaxy.workflow_editor.canvas.js @@ -805,7 +805,7 @@ nodeView.addDataOutput( output ); } ); nodeView.render(); - workflow.node_changed( this ); + workflow.node_changed( this, true); }, update_field_data : function( data ) { var node = this; @@ -1089,17 +1089,25 @@ if ( this.active_node != node ) { this.check_changes_in_active_form(); this.clear_active_node(); - parent.show_form_for_tool( node.form_html + node.tooltip, node ); + if (parent.__NEWTOOLFORM__) { + parent.show_form_for_tool( node.form_html, node ); + } else { + parent.show_form_for_tool( node.form_html + node.tooltip, node ); + } node.make_active(); 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 ); + if (parent.__NEWTOOLFORM__) { + parent.show_form_for_tool( node.form_html, node ); + } else { + parent.show_form_for_tool( node.form_html + node.tooltip, node ); + } } }, layout : function () { @@ -1305,7 +1313,6 @@ return node; } - var ext_to_type = null; var type_to_type = null; @@ -1863,6 +1870,9 @@ left: x, top: y }); + self.cv.css( { "background-position-x": x, + "background-position-y": y + }); self.update_viewport_overlay(); }; // Dragging within canvas background diff -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d client/galaxy/scripts/libs/handlebars.runtime.js --- a/client/galaxy/scripts/libs/handlebars.runtime.js +++ b/client/galaxy/scripts/libs/handlebars.runtime.js @@ -1,6 +1,8 @@ -/* +/*! -Copyright (C) 2011 by Yehuda Katz + handlebars v2.0.0 + +Copyright (C) 2011-2014 by Yehuda Katz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,343 +22,639 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +@license */ - -// lib/handlebars/browser-prefix.js -var Handlebars = {}; - -(function(Handlebars, undefined) { -; -// lib/handlebars/base.js - -Handlebars.VERSION = "1.0.0"; -Handlebars.COMPILER_REVISION = 4; - -Handlebars.REVISION_CHANGES = { - 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it - 2: '== 1.0.0-rc.3', - 3: '== 1.0.0-rc.4', - 4: '>= 1.0.0' -}; - -Handlebars.helpers = {}; -Handlebars.partials = {}; - -var toString = Object.prototype.toString, - functionType = '[object Function]', - objectType = '[object Object]'; - -Handlebars.registerHelper = function(name, fn, inverse) { - if (toString.call(name) === objectType) { - if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); } - Handlebars.Utils.extend(this.helpers, name); +/* exported Handlebars */ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); + } else if (typeof exports === 'object') { + module.exports = factory(); } else { - if (inverse) { fn.not = inverse; } - this.helpers[name] = fn; + root.Handlebars = root.Handlebars || factory(); } -}; - -Handlebars.registerPartial = function(name, str) { - if (toString.call(name) === objectType) { - Handlebars.Utils.extend(this.partials, name); - } else { - this.partials[name] = str; - } -}; - -Handlebars.registerHelper('helperMissing', function(arg) { - if(arguments.length === 2) { - return undefined; - } else { - throw new Error("Missing helper: '" + arg + "'"); - } -}); - -Handlebars.registerHelper('blockHelperMissing', function(context, options) { - var inverse = options.inverse || function() {}, fn = options.fn; - - var type = toString.call(context); - - if(type === functionType) { context = context.call(this); } - - if(context === true) { - return fn(this); - } else if(context === false || context == null) { - return inverse(this); - } else if(type === "[object Array]") { - if(context.length > 0) { - return Handlebars.helpers.each(context, options); - } else { - return inverse(this); - } - } else { - return fn(context); - } -}); - -Handlebars.K = function() {}; - -Handlebars.createFrame = Object.create || function(object) { - Handlebars.K.prototype = object; - var obj = new Handlebars.K(); - Handlebars.K.prototype = null; - return obj; -}; - -Handlebars.logger = { - DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, - - methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'}, - - // can be overridden in the host environment - log: function(level, obj) { - if (Handlebars.logger.level <= level) { - var method = Handlebars.logger.methodMap[level]; - if (typeof console !== 'undefined' && console[method]) { - console[method].call(console, obj); - } - } - } -}; - -Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); }; - -Handlebars.registerHelper('each', function(context, options) { - var fn = options.fn, inverse = options.inverse; - var i = 0, ret = "", data; - - var type = toString.call(context); - if(type === functionType) { context = context.call(this); } - - if (options.data) { - data = Handlebars.createFrame(options.data); +}(this, function () { +// handlebars/safe-string.js +var __module3__ = (function() { + "use strict"; + var __exports__; + // Build out our basic SafeString type + function SafeString(string) { + this.string = string; } - if(context && typeof context === 'object') { - if(context instanceof Array){ - for(var j = context.length; i<j; i++) { - if (data) { data.index = i; } - ret = ret + fn(context[i], { data: data }); - } - } else { - for(var key in context) { - if(context.hasOwnProperty(key)) { - if(data) { data.key = key; } - ret = ret + fn(context[key], {data: data}); - i++; + SafeString.prototype.toString = function() { + return "" + this.string; + }; + + __exports__ = SafeString; + return __exports__; +})(); + +// handlebars/utils.js +var __module2__ = (function(__dependency1__) { + "use strict"; + var __exports__ = {}; + /*jshint -W004 */ + var SafeString = __dependency1__; + + var escape = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; + + var badChars = /[&<>"'`]/g; + var possible = /[&<>"'`]/; + + function escapeChar(chr) { + return escape[chr]; + } + + function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; } } } + + return obj; } - if(i === 0){ - ret = inverse(this); + __exports__.extend = extend;var toString = Object.prototype.toString; + __exports__.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + var isFunction = function(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ + if (isFunction(/x/)) { + isFunction = function(value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; } + var isFunction; + __exports__.isFunction = isFunction; + /* istanbul ignore next */ + var isArray = Array.isArray || function(value) { + return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; + }; + __exports__.isArray = isArray; - return ret; -}); - -Handlebars.registerHelper('if', function(conditional, options) { - var type = toString.call(conditional); - if(type === functionType) { conditional = conditional.call(this); } - - if(!conditional || Handlebars.Utils.isEmpty(conditional)) { - return options.inverse(this); - } else { - return options.fn(this); - } -}); - -Handlebars.registerHelper('unless', function(conditional, options) { - return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn}); -}); - -Handlebars.registerHelper('with', function(context, options) { - var type = toString.call(context); - if(type === functionType) { context = context.call(this); } - - if (!Handlebars.Utils.isEmpty(context)) return options.fn(context); -}); - -Handlebars.registerHelper('log', function(context, options) { - var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1; - Handlebars.log(level, context); -}); -; -// lib/handlebars/utils.js - -var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; - -Handlebars.Exception = function(message) { - var tmp = Error.prototype.constructor.apply(this, arguments); - - // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. - for (var idx = 0; idx < errorProps.length; idx++) { - this[errorProps[idx]] = tmp[errorProps[idx]]; - } -}; -Handlebars.Exception.prototype = new Error(); - -// Build out our basic SafeString type -Handlebars.SafeString = function(string) { - this.string = string; -}; -Handlebars.SafeString.prototype.toString = function() { - return this.string.toString(); -}; - -var escape = { - "&": "&", - "<": "<", - ">": ">", - '"': """, - "'": "'", - "`": "`" -}; - -var badChars = /[&<>"'`]/g; -var possible = /[&<>"'`]/; - -var escapeChar = function(chr) { - return escape[chr] || "&"; -}; - -Handlebars.Utils = { - extend: function(obj, value) { - for(var key in value) { - if(value.hasOwnProperty(key)) { - obj[key] = value[key]; - } - } - }, - - escapeExpression: function(string) { + function escapeExpression(string) { // don't escape SafeStrings, since they're already safe - if (string instanceof Handlebars.SafeString) { + if (string instanceof SafeString) { return string.toString(); - } else if (string == null || string === false) { + } else if (string == null) { return ""; + } else if (!string) { + return string + ''; } // Force a string conversion as this will be done by the append regardless and // the regex test will do this transparently behind the scenes, causing issues if // an object's to string has escaped characters in it. - string = string.toString(); + string = "" + string; if(!possible.test(string)) { return string; } return string.replace(badChars, escapeChar); - }, + } - isEmpty: function(value) { + __exports__.escapeExpression = escapeExpression;function isEmpty(value) { if (!value && value !== 0) { return true; - } else if(toString.call(value) === "[object Array]" && value.length === 0) { + } else if (isArray(value) && value.length === 0) { return true; } else { return false; } } -}; -; -// lib/handlebars/runtime.js -Handlebars.VM = { - template: function(templateSpec) { + __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + + __exports__.appendContextPath = appendContextPath; + return __exports__; +})(__module3__); + +// handlebars/exception.js +var __module4__ = (function() { + "use strict"; + var __exports__; + + var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + + function Exception(message, node) { + var line; + if (node && node.firstLine) { + line = node.firstLine; + + message += ' - ' + line + ':' + node.firstColumn; + } + + var tmp = Error.prototype.constructor.call(this, message); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + + if (line) { + this.lineNumber = line; + this.column = node.firstColumn; + } + } + + Exception.prototype = new Error(); + + __exports__ = Exception; + return __exports__; +})(); + +// handlebars/base.js +var __module1__ = (function(__dependency1__, __dependency2__) { + "use strict"; + var __exports__ = {}; + var Utils = __dependency1__; + var Exception = __dependency2__; + + var VERSION = "2.0.0"; + __exports__.VERSION = VERSION;var COMPILER_REVISION = 6; + __exports__.COMPILER_REVISION = COMPILER_REVISION; + var REVISION_CHANGES = { + 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it + 2: '== 1.0.0-rc.3', + 3: '== 1.0.0-rc.4', + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1' + }; + __exports__.REVISION_CHANGES = REVISION_CHANGES; + var isArray = Utils.isArray, + isFunction = Utils.isFunction, + toString = Utils.toString, + objectType = '[object Object]'; + + function HandlebarsEnvironment(helpers, partials) { + this.helpers = helpers || {}; + this.partials = partials || {}; + + registerDefaultHelpers(this); + } + + __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { + constructor: HandlebarsEnvironment, + + logger: logger, + log: log, + + registerHelper: function(name, fn) { + if (toString.call(name) === objectType) { + if (fn) { throw new Exception('Arg not supported with multiple helpers'); } + Utils.extend(this.helpers, name); + } else { + this.helpers[name] = fn; + } + }, + unregisterHelper: function(name) { + delete this.helpers[name]; + }, + + registerPartial: function(name, partial) { + if (toString.call(name) === objectType) { + Utils.extend(this.partials, name); + } else { + this.partials[name] = partial; + } + }, + unregisterPartial: function(name) { + delete this.partials[name]; + } + }; + + function registerDefaultHelpers(instance) { + instance.registerHelper('helperMissing', function(/* [args, ]options */) { + if(arguments.length === 1) { + // A missing field in a {{foo}} constuct. + return undefined; + } else { + // Someone is actually trying to call something, blow up. + throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'"); + } + }); + + instance.registerHelper('blockHelperMissing', function(context, options) { + var inverse = options.inverse, + fn = options.fn; + + if(context === true) { + return fn(this); + } else if(context === false || context == null) { + return inverse(this); + } else if (isArray(context)) { + if(context.length > 0) { + if (options.ids) { + options.ids = [options.name]; + } + + return instance.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + if (options.data && options.ids) { + var data = createFrame(options.data); + data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name); + options = {data: data}; + } + + return fn(context, options); + } + }); + + instance.registerHelper('each', function(context, options) { + if (!options) { + throw new Exception('Must pass iterator to #each'); + } + + var fn = options.fn, inverse = options.inverse; + var i = 0, ret = "", data; + + var contextPath; + if (options.data && options.ids) { + contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } + + if (isFunction(context)) { context = context.call(this); } + + if (options.data) { + data = createFrame(options.data); + } + + if(context && typeof context === 'object') { + if (isArray(context)) { + for(var j = context.length; i<j; i++) { + if (data) { + data.index = i; + data.first = (i === 0); + data.last = (i === (context.length-1)); + + if (contextPath) { + data.contextPath = contextPath + i; + } + } + ret = ret + fn(context[i], { data: data }); + } + } else { + for(var key in context) { + if(context.hasOwnProperty(key)) { + if(data) { + data.key = key; + data.index = i; + data.first = (i === 0); + + if (contextPath) { + data.contextPath = contextPath + key; + } + } + ret = ret + fn(context[key], {data: data}); + i++; + } + } + } + } + + if(i === 0){ + ret = inverse(this); + } + + return ret; + }); + + instance.registerHelper('if', function(conditional, options) { + if (isFunction(conditional)) { conditional = conditional.call(this); } + + // Default behavior is to render the positive path if the value is truthy and not empty. + // The `includeZero` option may be set to treat the condtional as purely not empty based on the + // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. + if ((!options.hash.includeZero && !conditional) || Utils.isEmpty(conditional)) { + return options.inverse(this); + } else { + return options.fn(this); + } + }); + + instance.registerHelper('unless', function(conditional, options) { + return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash}); + }); + + instance.registerHelper('with', function(context, options) { + if (isFunction(context)) { context = context.call(this); } + + var fn = options.fn; + + if (!Utils.isEmpty(context)) { + if (options.data && options.ids) { + var data = createFrame(options.data); + data.contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]); + options = {data:data}; + } + + return fn(context, options); + } else { + return options.inverse(this); + } + }); + + instance.registerHelper('log', function(message, options) { + var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1; + instance.log(level, message); + }); + + instance.registerHelper('lookup', function(obj, field) { + return obj && obj[field]; + }); + } + + var logger = { + methodMap: { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' }, + + // State enum + DEBUG: 0, + INFO: 1, + WARN: 2, + ERROR: 3, + level: 3, + + // can be overridden in the host environment + log: function(level, message) { + if (logger.level <= level) { + var method = logger.methodMap[level]; + if (typeof console !== 'undefined' && console[method]) { + console[method].call(console, message); + } + } + } + }; + __exports__.logger = logger; + var log = logger.log; + __exports__.log = log; + var createFrame = function(object) { + var frame = Utils.extend({}, object); + frame._parent = object; + return frame; + }; + __exports__.createFrame = createFrame; + return __exports__; +})(__module2__, __module4__); + +// handlebars/runtime.js +var __module5__ = (function(__dependency1__, __dependency2__, __dependency3__) { + "use strict"; + var __exports__ = {}; + var Utils = __dependency1__; + var Exception = __dependency2__; + var COMPILER_REVISION = __dependency3__.COMPILER_REVISION; + var REVISION_CHANGES = __dependency3__.REVISION_CHANGES; + var createFrame = __dependency3__.createFrame; + + function checkRevision(compilerInfo) { + var compilerRevision = compilerInfo && compilerInfo[0] || 1, + currentRevision = COMPILER_REVISION; + + if (compilerRevision !== currentRevision) { + if (compilerRevision < currentRevision) { + var runtimeVersions = REVISION_CHANGES[currentRevision], + compilerVersions = REVISION_CHANGES[compilerRevision]; + throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+ + "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+")."); + } else { + // Use the embedded version info since the runtime doesn't know about this revision yet + throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+ + "Please update your runtime to a newer version ("+compilerInfo[1]+")."); + } + } + } + + __exports__.checkRevision = checkRevision;// TODO: Remove this line and break up compilePartial + + function template(templateSpec, env) { + /* istanbul ignore next */ + if (!env) { + throw new Exception("No environment passed to template"); + } + if (!templateSpec || !templateSpec.main) { + throw new Exception('Unknown template object: ' + typeof templateSpec); + } + + // Note: Using env.VM references rather than local var references throughout this section to allow + // for external users to override these as psuedo-supported APIs. + env.VM.checkRevision(templateSpec.compiler); + + var invokePartialWrapper = function(partial, indent, name, context, hash, helpers, partials, data, depths) { + if (hash) { + context = Utils.extend({}, context, hash); + } + + var result = env.VM.invokePartial.call(this, partial, name, context, helpers, partials, data, depths); + + if (result == null && env.compile) { + var options = { helpers: helpers, partials: partials, data: data, depths: depths }; + partials[name] = env.compile(partial, { data: data !== undefined, compat: templateSpec.compat }, env); + result = partials[name](context, options); + } + if (result != null) { + if (indent) { + var lines = result.split('\n'); + for (var i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = indent + lines[i]; + } + result = lines.join('\n'); + } + return result; + } else { + throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); + } + }; + // Just add water var container = { - escapeExpression: Handlebars.Utils.escapeExpression, - invokePartial: Handlebars.VM.invokePartial, + lookup: function(depths, name) { + var len = depths.length; + for (var i = 0; i < len; i++) { + if (depths[i] && depths[i][name] != null) { + return depths[i][name]; + } + } + }, + lambda: function(current, context) { + return typeof current === 'function' ? current.call(context) : current; + }, + + escapeExpression: Utils.escapeExpression, + invokePartial: invokePartialWrapper, + + fn: function(i) { + return templateSpec[i]; + }, + programs: [], - program: function(i, fn, data) { - var programWrapper = this.programs[i]; - if(data) { - programWrapper = Handlebars.VM.program(i, fn, data); + program: function(i, data, depths) { + var programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths) { + programWrapper = program(this, i, fn, data, depths); } else if (!programWrapper) { - programWrapper = this.programs[i] = Handlebars.VM.program(i, fn); + programWrapper = this.programs[i] = program(this, i, fn); } return programWrapper; }, + + data: function(data, depth) { + while (data && depth--) { + data = data._parent; + } + return data; + }, merge: function(param, common) { var ret = param || common; - if (param && common) { - ret = {}; - Handlebars.Utils.extend(ret, common); - Handlebars.Utils.extend(ret, param); + if (param && common && (param !== common)) { + ret = Utils.extend({}, common, param); } + return ret; }, - programWithDepth: Handlebars.VM.programWithDepth, - noop: Handlebars.VM.noop, - compilerInfo: null + + noop: env.VM.noop, + compilerInfo: templateSpec.compiler }; - return function(context, options) { + var ret = function(context, options) { options = options || {}; - var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data); + var data = options.data; - var compilerInfo = container.compilerInfo || [], - compilerRevision = compilerInfo[0] || 1, - currentRevision = Handlebars.COMPILER_REVISION; - - if (compilerRevision !== currentRevision) { - if (compilerRevision < currentRevision) { - var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision], - compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision]; - throw "Template was precompiled with an older version of Handlebars than the current runtime. "+ - "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+")."; - } else { - // Use the embedded version info since the runtime doesn't know about this revision yet - throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+ - "Please update your runtime to a newer version ("+compilerInfo[1]+")."; - } + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); + } + var depths; + if (templateSpec.useDepths) { + depths = options.depths ? [context].concat(options.depths) : [context]; } - return result; + return templateSpec.main.call(container, context, container.helpers, container.partials, data, depths); }; - }, + ret.isTop = true; - programWithDepth: function(i, fn, data /*, $depth */) { - var args = Array.prototype.slice.call(arguments, 3); + ret._setup = function(options) { + if (!options.partial) { + container.helpers = container.merge(options.helpers, env.helpers); - var program = function(context, options) { + if (templateSpec.usePartial) { + container.partials = container.merge(options.partials, env.partials); + } + } else { + container.helpers = options.helpers; + container.partials = options.partials; + } + }; + + ret._child = function(i, data, depths) { + if (templateSpec.useDepths && !depths) { + throw new Exception('must pass parent depths'); + } + + return program(container, i, templateSpec[i], data, depths); + }; + return ret; + } + + __exports__.template = template;function program(container, i, fn, data, depths) { + var prog = function(context, options) { options = options || {}; - return fn.apply(this, [context, options.data || data].concat(args)); + return fn.call(container, context, container.helpers, container.partials, options.data || data, depths && [context].concat(depths)); }; - program.program = i; - program.depth = args.length; - return program; - }, - program: function(i, fn, data) { - var program = function(context, options) { - options = options || {}; + prog.program = i; + prog.depth = depths ? depths.length : 0; + return prog; + } - return fn(context, options.data || data); - }; - program.program = i; - program.depth = 0; - return program; - }, - noop: function() { return ""; }, - invokePartial: function(partial, name, context, helpers, partials, data) { - var options = { helpers: helpers, partials: partials, data: data }; + __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data, depths) { + var options = { partial: true, helpers: helpers, partials: partials, data: data, depths: depths }; if(partial === undefined) { - throw new Handlebars.Exception("The partial " + name + " could not be found"); + throw new Exception("The partial " + name + " could not be found"); } else if(partial instanceof Function) { return partial(context, options); - } else if (!Handlebars.compile) { - throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); - } else { - partials[name] = Handlebars.compile(partial, {data: data !== undefined}); - return partials[name](context, options); } } -}; -Handlebars.template = Handlebars.VM.template; -; -// lib/handlebars/browser-suffix.js -})(Handlebars); -; + __exports__.invokePartial = invokePartial;function noop() { return ""; } + + __exports__.noop = noop;function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? createFrame(data) : {}; + data.root = context; + } + return data; + } + return __exports__; +})(__module2__, __module4__, __module1__); + +// handlebars.runtime.js +var __module0__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { + "use strict"; + var __exports__; + /*globals Handlebars: true */ + var base = __dependency1__; + + // Each of these augment the Handlebars object. No need to setup here. + // (This is done to easily share code between commonjs and browse envs) + var SafeString = __dependency2__; + var Exception = __dependency3__; + var Utils = __dependency4__; + var runtime = __dependency5__; + + // For compatibility and usage outside of module systems, make the Handlebars object a namespace + var create = function() { + var hb = new base.HandlebarsEnvironment(); + + Utils.extend(hb, base); + hb.SafeString = SafeString; + hb.Exception = Exception; + hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; + + hb.VM = runtime; + hb.template = function(spec) { + return runtime.template(spec, hb); + }; + + return hb; + }; + + var Handlebars = create(); + Handlebars.create = create; + + Handlebars['default'] = Handlebars; + + __exports__ = Handlebars; + return __exports__; +})(__module1__, __module3__, __module4__, __module2__, __module5__); + + return __module0__; +})); diff -r bee9255a428c5fcda5b3d3b03a7a3f38cdf5d7ff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d client/galaxy/scripts/libs/jquery/jquery.js --- a/client/galaxy/scripts/libs/jquery/jquery.js +++ b/client/galaxy/scripts/libs/jquery/jquery.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.11.1 + * jQuery JavaScript Library v1.11.2 * http://jquery.com/ * * Includes Sizzle.js @@ -9,7 +9,7 @@ * Released under the MIT license * http://jquery.org/license * - * Date: 2014-05-01T17:42Z + * Date: 2014-12-17T15:27Z */ (function( global, factory ) { @@ -64,7 +64,7 @@ var - version = "1.11.1", + version = "1.11.2", // Define a local copy of jQuery jQuery = function( selector, context ) { @@ -269,7 +269,8 @@ // parseFloat NaNs numeric-cast false positives (null|true|false|"") // ...but misinterprets leading-number strings, particularly hex literals ("0x...") // subtraction forces infinities to NaN - return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; + // adding 1 corrects loss of precision from parseFloat (#15100) + return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; }, isEmptyObject: function( obj ) { @@ -584,14 +585,14 @@ } var Sizzle = /*! - * Sizzle CSS Selector Engine v1.10.19 + * Sizzle CSS Selector Engine v2.2.0-pre * http://sizzlejs.com/ * - * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2014-04-18 + * Date: 2014-12-16 */ (function( window ) { @@ -618,7 +619,7 @@ contains, // Instance-specific data - expando = "sizzle" + -(new Date()), + expando = "sizzle" + 1 * new Date(), preferredDoc = window.document, dirruns = 0, done = 0, @@ -633,7 +634,6 @@ }, // General-purpose constants - strundefined = typeof undefined, MAX_NEGATIVE = 1 << 31, // Instance methods @@ -643,12 +643,13 @@ push_native = arr.push, push = arr.push, slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function( elem ) { + // Use a stripped-down indexOf as it's faster than native + // http://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { var i = 0, - len = this.length; + len = list.length; for ( ; i < len; i++ ) { - if ( this[i] === elem ) { + if ( list[i] === elem ) { return i; } } @@ -688,6 +689,7 @@ ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), @@ -739,6 +741,14 @@ String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); }; // Optimize for push.apply( _, NodeList ) @@ -781,19 +791,18 @@ context = context || document; results = results || []; - - if ( !selector || typeof selector !== "string" ) { + nodeType = context.nodeType; + + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + return results; } - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; - } - - if ( documentIsHTML && !seed ) { - - // Shortcuts - if ( (match = rquickExpr.exec( selector )) ) { + if ( !seed && documentIsHTML ) { + + // Try to shortcut find operations when possible (e.g., not under DocumentFragment) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#ID") if ( (m = match[1]) ) { if ( nodeType === 9 ) { @@ -825,7 +834,7 @@ return results; // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + } else if ( (m = match[3]) && support.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } @@ -835,7 +844,7 @@ if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { nid = old = expando; newContext = context; - newSelector = nodeType === 9 && selector; + newSelector = nodeType !== 1 && selector; // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root @@ -1022,7 +1031,7 @@ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext( context ) { - return context && typeof context.getElementsByTagName !== strundefined && context; + return context && typeof context.getElementsByTagName !== "undefined" && context; } // Expose support vars for convenience @@ -1046,9 +1055,8 @@ * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, - doc = node ? node.ownerDocument || node : preferredDoc, - parent = doc.defaultView; + var hasCompare, parent, + doc = node ? node.ownerDocument || node : preferredDoc; // If no document and documentElement is available, return if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { @@ -1058,9 +1066,7 @@ // Set our document document = doc; docElem = doc.documentElement; - - // Support tests - documentIsHTML = !isXML( doc ); + parent = doc.defaultView; // Support: IE>8 // If iframe document is assigned to "document" variable and if iframe has been reloaded, @@ -1069,21 +1075,22 @@ if ( parent && parent !== parent.top ) { // IE11 does not have attachEvent, so all must suffer if ( parent.addEventListener ) { - parent.addEventListener( "unload", function() { - setDocument(); - }, false ); + parent.addEventListener( "unload", unloadHandler, false ); } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", function() { - setDocument(); - }); - } - } + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Support tests + ---------------------------------------------------------------------- */ + documentIsHTML = !isXML( doc ); /* Attributes ---------------------------------------------------------------------- */ // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) support.attributes = assert(function( div ) { div.className = "i"; return !div.getAttribute("className"); @@ -1098,17 +1105,8 @@ return !div.getElementsByTagName("*").length; }); - // Check if getElementsByClassName can be trusted - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { - div.innerHTML = "<div class='a'></div><div class='a i'></div>"; - - // Support: Safari<4 - // Catch class over-caching - div.firstChild.className = "i"; - // Support: Opera<10 - // Catch gEBCN failure to find non-leading classes - return div.getElementsByClassName("i").length === 2; - }); + // Support: IE<9 + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); // Support: IE<10 // Check if getElementById returns elements by name @@ -1122,7 +1120,7 @@ // ID find and filter if ( support.getById ) { Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var m = context.getElementById( id ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 @@ -1143,7 +1141,7 @@ Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; @@ -1152,14 +1150,20 @@ // Tag Expr.find["TAG"] = support.getElementsByTagName ? function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); } } : + function( tag, context ) { var elem, tmp = [], i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too results = context.getElementsByTagName( tag ); // Filter out possible comments @@ -1177,7 +1181,7 @@ // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + if ( documentIsHTML ) { return context.getElementsByClassName( className ); } }; @@ -1206,13 +1210,15 @@ // setting a boolean content attribute, // since its presence should be enough // http://bugs.jquery.com/ticket/12359 - div.innerHTML = "<select msallowclip=''><option selected=''></option></select>"; + docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" + + "<select id='" + expando + "-\f]' msallowcapture=''>" + + "<option selected=''></option></select>"; // Support: IE8, Opera 11-12.16 // Nothing should be selected when empty strings follow ^= or $= or *= // The test attribute must be unknown in Opera but "safe" for WinRT // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( div.querySelectorAll("[msallowclip^='']").length ) { + if ( div.querySelectorAll("[msallowcapture^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } @@ -1222,12 +1228,24 @@ rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } + // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ + if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } }); assert(function( div ) { @@ -1344,7 +1362,7 @@ // Maintain original order return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; } @@ -1371,7 +1389,7 @@ aup ? -1 : bup ? 1 : sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check @@ -1434,7 +1452,7 @@ elem.document && elem.document.nodeType !== 11 ) { return ret; } - } catch(e) {} + } catch (e) {} } return Sizzle( expr, document, null, [ elem ] ).length > 0; @@ -1653,7 +1671,7 @@ return pattern || (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); }); }, @@ -1675,7 +1693,7 @@ operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; }; @@ -1795,7 +1813,7 @@ matched = fn( seed, argument ), i = matched.length; while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); + idx = indexOf( seed, matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : @@ -1834,6 +1852,8 @@ function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; return !results.pop(); }; }), @@ -1845,6 +1865,7 @@ }), "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; @@ -2266,7 +2287,7 @@ i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } @@ -2301,13 +2322,16 @@ return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; + return indexOf( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; } ]; for ( ; i < len; i++ ) { @@ -2557,7 +2581,7 @@ // Sort stability support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; -// Support: Chrome<14 +// Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function support.detectDuplicates = !!hasDuplicate; @@ -6115,7 +6139,14 @@ if ( window.getComputedStyle ) { getStyles = function( elem ) { - return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); + // Support: IE<=11+, Firefox<=30+ (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + if ( elem.ownerDocument.defaultView.opener ) { + return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); + } + + return window.getComputedStyle( elem, null ); }; curCSS = function( elem, name, computed ) { @@ -6363,6 +6394,8 @@ reliableMarginRightVal = !parseFloat( ( window.getComputedStyle( contents, null ) || {} ).marginRight ); + + div.removeChild( contents ); } // Support: IE8 @@ -9070,7 +9103,8 @@ } // We can fire global events as of now if asked to - fireGlobals = s.global; + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; // Watch for a new set of requests if ( fireGlobals && jQuery.active++ === 0 ) { @@ -9329,13 +9363,6 @@ }; }); -// Attach a bunch of functions for handling common AJAX events -jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) { - jQuery.fn[ type ] = function( fn ) { - return this.on( type, fn ); - }; -}); - jQuery._evalUrl = function( url ) { return jQuery.ajax({ @@ -9561,8 +9588,9 @@ // Support: IE<10 // Open requests must be manually aborted on unload (#5280) -if ( window.ActiveXObject ) { - jQuery( window ).on( "unload", function() { +// See https://support.microsoft.com/kb/2856746 for more info +if ( window.attachEvent ) { + window.attachEvent( "onunload", function() { for ( var key in xhrCallbacks ) { xhrCallbacks[ key ]( undefined, true ); } @@ -9996,6 +10024,16 @@ +// Attach a bunch of functions for handling common AJAX events +jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) { + jQuery.fn[ type ] = function( fn ) { + return this.on( type, fn ); + }; +}); + + + + jQuery.expr.filters.animated = function( elem ) { return jQuery.grep(jQuery.timers, function( fn ) { return elem === fn.elem; This diff is so big that we needed to truncate the remainder. https://bitbucket.org/galaxy/galaxy-central/commits/35ddbdd32fb3/ Changeset: 35ddbdd32fb3 User: erasche2 Date: 2015-02-05 23:37:10+00:00 Summary: Fixes for allowing custom entry Affected #: 1 file diff -r cf2636a3d9c3e29a4b045b4112de14f388e2168d -r 35ddbdd32fb3f36aadb7de52721f5ad66b753b8e templates/webapps/galaxy/history/share.mako --- a/templates/webapps/galaxy/history/share.mako +++ b/templates/webapps/galaxy/history/share.mako @@ -59,38 +59,85 @@ /* This should be ripped out and made generic at some point for the * various API bindings available, and once the API can filter list * queries (term, below) */ + + var user_id = "${trans.security.encode_id(trans.user.id)}"; + var history_id = "${trans.security.encode_id( history.id )}"; + + function item_to_label(item){ + var text = ""; + if(typeof(item.username) === "string" && typeof(item.email) === "string"){ + text = item.username + " <" + item.email + ">"; + }else if(typeof(item.username) === "string"){ + text = item.username; + }else{ + text = item.email; + } + return text; + //return "id:" + item.id + "|e:" + item.email + "|u:" + item.username; + } + $("#email_select").select2({ placeholder: "Select a user", + multiple: true, + initSelection: function(element, callback) { + var data = [ + // Must be here to loop across the users that this has been shared with. + %for i, association in enumerate( history.users_shared_with ): + <% shared_with = association.user %> + { + email: "${ shared_with.email }", + id: "${trans.security.encode_id(shared_with.id)}", + text: item_to_label({"email": "${ shared_with.email }", "username": "${ shared_with.username }" }) + }, + %endfor + ]; + callback(data); + }, + tokenSeparators: [',', ' '], + // Required for initSelection + id: function(object) { + return object.id; + }, ajax: { url: "${h.url_for(controller="/api/users", action="index")}", - dataType: 'json', - quietMillis: 250, - matcher: function(term, text) { return text.toUpperCase().indexOf(term.toUpperCase())>=0; }, data: function (term) { return { f_email: term }; }, + dataType: 'json', + quietMillis: 250, results: function (data) { - var results = []; - $.each(data, function(index, item){ - var text = ""; - if(typeof(item.username) === "string" && typeof(item.email) === "string"){ - text = item.username + " <" + item.email + ">"; - }else if(typeof(item.username) === "string"){ - text = item.username; - }else{ - text = item.email; + var results = []; + // For every user returned by the API call, + $.each(data, function(index, item){ + // If they aren't the requesting user, add to the + // list that will populate the select + if(item.id != "${trans.security.encode_id(trans.user.id)}"){ + results.push({ + id: item.id, + name: item.username, + text: item_to_label(item), + }); } - results.push({ - id: item.email, - name: item.username, - text: text - }); - }); - return { - results: results - }; + }); + return { + results: results + }; + } + }, + createSearchChoice: function(term, data) { + // Check for a user with a matching email. + var matches = _.filter(data, function(user){ + return user.text.indexOf(term) > -1; + }); + // If there aren't any users with matching object labels, then + // display a "default" entry with whatever text they're entering. + // id is set to term as that will be used in + if(matches.length == 0){ + return {id: term, text:term}; + }else{ + // No extra needed } } }); https://bitbucket.org/galaxy/galaxy-central/commits/d45c3a08dc0c/ Changeset: d45c3a08dc0c User: erasche2 Date: 2015-02-09 21:17:40+00:00 Summary: Use suggestion from Carl to fix initial selection Affected #: 1 file diff -r 35ddbdd32fb3f36aadb7de52721f5ad66b753b8e -r d45c3a08dc0c3d4eb0cc3d88160253f4213bd1d4 templates/webapps/galaxy/history/share.mako --- a/templates/webapps/galaxy/history/share.mako +++ b/templates/webapps/galaxy/history/share.mako @@ -36,8 +36,9 @@ </div><div style="clear: both"></div><div class="form-row"> + <% existing_emails = [ d.user.email for d in history.users_shared_with ] %><label>Galaxy user emails with which to share histories</label> - <input type="hidden" id="email_select" name="email" style="float: left; width: 250px; margin-right: 10px;"> + <input type="hidden" id="email_select" name="email" value="${ existing_emails }" style="float: left; width: 250px; margin-right: 10px;"></input><div class="toolParamHelp" style="clear: both;"> Enter a Galaxy user email address or a comma-separated list of addresses if sharing with multiple users https://bitbucket.org/galaxy/galaxy-central/commits/153887a934e6/ Changeset: 153887a934e6 User: erasche2 Date: 2015-02-12 20:32:10+00:00 Summary: Works correctly now Affected #: 1 file diff -r d45c3a08dc0c3d4eb0cc3d88160253f4213bd1d4 -r 153887a934e6ffa1c79b01ede9850126b2db2a76 templates/webapps/galaxy/history/share.mako --- a/templates/webapps/galaxy/history/share.mako +++ b/templates/webapps/galaxy/history/share.mako @@ -36,7 +36,7 @@ </div><div style="clear: both"></div><div class="form-row"> - <% existing_emails = [ d.user.email for d in history.users_shared_with ] %> + <% existing_emails = ','.join([ d.user.email for d in history.users_shared_with ]) %><label>Galaxy user emails with which to share histories</label><input type="hidden" id="email_select" name="email" value="${ existing_emails }" style="float: left; width: 250px; margin-right: 10px;"></input> https://bitbucket.org/galaxy/galaxy-central/commits/8519e2f351f8/ Changeset: 8519e2f351f8 User: dannon Date: 2015-02-17 14:46:09+00:00 Summary: Merged in erasche2/galaxy-central (pull request #570) Enable user/email list for sharing of datasets Affected #: 5 files diff -r 2e3855e25b9bdbe6e574a1795fe3dd4334e85eb7 -r 8519e2f351f82229ccec63f5b33ff719b8837fd5 config/galaxy.ini.sample --- a/config/galaxy.ini.sample +++ b/config/galaxy.ini.sample @@ -786,6 +786,14 @@ # public. #new_user_dataset_access_role_default_private = False +# Expose user list. Setting this to true will expose the user list to authenticated users. This +# makes sharing datasets in smaller galaxy instances much easier as they can type a name/email and +# have the correct user show up. This makes less sense on large public galaxy instances where +# that data shouldn't be exposed. For semi-public galaxies, it may make sense to expose just the +# username and not email, or vice versa. +#expose_user_name = False +#expose_user_email = False + # -- Beta features # Use new tool form diff -r 2e3855e25b9bdbe6e574a1795fe3dd4334e85eb7 -r 8519e2f351f82229ccec63f5b33ff719b8837fd5 lib/galaxy/config.py --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -106,6 +106,9 @@ self.user_label_filters = listify( kwargs.get( "user_tool_label_filters", [] ), do_strip=True ) self.user_section_filters = listify( kwargs.get( "user_tool_section_filters", [] ), do_strip=True ) + self.expose_user_name = kwargs.get( "expose_user_name", False ) + self.expose_user_email = kwargs.get( "expose_user_email", False ) + # Check for tools defined in the above non-shed tool configs (i.e., tool_conf.xml) tht have # been migrated from the Galaxy code distribution to the Tool Shed. self.check_migrate_tools = string_as_bool( kwargs.get( 'check_migrate_tools', True ) ) diff -r 2e3855e25b9bdbe6e574a1795fe3dd4334e85eb7 -r 8519e2f351f82229ccec63f5b33ff719b8837fd5 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -131,7 +131,7 @@ histories, credentials, and roles. """ # attributes that will be accessed and returned when calling to_dict( view='collection' ) - dict_collection_visible_keys = ( 'id', 'email' ) + dict_collection_visible_keys = ( 'id', 'email', 'username' ) # attributes that will be accessed and returned when calling to_dict( view='element' ) dict_element_visible_keys = ( 'id', 'email', 'username', 'total_disk_usage', 'nice_total_disk_usage' ) diff -r 2e3855e25b9bdbe6e574a1795fe3dd4334e85eb7 -r 8519e2f351f82229ccec63f5b33ff719b8837fd5 lib/galaxy/webapps/galaxy/api/users.py --- a/lib/galaxy/webapps/galaxy/api/users.py +++ b/lib/galaxy/webapps/galaxy/api/users.py @@ -43,11 +43,19 @@ else: query = query.filter( trans.app.model.User.table.c.deleted == False ) # noqa # special case: user can see only their own user - if not trans.user_is_admin(): + # special case2: if the galaxy admin has specified that other user email/names are + # exposed, we don't want special case #1 + if not trans.user_is_admin() and not trans.app.config.expose_user_name and not trans.app.config.expose_user_email: item = trans.user.to_dict( value_mapper={ 'id': trans.security.encode_id } ) return [item] for user in query: item = user.to_dict( value_mapper={ 'id': trans.security.encode_id } ) + # If NOT configured to expose_email, do not expose email UNLESS the user is self, or + # the user is an admin + if not trans.app.config.expose_user_name and user is not trans.user and not trans.user_is_admin(): + del item['username'] + if not trans.app.config.expose_user_email and user is not trans.user and not trans.user_is_admin(): + del item['email'] # TODO: move into api_values rval.append( item ) return rval diff -r 2e3855e25b9bdbe6e574a1795fe3dd4334e85eb7 -r 8519e2f351f82229ccec63f5b33ff719b8837fd5 templates/webapps/galaxy/history/share.mako --- a/templates/webapps/galaxy/history/share.mako +++ b/templates/webapps/galaxy/history/share.mako @@ -36,10 +36,10 @@ </div><div style="clear: both"></div><div class="form-row"> + <% existing_emails = ','.join([ d.user.email for d in history.users_shared_with ]) %><label>Galaxy user emails with which to share histories</label> - <div style="float: left; width: 250px; margin-right: 10px;"> - <input type="text" name="email" value="${email}" size="40"> - </div> + <input type="hidden" id="email_select" name="email" value="${ existing_emails }" style="float: left; width: 250px; margin-right: 10px;"> + </input><div class="toolParamHelp" style="clear: both;"> Enter a Galaxy user email address or a comma-separated list of addresses if sharing with multiple users </div> @@ -55,6 +55,94 @@ <input type="submit" name="share_button" value="Submit"></div></form> + <script type="text/javascript"> + // stolen from templates/admin/impersonate.mako + /* This should be ripped out and made generic at some point for the + * various API bindings available, and once the API can filter list + * queries (term, below) */ + + var user_id = "${trans.security.encode_id(trans.user.id)}"; + var history_id = "${trans.security.encode_id( history.id )}"; + + function item_to_label(item){ + var text = ""; + if(typeof(item.username) === "string" && typeof(item.email) === "string"){ + text = item.username + " <" + item.email + ">"; + }else if(typeof(item.username) === "string"){ + text = item.username; + }else{ + text = item.email; + } + return text; + //return "id:" + item.id + "|e:" + item.email + "|u:" + item.username; + } + + $("#email_select").select2({ + placeholder: "Select a user", + multiple: true, + initSelection: function(element, callback) { + var data = [ + // Must be here to loop across the users that this has been shared with. + %for i, association in enumerate( history.users_shared_with ): + <% shared_with = association.user %> + { + email: "${ shared_with.email }", + id: "${trans.security.encode_id(shared_with.id)}", + text: item_to_label({"email": "${ shared_with.email }", "username": "${ shared_with.username }" }) + }, + %endfor + ]; + callback(data); + }, + tokenSeparators: [',', ' '], + // Required for initSelection + id: function(object) { + return object.id; + }, + ajax: { + url: "${h.url_for(controller="/api/users", action="index")}", + data: function (term) { + return { + f_email: term + }; + }, + dataType: 'json', + quietMillis: 250, + results: function (data) { + var results = []; + // For every user returned by the API call, + $.each(data, function(index, item){ + // If they aren't the requesting user, add to the + // list that will populate the select + if(item.id != "${trans.security.encode_id(trans.user.id)}"){ + results.push({ + id: item.id, + name: item.username, + text: item_to_label(item), + }); + } + }); + return { + results: results + }; + } + }, + createSearchChoice: function(term, data) { + // Check for a user with a matching email. + var matches = _.filter(data, function(user){ + return user.text.indexOf(term) > -1; + }); + // If there aren't any users with matching object labels, then + // display a "default" entry with whatever text they're entering. + // id is set to term as that will be used in + if(matches.length == 0){ + return {id: term, text:term}; + }else{ + // No extra needed + } + } + }); + </script> %else: ## We are sharing restricted histories %if no_change_needed or can_change: Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.